1 //========================================================================
2 //
3 // MarkedContentOutputDev.cc
4 //
5 // This file is licensed under the GPLv2 or later
6 //
7 // Copyright 2013 Igalia S.L.
8 //
9 //========================================================================
10 
11 #include "MarkedContentOutputDev.h"
12 #include "GlobalParams.h"
13 #include "UnicodeMap.h"
14 #include "GfxState.h"
15 #include "GfxFont.h"
16 #include "Annot.h"
17 #include <vector>
18 
19 
MarkedContentOutputDev(int mcidA)20 MarkedContentOutputDev::MarkedContentOutputDev(int mcidA):
21   currentFont(NULL),
22   currentText(NULL),
23   mcid(mcidA),
24   pageWidth(0.0),
25   pageHeight(0.0),
26   unicodeMap(NULL)
27 {
28   currentColor.r = currentColor.g = currentColor.b = 0;
29 }
30 
31 
~MarkedContentOutputDev()32 MarkedContentOutputDev::~MarkedContentOutputDev()
33 {
34   if (unicodeMap)
35     unicodeMap->decRefCnt();
36   if (currentFont)
37     currentFont->decRefCnt();
38   delete currentText;
39 }
40 
41 
endSpan()42 void MarkedContentOutputDev::endSpan()
43 {
44   if (currentText && currentText->getLength()) {
45     // The TextSpan takes ownership of currentText and
46     // increases the reference count for currentFont.
47     textSpans.push_back(TextSpan(currentText,
48                                  currentFont,
49                                  currentColor));
50   }
51   currentText = NULL;
52 }
53 
54 
startPage(int pageNum,GfxState * state,XRef * xref)55 void MarkedContentOutputDev::startPage(int pageNum, GfxState *state, XRef *xref)
56 {
57   if (state) {
58     pageWidth  = state->getPageWidth();
59     pageHeight = state->getPageHeight();
60   } else {
61     pageWidth = pageHeight = 0.0;
62   }
63 }
64 
65 
endPage()66 void MarkedContentOutputDev::endPage()
67 {
68   pageWidth = pageHeight = 0.0;
69 }
70 
71 
beginMarkedContent(char * name,Dict * properties)72 void MarkedContentOutputDev::beginMarkedContent(char *name, Dict *properties)
73 {
74   int id = -1;
75   if (properties)
76     properties->lookupInt("MCID", NULL, &id);
77 
78   if (id == -1)
79     return;
80 
81   // The stack keep track of MCIDs of nested marked content.
82   if (inMarkedContent() || id == mcid)
83     mcidStack.push_back(id);
84 }
85 
86 
endMarkedContent(GfxState * state)87 void MarkedContentOutputDev::endMarkedContent(GfxState *state)
88 {
89   if (inMarkedContent()) {
90       mcidStack.pop_back();
91       // The outer marked content sequence MCID was popped, ensure
92       // that the last piece of text collected ends up in a TextSpan.
93       if (!inMarkedContent())
94         endSpan();
95   }
96 }
97 
98 
needFontChange(GfxFont * font) const99 bool MarkedContentOutputDev::needFontChange(GfxFont* font) const
100 {
101   if (currentFont == font)
102     return gFalse;
103 
104   if (!currentFont)
105     return font != NULL && font->isOk();
106 
107   if (font == NULL)
108     return gTrue;
109 
110   // Two non-null valid fonts are the same if they point to the same Ref
111   if (currentFont->getID()->num == font->getID()->num &&
112       currentFont->getID()->gen == font->getID()->gen)
113     return gFalse;
114 
115   return gTrue;
116 }
117 
118 
drawChar(GfxState * state,double xx,double yy,double dx,double dy,double ox,double oy,CharCode c,int nBytes,Unicode * u,int uLen)119 void MarkedContentOutputDev::drawChar(GfxState *state,
120                                       double xx, double yy,
121                                       double dx, double dy,
122                                       double ox, double oy,
123                                       CharCode c, int nBytes,
124                                       Unicode *u, int uLen)
125 {
126   if (!inMarkedContent() || !uLen)
127     return;
128 
129 
130   // Color changes are tracked here so the color can be chosen depending on
131   // the render mode (for mode 1 stroke color is used), so there is no need
132   // to implement both updateFillColor() and updateStrokeColor().
133   GBool colorChange = gFalse;
134   GfxRGB color;
135   if ((state->getRender() & 3) == 1)
136     state->getStrokeRGB(&color);
137   else
138     state->getFillRGB(&color);
139 
140   colorChange = (color.r != currentColor.r ||
141                  color.g != currentColor.g ||
142                  color.b != currentColor.b);
143 
144   // Check also for font changes.
145   GBool fontChange = needFontChange(state->getFont());
146 
147   // Save a span with the current changes.
148   if (colorChange || fontChange) {
149     endSpan();
150   }
151 
152   // Perform the color/font changes.
153   if (colorChange)
154     currentColor = color;
155 
156   if (fontChange) {
157     if (currentFont != NULL) {
158       currentFont->decRefCnt();
159       currentFont = NULL;
160     }
161     if (state->getFont() != NULL) {
162       currentFont = state->getFont();
163       currentFont->incRefCnt();
164     }
165   }
166 
167 
168   double sp, dx2, dy2, w1, h1, x1, y1;
169 
170   // Subtract char and word spacing from the (dx,dy) values
171   sp = state->getCharSpace();
172   if (c == (CharCode) 0x20)
173     sp += state->getWordSpace();
174   state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2);
175   dx -= dx2;
176   dy -= dy2;
177   state->transformDelta(dx, dy, &w1, &h1);
178   state->transform(xx, yy, &x1, &y1);
179 
180   // Throw away characters that are not inside the page boundaries.
181   if (x1 + w1 < 0 || x1 > pageWidth || y1 + h1 < 0 || y1 > pageHeight)
182     return;
183 
184   // Make a sanity check on character size. Note: (x != x) <-> isnan(x)
185   if (x1 != x1 || y1 != y1 || w1 != w1 || h1 != h1)
186     return;
187 
188   for (int i = 0; i < uLen; i++) {
189     // Soft hyphen markers are skipped, as they are invisible unless
190     // rendering is done to an actual device and the hyphenation hint
191     // used. MarkedContentOutputDev extracts the *visible* text content.
192     if (u[i] != 0x00AD) {
193       // Add the UTF-8 sequence to the current text span.
194       if (!unicodeMap)
195         unicodeMap = globalParams->getTextEncoding();
196 
197       char buf[8];
198       int n = unicodeMap->mapUnicode(u[i], buf, sizeof(buf));
199       if (n > 0) {
200         if (currentText == NULL)
201           currentText = new GooString();
202         currentText->append(buf, n);
203       }
204     }
205   }
206 }
207 
208 
getTextSpans() const209 const TextSpanArray& MarkedContentOutputDev::getTextSpans() const
210 {
211   return textSpans;
212 }
213