/src/xpdf-4.05/xpdf/Gfx.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //======================================================================== |
2 | | // |
3 | | // Gfx.cc |
4 | | // |
5 | | // Copyright 1996-2016 Glyph & Cog, LLC |
6 | | // |
7 | | //======================================================================== |
8 | | |
9 | | #include <aconf.h> |
10 | | |
11 | | #include <stdlib.h> |
12 | | #include <stdio.h> |
13 | | #include <stddef.h> |
14 | | #include <string.h> |
15 | | #include <math.h> |
16 | | #include "gmem.h" |
17 | | #include "gmempp.h" |
18 | | #include "GString.h" |
19 | | #include "GList.h" |
20 | | #include "Trace.h" |
21 | | #include "GlobalParams.h" |
22 | | #include "CharTypes.h" |
23 | | #include "Object.h" |
24 | | #include "PDFDoc.h" |
25 | | #include "Array.h" |
26 | | #include "Dict.h" |
27 | | #include "Stream.h" |
28 | | #include "Lexer.h" |
29 | | #include "Parser.h" |
30 | | #include "GfxFont.h" |
31 | | #include "GfxState.h" |
32 | | #include "OutputDev.h" |
33 | | #include "Page.h" |
34 | | #include "Annot.h" |
35 | | #include "OptionalContent.h" |
36 | | #include "Error.h" |
37 | | #include "TextString.h" |
38 | | #include "Gfx.h" |
39 | | |
40 | | // the MSVC math.h doesn't define this |
41 | | #ifndef M_PI |
42 | | #define M_PI 3.14159265358979323846 |
43 | | #endif |
44 | | |
45 | | //------------------------------------------------------------------------ |
46 | | // constants |
47 | | //------------------------------------------------------------------------ |
48 | | |
49 | | // Max recursive depth for a function shading fill. |
50 | 0 | #define functionMaxDepth 6 |
51 | | |
52 | | // Max delta allowed in any color component for a function shading fill. |
53 | 0 | #define functionColorDelta (dblToCol(1 / 256.0)) |
54 | | |
55 | | // Number of splits along the t axis for an axial shading fill. |
56 | 0 | #define axialSplits 256 |
57 | | |
58 | | // Max delta allowed in any color component for an axial shading fill. |
59 | 0 | #define axialColorDelta (dblToCol(1 / 256.0)) |
60 | | |
61 | | // Max number of splits along the t axis for a radial shading fill. |
62 | 0 | #define radialMaxSplits 256 |
63 | | |
64 | | // Max delta allowed in any color component for a radial shading fill. |
65 | 0 | #define radialColorDelta (dblToCol(1 / 256.0)) |
66 | | |
67 | | // Max recursive depth for a Gouraud triangle shading fill. |
68 | 0 | #define gouraudMaxDepth 6 |
69 | | |
70 | | // Max delta allowed in any color component for a Gouraud triangle |
71 | | // shading fill. |
72 | 0 | #define gouraudColorDelta (dblToCol(1 / 256.0)) |
73 | | |
74 | | // Max recursive depth for a patch mesh shading fill. |
75 | 0 | #define patchMaxDepth 6 |
76 | | |
77 | | // Max delta allowed in any color component for a patch mesh shading |
78 | | // fill. |
79 | 0 | #define patchColorDelta (dblToCol(1 / 256.0)) |
80 | | |
81 | | // Max errors (undefined operator, wrong number of args) allowed before |
82 | | // giving up on a content stream. |
83 | 0 | #define contentStreamErrorLimit 500 |
84 | | |
85 | | //------------------------------------------------------------------------ |
86 | | // Operator table |
87 | | //------------------------------------------------------------------------ |
88 | | |
89 | | #ifdef _WIN32 // this works around a bug in the VC7 compiler |
90 | | # pragma optimize("",off) |
91 | | #endif |
92 | | |
93 | | Operator Gfx::opTab[] = { |
94 | | {"\"", 3, {tchkNum, tchkNum, tchkString}, |
95 | | &Gfx::opMoveSetShowText}, |
96 | | {"'", 1, {tchkString}, |
97 | | &Gfx::opMoveShowText}, |
98 | | {"B", 0, {tchkNone}, |
99 | | &Gfx::opFillStroke}, |
100 | | {"B*", 0, {tchkNone}, |
101 | | &Gfx::opEOFillStroke}, |
102 | | {"BDC", 2, {tchkName, tchkProps}, |
103 | | &Gfx::opBeginMarkedContent}, |
104 | | {"BI", 0, {tchkNone}, |
105 | | &Gfx::opBeginImage}, |
106 | | {"BMC", 1, {tchkName}, |
107 | | &Gfx::opBeginMarkedContent}, |
108 | | {"BT", 0, {tchkNone}, |
109 | | &Gfx::opBeginText}, |
110 | | {"BX", 0, {tchkNone}, |
111 | | &Gfx::opBeginIgnoreUndef}, |
112 | | {"CS", 1, {tchkName}, |
113 | | &Gfx::opSetStrokeColorSpace}, |
114 | | {"DP", 2, {tchkName, tchkProps}, |
115 | | &Gfx::opMarkPoint}, |
116 | | {"Do", 1, {tchkName}, |
117 | | &Gfx::opXObject}, |
118 | | {"EI", 0, {tchkNone}, |
119 | | &Gfx::opEndImage}, |
120 | | {"EMC", 0, {tchkNone}, |
121 | | &Gfx::opEndMarkedContent}, |
122 | | {"ET", 0, {tchkNone}, |
123 | | &Gfx::opEndText}, |
124 | | {"EX", 0, {tchkNone}, |
125 | | &Gfx::opEndIgnoreUndef}, |
126 | | {"F", 0, {tchkNone}, |
127 | | &Gfx::opFill}, |
128 | | {"G", 1, {tchkNum}, |
129 | | &Gfx::opSetStrokeGray}, |
130 | | {"ID", 0, {tchkNone}, |
131 | | &Gfx::opImageData}, |
132 | | {"J", 1, {tchkInt}, |
133 | | &Gfx::opSetLineCap}, |
134 | | {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, |
135 | | &Gfx::opSetStrokeCMYKColor}, |
136 | | {"M", 1, {tchkNum}, |
137 | | &Gfx::opSetMiterLimit}, |
138 | | {"MP", 1, {tchkName}, |
139 | | &Gfx::opMarkPoint}, |
140 | | {"Q", 0, {tchkNone}, |
141 | | &Gfx::opRestore}, |
142 | | {"RG", 3, {tchkNum, tchkNum, tchkNum}, |
143 | | &Gfx::opSetStrokeRGBColor}, |
144 | | {"S", 0, {tchkNone}, |
145 | | &Gfx::opStroke}, |
146 | | {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum, |
147 | | tchkNum, tchkNum, tchkNum, tchkNum, |
148 | | tchkNum, tchkNum, tchkNum, tchkNum, |
149 | | tchkNum, tchkNum, tchkNum, tchkNum, |
150 | | tchkNum, tchkNum, tchkNum, tchkNum, |
151 | | tchkNum, tchkNum, tchkNum, tchkNum, |
152 | | tchkNum, tchkNum, tchkNum, tchkNum, |
153 | | tchkNum, tchkNum, tchkNum, tchkNum, |
154 | | tchkNum}, |
155 | | &Gfx::opSetStrokeColor}, |
156 | | {"SCN", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
157 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
158 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
159 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
160 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
161 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
162 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
163 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
164 | | tchkSCN}, |
165 | | &Gfx::opSetStrokeColorN}, |
166 | | {"T*", 0, {tchkNone}, |
167 | | &Gfx::opTextNextLine}, |
168 | | {"TD", 2, {tchkNum, tchkNum}, |
169 | | &Gfx::opTextMoveSet}, |
170 | | {"TJ", 1, {tchkArray}, |
171 | | &Gfx::opShowSpaceText}, |
172 | | {"TL", 1, {tchkNum}, |
173 | | &Gfx::opSetTextLeading}, |
174 | | {"Tc", 1, {tchkNum}, |
175 | | &Gfx::opSetCharSpacing}, |
176 | | {"Td", 2, {tchkNum, tchkNum}, |
177 | | &Gfx::opTextMove}, |
178 | | {"Tf", 2, {tchkName, tchkNum}, |
179 | | &Gfx::opSetFont}, |
180 | | {"Tj", 1, {tchkString}, |
181 | | &Gfx::opShowText}, |
182 | | {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum, |
183 | | tchkNum, tchkNum}, |
184 | | &Gfx::opSetTextMatrix}, |
185 | | {"Tr", 1, {tchkInt}, |
186 | | &Gfx::opSetTextRender}, |
187 | | {"Ts", 1, {tchkNum}, |
188 | | &Gfx::opSetTextRise}, |
189 | | {"Tw", 1, {tchkNum}, |
190 | | &Gfx::opSetWordSpacing}, |
191 | | {"Tz", 1, {tchkNum}, |
192 | | &Gfx::opSetHorizScaling}, |
193 | | {"W", 0, {tchkNone}, |
194 | | &Gfx::opClip}, |
195 | | {"W*", 0, {tchkNone}, |
196 | | &Gfx::opEOClip}, |
197 | | {"b", 0, {tchkNone}, |
198 | | &Gfx::opCloseFillStroke}, |
199 | | {"b*", 0, {tchkNone}, |
200 | | &Gfx::opCloseEOFillStroke}, |
201 | | {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum, |
202 | | tchkNum, tchkNum}, |
203 | | &Gfx::opCurveTo}, |
204 | | {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum, |
205 | | tchkNum, tchkNum}, |
206 | | &Gfx::opConcat}, |
207 | | {"cs", 1, {tchkName}, |
208 | | &Gfx::opSetFillColorSpace}, |
209 | | {"d", 2, {tchkArray, tchkNum}, |
210 | | &Gfx::opSetDash}, |
211 | | {"d0", 2, {tchkNum, tchkNum}, |
212 | | &Gfx::opSetCharWidth}, |
213 | | {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum, |
214 | | tchkNum, tchkNum}, |
215 | | &Gfx::opSetCacheDevice}, |
216 | | {"f", 0, {tchkNone}, |
217 | | &Gfx::opFill}, |
218 | | {"f*", 0, {tchkNone}, |
219 | | &Gfx::opEOFill}, |
220 | | {"g", 1, {tchkNum}, |
221 | | &Gfx::opSetFillGray}, |
222 | | {"gs", 1, {tchkName}, |
223 | | &Gfx::opSetExtGState}, |
224 | | {"h", 0, {tchkNone}, |
225 | | &Gfx::opClosePath}, |
226 | | {"i", 1, {tchkNum}, |
227 | | &Gfx::opSetFlat}, |
228 | | {"j", 1, {tchkInt}, |
229 | | &Gfx::opSetLineJoin}, |
230 | | {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, |
231 | | &Gfx::opSetFillCMYKColor}, |
232 | | {"l", 2, {tchkNum, tchkNum}, |
233 | | &Gfx::opLineTo}, |
234 | | {"m", 2, {tchkNum, tchkNum}, |
235 | | &Gfx::opMoveTo}, |
236 | | {"n", 0, {tchkNone}, |
237 | | &Gfx::opEndPath}, |
238 | | {"q", 0, {tchkNone}, |
239 | | &Gfx::opSave}, |
240 | | {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, |
241 | | &Gfx::opRectangle}, |
242 | | {"rg", 3, {tchkNum, tchkNum, tchkNum}, |
243 | | &Gfx::opSetFillRGBColor}, |
244 | | {"ri", 1, {tchkName}, |
245 | | &Gfx::opSetRenderingIntent}, |
246 | | {"s", 0, {tchkNone}, |
247 | | &Gfx::opCloseStroke}, |
248 | | {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum, |
249 | | tchkNum, tchkNum, tchkNum, tchkNum, |
250 | | tchkNum, tchkNum, tchkNum, tchkNum, |
251 | | tchkNum, tchkNum, tchkNum, tchkNum, |
252 | | tchkNum, tchkNum, tchkNum, tchkNum, |
253 | | tchkNum, tchkNum, tchkNum, tchkNum, |
254 | | tchkNum, tchkNum, tchkNum, tchkNum, |
255 | | tchkNum, tchkNum, tchkNum, tchkNum, |
256 | | tchkNum}, |
257 | | &Gfx::opSetFillColor}, |
258 | | {"scn", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
259 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
260 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
261 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
262 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
263 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
264 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
265 | | tchkSCN, tchkSCN, tchkSCN, tchkSCN, |
266 | | tchkSCN}, |
267 | | &Gfx::opSetFillColorN}, |
268 | | {"sh", 1, {tchkName}, |
269 | | &Gfx::opShFill}, |
270 | | {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, |
271 | | &Gfx::opCurveTo1}, |
272 | | {"w", 1, {tchkNum}, |
273 | | &Gfx::opSetLineWidth}, |
274 | | {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum}, |
275 | | &Gfx::opCurveTo2}, |
276 | | }; |
277 | | |
278 | | #ifdef _WIN32 // this works around a bug in the VC7 compiler |
279 | | # pragma optimize("",on) |
280 | | #endif |
281 | | |
282 | 0 | #define numOps (sizeof(opTab) / sizeof(Operator)) |
283 | | |
284 | | //------------------------------------------------------------------------ |
285 | | // GfxResources |
286 | | //------------------------------------------------------------------------ |
287 | | |
288 | 0 | GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) { |
289 | 0 | Object obj1, obj2; |
290 | 0 | Ref r; |
291 | |
|
292 | 0 | if (resDict) { |
293 | 0 | valid = gTrue; |
294 | | |
295 | | // build font dictionary |
296 | 0 | fonts = NULL; |
297 | 0 | resDict->lookupNF("Font", &obj1); |
298 | 0 | if (obj1.isRef()) { |
299 | 0 | obj1.fetch(xref, &obj2); |
300 | 0 | if (obj2.isDict()) { |
301 | 0 | r = obj1.getRef(); |
302 | 0 | fonts = new GfxFontDict(xref, &r, obj2.getDict()); |
303 | 0 | } |
304 | 0 | obj2.free(); |
305 | 0 | } else if (obj1.isDict()) { |
306 | 0 | fonts = new GfxFontDict(xref, NULL, obj1.getDict()); |
307 | 0 | } |
308 | 0 | obj1.free(); |
309 | | |
310 | | // get XObject dictionary |
311 | 0 | resDict->lookup("XObject", &xObjDict); |
312 | | |
313 | | // get color space dictionary |
314 | 0 | resDict->lookup("ColorSpace", &colorSpaceDict); |
315 | | |
316 | | // get pattern dictionary |
317 | 0 | resDict->lookup("Pattern", &patternDict); |
318 | | |
319 | | // get shading dictionary |
320 | 0 | resDict->lookup("Shading", &shadingDict); |
321 | | |
322 | | // get graphics state parameter dictionary |
323 | 0 | resDict->lookup("ExtGState", &gStateDict); |
324 | | |
325 | | // get properties dictionary |
326 | 0 | resDict->lookup("Properties", &propsDict); |
327 | |
|
328 | 0 | } else { |
329 | 0 | valid = gFalse; |
330 | 0 | fonts = NULL; |
331 | 0 | xObjDict.initNull(); |
332 | 0 | colorSpaceDict.initNull(); |
333 | 0 | patternDict.initNull(); |
334 | 0 | shadingDict.initNull(); |
335 | 0 | gStateDict.initNull(); |
336 | 0 | propsDict.initNull(); |
337 | 0 | } |
338 | |
|
339 | 0 | next = nextA; |
340 | 0 | } |
341 | | |
342 | 0 | GfxResources::~GfxResources() { |
343 | 0 | if (fonts) { |
344 | 0 | delete fonts; |
345 | 0 | } |
346 | 0 | xObjDict.free(); |
347 | 0 | colorSpaceDict.free(); |
348 | 0 | patternDict.free(); |
349 | 0 | shadingDict.free(); |
350 | 0 | gStateDict.free(); |
351 | 0 | propsDict.free(); |
352 | 0 | } |
353 | | |
354 | 0 | GfxFont *GfxResources::lookupFont(char *name) { |
355 | 0 | GfxFont *font; |
356 | 0 | GfxResources *resPtr; |
357 | |
|
358 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
359 | 0 | if (resPtr->fonts) { |
360 | 0 | if ((font = resPtr->fonts->lookup(name))) { |
361 | 0 | return font; |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | 0 | error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name); |
366 | 0 | return NULL; |
367 | 0 | } |
368 | | |
369 | 0 | GfxFont *GfxResources::lookupFontByRef(Ref ref) { |
370 | 0 | GfxFont *font; |
371 | 0 | GfxResources *resPtr; |
372 | |
|
373 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
374 | 0 | if (resPtr->fonts) { |
375 | 0 | if ((font = resPtr->fonts->lookupByRef(ref))) { |
376 | 0 | return font; |
377 | 0 | } |
378 | 0 | } |
379 | 0 | } |
380 | 0 | error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen); |
381 | 0 | return NULL; |
382 | 0 | } |
383 | | |
384 | 0 | GBool GfxResources::lookupXObject(const char *name, Object *obj) { |
385 | 0 | GfxResources *resPtr; |
386 | |
|
387 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
388 | 0 | if (resPtr->xObjDict.isDict()) { |
389 | 0 | if (!resPtr->xObjDict.dictLookup(name, obj)->isNull()) |
390 | 0 | return gTrue; |
391 | 0 | obj->free(); |
392 | 0 | } |
393 | 0 | } |
394 | 0 | error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name); |
395 | 0 | return gFalse; |
396 | 0 | } |
397 | | |
398 | 0 | GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) { |
399 | 0 | GfxResources *resPtr; |
400 | |
|
401 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
402 | 0 | if (resPtr->xObjDict.isDict()) { |
403 | 0 | if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull()) |
404 | 0 | return gTrue; |
405 | 0 | obj->free(); |
406 | 0 | } |
407 | 0 | } |
408 | 0 | error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name); |
409 | 0 | return gFalse; |
410 | 0 | } |
411 | | |
412 | | void GfxResources::lookupColorSpace(const char *name, Object *obj, |
413 | 0 | GBool inherit) { |
414 | 0 | GfxResources *resPtr; |
415 | | |
416 | | //~ should also test for G, RGB, and CMYK - but only in inline images (?) |
417 | 0 | if (!strcmp(name, "DeviceGray") || |
418 | 0 | !strcmp(name, "DeviceRGB") || |
419 | 0 | !strcmp(name, "DeviceCMYK")) { |
420 | 0 | obj->initNull(); |
421 | 0 | return; |
422 | 0 | } |
423 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
424 | 0 | if (resPtr->colorSpaceDict.isDict()) { |
425 | 0 | if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) { |
426 | 0 | return; |
427 | 0 | } |
428 | 0 | obj->free(); |
429 | 0 | } |
430 | 0 | if (!inherit && valid) { |
431 | 0 | break; |
432 | 0 | } |
433 | 0 | } |
434 | 0 | obj->initNull(); |
435 | 0 | } |
436 | | |
437 | | GfxPattern *GfxResources::lookupPattern(const char *name |
438 | 0 | ) { |
439 | 0 | GfxResources *resPtr; |
440 | 0 | GfxPattern *pattern; |
441 | 0 | Object objRef, obj; |
442 | |
|
443 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
444 | 0 | if (resPtr->patternDict.isDict()) { |
445 | 0 | if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) { |
446 | 0 | resPtr->patternDict.dictLookupNF(name, &objRef); |
447 | 0 | pattern = GfxPattern::parse(&objRef, &obj |
448 | 0 | ); |
449 | 0 | objRef.free(); |
450 | 0 | obj.free(); |
451 | 0 | return pattern; |
452 | 0 | } |
453 | 0 | obj.free(); |
454 | 0 | } |
455 | 0 | } |
456 | 0 | error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name); |
457 | 0 | return NULL; |
458 | 0 | } |
459 | | |
460 | | GfxShading *GfxResources::lookupShading(const char *name |
461 | 0 | ) { |
462 | 0 | GfxResources *resPtr; |
463 | 0 | GfxShading *shading; |
464 | 0 | Object obj; |
465 | |
|
466 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
467 | 0 | if (resPtr->shadingDict.isDict()) { |
468 | 0 | if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) { |
469 | 0 | shading = GfxShading::parse(&obj |
470 | 0 | ); |
471 | 0 | obj.free(); |
472 | 0 | return shading; |
473 | 0 | } |
474 | 0 | obj.free(); |
475 | 0 | } |
476 | 0 | } |
477 | 0 | error(errSyntaxError, -1, "Unknown shading '{0:s}'", name); |
478 | 0 | return NULL; |
479 | 0 | } |
480 | | |
481 | 0 | GBool GfxResources::lookupGState(const char *name, Object *obj) { |
482 | 0 | GfxResources *resPtr; |
483 | |
|
484 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
485 | 0 | if (resPtr->gStateDict.isDict()) { |
486 | 0 | if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) { |
487 | 0 | return gTrue; |
488 | 0 | } |
489 | 0 | obj->free(); |
490 | 0 | } |
491 | 0 | } |
492 | 0 | error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name); |
493 | 0 | return gFalse; |
494 | 0 | } |
495 | | |
496 | 0 | GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) { |
497 | 0 | GfxResources *resPtr; |
498 | |
|
499 | 0 | for (resPtr = this; resPtr; resPtr = resPtr->next) { |
500 | 0 | if (resPtr->propsDict.isDict()) { |
501 | 0 | if (!resPtr->propsDict.dictLookupNF(name, obj)->isNull()) { |
502 | 0 | return gTrue; |
503 | 0 | } |
504 | 0 | obj->free(); |
505 | 0 | } |
506 | 0 | } |
507 | 0 | error(errSyntaxError, -1, "Properties '{0:s}' is unknown", name); |
508 | 0 | return gFalse; |
509 | 0 | } |
510 | | |
511 | | //------------------------------------------------------------------------ |
512 | | // Gfx |
513 | | //------------------------------------------------------------------------ |
514 | | |
515 | | Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict, |
516 | | double hDPI, double vDPI, PDFRectangle *box, |
517 | | PDFRectangle *cropBox, int rotate, |
518 | | GBool (*abortCheckCbkA)(void *data), |
519 | 0 | void *abortCheckCbkDataA) { |
520 | 0 | int i; |
521 | |
|
522 | 0 | doc = docA; |
523 | 0 | xref = doc->getXRef(); |
524 | 0 | subPage = gFalse; |
525 | 0 | printCommands = globalParams->getPrintCommands(); |
526 | 0 | defaultFont = NULL; |
527 | | |
528 | | // start the resource stack |
529 | 0 | res = new GfxResources(xref, resDict, NULL); |
530 | | |
531 | | // initialize |
532 | 0 | out = outA; |
533 | 0 | state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown()); |
534 | 0 | fontChanged = gFalse; |
535 | 0 | haveSavedClipPath = gFalse; |
536 | 0 | clip = clipNone; |
537 | 0 | ignoreUndef = 0; |
538 | 0 | out->startPage(pageNum, state); |
539 | 0 | out->setDefaultCTM(state->getCTM()); |
540 | 0 | out->updateAll(state); |
541 | 0 | for (i = 0; i < 6; ++i) { |
542 | 0 | baseMatrix[i] = state->getCTM()[i]; |
543 | 0 | } |
544 | 0 | formDepth = 0; |
545 | 0 | markedContentStack = new GList(); |
546 | 0 | ocState = gTrue; |
547 | 0 | parser = NULL; |
548 | 0 | contentStreamStack = new GList(); |
549 | 0 | abortCheckCbk = abortCheckCbkA; |
550 | 0 | abortCheckCbkData = abortCheckCbkDataA; |
551 | | |
552 | | // set crop box |
553 | 0 | if (cropBox) { |
554 | 0 | state->moveTo(cropBox->x1, cropBox->y1); |
555 | 0 | state->lineTo(cropBox->x2, cropBox->y1); |
556 | 0 | state->lineTo(cropBox->x2, cropBox->y2); |
557 | 0 | state->lineTo(cropBox->x1, cropBox->y2); |
558 | 0 | state->closePath(); |
559 | 0 | state->clip(); |
560 | 0 | out->clip(state); |
561 | 0 | state->clearPath(); |
562 | 0 | } |
563 | 0 | } |
564 | | |
565 | | Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict, |
566 | | PDFRectangle *box, PDFRectangle *cropBox, |
567 | | GBool (*abortCheckCbkA)(void *data), |
568 | 0 | void *abortCheckCbkDataA) { |
569 | 0 | int i; |
570 | |
|
571 | 0 | doc = docA; |
572 | 0 | xref = doc->getXRef(); |
573 | 0 | subPage = gTrue; |
574 | 0 | printCommands = globalParams->getPrintCommands(); |
575 | 0 | defaultFont = NULL; |
576 | | |
577 | | // start the resource stack |
578 | 0 | res = new GfxResources(xref, resDict, NULL); |
579 | | |
580 | | // initialize |
581 | 0 | out = outA; |
582 | 0 | state = new GfxState(72, 72, box, 0, gFalse); |
583 | 0 | fontChanged = gFalse; |
584 | 0 | haveSavedClipPath = gFalse; |
585 | 0 | clip = clipNone; |
586 | 0 | ignoreUndef = 0; |
587 | 0 | for (i = 0; i < 6; ++i) { |
588 | 0 | baseMatrix[i] = state->getCTM()[i]; |
589 | 0 | } |
590 | 0 | formDepth = 0; |
591 | 0 | markedContentStack = new GList(); |
592 | 0 | ocState = gTrue; |
593 | 0 | parser = NULL; |
594 | 0 | contentStreamStack = new GList(); |
595 | 0 | abortCheckCbk = abortCheckCbkA; |
596 | 0 | abortCheckCbkData = abortCheckCbkDataA; |
597 | | |
598 | | // set crop box |
599 | 0 | if (cropBox) { |
600 | 0 | state->moveTo(cropBox->x1, cropBox->y1); |
601 | 0 | state->lineTo(cropBox->x2, cropBox->y1); |
602 | 0 | state->lineTo(cropBox->x2, cropBox->y2); |
603 | 0 | state->lineTo(cropBox->x1, cropBox->y2); |
604 | 0 | state->closePath(); |
605 | 0 | state->clip(); |
606 | 0 | out->clip(state); |
607 | 0 | state->clearPath(); |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | 0 | Gfx::~Gfx() { |
612 | 0 | if (defaultFont) { |
613 | 0 | delete defaultFont; |
614 | 0 | } |
615 | 0 | if (!subPage) { |
616 | 0 | out->endPage(); |
617 | 0 | } |
618 | 0 | while (state->hasSaves()) { |
619 | 0 | restoreState(); |
620 | 0 | } |
621 | 0 | delete state; |
622 | 0 | while (res) { |
623 | 0 | popResources(); |
624 | 0 | } |
625 | 0 | deleteGList(markedContentStack, GfxMarkedContent); |
626 | 0 | delete contentStreamStack; |
627 | 0 | } |
628 | | |
629 | 0 | void Gfx::display(Object *objRef, GBool topLevel) { |
630 | 0 | Object obj1, obj2; |
631 | 0 | int i; |
632 | |
|
633 | 0 | objRef->fetch(xref, &obj1); |
634 | 0 | if (obj1.isArray()) { |
635 | 0 | for (i = 0; i < obj1.arrayGetLength(); ++i) { |
636 | 0 | obj1.arrayGetNF(i, &obj2); |
637 | 0 | if (checkForContentStreamLoop(&obj2)) { |
638 | 0 | obj2.free(); |
639 | 0 | obj1.free(); |
640 | 0 | return; |
641 | 0 | } |
642 | 0 | obj2.free(); |
643 | 0 | } |
644 | 0 | for (i = 0; i < obj1.arrayGetLength(); ++i) { |
645 | 0 | obj1.arrayGet(i, &obj2); |
646 | 0 | if (!obj2.isStream()) { |
647 | 0 | error(errSyntaxError, -1, "Invalid object type for content stream"); |
648 | 0 | obj2.free(); |
649 | 0 | obj1.free(); |
650 | 0 | return; |
651 | 0 | } |
652 | 0 | obj2.free(); |
653 | 0 | } |
654 | 0 | contentStreamStack->append(&obj1); |
655 | 0 | } else if (obj1.isStream()) { |
656 | 0 | if (checkForContentStreamLoop(objRef)) { |
657 | 0 | obj1.free(); |
658 | 0 | return; |
659 | 0 | } |
660 | 0 | contentStreamStack->append(objRef); |
661 | 0 | } else { |
662 | 0 | error(errSyntaxError, -1, "Invalid object type for content stream"); |
663 | 0 | obj1.free(); |
664 | 0 | return; |
665 | 0 | } |
666 | 0 | parser = new Parser(xref, new Lexer(xref, &obj1), gFalse); |
667 | 0 | go(topLevel); |
668 | 0 | delete parser; |
669 | 0 | parser = NULL; |
670 | 0 | contentStreamStack->del(contentStreamStack->getLength() - 1); |
671 | 0 | obj1.free(); |
672 | 0 | } |
673 | | |
674 | | // If <ref> is already on contentStreamStack, i.e., if there is a loop |
675 | | // in the content streams, report an error, and return true. |
676 | 0 | GBool Gfx::checkForContentStreamLoop(Object *ref) { |
677 | 0 | Object *objPtr; |
678 | 0 | Object obj1; |
679 | 0 | int i, j; |
680 | |
|
681 | 0 | if (ref->isRef()) { |
682 | 0 | for (i = 0; i < contentStreamStack->getLength(); ++i) { |
683 | 0 | objPtr = (Object *)contentStreamStack->get(i); |
684 | 0 | if (objPtr->isRef()) { |
685 | 0 | if (ref->getRefNum() == objPtr->getRefNum() && |
686 | 0 | ref->getRefGen() == objPtr->getRefGen()) { |
687 | 0 | error(errSyntaxError, -1, "Loop in content streams"); |
688 | 0 | return gTrue; |
689 | 0 | } |
690 | 0 | } else if (objPtr->isArray()) { |
691 | 0 | for (j = 0; j < objPtr->arrayGetLength(); ++j) { |
692 | 0 | objPtr->arrayGetNF(j, &obj1); |
693 | 0 | if (obj1.isRef()) { |
694 | 0 | if (ref->getRefNum() == obj1.getRefNum() && |
695 | 0 | ref->getRefGen() == obj1.getRefGen()) { |
696 | 0 | error(errSyntaxError, -1, "Loop in content streams"); |
697 | 0 | obj1.free(); |
698 | 0 | return gTrue; |
699 | 0 | } |
700 | 0 | } |
701 | 0 | obj1.free(); |
702 | 0 | } |
703 | 0 | } |
704 | 0 | } |
705 | 0 | } |
706 | 0 | return gFalse; |
707 | 0 | } |
708 | | |
709 | 0 | void Gfx::go(GBool topLevel) { |
710 | 0 | Object obj; |
711 | 0 | Object args[maxArgs]; |
712 | 0 | GBool aborted; |
713 | 0 | int numArgs, i; |
714 | 0 | int errCount; |
715 | | |
716 | | // scan a sequence of objects |
717 | 0 | opCounter = 0; |
718 | 0 | aborted = gFalse; |
719 | 0 | errCount = 0; |
720 | 0 | numArgs = 0; |
721 | 0 | getContentObj(&obj); |
722 | 0 | while (!obj.isEOF()) { |
723 | | |
724 | | // check for an abort |
725 | 0 | ++opCounter; |
726 | 0 | if (abortCheckCbk && opCounter > 100) { |
727 | 0 | if ((*abortCheckCbk)(abortCheckCbkData)) { |
728 | 0 | aborted = gTrue; |
729 | 0 | break; |
730 | 0 | } |
731 | 0 | opCounter = 0; |
732 | 0 | } |
733 | | |
734 | | // got a command - execute it |
735 | 0 | if (obj.isCmd()) { |
736 | 0 | if (printCommands) { |
737 | 0 | obj.print(stdout); |
738 | 0 | for (i = 0; i < numArgs; ++i) { |
739 | 0 | printf(" "); |
740 | 0 | args[i].print(stdout); |
741 | 0 | } |
742 | 0 | printf("\n"); |
743 | 0 | fflush(stdout); |
744 | 0 | } |
745 | 0 | if (!execOp(&obj, args, numArgs)) { |
746 | 0 | ++errCount; |
747 | 0 | } |
748 | 0 | obj.free(); |
749 | 0 | for (i = 0; i < numArgs; ++i) |
750 | 0 | args[i].free(); |
751 | 0 | numArgs = 0; |
752 | | |
753 | | // check for too many errors |
754 | 0 | if (errCount > contentStreamErrorLimit) { |
755 | 0 | error(errSyntaxError, -1, |
756 | 0 | "Too many errors - giving up on this content stream"); |
757 | 0 | break; |
758 | 0 | } |
759 | | |
760 | | // got an argument - save it |
761 | 0 | } else if (numArgs < maxArgs) { |
762 | 0 | args[numArgs++] = obj; |
763 | | |
764 | | // too many arguments - something is wrong |
765 | 0 | } else { |
766 | 0 | error(errSyntaxError, getPos(), "Too many args in content stream"); |
767 | 0 | if (printCommands) { |
768 | 0 | printf("throwing away arg: "); |
769 | 0 | obj.print(stdout); |
770 | 0 | printf("\n"); |
771 | 0 | fflush(stdout); |
772 | 0 | } |
773 | 0 | obj.free(); |
774 | 0 | } |
775 | | |
776 | | // grab the next object |
777 | 0 | getContentObj(&obj); |
778 | 0 | } |
779 | 0 | obj.free(); |
780 | | |
781 | | // args at end with no command |
782 | 0 | if (numArgs > 0) { |
783 | 0 | if (!aborted) { |
784 | 0 | error(errSyntaxError, getPos(), "Leftover args in content stream"); |
785 | 0 | if (printCommands) { |
786 | 0 | printf("%d leftovers:", numArgs); |
787 | 0 | for (i = 0; i < numArgs; ++i) { |
788 | 0 | printf(" "); |
789 | 0 | args[i].print(stdout); |
790 | 0 | } |
791 | 0 | printf("\n"); |
792 | 0 | fflush(stdout); |
793 | 0 | } |
794 | 0 | } |
795 | 0 | for (i = 0; i < numArgs; ++i) { |
796 | 0 | args[i].free(); |
797 | 0 | } |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | 0 | void Gfx::getContentObj(Object *obj) { |
802 | 0 | parser->getObj(obj); |
803 | 0 | if (obj->isRef()) { |
804 | 0 | error(errSyntaxError, getPos(), "Indirect reference in content stream"); |
805 | 0 | obj->free(); |
806 | 0 | obj->initError(); |
807 | 0 | } |
808 | 0 | } |
809 | | |
810 | | // Returns true if successful, false on error. |
811 | 0 | GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) { |
812 | 0 | Operator *op; |
813 | 0 | char *name; |
814 | 0 | Object *argPtr; |
815 | 0 | int i; |
816 | | |
817 | | // find operator |
818 | 0 | name = cmd->getCmd(); |
819 | 0 | if (!(op = findOp(name))) { |
820 | 0 | if (ignoreUndef > 0) { |
821 | 0 | return gTrue; |
822 | 0 | } |
823 | 0 | error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name); |
824 | 0 | return gFalse; |
825 | 0 | } |
826 | | |
827 | | // type check args |
828 | 0 | argPtr = args; |
829 | 0 | if (op->numArgs >= 0) { |
830 | 0 | if (numArgs < op->numArgs) { |
831 | 0 | error(errSyntaxError, getPos(), |
832 | 0 | "Too few ({0:d}) args to '{1:s}' operator", numArgs, name); |
833 | 0 | return gFalse; |
834 | 0 | } |
835 | 0 | if (numArgs > op->numArgs) { |
836 | | #if 0 |
837 | | error(errSyntaxWarning, getPos(), |
838 | | "Too many ({0:d}) args to '{1:s}' operator", numArgs, name); |
839 | | #endif |
840 | 0 | argPtr += numArgs - op->numArgs; |
841 | 0 | numArgs = op->numArgs; |
842 | 0 | } |
843 | 0 | } else { |
844 | 0 | if (numArgs > -op->numArgs) { |
845 | 0 | error(errSyntaxWarning, getPos(), |
846 | 0 | "Too many ({0:d}) args to '{1:s}' operator", |
847 | 0 | numArgs, name); |
848 | 0 | } |
849 | 0 | } |
850 | 0 | for (i = 0; i < numArgs; ++i) { |
851 | 0 | if (!checkArg(&argPtr[i], op->tchk[i])) { |
852 | 0 | error(errSyntaxError, getPos(), |
853 | 0 | "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})", |
854 | 0 | i, name, argPtr[i].getTypeName()); |
855 | 0 | return gFalse; |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | | // do it |
860 | 0 | (this->*op->func)(argPtr, numArgs); |
861 | |
|
862 | 0 | return gTrue; |
863 | 0 | } |
864 | | |
865 | 0 | Operator *Gfx::findOp(char *name) { |
866 | 0 | int a, b, m, cmp; |
867 | |
|
868 | 0 | a = -1; |
869 | 0 | b = numOps; |
870 | 0 | cmp = 0; // make gcc happy |
871 | | // invariant: opTab[a] < name < opTab[b] |
872 | 0 | while (b - a > 1) { |
873 | 0 | m = (a + b) / 2; |
874 | 0 | cmp = strcmp(opTab[m].name, name); |
875 | 0 | if (cmp < 0) |
876 | 0 | a = m; |
877 | 0 | else if (cmp > 0) |
878 | 0 | b = m; |
879 | 0 | else |
880 | 0 | a = b = m; |
881 | 0 | } |
882 | 0 | if (cmp != 0) |
883 | 0 | return NULL; |
884 | 0 | return &opTab[a]; |
885 | 0 | } |
886 | | |
887 | 0 | GBool Gfx::checkArg(Object *arg, TchkType type) { |
888 | 0 | switch (type) { |
889 | 0 | case tchkBool: return arg->isBool(); |
890 | 0 | case tchkInt: return arg->isInt(); |
891 | 0 | case tchkNum: return arg->isNum(); |
892 | 0 | case tchkString: return arg->isString(); |
893 | 0 | case tchkName: return arg->isName(); |
894 | 0 | case tchkArray: return arg->isArray(); |
895 | 0 | case tchkProps: return arg->isDict() || arg->isName(); |
896 | 0 | case tchkSCN: return arg->isNum() || arg->isName(); |
897 | 0 | case tchkNone: return gFalse; |
898 | 0 | } |
899 | 0 | return gFalse; |
900 | 0 | } |
901 | | |
902 | 0 | GFileOffset Gfx::getPos() { |
903 | 0 | return parser ? parser->getPos() : -1; |
904 | 0 | } |
905 | | |
906 | | //------------------------------------------------------------------------ |
907 | | // graphics state operators |
908 | | //------------------------------------------------------------------------ |
909 | | |
910 | 0 | void Gfx::opSave(Object args[], int numArgs) { |
911 | 0 | saveState(); |
912 | 0 | } |
913 | | |
914 | 0 | void Gfx::opRestore(Object args[], int numArgs) { |
915 | 0 | restoreState(); |
916 | 0 | } |
917 | | |
918 | 0 | void Gfx::opConcat(Object args[], int numArgs) { |
919 | 0 | state->concatCTM(args[0].getNum(), args[1].getNum(), |
920 | 0 | args[2].getNum(), args[3].getNum(), |
921 | 0 | args[4].getNum(), args[5].getNum()); |
922 | 0 | out->updateCTM(state, args[0].getNum(), args[1].getNum(), |
923 | 0 | args[2].getNum(), args[3].getNum(), |
924 | 0 | args[4].getNum(), args[5].getNum()); |
925 | 0 | fontChanged = gTrue; |
926 | 0 | } |
927 | | |
928 | 0 | void Gfx::opSetDash(Object args[], int numArgs) { |
929 | 0 | Array *a; |
930 | 0 | int length; |
931 | 0 | Object obj; |
932 | 0 | double *dash; |
933 | 0 | int i; |
934 | |
|
935 | 0 | a = args[0].getArray(); |
936 | 0 | length = a->getLength(); |
937 | 0 | if (length == 0) { |
938 | 0 | dash = NULL; |
939 | 0 | } else { |
940 | 0 | dash = (double *)gmallocn(length, sizeof(double)); |
941 | 0 | for (i = 0; i < length; ++i) { |
942 | 0 | dash[i] = a->get(i, &obj)->getNum(); |
943 | 0 | obj.free(); |
944 | 0 | } |
945 | 0 | } |
946 | 0 | state->setLineDash(dash, length, args[1].getNum()); |
947 | 0 | out->updateLineDash(state); |
948 | 0 | } |
949 | | |
950 | 0 | void Gfx::opSetFlat(Object args[], int numArgs) { |
951 | 0 | state->setFlatness((int)args[0].getNum()); |
952 | 0 | out->updateFlatness(state); |
953 | 0 | } |
954 | | |
955 | 0 | void Gfx::opSetLineJoin(Object args[], int numArgs) { |
956 | 0 | int lineJoin; |
957 | |
|
958 | 0 | lineJoin = args[0].getInt(); |
959 | 0 | if (lineJoin < 0 || lineJoin > 2) { |
960 | 0 | lineJoin = 0; |
961 | 0 | } |
962 | 0 | state->setLineJoin(lineJoin); |
963 | 0 | out->updateLineJoin(state); |
964 | 0 | } |
965 | | |
966 | 0 | void Gfx::opSetLineCap(Object args[], int numArgs) { |
967 | 0 | int lineCap; |
968 | |
|
969 | 0 | lineCap = args[0].getInt(); |
970 | 0 | if (lineCap < 0 || lineCap > 2) { |
971 | 0 | lineCap = 0; |
972 | 0 | } |
973 | 0 | state->setLineCap(lineCap); |
974 | 0 | out->updateLineCap(state); |
975 | 0 | } |
976 | | |
977 | 0 | void Gfx::opSetMiterLimit(Object args[], int numArgs) { |
978 | 0 | state->setMiterLimit(args[0].getNum()); |
979 | 0 | out->updateMiterLimit(state); |
980 | 0 | } |
981 | | |
982 | 0 | void Gfx::opSetLineWidth(Object args[], int numArgs) { |
983 | 0 | state->setLineWidth(args[0].getNum()); |
984 | 0 | out->updateLineWidth(state); |
985 | 0 | } |
986 | | |
987 | 0 | void Gfx::opSetExtGState(Object args[], int numArgs) { |
988 | 0 | Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj; |
989 | 0 | Object args2[2]; |
990 | 0 | GfxBlendMode mode; |
991 | 0 | GBool haveFillOP; |
992 | 0 | Function *funcs[4]; |
993 | 0 | GBool alpha, knockout; |
994 | 0 | double opac; |
995 | 0 | int i; |
996 | |
|
997 | 0 | if (!res->lookupGState(args[0].getName(), &obj1)) { |
998 | 0 | return; |
999 | 0 | } |
1000 | 0 | if (!obj1.isDict()) { |
1001 | 0 | error(errSyntaxError, getPos(), |
1002 | 0 | "ExtGState '{0:s}' is wrong type", args[0].getName()); |
1003 | 0 | obj1.free(); |
1004 | 0 | return; |
1005 | 0 | } |
1006 | 0 | if (printCommands) { |
1007 | 0 | printf(" gfx state dict: "); |
1008 | 0 | obj1.print(); |
1009 | 0 | printf("\n"); |
1010 | 0 | } |
1011 | | |
1012 | | // parameters that are also set by individual PDF operators |
1013 | 0 | if (obj1.dictLookup("LW", &obj2)->isNum()) { |
1014 | 0 | opSetLineWidth(&obj2, 1); |
1015 | 0 | } |
1016 | 0 | obj2.free(); |
1017 | 0 | if (obj1.dictLookup("LC", &obj2)->isInt()) { |
1018 | 0 | opSetLineCap(&obj2, 1); |
1019 | 0 | } |
1020 | 0 | obj2.free(); |
1021 | 0 | if (obj1.dictLookup("LJ", &obj2)->isInt()) { |
1022 | 0 | opSetLineJoin(&obj2, 1); |
1023 | 0 | } |
1024 | 0 | obj2.free(); |
1025 | 0 | if (obj1.dictLookup("ML", &obj2)->isNum()) { |
1026 | 0 | opSetMiterLimit(&obj2, 1); |
1027 | 0 | } |
1028 | 0 | obj2.free(); |
1029 | 0 | if (obj1.dictLookup("D", &obj2)->isArray() && |
1030 | 0 | obj2.arrayGetLength() == 2) { |
1031 | 0 | obj2.arrayGet(0, &args2[0]); |
1032 | 0 | obj2.arrayGet(1, &args2[1]); |
1033 | 0 | if (args2[0].isArray() && args2[1].isNum()) { |
1034 | 0 | opSetDash(args2, 2); |
1035 | 0 | } |
1036 | 0 | args2[0].free(); |
1037 | 0 | args2[1].free(); |
1038 | 0 | } |
1039 | 0 | obj2.free(); |
1040 | 0 | if (obj1.dictLookup("FL", &obj2)->isNum()) { |
1041 | 0 | opSetFlat(&obj2, 1); |
1042 | 0 | } |
1043 | 0 | obj2.free(); |
1044 | 0 | if (obj1.dictLookup("RI", &obj2)->isName()) { |
1045 | 0 | opSetRenderingIntent(&obj2, 1); |
1046 | 0 | } |
1047 | 0 | obj2.free(); |
1048 | | |
1049 | | // font |
1050 | 0 | if (obj1.dictLookup("Font", &obj2)->isArray() && |
1051 | 0 | obj2.arrayGetLength() == 2) { |
1052 | 0 | obj2.arrayGetNF(0, &obj3); |
1053 | 0 | obj2.arrayGetNF(1, &obj4); |
1054 | 0 | if (obj3.isRef() && obj4.isNum()) { |
1055 | 0 | doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum()); |
1056 | 0 | } |
1057 | 0 | obj3.free(); |
1058 | 0 | obj4.free(); |
1059 | 0 | } |
1060 | 0 | obj2.free(); |
1061 | | |
1062 | | // transparency support: blend mode, fill/stroke opacity |
1063 | 0 | if (!obj1.dictLookup("BM", &obj2)->isNull()) { |
1064 | 0 | if (state->parseBlendMode(&obj2, &mode)) { |
1065 | 0 | state->setBlendMode(mode); |
1066 | 0 | out->updateBlendMode(state); |
1067 | 0 | } else { |
1068 | 0 | error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState"); |
1069 | 0 | } |
1070 | 0 | } |
1071 | 0 | obj2.free(); |
1072 | 0 | if (obj1.dictLookup("ca", &obj2)->isNum()) { |
1073 | 0 | opac = obj2.getNum(); |
1074 | 0 | state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); |
1075 | 0 | out->updateFillOpacity(state); |
1076 | 0 | } |
1077 | 0 | obj2.free(); |
1078 | 0 | if (obj1.dictLookup("CA", &obj2)->isNum()) { |
1079 | 0 | opac = obj2.getNum(); |
1080 | 0 | state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); |
1081 | 0 | out->updateStrokeOpacity(state); |
1082 | 0 | } |
1083 | 0 | obj2.free(); |
1084 | | |
1085 | | // fill/stroke overprint, overprint mode |
1086 | 0 | if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) { |
1087 | 0 | if (!state->getIgnoreColorOps()) { |
1088 | 0 | state->setFillOverprint(obj2.getBool()); |
1089 | 0 | out->updateFillOverprint(state); |
1090 | 0 | } else { |
1091 | 0 | error(errSyntaxWarning, getPos(), "Ignoring overprint setting" |
1092 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1093 | 0 | } |
1094 | 0 | } |
1095 | 0 | obj2.free(); |
1096 | 0 | if (obj1.dictLookup("OP", &obj2)->isBool()) { |
1097 | 0 | if (!state->getIgnoreColorOps()) { |
1098 | 0 | state->setStrokeOverprint(obj2.getBool()); |
1099 | 0 | out->updateStrokeOverprint(state); |
1100 | 0 | if (!haveFillOP) { |
1101 | 0 | state->setFillOverprint(obj2.getBool()); |
1102 | 0 | out->updateFillOverprint(state); |
1103 | 0 | } |
1104 | 0 | } else { |
1105 | 0 | error(errSyntaxWarning, getPos(), "Ignoring overprint setting" |
1106 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1107 | 0 | } |
1108 | 0 | } |
1109 | 0 | obj2.free(); |
1110 | 0 | if (obj1.dictLookup("OPM", &obj2)->isInt()) { |
1111 | 0 | if (!state->getIgnoreColorOps()) { |
1112 | 0 | state->setOverprintMode(obj2.getInt()); |
1113 | 0 | out->updateOverprintMode(state); |
1114 | 0 | } else { |
1115 | 0 | error(errSyntaxWarning, getPos(), "Ignoring overprint setting" |
1116 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1117 | 0 | } |
1118 | 0 | } |
1119 | 0 | obj2.free(); |
1120 | | |
1121 | | // stroke adjust |
1122 | 0 | if (obj1.dictLookup("SA", &obj2)->isBool()) { |
1123 | 0 | state->setStrokeAdjust(obj2.getBool()); |
1124 | 0 | out->updateStrokeAdjust(state); |
1125 | 0 | } |
1126 | 0 | obj2.free(); |
1127 | | |
1128 | | // transfer function |
1129 | 0 | if (obj1.dictLookup("TR2", &obj2)->isNull()) { |
1130 | 0 | obj2.free(); |
1131 | 0 | obj1.dictLookup("TR", &obj2); |
1132 | 0 | } |
1133 | 0 | if (!obj2.isNull()) { |
1134 | 0 | if (!state->getIgnoreColorOps()) { |
1135 | 0 | if (obj2.isName("Default") || |
1136 | 0 | obj2.isName("Identity")) { |
1137 | 0 | funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL; |
1138 | 0 | state->setTransfer(funcs); |
1139 | 0 | out->updateTransfer(state); |
1140 | 0 | } else if (obj2.isArray() && obj2.arrayGetLength() == 4) { |
1141 | 0 | for (i = 0; i < 4; ++i) { |
1142 | 0 | obj2.arrayGet(i, &obj3); |
1143 | 0 | funcs[i] = Function::parse(&obj3, 1, 1); |
1144 | 0 | obj3.free(); |
1145 | 0 | if (!funcs[i]) { |
1146 | 0 | break; |
1147 | 0 | } |
1148 | 0 | } |
1149 | 0 | if (i == 4) { |
1150 | 0 | state->setTransfer(funcs); |
1151 | 0 | out->updateTransfer(state); |
1152 | 0 | } |
1153 | 0 | } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) { |
1154 | 0 | if ((funcs[0] = Function::parse(&obj2, 1, 1))) { |
1155 | 0 | funcs[1] = funcs[2] = funcs[3] = NULL; |
1156 | 0 | state->setTransfer(funcs); |
1157 | 0 | out->updateTransfer(state); |
1158 | 0 | } |
1159 | 0 | } else { |
1160 | 0 | error(errSyntaxError, getPos(), |
1161 | 0 | "Invalid transfer function in ExtGState"); |
1162 | 0 | } |
1163 | 0 | } else { |
1164 | 0 | error(errSyntaxWarning, getPos(), "Ignoring transfer function setting" |
1165 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1166 | 0 | } |
1167 | 0 | } |
1168 | 0 | obj2.free(); |
1169 | | |
1170 | | // soft mask |
1171 | 0 | if (!obj1.dictLookup("SMask", &obj2)->isNull()) { |
1172 | 0 | if (obj2.isName("None")) { |
1173 | 0 | out->clearSoftMask(state); |
1174 | 0 | } else if (obj2.isDict()) { |
1175 | 0 | obj2.dictLookup("S", &obj3); |
1176 | 0 | if (obj3.isName("Alpha")) { |
1177 | 0 | alpha = gTrue; |
1178 | 0 | } else if (obj3.isName("Luminosity")) { |
1179 | 0 | alpha = gFalse; |
1180 | 0 | } else { |
1181 | 0 | error(errSyntaxError, getPos(), |
1182 | 0 | "Missing S (subtype) entry in soft mask"); |
1183 | 0 | alpha = gFalse; |
1184 | 0 | } |
1185 | 0 | obj3.free(); |
1186 | 0 | funcs[0] = NULL; |
1187 | 0 | if (!obj2.dictLookup("TR", &obj3)->isNull()) { |
1188 | 0 | if (obj3.isName("Default") || |
1189 | 0 | obj3.isName("Identity")) { |
1190 | 0 | funcs[0] = NULL; |
1191 | 0 | } else { |
1192 | 0 | if (!(funcs[0] = Function::parse(&obj3, 1, 1))) { |
1193 | 0 | error(errSyntaxError, getPos(), |
1194 | 0 | "Invalid transfer function in soft mask in ExtGState"); |
1195 | 0 | delete funcs[0]; |
1196 | 0 | funcs[0] = NULL; |
1197 | 0 | } |
1198 | 0 | } |
1199 | 0 | } |
1200 | 0 | obj3.free(); |
1201 | 0 | obj2.dictLookup("BC", &backdropColorObj); |
1202 | 0 | if (obj2.dictLookup("G", &obj3)->isStream()) { |
1203 | 0 | if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) { |
1204 | 0 | knockout = gFalse; |
1205 | 0 | if (obj4.dictLookup("K", &obj5)->isBool()) { |
1206 | 0 | knockout = obj5.getBool(); |
1207 | 0 | } |
1208 | 0 | obj5.free(); |
1209 | 0 | obj2.dictLookupNF("G", &objRef3); |
1210 | | // it doesn't make sense for softmasks to be non-isolated, |
1211 | | // because they're blended with a backdrop color, rather |
1212 | | // than the original backdrop |
1213 | 0 | doSoftMask(&obj3, &objRef3, alpha, gTrue, knockout, funcs[0], |
1214 | 0 | &backdropColorObj); |
1215 | 0 | objRef3.free(); |
1216 | 0 | if (funcs[0]) { |
1217 | 0 | delete funcs[0]; |
1218 | 0 | } |
1219 | 0 | } else { |
1220 | 0 | error(errSyntaxError, getPos(), |
1221 | 0 | "Invalid soft mask in ExtGState - missing group"); |
1222 | 0 | } |
1223 | 0 | obj4.free(); |
1224 | 0 | } else { |
1225 | 0 | error(errSyntaxError, getPos(), |
1226 | 0 | "Invalid soft mask in ExtGState - missing group"); |
1227 | 0 | } |
1228 | 0 | obj3.free(); |
1229 | 0 | backdropColorObj.free(); |
1230 | 0 | } else if (!obj2.isNull()) { |
1231 | 0 | error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState"); |
1232 | 0 | } |
1233 | 0 | } |
1234 | 0 | obj2.free(); |
1235 | |
|
1236 | 0 | obj1.free(); |
1237 | 0 | } |
1238 | | |
1239 | | void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha, |
1240 | | GBool isolated, GBool knockout, |
1241 | 0 | Function *transferFunc, Object *backdropColorObj) { |
1242 | 0 | Dict *dict, *resDict; |
1243 | 0 | double m[6], bbox[4]; |
1244 | 0 | Object obj1, obj2; |
1245 | 0 | int i; |
1246 | | |
1247 | | // check for excessive recursion |
1248 | 0 | if (formDepth > 20) { |
1249 | 0 | return; |
1250 | 0 | } |
1251 | | |
1252 | | // get stream dict |
1253 | 0 | dict = str->streamGetDict(); |
1254 | | |
1255 | | // check form type |
1256 | 0 | dict->lookup("FormType", &obj1); |
1257 | 0 | if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { |
1258 | 0 | error(errSyntaxError, getPos(), "Unknown form type"); |
1259 | 0 | } |
1260 | 0 | obj1.free(); |
1261 | | |
1262 | | // get bounding box |
1263 | 0 | dict->lookup("BBox", &obj1); |
1264 | 0 | if (!obj1.isArray()) { |
1265 | 0 | obj1.free(); |
1266 | 0 | error(errSyntaxError, getPos(), "Bad form bounding box"); |
1267 | 0 | return; |
1268 | 0 | } |
1269 | 0 | for (i = 0; i < 4; ++i) { |
1270 | 0 | obj1.arrayGet(i, &obj2); |
1271 | 0 | bbox[i] = obj2.getNum(); |
1272 | 0 | obj2.free(); |
1273 | 0 | } |
1274 | 0 | obj1.free(); |
1275 | | |
1276 | | // get matrix |
1277 | 0 | dict->lookup("Matrix", &obj1); |
1278 | 0 | if (obj1.isArray()) { |
1279 | 0 | for (i = 0; i < 6; ++i) { |
1280 | 0 | obj1.arrayGet(i, &obj2); |
1281 | 0 | m[i] = obj2.getNum(); |
1282 | 0 | obj2.free(); |
1283 | 0 | } |
1284 | 0 | } else { |
1285 | 0 | m[0] = 1; m[1] = 0; |
1286 | 0 | m[2] = 0; m[3] = 1; |
1287 | 0 | m[4] = 0; m[5] = 0; |
1288 | 0 | } |
1289 | 0 | obj1.free(); |
1290 | | |
1291 | | // get resources |
1292 | 0 | dict->lookup("Resources", &obj1); |
1293 | 0 | resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL; |
1294 | | |
1295 | | // draw it |
1296 | 0 | ++formDepth; |
1297 | 0 | drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout, |
1298 | 0 | alpha, transferFunc, backdropColorObj); |
1299 | 0 | --formDepth; |
1300 | |
|
1301 | 0 | obj1.free(); |
1302 | 0 | } |
1303 | | |
1304 | 0 | void Gfx::opSetRenderingIntent(Object args[], int numArgs) { |
1305 | 0 | GfxRenderingIntent ri; |
1306 | |
|
1307 | 0 | if (state->getIgnoreColorOps()) { |
1308 | 0 | error(errSyntaxWarning, getPos(), "Ignoring rendering intent setting" |
1309 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1310 | 0 | return; |
1311 | 0 | } |
1312 | 0 | ri = parseRenderingIntent(args[0].getName()); |
1313 | 0 | state->setRenderingIntent(ri); |
1314 | 0 | out->updateRenderingIntent(state); |
1315 | 0 | } |
1316 | | |
1317 | 0 | GfxRenderingIntent Gfx::parseRenderingIntent(const char *name) { |
1318 | 0 | if (!strcmp(name, "AbsoluteColorimetric")) { |
1319 | 0 | return gfxRenderingIntentAbsoluteColorimetric; |
1320 | 0 | } |
1321 | 0 | if (!strcmp(name, "Saturation")) { |
1322 | 0 | return gfxRenderingIntentSaturation; |
1323 | 0 | } |
1324 | 0 | if (!strcmp(name, "Perceptual")) { |
1325 | 0 | return gfxRenderingIntentPerceptual; |
1326 | 0 | } |
1327 | 0 | return gfxRenderingIntentRelativeColorimetric; |
1328 | 0 | } |
1329 | | |
1330 | | //------------------------------------------------------------------------ |
1331 | | // color operators |
1332 | | //------------------------------------------------------------------------ |
1333 | | |
1334 | 0 | void Gfx::opSetFillGray(Object args[], int numArgs) { |
1335 | 0 | GfxColor color; |
1336 | |
|
1337 | 0 | if (state->getIgnoreColorOps()) { |
1338 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1339 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1340 | 0 | return; |
1341 | 0 | } |
1342 | 0 | state->setFillPattern(NULL); |
1343 | 0 | state->setFillColorSpace(GfxColorSpace::create(csDeviceGray)); |
1344 | 0 | out->updateFillColorSpace(state); |
1345 | 0 | color.c[0] = dblToCol(args[0].getNum()); |
1346 | 0 | state->setFillColor(&color); |
1347 | 0 | out->updateFillColor(state); |
1348 | 0 | } |
1349 | | |
1350 | 0 | void Gfx::opSetStrokeGray(Object args[], int numArgs) { |
1351 | 0 | GfxColor color; |
1352 | |
|
1353 | 0 | if (state->getIgnoreColorOps()) { |
1354 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1355 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1356 | 0 | return; |
1357 | 0 | } |
1358 | 0 | state->setStrokePattern(NULL); |
1359 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray)); |
1360 | 0 | out->updateStrokeColorSpace(state); |
1361 | 0 | color.c[0] = dblToCol(args[0].getNum()); |
1362 | 0 | state->setStrokeColor(&color); |
1363 | 0 | out->updateStrokeColor(state); |
1364 | 0 | } |
1365 | | |
1366 | 0 | void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { |
1367 | 0 | GfxColor color; |
1368 | 0 | int i; |
1369 | |
|
1370 | 0 | if (state->getIgnoreColorOps()) { |
1371 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1372 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1373 | 0 | return; |
1374 | 0 | } |
1375 | 0 | state->setFillPattern(NULL); |
1376 | 0 | state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK)); |
1377 | 0 | out->updateFillColorSpace(state); |
1378 | 0 | for (i = 0; i < 4; ++i) { |
1379 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1380 | 0 | } |
1381 | 0 | state->setFillColor(&color); |
1382 | 0 | out->updateFillColor(state); |
1383 | 0 | } |
1384 | | |
1385 | 0 | void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { |
1386 | 0 | GfxColor color; |
1387 | 0 | int i; |
1388 | |
|
1389 | 0 | if (state->getIgnoreColorOps()) { |
1390 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1391 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1392 | 0 | return; |
1393 | 0 | } |
1394 | 0 | state->setStrokePattern(NULL); |
1395 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK)); |
1396 | 0 | out->updateStrokeColorSpace(state); |
1397 | 0 | for (i = 0; i < 4; ++i) { |
1398 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1399 | 0 | } |
1400 | 0 | state->setStrokeColor(&color); |
1401 | 0 | out->updateStrokeColor(state); |
1402 | 0 | } |
1403 | | |
1404 | 0 | void Gfx::opSetFillRGBColor(Object args[], int numArgs) { |
1405 | 0 | GfxColor color; |
1406 | 0 | int i; |
1407 | |
|
1408 | 0 | if (state->getIgnoreColorOps()) { |
1409 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1410 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1411 | 0 | return; |
1412 | 0 | } |
1413 | 0 | state->setFillPattern(NULL); |
1414 | 0 | state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB)); |
1415 | 0 | out->updateFillColorSpace(state); |
1416 | 0 | for (i = 0; i < 3; ++i) { |
1417 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1418 | 0 | } |
1419 | 0 | state->setFillColor(&color); |
1420 | 0 | out->updateFillColor(state); |
1421 | 0 | } |
1422 | | |
1423 | 0 | void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { |
1424 | 0 | GfxColor color; |
1425 | 0 | int i; |
1426 | |
|
1427 | 0 | if (state->getIgnoreColorOps()) { |
1428 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1429 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1430 | 0 | return; |
1431 | 0 | } |
1432 | 0 | state->setStrokePattern(NULL); |
1433 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB)); |
1434 | 0 | out->updateStrokeColorSpace(state); |
1435 | 0 | for (i = 0; i < 3; ++i) { |
1436 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1437 | 0 | } |
1438 | 0 | state->setStrokeColor(&color); |
1439 | 0 | out->updateStrokeColor(state); |
1440 | 0 | } |
1441 | | |
1442 | 0 | void Gfx::opSetFillColorSpace(Object args[], int numArgs) { |
1443 | 0 | Object obj; |
1444 | 0 | GfxColorSpace *colorSpace; |
1445 | 0 | GfxColor color; |
1446 | |
|
1447 | 0 | if (state->getIgnoreColorOps()) { |
1448 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color space setting" |
1449 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1450 | 0 | return; |
1451 | 0 | } |
1452 | 0 | state->setFillPattern(NULL); |
1453 | 0 | res->lookupColorSpace(args[0].getName(), &obj); |
1454 | 0 | if (obj.isNull()) { |
1455 | 0 | colorSpace = GfxColorSpace::parse(&args[0] |
1456 | 0 | ); |
1457 | 0 | } else { |
1458 | 0 | colorSpace = GfxColorSpace::parse(&obj |
1459 | 0 | ); |
1460 | 0 | } |
1461 | 0 | obj.free(); |
1462 | 0 | if (colorSpace) { |
1463 | 0 | state->setFillColorSpace(colorSpace); |
1464 | 0 | out->updateFillColorSpace(state); |
1465 | 0 | colorSpace->getDefaultColor(&color); |
1466 | 0 | state->setFillColor(&color); |
1467 | 0 | out->updateFillColor(state); |
1468 | 0 | } else { |
1469 | 0 | error(errSyntaxError, getPos(), "Bad color space (fill)"); |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | 0 | void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { |
1474 | 0 | Object obj; |
1475 | 0 | GfxColorSpace *colorSpace; |
1476 | 0 | GfxColor color; |
1477 | |
|
1478 | 0 | if (state->getIgnoreColorOps()) { |
1479 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color space setting" |
1480 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1481 | 0 | return; |
1482 | 0 | } |
1483 | 0 | state->setStrokePattern(NULL); |
1484 | 0 | res->lookupColorSpace(args[0].getName(), &obj); |
1485 | 0 | if (obj.isNull()) { |
1486 | 0 | colorSpace = GfxColorSpace::parse(&args[0] |
1487 | 0 | ); |
1488 | 0 | } else { |
1489 | 0 | colorSpace = GfxColorSpace::parse(&obj |
1490 | 0 | ); |
1491 | 0 | } |
1492 | 0 | obj.free(); |
1493 | 0 | if (colorSpace) { |
1494 | 0 | state->setStrokeColorSpace(colorSpace); |
1495 | 0 | out->updateStrokeColorSpace(state); |
1496 | 0 | colorSpace->getDefaultColor(&color); |
1497 | 0 | state->setStrokeColor(&color); |
1498 | 0 | out->updateStrokeColor(state); |
1499 | 0 | } else { |
1500 | 0 | error(errSyntaxError, getPos(), "Bad color space (stroke)"); |
1501 | 0 | } |
1502 | 0 | } |
1503 | | |
1504 | | // Technically, per the PDF spec, the 'sc' operator can only be used |
1505 | | // with device, CIE, and Indexed color spaces. Some buggy PDF |
1506 | | // generators use it for DeviceN, so we also allow that (same as |
1507 | | // 'scn', minus the optional pattern name argument). |
1508 | 0 | void Gfx::opSetFillColor(Object args[], int numArgs) { |
1509 | 0 | GfxColor color; |
1510 | 0 | int i; |
1511 | |
|
1512 | 0 | if (state->getIgnoreColorOps()) { |
1513 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1514 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1515 | 0 | return; |
1516 | 0 | } |
1517 | 0 | if (numArgs != state->getFillColorSpace()->getNComps()) { |
1518 | 0 | error(errSyntaxError, getPos(), |
1519 | 0 | "Incorrect number of arguments in 'sc' command"); |
1520 | 0 | return; |
1521 | 0 | } |
1522 | 0 | state->setFillPattern(NULL); |
1523 | 0 | for (i = 0; i < numArgs; ++i) { |
1524 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1525 | 0 | } |
1526 | 0 | state->setFillColor(&color); |
1527 | 0 | out->updateFillColor(state); |
1528 | 0 | } |
1529 | | |
1530 | | // Technically, per the PDF spec, the 'SC' operator can only be used |
1531 | | // with device, CIE, and Indexed color spaces. Some buggy PDF |
1532 | | // generators use it for DeviceN, so we also allow that (same as |
1533 | | // 'SCN', minus the optional pattern name argument). |
1534 | 0 | void Gfx::opSetStrokeColor(Object args[], int numArgs) { |
1535 | 0 | GfxColor color; |
1536 | 0 | int i; |
1537 | |
|
1538 | 0 | if (numArgs != state->getStrokeColorSpace()->getNComps()) { |
1539 | 0 | error(errSyntaxError, getPos(), |
1540 | 0 | "Incorrect number of arguments in 'SC' command"); |
1541 | 0 | return; |
1542 | 0 | } |
1543 | 0 | state->setStrokePattern(NULL); |
1544 | 0 | for (i = 0; i < numArgs; ++i) { |
1545 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1546 | 0 | } |
1547 | 0 | state->setStrokeColor(&color); |
1548 | 0 | out->updateStrokeColor(state); |
1549 | 0 | } |
1550 | | |
1551 | 0 | void Gfx::opSetFillColorN(Object args[], int numArgs) { |
1552 | 0 | GfxColor color; |
1553 | 0 | GfxPattern *pattern; |
1554 | 0 | int i; |
1555 | |
|
1556 | 0 | if (state->getIgnoreColorOps()) { |
1557 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1558 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1559 | 0 | return; |
1560 | 0 | } |
1561 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1562 | 0 | if (numArgs == 0 || !args[numArgs-1].isName()) { |
1563 | 0 | error(errSyntaxError, getPos(), |
1564 | 0 | "Invalid arguments in 'scn' command"); |
1565 | 0 | return; |
1566 | 0 | } |
1567 | 0 | if (numArgs > 1) { |
1568 | 0 | if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() || |
1569 | 0 | numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace()) |
1570 | 0 | ->getUnder()->getNComps()) { |
1571 | 0 | error(errSyntaxError, getPos(), |
1572 | 0 | "Incorrect number of arguments in 'scn' command"); |
1573 | 0 | return; |
1574 | 0 | } |
1575 | 0 | for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) { |
1576 | 0 | if (args[i].isNum()) { |
1577 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1578 | 0 | } |
1579 | 0 | } |
1580 | 0 | state->setFillColor(&color); |
1581 | 0 | out->updateFillColor(state); |
1582 | 0 | } |
1583 | 0 | if ((pattern = res->lookupPattern(args[numArgs-1].getName() |
1584 | 0 | ))) { |
1585 | 0 | state->setFillPattern(pattern); |
1586 | 0 | } |
1587 | |
|
1588 | 0 | } else { |
1589 | 0 | if (numArgs != state->getFillColorSpace()->getNComps()) { |
1590 | 0 | error(errSyntaxError, getPos(), |
1591 | 0 | "Incorrect number of arguments in 'scn' command"); |
1592 | 0 | return; |
1593 | 0 | } |
1594 | 0 | state->setFillPattern(NULL); |
1595 | 0 | for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) { |
1596 | 0 | if (args[i].isNum()) { |
1597 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1598 | 0 | } |
1599 | 0 | } |
1600 | 0 | state->setFillColor(&color); |
1601 | 0 | out->updateFillColor(state); |
1602 | 0 | } |
1603 | 0 | } |
1604 | | |
1605 | 0 | void Gfx::opSetStrokeColorN(Object args[], int numArgs) { |
1606 | 0 | GfxColor color; |
1607 | 0 | GfxPattern *pattern; |
1608 | 0 | int i; |
1609 | |
|
1610 | 0 | if (state->getIgnoreColorOps()) { |
1611 | 0 | error(errSyntaxWarning, getPos(), "Ignoring color setting" |
1612 | 0 | " in uncolored Type 3 char or tiling pattern"); |
1613 | 0 | return; |
1614 | 0 | } |
1615 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1616 | 0 | if (numArgs == 0 || !args[numArgs-1].isName()) { |
1617 | 0 | error(errSyntaxError, getPos(), |
1618 | 0 | "Invalid arguments in 'SCN' command"); |
1619 | 0 | return; |
1620 | 0 | } |
1621 | 0 | if (numArgs > 1) { |
1622 | 0 | if (!((GfxPatternColorSpace *)state->getStrokeColorSpace()) |
1623 | 0 | ->getUnder() || |
1624 | 0 | numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace()) |
1625 | 0 | ->getUnder()->getNComps()) { |
1626 | 0 | error(errSyntaxError, getPos(), |
1627 | 0 | "Incorrect number of arguments in 'SCN' command"); |
1628 | 0 | return; |
1629 | 0 | } |
1630 | 0 | for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) { |
1631 | 0 | if (args[i].isNum()) { |
1632 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1633 | 0 | } |
1634 | 0 | } |
1635 | 0 | state->setStrokeColor(&color); |
1636 | 0 | out->updateStrokeColor(state); |
1637 | 0 | } |
1638 | 0 | if ((pattern = res->lookupPattern(args[numArgs-1].getName() |
1639 | 0 | ))) { |
1640 | 0 | state->setStrokePattern(pattern); |
1641 | 0 | } |
1642 | |
|
1643 | 0 | } else { |
1644 | 0 | if (numArgs != state->getStrokeColorSpace()->getNComps()) { |
1645 | 0 | error(errSyntaxError, getPos(), |
1646 | 0 | "Incorrect number of arguments in 'SCN' command"); |
1647 | 0 | return; |
1648 | 0 | } |
1649 | 0 | state->setStrokePattern(NULL); |
1650 | 0 | for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) { |
1651 | 0 | if (args[i].isNum()) { |
1652 | 0 | color.c[i] = dblToCol(args[i].getNum()); |
1653 | 0 | } |
1654 | 0 | } |
1655 | 0 | state->setStrokeColor(&color); |
1656 | 0 | out->updateStrokeColor(state); |
1657 | 0 | } |
1658 | 0 | } |
1659 | | |
1660 | | //------------------------------------------------------------------------ |
1661 | | // path segment operators |
1662 | | //------------------------------------------------------------------------ |
1663 | | |
1664 | 0 | void Gfx::opMoveTo(Object args[], int numArgs) { |
1665 | 0 | state->moveTo(args[0].getNum(), args[1].getNum()); |
1666 | 0 | } |
1667 | | |
1668 | 0 | void Gfx::opLineTo(Object args[], int numArgs) { |
1669 | 0 | if (!state->isCurPt()) { |
1670 | 0 | error(errSyntaxError, getPos(), "No current point in lineto"); |
1671 | 0 | return; |
1672 | 0 | } |
1673 | 0 | state->lineTo(args[0].getNum(), args[1].getNum()); |
1674 | 0 | } |
1675 | | |
1676 | 0 | void Gfx::opCurveTo(Object args[], int numArgs) { |
1677 | 0 | double x1, y1, x2, y2, x3, y3; |
1678 | |
|
1679 | 0 | if (!state->isCurPt()) { |
1680 | 0 | error(errSyntaxError, getPos(), "No current point in curveto"); |
1681 | 0 | return; |
1682 | 0 | } |
1683 | 0 | x1 = args[0].getNum(); |
1684 | 0 | y1 = args[1].getNum(); |
1685 | 0 | x2 = args[2].getNum(); |
1686 | 0 | y2 = args[3].getNum(); |
1687 | 0 | x3 = args[4].getNum(); |
1688 | 0 | y3 = args[5].getNum(); |
1689 | 0 | state->curveTo(x1, y1, x2, y2, x3, y3); |
1690 | 0 | } |
1691 | | |
1692 | 0 | void Gfx::opCurveTo1(Object args[], int numArgs) { |
1693 | 0 | double x1, y1, x2, y2, x3, y3; |
1694 | |
|
1695 | 0 | if (!state->isCurPt()) { |
1696 | 0 | error(errSyntaxError, getPos(), "No current point in curveto1"); |
1697 | 0 | return; |
1698 | 0 | } |
1699 | 0 | x1 = state->getCurX(); |
1700 | 0 | y1 = state->getCurY(); |
1701 | 0 | x2 = args[0].getNum(); |
1702 | 0 | y2 = args[1].getNum(); |
1703 | 0 | x3 = args[2].getNum(); |
1704 | 0 | y3 = args[3].getNum(); |
1705 | 0 | state->curveTo(x1, y1, x2, y2, x3, y3); |
1706 | 0 | } |
1707 | | |
1708 | 0 | void Gfx::opCurveTo2(Object args[], int numArgs) { |
1709 | 0 | double x1, y1, x2, y2, x3, y3; |
1710 | |
|
1711 | 0 | if (!state->isCurPt()) { |
1712 | 0 | error(errSyntaxError, getPos(), "No current point in curveto2"); |
1713 | 0 | return; |
1714 | 0 | } |
1715 | 0 | x1 = args[0].getNum(); |
1716 | 0 | y1 = args[1].getNum(); |
1717 | 0 | x2 = args[2].getNum(); |
1718 | 0 | y2 = args[3].getNum(); |
1719 | 0 | x3 = x2; |
1720 | 0 | y3 = y2; |
1721 | 0 | state->curveTo(x1, y1, x2, y2, x3, y3); |
1722 | 0 | } |
1723 | | |
1724 | 0 | void Gfx::opRectangle(Object args[], int numArgs) { |
1725 | 0 | double x, y, w, h; |
1726 | |
|
1727 | 0 | x = args[0].getNum(); |
1728 | 0 | y = args[1].getNum(); |
1729 | 0 | w = args[2].getNum(); |
1730 | 0 | h = args[3].getNum(); |
1731 | 0 | state->moveTo(x, y); |
1732 | 0 | state->lineTo(x + w, y); |
1733 | 0 | state->lineTo(x + w, y + h); |
1734 | 0 | state->lineTo(x, y + h); |
1735 | 0 | state->closePath(); |
1736 | 0 | } |
1737 | | |
1738 | 0 | void Gfx::opClosePath(Object args[], int numArgs) { |
1739 | 0 | if (!state->isCurPt()) { |
1740 | 0 | error(errSyntaxError, getPos(), "No current point in closepath"); |
1741 | 0 | return; |
1742 | 0 | } |
1743 | 0 | state->closePath(); |
1744 | 0 | } |
1745 | | |
1746 | | //------------------------------------------------------------------------ |
1747 | | // path painting operators |
1748 | | //------------------------------------------------------------------------ |
1749 | | |
1750 | 0 | void Gfx::opEndPath(Object args[], int numArgs) { |
1751 | 0 | doEndPath(); |
1752 | 0 | } |
1753 | | |
1754 | 0 | void Gfx::opStroke(Object args[], int numArgs) { |
1755 | 0 | if (!state->isCurPt()) { |
1756 | | //error(errSyntaxError, getPos(), "No path in stroke"); |
1757 | 0 | return; |
1758 | 0 | } |
1759 | 0 | if (state->isPath()) { |
1760 | 0 | if (ocState) { |
1761 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1762 | 0 | doPatternStroke(); |
1763 | 0 | } else { |
1764 | 0 | out->stroke(state); |
1765 | 0 | } |
1766 | 0 | } |
1767 | 0 | } |
1768 | 0 | doEndPath(); |
1769 | 0 | } |
1770 | | |
1771 | 0 | void Gfx::opCloseStroke(Object args[], int numArgs) { |
1772 | 0 | if (!state->isCurPt()) { |
1773 | | //error(errSyntaxError, getPos(), "No path in closepath/stroke"); |
1774 | 0 | return; |
1775 | 0 | } |
1776 | 0 | if (state->isPath()) { |
1777 | 0 | state->closePath(); |
1778 | 0 | if (ocState) { |
1779 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1780 | 0 | doPatternStroke(); |
1781 | 0 | } else { |
1782 | 0 | out->stroke(state); |
1783 | 0 | } |
1784 | 0 | } |
1785 | 0 | } |
1786 | 0 | doEndPath(); |
1787 | 0 | } |
1788 | | |
1789 | 0 | void Gfx::opFill(Object args[], int numArgs) { |
1790 | 0 | if (!state->isCurPt()) { |
1791 | | //error(errSyntaxError, getPos(), "No path in fill"); |
1792 | 0 | return; |
1793 | 0 | } |
1794 | 0 | if (state->isPath()) { |
1795 | 0 | if (ocState) { |
1796 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1797 | 0 | doPatternFill(gFalse); |
1798 | 0 | } else { |
1799 | 0 | out->fill(state); |
1800 | 0 | } |
1801 | 0 | } |
1802 | 0 | } |
1803 | 0 | doEndPath(); |
1804 | 0 | } |
1805 | | |
1806 | 0 | void Gfx::opEOFill(Object args[], int numArgs) { |
1807 | 0 | if (!state->isCurPt()) { |
1808 | | //error(errSyntaxError, getPos(), "No path in eofill"); |
1809 | 0 | return; |
1810 | 0 | } |
1811 | 0 | if (state->isPath()) { |
1812 | 0 | if (ocState) { |
1813 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1814 | 0 | doPatternFill(gTrue); |
1815 | 0 | } else { |
1816 | 0 | out->eoFill(state); |
1817 | 0 | } |
1818 | 0 | } |
1819 | 0 | } |
1820 | 0 | doEndPath(); |
1821 | 0 | } |
1822 | | |
1823 | 0 | void Gfx::opFillStroke(Object args[], int numArgs) { |
1824 | 0 | if (!state->isCurPt()) { |
1825 | | //error(errSyntaxError, getPos(), "No path in fill/stroke"); |
1826 | 0 | return; |
1827 | 0 | } |
1828 | 0 | if (state->isPath()) { |
1829 | 0 | if (ocState) { |
1830 | 0 | if (state->getFillColorSpace()->getMode() == csPattern || |
1831 | 0 | state->getStrokeColorSpace()->getMode() == csPattern) { |
1832 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1833 | 0 | doPatternFill(gFalse); |
1834 | 0 | } else { |
1835 | 0 | out->fill(state); |
1836 | 0 | } |
1837 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1838 | 0 | doPatternStroke(); |
1839 | 0 | } else { |
1840 | 0 | out->stroke(state); |
1841 | 0 | } |
1842 | 0 | } else { |
1843 | 0 | out->fillStroke(state, gFalse); |
1844 | 0 | } |
1845 | 0 | } |
1846 | 0 | } |
1847 | 0 | doEndPath(); |
1848 | 0 | } |
1849 | | |
1850 | 0 | void Gfx::opCloseFillStroke(Object args[], int numArgs) { |
1851 | 0 | if (!state->isCurPt()) { |
1852 | | //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke"); |
1853 | 0 | return; |
1854 | 0 | } |
1855 | 0 | if (state->isPath()) { |
1856 | 0 | state->closePath(); |
1857 | 0 | if (ocState) { |
1858 | 0 | if (state->getFillColorSpace()->getMode() == csPattern || |
1859 | 0 | state->getStrokeColorSpace()->getMode() == csPattern) { |
1860 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1861 | 0 | doPatternFill(gFalse); |
1862 | 0 | } else { |
1863 | 0 | out->fill(state); |
1864 | 0 | } |
1865 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1866 | 0 | doPatternStroke(); |
1867 | 0 | } else { |
1868 | 0 | out->stroke(state); |
1869 | 0 | } |
1870 | 0 | } else { |
1871 | 0 | out->fillStroke(state, gFalse); |
1872 | 0 | } |
1873 | 0 | } |
1874 | 0 | } |
1875 | 0 | doEndPath(); |
1876 | 0 | } |
1877 | | |
1878 | 0 | void Gfx::opEOFillStroke(Object args[], int numArgs) { |
1879 | 0 | if (!state->isCurPt()) { |
1880 | | //error(errSyntaxError, getPos(), "No path in eofill/stroke"); |
1881 | 0 | return; |
1882 | 0 | } |
1883 | 0 | if (state->isPath()) { |
1884 | 0 | if (ocState) { |
1885 | 0 | if (state->getFillColorSpace()->getMode() == csPattern || |
1886 | 0 | state->getStrokeColorSpace()->getMode() == csPattern) { |
1887 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1888 | 0 | doPatternFill(gTrue); |
1889 | 0 | } else { |
1890 | 0 | out->eoFill(state); |
1891 | 0 | } |
1892 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1893 | 0 | doPatternStroke(); |
1894 | 0 | } else { |
1895 | 0 | out->stroke(state); |
1896 | 0 | } |
1897 | 0 | } else { |
1898 | 0 | out->fillStroke(state, gTrue); |
1899 | 0 | } |
1900 | 0 | } |
1901 | 0 | } |
1902 | 0 | doEndPath(); |
1903 | 0 | } |
1904 | | |
1905 | 0 | void Gfx::opCloseEOFillStroke(Object args[], int numArgs) { |
1906 | 0 | if (!state->isCurPt()) { |
1907 | | //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke"); |
1908 | 0 | return; |
1909 | 0 | } |
1910 | 0 | if (state->isPath()) { |
1911 | 0 | state->closePath(); |
1912 | 0 | if (ocState) { |
1913 | 0 | if (state->getFillColorSpace()->getMode() == csPattern || |
1914 | 0 | state->getStrokeColorSpace()->getMode() == csPattern) { |
1915 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
1916 | 0 | doPatternFill(gTrue); |
1917 | 0 | } else { |
1918 | 0 | out->eoFill(state); |
1919 | 0 | } |
1920 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
1921 | 0 | doPatternStroke(); |
1922 | 0 | } else { |
1923 | 0 | out->stroke(state); |
1924 | 0 | } |
1925 | 0 | } else { |
1926 | 0 | out->fillStroke(state, gTrue); |
1927 | 0 | } |
1928 | 0 | } |
1929 | 0 | } |
1930 | 0 | doEndPath(); |
1931 | 0 | } |
1932 | | |
1933 | 0 | void Gfx::doPatternFill(GBool eoFill) { |
1934 | 0 | GfxPattern *pattern; |
1935 | | |
1936 | | // this is a bit of a kludge -- patterns can be really slow, so we |
1937 | | // skip them if we're only doing text extraction, since they almost |
1938 | | // certainly don't contain any text |
1939 | 0 | if (!out->needNonText()) { |
1940 | 0 | return; |
1941 | 0 | } |
1942 | | |
1943 | 0 | if (!(pattern = state->getFillPattern())) { |
1944 | 0 | return; |
1945 | 0 | } |
1946 | 0 | switch (pattern->getType()) { |
1947 | 0 | case 1: |
1948 | 0 | doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse); |
1949 | 0 | break; |
1950 | 0 | case 2: |
1951 | 0 | doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse); |
1952 | 0 | break; |
1953 | 0 | default: |
1954 | 0 | error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", |
1955 | 0 | pattern->getType()); |
1956 | 0 | break; |
1957 | 0 | } |
1958 | 0 | } |
1959 | | |
1960 | 0 | void Gfx::doPatternStroke() { |
1961 | 0 | GfxPattern *pattern; |
1962 | | |
1963 | | // this is a bit of a kludge -- patterns can be really slow, so we |
1964 | | // skip them if we're only doing text extraction, since they almost |
1965 | | // certainly don't contain any text |
1966 | 0 | if (!out->needNonText()) { |
1967 | 0 | return; |
1968 | 0 | } |
1969 | | |
1970 | 0 | if (!(pattern = state->getStrokePattern())) { |
1971 | 0 | return; |
1972 | 0 | } |
1973 | 0 | switch (pattern->getType()) { |
1974 | 0 | case 1: |
1975 | 0 | doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse); |
1976 | 0 | break; |
1977 | 0 | case 2: |
1978 | 0 | doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse); |
1979 | 0 | break; |
1980 | 0 | default: |
1981 | 0 | error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke", |
1982 | 0 | pattern->getType()); |
1983 | 0 | break; |
1984 | 0 | } |
1985 | 0 | } |
1986 | | |
1987 | 0 | void Gfx::doPatternText(GBool stroke) { |
1988 | 0 | GfxPattern *pattern; |
1989 | | |
1990 | | // this is a bit of a kludge -- patterns can be really slow, so we |
1991 | | // skip them if we're only doing text extraction, since they almost |
1992 | | // certainly don't contain any text |
1993 | 0 | if (!out->needNonText()) { |
1994 | 0 | return; |
1995 | 0 | } |
1996 | | |
1997 | 0 | pattern = stroke ? state->getStrokePattern() : state->getFillPattern(); |
1998 | 0 | if (!pattern) { |
1999 | 0 | return; |
2000 | 0 | } |
2001 | 0 | switch (pattern->getType()) { |
2002 | 0 | case 1: |
2003 | 0 | doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue); |
2004 | 0 | break; |
2005 | 0 | case 2: |
2006 | 0 | doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue); |
2007 | 0 | break; |
2008 | 0 | default: |
2009 | 0 | error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", |
2010 | 0 | pattern->getType()); |
2011 | 0 | break; |
2012 | 0 | } |
2013 | 0 | } |
2014 | | |
2015 | | void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, |
2016 | 0 | GBool invert, GBool inlineImg, GBool interpolate) { |
2017 | 0 | saveState(); |
2018 | |
|
2019 | 0 | out->setSoftMaskFromImageMask(state, ref, str, |
2020 | 0 | width, height, invert, inlineImg, interpolate); |
2021 | |
|
2022 | 0 | state->clearPath(); |
2023 | 0 | state->moveTo(0, 0); |
2024 | 0 | state->lineTo(1, 0); |
2025 | 0 | state->lineTo(1, 1); |
2026 | 0 | state->lineTo(0, 1); |
2027 | 0 | state->closePath(); |
2028 | 0 | doPatternFill(gTrue); |
2029 | 0 | state->clearPath(); |
2030 | |
|
2031 | 0 | restoreState(); |
2032 | 0 | } |
2033 | | |
2034 | | void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, |
2035 | 0 | GBool stroke, GBool eoFill, GBool text) { |
2036 | 0 | GfxPatternColorSpace *patCS; |
2037 | 0 | GfxColorSpace *cs; |
2038 | 0 | GfxColor color; |
2039 | 0 | GfxState *savedState; |
2040 | 0 | double xMin, yMin, xMax, yMax, x, y, x1, y1, t; |
2041 | 0 | double cxMin, cyMin, cxMax, cyMax; |
2042 | 0 | int xi0, yi0, xi1, yi1, xi, yi; |
2043 | 0 | double *ctm, *btm, *ptm; |
2044 | 0 | double bbox[4], m[6], ictm[6], m1[6], imb[6]; |
2045 | 0 | double det; |
2046 | 0 | double xstep, ystep; |
2047 | 0 | int abortCheckCounter, i; |
2048 | | |
2049 | | // get color space |
2050 | 0 | patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace() |
2051 | 0 | : state->getFillColorSpace()); |
2052 | | |
2053 | | // construct a (pattern space) -> (current space) transform matrix |
2054 | 0 | ctm = state->getCTM(); |
2055 | 0 | btm = baseMatrix; |
2056 | 0 | ptm = tPat->getMatrix(); |
2057 | | // iCTM = invert CTM |
2058 | 0 | det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; |
2059 | 0 | if (fabs(det) <= 1e-10) { |
2060 | 0 | error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill"); |
2061 | 0 | return; |
2062 | 0 | } |
2063 | 0 | det = 1 / det; |
2064 | 0 | ictm[0] = ctm[3] * det; |
2065 | 0 | ictm[1] = -ctm[1] * det; |
2066 | 0 | ictm[2] = -ctm[2] * det; |
2067 | 0 | ictm[3] = ctm[0] * det; |
2068 | 0 | ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; |
2069 | 0 | ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; |
2070 | | // m1 = PTM * BTM = PTM * base transform matrix |
2071 | 0 | m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2]; |
2072 | 0 | m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3]; |
2073 | 0 | m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2]; |
2074 | 0 | m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3]; |
2075 | 0 | m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4]; |
2076 | 0 | m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5]; |
2077 | | // m = m1 * iCTM = (PTM * BTM) * (iCTM) |
2078 | 0 | m[0] = m1[0] * ictm[0] + m1[1] * ictm[2]; |
2079 | 0 | m[1] = m1[0] * ictm[1] + m1[1] * ictm[3]; |
2080 | 0 | m[2] = m1[2] * ictm[0] + m1[3] * ictm[2]; |
2081 | 0 | m[3] = m1[2] * ictm[1] + m1[3] * ictm[3]; |
2082 | 0 | m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; |
2083 | 0 | m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; |
2084 | | |
2085 | | // construct a (device space) -> (pattern space) transform matrix |
2086 | 0 | det = m1[0] * m1[3] - m1[1] * m1[2]; |
2087 | 0 | if (fabs(det) <= 1e-10) { |
2088 | 0 | error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill"); |
2089 | 0 | return; |
2090 | 0 | } |
2091 | 0 | det = 1 / det; |
2092 | 0 | imb[0] = m1[3] * det; |
2093 | 0 | imb[1] = -m1[1] * det; |
2094 | 0 | imb[2] = -m1[2] * det; |
2095 | 0 | imb[3] = m1[0] * det; |
2096 | 0 | imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det; |
2097 | 0 | imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det; |
2098 | | |
2099 | | // save current graphics state |
2100 | 0 | savedState = saveStateStack(); |
2101 | | |
2102 | | // set underlying color space (for uncolored tiling patterns); set |
2103 | | // various other parameters (stroke color, line width) to match |
2104 | | // Adobe's behavior |
2105 | 0 | state->setFillPattern(NULL); |
2106 | 0 | state->setStrokePattern(NULL); |
2107 | 0 | state->setFillOverprint(gFalse); |
2108 | 0 | state->setStrokeOverprint(gFalse); |
2109 | 0 | state->setOverprintMode(0); |
2110 | 0 | if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) { |
2111 | 0 | state->setFillColorSpace(cs->copy()); |
2112 | 0 | out->updateFillColorSpace(state); |
2113 | 0 | state->setStrokeColorSpace(cs->copy()); |
2114 | 0 | out->updateStrokeColorSpace(state); |
2115 | 0 | if (stroke) { |
2116 | 0 | state->setFillColor(state->getStrokeColor()); |
2117 | 0 | } else { |
2118 | 0 | state->setStrokeColor(state->getFillColor()); |
2119 | 0 | } |
2120 | 0 | out->updateFillColor(state); |
2121 | 0 | out->updateStrokeColor(state); |
2122 | 0 | state->setIgnoreColorOps(gTrue); |
2123 | 0 | } else { |
2124 | 0 | state->setFillColorSpace(GfxColorSpace::create(csDeviceGray)); |
2125 | 0 | out->updateFillColorSpace(state); |
2126 | 0 | state->getFillColorSpace()->getDefaultColor(&color); |
2127 | 0 | state->setFillColor(&color); |
2128 | 0 | out->updateFillColor(state); |
2129 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray)); |
2130 | 0 | out->updateStrokeColorSpace(state); |
2131 | 0 | state->getStrokeColorSpace()->getDefaultColor(&color); |
2132 | 0 | state->setStrokeColor(&color); |
2133 | 0 | out->updateStrokeColor(state); |
2134 | 0 | } |
2135 | 0 | if (!stroke) { |
2136 | 0 | state->setLineWidth(0); |
2137 | 0 | out->updateLineWidth(state); |
2138 | 0 | state->setLineDash(NULL, 0, 0); |
2139 | 0 | out->updateLineDash(state); |
2140 | 0 | } |
2141 | | |
2142 | | // clip to current path |
2143 | 0 | if (stroke) { |
2144 | 0 | state->clipToStrokePath(); |
2145 | 0 | out->clipToStrokePath(state); |
2146 | 0 | } else if (!text) { |
2147 | 0 | state->clip(); |
2148 | 0 | if (eoFill) { |
2149 | 0 | out->eoClip(state); |
2150 | 0 | } else { |
2151 | 0 | out->clip(state); |
2152 | 0 | } |
2153 | 0 | } |
2154 | 0 | state->clearPath(); |
2155 | | |
2156 | | // get the clip region, check for empty |
2157 | 0 | state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax); |
2158 | 0 | if (cxMin > cxMax || cyMin > cyMax) { |
2159 | 0 | goto err; |
2160 | 0 | } |
2161 | | |
2162 | | // transform clip region bbox to pattern space |
2163 | 0 | xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4]; |
2164 | 0 | yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5]; |
2165 | 0 | x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4]; |
2166 | 0 | y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5]; |
2167 | 0 | if (x1 < xMin) { |
2168 | 0 | xMin = x1; |
2169 | 0 | } else if (x1 > xMax) { |
2170 | 0 | xMax = x1; |
2171 | 0 | } |
2172 | 0 | if (y1 < yMin) { |
2173 | 0 | yMin = y1; |
2174 | 0 | } else if (y1 > yMax) { |
2175 | 0 | yMax = y1; |
2176 | 0 | } |
2177 | 0 | x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4]; |
2178 | 0 | y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5]; |
2179 | 0 | if (x1 < xMin) { |
2180 | 0 | xMin = x1; |
2181 | 0 | } else if (x1 > xMax) { |
2182 | 0 | xMax = x1; |
2183 | 0 | } |
2184 | 0 | if (y1 < yMin) { |
2185 | 0 | yMin = y1; |
2186 | 0 | } else if (y1 > yMax) { |
2187 | 0 | yMax = y1; |
2188 | 0 | } |
2189 | 0 | x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4]; |
2190 | 0 | y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5]; |
2191 | 0 | if (x1 < xMin) { |
2192 | 0 | xMin = x1; |
2193 | 0 | } else if (x1 > xMax) { |
2194 | 0 | xMax = x1; |
2195 | 0 | } |
2196 | 0 | if (y1 < yMin) { |
2197 | 0 | yMin = y1; |
2198 | 0 | } else if (y1 > yMax) { |
2199 | 0 | yMax = y1; |
2200 | 0 | } |
2201 | | |
2202 | | // draw the pattern |
2203 | | //~ this should treat negative steps differently -- start at right/top |
2204 | | //~ edge instead of left/bottom (?) |
2205 | 0 | bbox[0] = tPat->getBBox()[0]; |
2206 | 0 | bbox[1] = tPat->getBBox()[1]; |
2207 | 0 | bbox[2] = tPat->getBBox()[2]; |
2208 | 0 | bbox[3] = tPat->getBBox()[3]; |
2209 | 0 | if (bbox[0] > bbox[2]) { |
2210 | 0 | t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t; |
2211 | 0 | } |
2212 | 0 | if (bbox[1] > bbox[3]) { |
2213 | 0 | t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t; |
2214 | 0 | } |
2215 | 0 | xstep = tPat->getXStep(); |
2216 | 0 | ystep = tPat->getYStep(); |
2217 | 0 | if (xstep == 0 || ystep == 0) { |
2218 | 0 | error(errSyntaxError, getPos(), "Zero x or y step in tiling pattern fill"); |
2219 | 0 | goto err; |
2220 | 0 | } |
2221 | 0 | if (xstep > 0) { |
2222 | 0 | xi0 = (int)ceil((xMin - bbox[2]) / xstep); |
2223 | 0 | xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1; |
2224 | 0 | } else { |
2225 | 0 | xi0 = (int)ceil((xMax - bbox[0]) / xstep); |
2226 | 0 | xi1 = (int)floor((xMin - bbox[2]) / xstep) + 1; |
2227 | 0 | } |
2228 | 0 | if (ystep > 0) { |
2229 | 0 | yi0 = (int)ceil((yMin - bbox[3]) / ystep); |
2230 | 0 | yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1; |
2231 | 0 | } else { |
2232 | 0 | yi0 = (int)ceil((yMax - bbox[1]) / ystep); |
2233 | 0 | yi1 = (int)floor((yMin - bbox[3]) / ystep) + 1; |
2234 | 0 | } |
2235 | 0 | for (i = 0; i < 4; ++i) { |
2236 | 0 | m1[i] = m[i]; |
2237 | 0 | } |
2238 | 0 | if (out->useTilingPatternFill() && !tPat->usesBlendMode(xref)) { |
2239 | 0 | m1[4] = m[4]; |
2240 | 0 | m1[5] = m[5]; |
2241 | 0 | out->tilingPatternFill(state, this, tPat->getContentStreamRef(), |
2242 | 0 | tPat->getPaintType(), tPat->getTilingType(), |
2243 | 0 | tPat->getResDict(), |
2244 | 0 | m1, bbox, |
2245 | 0 | xi0, yi0, xi1, yi1, xstep, ystep); |
2246 | 0 | } else { |
2247 | 0 | abortCheckCounter = 0; |
2248 | 0 | for (yi = yi0; yi < yi1; ++yi) { |
2249 | 0 | for (xi = xi0; xi < xi1; ++xi) { |
2250 | 0 | if (abortCheckCbk) { |
2251 | 0 | ++abortCheckCounter; |
2252 | 0 | if (abortCheckCounter > 100) { |
2253 | 0 | if ((*abortCheckCbk)(abortCheckCbkData)) { |
2254 | 0 | goto err; |
2255 | 0 | } |
2256 | 0 | abortCheckCounter = 0; |
2257 | 0 | } |
2258 | 0 | } |
2259 | 0 | x = xi * xstep; |
2260 | 0 | y = yi * ystep; |
2261 | 0 | m1[4] = x * m[0] + y * m[2] + m[4]; |
2262 | 0 | m1[5] = x * m[1] + y * m[3] + m[5]; |
2263 | 0 | drawForm(tPat->getContentStreamRef(), tPat->getResDict(), |
2264 | 0 | m1, bbox); |
2265 | 0 | } |
2266 | 0 | } |
2267 | 0 | } |
2268 | | |
2269 | | // restore graphics state |
2270 | 0 | err: |
2271 | 0 | restoreStateStack(savedState); |
2272 | 0 | } |
2273 | | |
2274 | | void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, |
2275 | 0 | GBool stroke, GBool eoFill, GBool text) { |
2276 | 0 | GfxShading *shading; |
2277 | 0 | GfxState *savedState; |
2278 | 0 | double *ctm, *btm, *ptm; |
2279 | 0 | double m[6], ictm[6], m1[6]; |
2280 | 0 | double xMin, yMin, xMax, yMax; |
2281 | 0 | double det; |
2282 | |
|
2283 | 0 | shading = sPat->getShading(); |
2284 | | |
2285 | | // save current graphics state |
2286 | 0 | savedState = saveStateStack(); |
2287 | | |
2288 | | // clip to current path |
2289 | 0 | if (stroke) { |
2290 | 0 | state->clipToStrokePath(); |
2291 | 0 | out->clipToStrokePath(state); |
2292 | 0 | state->setFillOverprint(state->getStrokeOverprint()); |
2293 | 0 | } else if (!text) { |
2294 | 0 | state->clip(); |
2295 | 0 | if (eoFill) { |
2296 | 0 | out->eoClip(state); |
2297 | 0 | } else { |
2298 | 0 | out->clip(state); |
2299 | 0 | } |
2300 | 0 | } |
2301 | 0 | state->clearPath(); |
2302 | | |
2303 | | // construct a (pattern space) -> (current space) transform matrix |
2304 | 0 | ctm = state->getCTM(); |
2305 | 0 | btm = baseMatrix; |
2306 | 0 | ptm = sPat->getMatrix(); |
2307 | | // iCTM = invert CTM |
2308 | 0 | det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; |
2309 | 0 | if (fabs(det) <= 1e-10) { |
2310 | 0 | error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill"); |
2311 | 0 | restoreStateStack(savedState); |
2312 | 0 | return; |
2313 | 0 | } |
2314 | 0 | det = 1 / det; |
2315 | 0 | ictm[0] = ctm[3] * det; |
2316 | 0 | ictm[1] = -ctm[1] * det; |
2317 | 0 | ictm[2] = -ctm[2] * det; |
2318 | 0 | ictm[3] = ctm[0] * det; |
2319 | 0 | ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; |
2320 | 0 | ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; |
2321 | | // m1 = PTM * BTM = PTM * base transform matrix |
2322 | 0 | m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2]; |
2323 | 0 | m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3]; |
2324 | 0 | m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2]; |
2325 | 0 | m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3]; |
2326 | 0 | m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4]; |
2327 | 0 | m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5]; |
2328 | | // m = m1 * iCTM = (PTM * BTM) * (iCTM) |
2329 | 0 | m[0] = m1[0] * ictm[0] + m1[1] * ictm[2]; |
2330 | 0 | m[1] = m1[0] * ictm[1] + m1[1] * ictm[3]; |
2331 | 0 | m[2] = m1[2] * ictm[0] + m1[3] * ictm[2]; |
2332 | 0 | m[3] = m1[2] * ictm[1] + m1[3] * ictm[3]; |
2333 | 0 | m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; |
2334 | 0 | m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; |
2335 | | |
2336 | | // set the new matrix |
2337 | 0 | state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]); |
2338 | 0 | out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]); |
2339 | | |
2340 | | // clip to bbox |
2341 | 0 | if (shading->getHasBBox()) { |
2342 | 0 | shading->getBBox(&xMin, &yMin, &xMax, &yMax); |
2343 | 0 | state->moveTo(xMin, yMin); |
2344 | 0 | state->lineTo(xMax, yMin); |
2345 | 0 | state->lineTo(xMax, yMax); |
2346 | 0 | state->lineTo(xMin, yMax); |
2347 | 0 | state->closePath(); |
2348 | 0 | state->clip(); |
2349 | 0 | out->clip(state); |
2350 | 0 | state->clearPath(); |
2351 | 0 | } |
2352 | | |
2353 | | // set the color space |
2354 | 0 | state->setFillColorSpace(shading->getColorSpace()->copy()); |
2355 | 0 | out->updateFillColorSpace(state); |
2356 | | |
2357 | | // background color fill |
2358 | 0 | if (shading->getHasBackground()) { |
2359 | 0 | state->setFillColor(shading->getBackground()); |
2360 | 0 | out->updateFillColor(state); |
2361 | 0 | state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
2362 | 0 | state->moveTo(xMin, yMin); |
2363 | 0 | state->lineTo(xMax, yMin); |
2364 | 0 | state->lineTo(xMax, yMax); |
2365 | 0 | state->lineTo(xMin, yMax); |
2366 | 0 | state->closePath(); |
2367 | 0 | out->fill(state); |
2368 | 0 | state->clearPath(); |
2369 | 0 | } |
2370 | | |
2371 | | // perform the fill |
2372 | 0 | doShFill(shading); |
2373 | | |
2374 | | // restore graphics state |
2375 | 0 | restoreStateStack(savedState); |
2376 | 0 | } |
2377 | | |
2378 | 0 | void Gfx::opShFill(Object args[], int numArgs) { |
2379 | 0 | GfxShading *shading; |
2380 | 0 | GfxState *savedState; |
2381 | 0 | double xMin, yMin, xMax, yMax; |
2382 | |
|
2383 | 0 | if (state->getIgnoreColorOps()) { |
2384 | 0 | error(errSyntaxWarning, getPos(), "Ignoring shaded fill" |
2385 | 0 | " in uncolored Type 3 char or tiling pattern"); |
2386 | 0 | return; |
2387 | 0 | } |
2388 | | |
2389 | 0 | if (!out->needNonText()) { |
2390 | 0 | return; |
2391 | 0 | } |
2392 | | |
2393 | 0 | if (!ocState) { |
2394 | 0 | return; |
2395 | 0 | } |
2396 | | |
2397 | 0 | if (!(shading = res->lookupShading(args[0].getName() |
2398 | 0 | ))) { |
2399 | 0 | return; |
2400 | 0 | } |
2401 | | |
2402 | | // save current graphics state |
2403 | 0 | savedState = saveStateStack(); |
2404 | | |
2405 | | // clip to bbox |
2406 | 0 | if (shading->getHasBBox()) { |
2407 | 0 | shading->getBBox(&xMin, &yMin, &xMax, &yMax); |
2408 | 0 | state->moveTo(xMin, yMin); |
2409 | 0 | state->lineTo(xMax, yMin); |
2410 | 0 | state->lineTo(xMax, yMax); |
2411 | 0 | state->lineTo(xMin, yMax); |
2412 | 0 | state->closePath(); |
2413 | 0 | state->clip(); |
2414 | 0 | out->clip(state); |
2415 | 0 | state->clearPath(); |
2416 | 0 | } |
2417 | | |
2418 | | // set the color space |
2419 | 0 | state->setFillColorSpace(shading->getColorSpace()->copy()); |
2420 | 0 | out->updateFillColorSpace(state); |
2421 | | |
2422 | | // perform the fill |
2423 | 0 | doShFill(shading); |
2424 | | |
2425 | | // restore graphics state |
2426 | 0 | restoreStateStack(savedState); |
2427 | |
|
2428 | 0 | delete shading; |
2429 | 0 | } |
2430 | | |
2431 | 0 | void Gfx::doShFill(GfxShading *shading) { |
2432 | 0 | if (out->shadedFill(state, shading)) { |
2433 | 0 | return; |
2434 | 0 | } |
2435 | | |
2436 | | // do shading type-specific operations |
2437 | 0 | switch (shading->getType()) { |
2438 | 0 | case 1: |
2439 | 0 | doFunctionShFill((GfxFunctionShading *)shading); |
2440 | 0 | break; |
2441 | 0 | case 2: |
2442 | 0 | doAxialShFill((GfxAxialShading *)shading); |
2443 | 0 | break; |
2444 | 0 | case 3: |
2445 | 0 | doRadialShFill((GfxRadialShading *)shading); |
2446 | 0 | break; |
2447 | 0 | case 4: |
2448 | 0 | case 5: |
2449 | 0 | doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); |
2450 | 0 | break; |
2451 | 0 | case 6: |
2452 | 0 | case 7: |
2453 | 0 | doPatchMeshShFill((GfxPatchMeshShading *)shading); |
2454 | 0 | break; |
2455 | 0 | } |
2456 | 0 | } |
2457 | | |
2458 | 0 | void Gfx::doFunctionShFill(GfxFunctionShading *shading) { |
2459 | 0 | double x0, y0, x1, y1; |
2460 | 0 | GfxColor colors[4]; |
2461 | |
|
2462 | 0 | shading->getDomain(&x0, &y0, &x1, &y1); |
2463 | 0 | shading->getColor(x0, y0, &colors[0]); |
2464 | 0 | shading->getColor(x0, y1, &colors[1]); |
2465 | 0 | shading->getColor(x1, y0, &colors[2]); |
2466 | 0 | shading->getColor(x1, y1, &colors[3]); |
2467 | 0 | doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0); |
2468 | 0 | } |
2469 | | |
2470 | | void Gfx::doFunctionShFill1(GfxFunctionShading *shading, |
2471 | | double x0, double y0, |
2472 | | double x1, double y1, |
2473 | 0 | GfxColor *colors, int depth) { |
2474 | 0 | GfxColor fillColor; |
2475 | 0 | GfxColor color0M, color1M, colorM0, colorM1, colorMM; |
2476 | 0 | GfxColor colors2[4]; |
2477 | 0 | double *matrix; |
2478 | 0 | double xM, yM; |
2479 | 0 | int nComps, i, j; |
2480 | |
|
2481 | 0 | nComps = shading->getColorSpace()->getNComps(); |
2482 | 0 | matrix = shading->getMatrix(); |
2483 | | |
2484 | | // compare the four corner colors |
2485 | 0 | for (i = 0; i < 4; ++i) { |
2486 | 0 | for (j = 0; j < nComps; ++j) { |
2487 | 0 | if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) { |
2488 | 0 | break; |
2489 | 0 | } |
2490 | 0 | } |
2491 | 0 | if (j < nComps) { |
2492 | 0 | break; |
2493 | 0 | } |
2494 | 0 | } |
2495 | | |
2496 | | // center of the rectangle |
2497 | 0 | xM = 0.5 * (x0 + x1); |
2498 | 0 | yM = 0.5 * (y0 + y1); |
2499 | | |
2500 | | // the four corner colors are close (or we hit the recursive limit) |
2501 | | // -- fill the rectangle; but require at least one subdivision |
2502 | | // (depth==0) to avoid problems when the four outer corners of the |
2503 | | // shaded region are the same color |
2504 | 0 | if ((i == 4 && depth > 0) || depth == functionMaxDepth) { |
2505 | | |
2506 | | // use the center color |
2507 | 0 | shading->getColor(xM, yM, &fillColor); |
2508 | 0 | state->setFillColor(&fillColor); |
2509 | 0 | out->updateFillColor(state); |
2510 | | |
2511 | | // fill the rectangle |
2512 | 0 | state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4], |
2513 | 0 | x0 * matrix[1] + y0 * matrix[3] + matrix[5]); |
2514 | 0 | state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4], |
2515 | 0 | x1 * matrix[1] + y0 * matrix[3] + matrix[5]); |
2516 | 0 | state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4], |
2517 | 0 | x1 * matrix[1] + y1 * matrix[3] + matrix[5]); |
2518 | 0 | state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4], |
2519 | 0 | x0 * matrix[1] + y1 * matrix[3] + matrix[5]); |
2520 | 0 | state->closePath(); |
2521 | 0 | out->fill(state); |
2522 | 0 | state->clearPath(); |
2523 | | |
2524 | | // the four corner colors are not close enough -- subdivide the |
2525 | | // rectangle |
2526 | 0 | } else { |
2527 | | |
2528 | | // colors[0] colorM0 colors[2] |
2529 | | // (x0,y0) (xM,y0) (x1,y0) |
2530 | | // +----------+----------+ |
2531 | | // | | | |
2532 | | // | UL | UR | |
2533 | | // color0M | colorMM | color1M |
2534 | | // (x0,yM) +----------+----------+ (x1,yM) |
2535 | | // | (xM,yM) | |
2536 | | // | LL | LR | |
2537 | | // | | | |
2538 | | // +----------+----------+ |
2539 | | // colors[1] colorM1 colors[3] |
2540 | | // (x0,y1) (xM,y1) (x1,y1) |
2541 | |
|
2542 | 0 | shading->getColor(x0, yM, &color0M); |
2543 | 0 | shading->getColor(x1, yM, &color1M); |
2544 | 0 | shading->getColor(xM, y0, &colorM0); |
2545 | 0 | shading->getColor(xM, y1, &colorM1); |
2546 | 0 | shading->getColor(xM, yM, &colorMM); |
2547 | | |
2548 | | // upper-left sub-rectangle |
2549 | 0 | colors2[0] = colors[0]; |
2550 | 0 | colors2[1] = color0M; |
2551 | 0 | colors2[2] = colorM0; |
2552 | 0 | colors2[3] = colorMM; |
2553 | 0 | doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1); |
2554 | | |
2555 | | // lower-left sub-rectangle |
2556 | 0 | colors2[0] = color0M; |
2557 | 0 | colors2[1] = colors[1]; |
2558 | 0 | colors2[2] = colorMM; |
2559 | 0 | colors2[3] = colorM1; |
2560 | 0 | doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1); |
2561 | | |
2562 | | // upper-right sub-rectangle |
2563 | 0 | colors2[0] = colorM0; |
2564 | 0 | colors2[1] = colorMM; |
2565 | 0 | colors2[2] = colors[2]; |
2566 | 0 | colors2[3] = color1M; |
2567 | 0 | doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1); |
2568 | | |
2569 | | // lower-right sub-rectangle |
2570 | 0 | colors2[0] = colorMM; |
2571 | 0 | colors2[1] = colorM1; |
2572 | 0 | colors2[2] = color1M; |
2573 | 0 | colors2[3] = colors[3]; |
2574 | 0 | doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1); |
2575 | 0 | } |
2576 | 0 | } |
2577 | | |
2578 | 0 | void Gfx::doAxialShFill(GfxAxialShading *shading) { |
2579 | 0 | double xMin, yMin, xMax, yMax; |
2580 | 0 | double x0, y0, x1, y1; |
2581 | 0 | double dx, dy, mul; |
2582 | 0 | GBool dxdyZero, horiz; |
2583 | 0 | double tMin, tMax, tMinExt, tMaxExt, t, tx, ty; |
2584 | 0 | double sMin, sMax, tmp; |
2585 | 0 | double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1; |
2586 | 0 | double t0, t1, tt; |
2587 | 0 | GfxColor colors[axialSplits]; |
2588 | 0 | int abortCheckCounter, nComps, i, j, k; |
2589 | | |
2590 | | // get the clip region bbox |
2591 | 0 | state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
2592 | | |
2593 | | // compute min and max t values, based on the four corners of the |
2594 | | // clip region bbox |
2595 | 0 | shading->getCoords(&x0, &y0, &x1, &y1); |
2596 | 0 | dx = x1 - x0; |
2597 | 0 | dy = y1 - y0; |
2598 | 0 | dxdyZero = fabs(dx) < 0.0001 && fabs(dy) < 0.0001; |
2599 | 0 | horiz = fabs(dy) < fabs(dx); |
2600 | 0 | if (dxdyZero) { |
2601 | 0 | tMinExt = tMaxExt = 0; |
2602 | 0 | tMin = tMax = 0; |
2603 | 0 | } else { |
2604 | 0 | mul = 1 / (dx * dx + dy * dy); |
2605 | 0 | tMinExt = tMaxExt = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; |
2606 | 0 | t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; |
2607 | 0 | if (t < tMinExt) { |
2608 | 0 | tMinExt = t; |
2609 | 0 | } else if (t > tMaxExt) { |
2610 | 0 | tMaxExt = t; |
2611 | 0 | } |
2612 | 0 | t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; |
2613 | 0 | if (t < tMinExt) { |
2614 | 0 | tMinExt = t; |
2615 | 0 | } else if (t > tMaxExt) { |
2616 | 0 | tMaxExt = t; |
2617 | 0 | } |
2618 | 0 | t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; |
2619 | 0 | if (t < tMinExt) { |
2620 | 0 | tMinExt = t; |
2621 | 0 | } else if (t > tMaxExt) { |
2622 | 0 | tMaxExt = t; |
2623 | 0 | } |
2624 | 0 | if ((tMin = tMinExt) < 0) { |
2625 | 0 | tMin = 0; |
2626 | 0 | } |
2627 | 0 | if (!shading->getExtend0()) { |
2628 | 0 | tMinExt = tMin; |
2629 | 0 | } |
2630 | 0 | if ((tMax = tMaxExt) > 1) { |
2631 | 0 | tMax = 1; |
2632 | 0 | } |
2633 | 0 | if (!shading->getExtend1()) { |
2634 | 0 | tMaxExt = tMax; |
2635 | 0 | } |
2636 | 0 | } |
2637 | | |
2638 | | // get the function domain |
2639 | 0 | t0 = shading->getDomain0(); |
2640 | 0 | t1 = shading->getDomain1(); |
2641 | | |
2642 | | // Traverse the t axis and do the shading. |
2643 | | // |
2644 | | // For each point (tx, ty) on the t axis, consider a line through |
2645 | | // that point perpendicular to the t axis: |
2646 | | // |
2647 | | // x(s) = tx + s * -dy --> s = (x - tx) / -dy |
2648 | | // y(s) = ty + s * dx --> s = (y - ty) / dx |
2649 | | // |
2650 | | // Then look at the intersection of this line with the bounding box |
2651 | | // (xMin, yMin, xMax, yMax). For -1 < |dy/dx| < 1, look at the |
2652 | | // intersection with yMin, yMax: |
2653 | | // |
2654 | | // s0 = (yMin - ty) / dx |
2655 | | // s1 = (yMax - ty) / dx |
2656 | | // |
2657 | | // else look at the intersection with xMin, xMax: |
2658 | | // |
2659 | | // s0 = (xMin - tx) / -dy |
2660 | | // s1 = (xMax - tx) / -dy |
2661 | | // |
2662 | | // Each filled polygon is bounded by two of these line segments |
2663 | | // perpdendicular to the t axis. |
2664 | | // |
2665 | | // The t axis is bisected into smaller regions until the color |
2666 | | // difference across a region is small enough, and then the region |
2667 | | // is painted with a single color. |
2668 | | |
2669 | | // compute the coordinates of the point on the t axis at t = tMin; |
2670 | | // then compute the intersection of the perpendicular line with the |
2671 | | // bounding box |
2672 | 0 | tx = x0 + tMin * dx; |
2673 | 0 | ty = y0 + tMin * dy; |
2674 | 0 | if (dxdyZero) { |
2675 | 0 | sMin = sMax = 0; |
2676 | 0 | } else { |
2677 | 0 | if (horiz) { |
2678 | 0 | sMin = (yMin - ty) / dx; |
2679 | 0 | sMax = (yMax - ty) / dx; |
2680 | 0 | } else { |
2681 | 0 | sMin = (xMin - tx) / -dy; |
2682 | 0 | sMax = (xMax - tx) / -dy; |
2683 | 0 | } |
2684 | 0 | if (sMin > sMax) { |
2685 | 0 | tmp = sMin; sMin = sMax; sMax = tmp; |
2686 | 0 | } |
2687 | 0 | } |
2688 | 0 | ux0 = tx - sMin * dy; |
2689 | 0 | uy0 = ty + sMin * dx; |
2690 | 0 | vx0 = tx - sMax * dy; |
2691 | 0 | vy0 = ty + sMax * dx; |
2692 | | |
2693 | | // fill the extension at t0 |
2694 | 0 | if (shading->getExtend0() && tMinExt < tMin) { |
2695 | | |
2696 | | // compute the color at t0 |
2697 | 0 | shading->getColor(t0, &colors[0]); |
2698 | | |
2699 | | // compute the coordinates of the point on the t axis at t = |
2700 | | // tMinExt; then compute the intersection of the perpendicular |
2701 | | // line with the bounding box |
2702 | 0 | tx = x0 + tMinExt * dx; |
2703 | 0 | ty = y0 + tMinExt * dy; |
2704 | 0 | if (dxdyZero) { |
2705 | 0 | sMin = sMax = 0; |
2706 | 0 | } else { |
2707 | 0 | if (horiz) { |
2708 | 0 | sMin = (yMin - ty) / dx; |
2709 | 0 | sMax = (yMax - ty) / dx; |
2710 | 0 | } else { |
2711 | 0 | sMin = (xMin - tx) / -dy; |
2712 | 0 | sMax = (xMax - tx) / -dy; |
2713 | 0 | } |
2714 | 0 | if (sMin > sMax) { |
2715 | 0 | tmp = sMin; sMin = sMax; sMax = tmp; |
2716 | 0 | } |
2717 | 0 | } |
2718 | 0 | ux1 = tx - sMin * dy; |
2719 | 0 | uy1 = ty + sMin * dx; |
2720 | 0 | vx1 = tx - sMax * dy; |
2721 | 0 | vy1 = ty + sMax * dx; |
2722 | | |
2723 | | // set the color |
2724 | 0 | state->setFillColor(&colors[0]); |
2725 | 0 | out->updateFillColor(state); |
2726 | | |
2727 | | // fill the region |
2728 | 0 | state->moveTo(ux1, uy1); |
2729 | 0 | state->lineTo(vx1, vy1); |
2730 | 0 | state->lineTo(vx0, vy0); |
2731 | 0 | state->lineTo(ux0, uy0); |
2732 | 0 | state->closePath(); |
2733 | 0 | out->fill(state); |
2734 | 0 | state->clearPath(); |
2735 | 0 | } |
2736 | | |
2737 | | // traverse the t axis, splitting [tMin, tMax] into axialSplits regions |
2738 | | |
2739 | | // compute the color in the center of each region |
2740 | 0 | for (i = 0; i < axialSplits; ++i) { |
2741 | 0 | t = tMin + (tMax - tMin) * (i + 0.5) / axialSplits; |
2742 | 0 | tt = t0 + (t1 - t0) * t; |
2743 | 0 | shading->getColor(tt, &colors[i]); |
2744 | 0 | } |
2745 | | |
2746 | | // each iteration draws one or more regions, starting at i -- |
2747 | | // if the colors are similar, it will combine regions i, i+1, ... |
2748 | 0 | nComps = shading->getColorSpace()->getNComps(); |
2749 | 0 | abortCheckCounter = 0; |
2750 | 0 | i = 0; |
2751 | 0 | while (i < axialSplits) { |
2752 | |
|
2753 | 0 | if (abortCheckCbk) { |
2754 | 0 | ++abortCheckCounter; |
2755 | 0 | if (abortCheckCounter > 100) { |
2756 | 0 | if ((*abortCheckCbk)(abortCheckCbkData)) { |
2757 | 0 | break; |
2758 | 0 | } |
2759 | 0 | abortCheckCounter = 0; |
2760 | 0 | } |
2761 | 0 | } |
2762 | | |
2763 | | // check for similar colors |
2764 | 0 | for (j = i + 1; j < axialSplits; ++j) { |
2765 | 0 | for (k = 0; k < nComps; ++k) { |
2766 | 0 | if (abs(colors[j].c[k] - colors[i].c[k]) > axialColorDelta) { |
2767 | 0 | break; |
2768 | 0 | } |
2769 | 0 | } |
2770 | 0 | if (k < nComps) { |
2771 | 0 | break; |
2772 | 0 | } |
2773 | 0 | } |
2774 | | |
2775 | | // compute the coordinates of the point on the t axis; then |
2776 | | // compute the intersection of the perpendicular line with the |
2777 | | // bounding box |
2778 | 0 | t = tMin + (tMax - tMin) * (double)j / axialSplits; |
2779 | 0 | tx = x0 + t * dx; |
2780 | 0 | ty = y0 + t * dy; |
2781 | 0 | if (dxdyZero) { |
2782 | 0 | sMin = sMax = 0; |
2783 | 0 | } else { |
2784 | 0 | if (horiz) { |
2785 | 0 | sMin = (yMin - ty) / dx; |
2786 | 0 | sMax = (yMax - ty) / dx; |
2787 | 0 | } else { |
2788 | 0 | sMin = (xMin - tx) / -dy; |
2789 | 0 | sMax = (xMax - tx) / -dy; |
2790 | 0 | } |
2791 | 0 | if (sMin > sMax) { |
2792 | 0 | tmp = sMin; sMin = sMax; sMax = tmp; |
2793 | 0 | } |
2794 | 0 | } |
2795 | 0 | ux1 = tx - sMin * dy; |
2796 | 0 | uy1 = ty + sMin * dx; |
2797 | 0 | vx1 = tx - sMax * dy; |
2798 | 0 | vy1 = ty + sMax * dx; |
2799 | | |
2800 | | // set the color |
2801 | 0 | state->setFillColor(&colors[i]); |
2802 | 0 | out->updateFillColor(state); |
2803 | | |
2804 | | // fill the region |
2805 | 0 | state->moveTo(ux0, uy0); |
2806 | 0 | state->lineTo(vx0, vy0); |
2807 | 0 | state->lineTo(vx1, vy1); |
2808 | 0 | state->lineTo(ux1, uy1); |
2809 | 0 | state->closePath(); |
2810 | 0 | out->fill(state); |
2811 | 0 | state->clearPath(); |
2812 | | |
2813 | | // set up for next region |
2814 | 0 | ux0 = ux1; |
2815 | 0 | uy0 = uy1; |
2816 | 0 | vx0 = vx1; |
2817 | 0 | vy0 = vy1; |
2818 | |
|
2819 | 0 | i = j; |
2820 | 0 | } |
2821 | | |
2822 | | // fill the extension at t1 |
2823 | 0 | if (shading->getExtend1() && tMaxExt > tMax) { |
2824 | | |
2825 | | // compute the color at t1 |
2826 | 0 | shading->getColor(t1, &colors[0]); |
2827 | | |
2828 | | // compute the coordinates of the point on the t axis at t = |
2829 | | // tMaxExt; then compute the intersection of the perpendicular |
2830 | | // line with the bounding box |
2831 | 0 | tx = x0 + tMaxExt * dx; |
2832 | 0 | ty = y0 + tMaxExt * dy; |
2833 | 0 | if (dxdyZero) { |
2834 | 0 | sMin = sMax = 0; |
2835 | 0 | } else { |
2836 | 0 | if (horiz) { |
2837 | 0 | sMin = (yMin - ty) / dx; |
2838 | 0 | sMax = (yMax - ty) / dx; |
2839 | 0 | } else { |
2840 | 0 | sMin = (xMin - tx) / -dy; |
2841 | 0 | sMax = (xMax - tx) / -dy; |
2842 | 0 | } |
2843 | 0 | if (sMin > sMax) { |
2844 | 0 | tmp = sMin; sMin = sMax; sMax = tmp; |
2845 | 0 | } |
2846 | 0 | } |
2847 | 0 | ux1 = tx - sMin * dy; |
2848 | 0 | uy1 = ty + sMin * dx; |
2849 | 0 | vx1 = tx - sMax * dy; |
2850 | 0 | vy1 = ty + sMax * dx; |
2851 | | |
2852 | | // set the color |
2853 | 0 | state->setFillColor(&colors[0]); |
2854 | 0 | out->updateFillColor(state); |
2855 | | |
2856 | | // fill the region |
2857 | 0 | state->moveTo(ux0, uy0); |
2858 | 0 | state->lineTo(vx0, vy0); |
2859 | 0 | state->lineTo(vx1, vy1); |
2860 | 0 | state->lineTo(ux1, uy1); |
2861 | 0 | state->closePath(); |
2862 | 0 | out->fill(state); |
2863 | 0 | state->clearPath(); |
2864 | 0 | } |
2865 | 0 | } |
2866 | | |
2867 | | #if defined(__GNUC__) && !defined(__clang__) |
2868 | | // this function makes a lot of sin()/cos() calls, which are slow |
2869 | | // with glibc 2.16 and newer on x86; accuracy isn't terribly |
2870 | | // important here, so tell gcc to use the fast version |
2871 | | #pragma GCC optimize ("fast-math") |
2872 | | #endif |
2873 | | |
2874 | 0 | void Gfx::doRadialShFill(GfxRadialShading *shading) { |
2875 | 0 | double xMin, yMin, xMax, yMax; |
2876 | 0 | double x0, y0, r0, x1, y1, r1, t0, t1; |
2877 | 0 | int nComps; |
2878 | 0 | GfxColor colorA, colorB; |
2879 | 0 | double xa, ya, xb, yb, ra, rb; |
2880 | 0 | double ta, tb, sa, sb; |
2881 | 0 | double sMin, sMax, h; |
2882 | 0 | double sLeft, sRight, sTop, sBottom, sZero, sDiag; |
2883 | 0 | GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; |
2884 | 0 | GBool haveSMin, haveSMax; |
2885 | 0 | GBool enclosed; |
2886 | 0 | double *ctm; |
2887 | 0 | double theta, alpha, angle, t; |
2888 | 0 | int abortCheckCounter, ia, ib, k, n; |
2889 | | |
2890 | | // get the shading info |
2891 | 0 | shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); |
2892 | 0 | t0 = shading->getDomain0(); |
2893 | 0 | t1 = shading->getDomain1(); |
2894 | 0 | nComps = shading->getColorSpace()->getNComps(); |
2895 | | |
2896 | | // Compute the point at which r(s) = 0; check for the enclosed |
2897 | | // circles case; and compute the angles for the tangent lines. |
2898 | 0 | h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); |
2899 | 0 | if (h == 0) { |
2900 | 0 | enclosed = gTrue; |
2901 | 0 | theta = 0; // make gcc happy |
2902 | 0 | } else if (r1 - r0 == 0) { |
2903 | 0 | enclosed = gFalse; |
2904 | 0 | theta = 0; |
2905 | 0 | } else if (fabs(r1 - r0) >= h - 0.0001) { |
2906 | 0 | enclosed = gTrue; |
2907 | 0 | theta = 0; // make gcc happy |
2908 | 0 | } else { |
2909 | 0 | enclosed = gFalse; |
2910 | 0 | theta = asin((r1 - r0) / h); |
2911 | 0 | } |
2912 | 0 | if (enclosed) { |
2913 | 0 | alpha = 0; |
2914 | 0 | } else { |
2915 | 0 | alpha = atan2(y1 - y0, x1 - x0); |
2916 | 0 | } |
2917 | | |
2918 | | // compute the (possibly extended) s range |
2919 | 0 | state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
2920 | 0 | if (enclosed) { |
2921 | 0 | sMin = 0; |
2922 | 0 | sMax = 1; |
2923 | 0 | } else { |
2924 | | // solve x(sLeft) + r(sLeft) = xMin |
2925 | 0 | if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { |
2926 | 0 | sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); |
2927 | 0 | } else { |
2928 | 0 | sLeft = 0; // make gcc happy |
2929 | 0 | } |
2930 | | // solve x(sRight) - r(sRight) = xMax |
2931 | 0 | if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { |
2932 | 0 | sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); |
2933 | 0 | } else { |
2934 | 0 | sRight = 0; // make gcc happy |
2935 | 0 | } |
2936 | | // solve y(sBottom) + r(sBottom) = yMin |
2937 | 0 | if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { |
2938 | 0 | sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); |
2939 | 0 | } else { |
2940 | 0 | sBottom = 0; // make gcc happy |
2941 | 0 | } |
2942 | | // solve y(sTop) - r(sTop) = yMax |
2943 | 0 | if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { |
2944 | 0 | sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); |
2945 | 0 | } else { |
2946 | 0 | sTop = 0; // make gcc happy |
2947 | 0 | } |
2948 | | // solve r(sZero) = 0 |
2949 | 0 | if ((haveSZero = fabs(r1 - r0) > 0.000001)) { |
2950 | 0 | sZero = -r0 / (r1 - r0); |
2951 | 0 | } else { |
2952 | 0 | sZero = 0; // make gcc happy |
2953 | 0 | } |
2954 | | // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) |
2955 | 0 | if (haveSZero) { |
2956 | 0 | sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + |
2957 | 0 | (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); |
2958 | 0 | } else { |
2959 | 0 | sDiag = 0; // make gcc happy |
2960 | 0 | } |
2961 | | // compute sMin |
2962 | 0 | if (shading->getExtend0()) { |
2963 | 0 | sMin = 0; |
2964 | 0 | haveSMin = gFalse; |
2965 | 0 | if (x0 < x1 && haveSLeft && sLeft < 0) { |
2966 | 0 | sMin = sLeft; |
2967 | 0 | haveSMin = gTrue; |
2968 | 0 | } else if (x0 > x1 && haveSRight && sRight < 0) { |
2969 | 0 | sMin = sRight; |
2970 | 0 | haveSMin = gTrue; |
2971 | 0 | } |
2972 | 0 | if (y0 < y1 && haveSBottom && sBottom < 0) { |
2973 | 0 | if (!haveSMin || sBottom > sMin) { |
2974 | 0 | sMin = sBottom; |
2975 | 0 | haveSMin = gTrue; |
2976 | 0 | } |
2977 | 0 | } else if (y0 > y1 && haveSTop && sTop < 0) { |
2978 | 0 | if (!haveSMin || sTop > sMin) { |
2979 | 0 | sMin = sTop; |
2980 | 0 | haveSMin = gTrue; |
2981 | 0 | } |
2982 | 0 | } |
2983 | 0 | if (haveSZero && sZero <= 0) { |
2984 | 0 | if (!haveSMin || sZero > sMin) { |
2985 | 0 | sMin = sZero; |
2986 | 0 | } |
2987 | 0 | } |
2988 | 0 | } else { |
2989 | 0 | sMin = 0; |
2990 | 0 | } |
2991 | | // compute sMax |
2992 | 0 | if (shading->getExtend1()) { |
2993 | 0 | sMax = 1; |
2994 | 0 | haveSMax = gFalse; |
2995 | 0 | if (x1 < x0 && haveSLeft && sLeft > 1) { |
2996 | 0 | sMax = sLeft; |
2997 | 0 | haveSMax = gTrue; |
2998 | 0 | } else if (x1 > x0 && haveSRight && sRight > 1) { |
2999 | 0 | sMax = sRight; |
3000 | 0 | haveSMax = gTrue; |
3001 | 0 | } |
3002 | 0 | if (y1 < y0 && haveSBottom && sBottom > 1) { |
3003 | 0 | if (!haveSMax || sBottom < sMax) { |
3004 | 0 | sMax = sBottom; |
3005 | 0 | haveSMax = gTrue; |
3006 | 0 | } |
3007 | 0 | } else if (y1 > y0 && haveSTop && sTop > 1) { |
3008 | 0 | if (!haveSMax || sTop < sMax) { |
3009 | 0 | sMax = sTop; |
3010 | 0 | haveSMax = gTrue; |
3011 | 0 | } |
3012 | 0 | } |
3013 | 0 | if (haveSZero && sDiag > 1) { |
3014 | 0 | if (!haveSMax || sDiag < sMax) { |
3015 | 0 | sMax = sDiag; |
3016 | 0 | } |
3017 | 0 | } |
3018 | 0 | } else { |
3019 | 0 | sMax = 1; |
3020 | 0 | } |
3021 | 0 | } |
3022 | | |
3023 | | // compute the number of steps into which circles must be divided to |
3024 | | // achieve a curve flatness of 0.1 pixel in device space for the |
3025 | | // largest circle (note that "device space" is 72 dpi when generating |
3026 | | // PostScript, hence the relatively small 0.1 pixel accuracy) |
3027 | 0 | ctm = state->getCTM(); |
3028 | 0 | t = fabs(ctm[0]); |
3029 | 0 | if (fabs(ctm[1]) > t) { |
3030 | 0 | t = fabs(ctm[1]); |
3031 | 0 | } |
3032 | 0 | if (fabs(ctm[2]) > t) { |
3033 | 0 | t = fabs(ctm[2]); |
3034 | 0 | } |
3035 | 0 | if (fabs(ctm[3]) > t) { |
3036 | 0 | t = fabs(ctm[3]); |
3037 | 0 | } |
3038 | 0 | if (r0 > r1) { |
3039 | 0 | t *= r0; |
3040 | 0 | } else { |
3041 | 0 | t *= r1; |
3042 | 0 | } |
3043 | 0 | if (t < 1) { |
3044 | 0 | n = 3; |
3045 | 0 | } else { |
3046 | 0 | n = (int)(M_PI / acos(1 - 0.1 / t)); |
3047 | 0 | if (n < 3) { |
3048 | 0 | n = 3; |
3049 | 0 | } else if (n > 200) { |
3050 | 0 | n = 200; |
3051 | 0 | } |
3052 | 0 | } |
3053 | | |
3054 | | // setup for the start circle |
3055 | 0 | ia = 0; |
3056 | 0 | sa = sMin; |
3057 | 0 | ta = t0 + sa * (t1 - t0); |
3058 | 0 | xa = x0 + sa * (x1 - x0); |
3059 | 0 | ya = y0 + sa * (y1 - y0); |
3060 | 0 | ra = r0 + sa * (r1 - r0); |
3061 | 0 | if (ta < t0) { |
3062 | 0 | shading->getColor(t0, &colorA); |
3063 | 0 | } else if (ta > t1) { |
3064 | 0 | shading->getColor(t1, &colorA); |
3065 | 0 | } else { |
3066 | 0 | shading->getColor(ta, &colorA); |
3067 | 0 | } |
3068 | | |
3069 | | // fill the circles |
3070 | 0 | abortCheckCounter = 0; |
3071 | 0 | while (ia < radialMaxSplits) { |
3072 | |
|
3073 | 0 | if (abortCheckCbk) { |
3074 | 0 | ++abortCheckCounter; |
3075 | 0 | if (abortCheckCounter > 100) { |
3076 | 0 | if ((*abortCheckCbk)(abortCheckCbkData)) { |
3077 | 0 | break; |
3078 | 0 | } |
3079 | 0 | abortCheckCounter = 0; |
3080 | 0 | } |
3081 | 0 | } |
3082 | | |
3083 | | // go as far along the t axis (toward t1) as we can, such that the |
3084 | | // color difference is within the tolerance (radialColorDelta) -- |
3085 | | // this uses bisection (between the current value, t, and t1), |
3086 | | // limited to radialMaxSplits points along the t axis; require at |
3087 | | // least one split to avoid problems when the innermost and |
3088 | | // outermost colors are the same |
3089 | 0 | ib = radialMaxSplits; |
3090 | 0 | sb = sMax; |
3091 | 0 | tb = t0 + sb * (t1 - t0); |
3092 | 0 | if (tb < t0) { |
3093 | 0 | shading->getColor(t0, &colorB); |
3094 | 0 | } else if (tb > t1) { |
3095 | 0 | shading->getColor(t1, &colorB); |
3096 | 0 | } else { |
3097 | 0 | shading->getColor(tb, &colorB); |
3098 | 0 | } |
3099 | 0 | while (ib - ia > 1) { |
3100 | 0 | for (k = 0; k < nComps; ++k) { |
3101 | 0 | if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) { |
3102 | 0 | break; |
3103 | 0 | } |
3104 | 0 | } |
3105 | 0 | if (k == nComps && ib < radialMaxSplits) { |
3106 | 0 | break; |
3107 | 0 | } |
3108 | 0 | ib = (ia + ib) / 2; |
3109 | 0 | sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); |
3110 | 0 | tb = t0 + sb * (t1 - t0); |
3111 | 0 | if (tb < t0) { |
3112 | 0 | shading->getColor(t0, &colorB); |
3113 | 0 | } else if (tb > t1) { |
3114 | 0 | shading->getColor(t1, &colorB); |
3115 | 0 | } else { |
3116 | 0 | shading->getColor(tb, &colorB); |
3117 | 0 | } |
3118 | 0 | } |
3119 | | |
3120 | | // compute center and radius of the circle |
3121 | 0 | xb = x0 + sb * (x1 - x0); |
3122 | 0 | yb = y0 + sb * (y1 - y0); |
3123 | 0 | rb = r0 + sb * (r1 - r0); |
3124 | | |
3125 | | // use the average of the colors at the two circles |
3126 | 0 | for (k = 0; k < nComps; ++k) { |
3127 | 0 | colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2; |
3128 | 0 | } |
3129 | 0 | state->setFillColor(&colorA); |
3130 | 0 | out->updateFillColor(state); |
3131 | |
|
3132 | 0 | if (enclosed) { |
3133 | | |
3134 | | // construct path for first circle (counterclockwise) |
3135 | 0 | state->moveTo(xa + ra, ya); |
3136 | 0 | for (k = 1; k < n; ++k) { |
3137 | 0 | angle = ((double)k / (double)n) * 2 * M_PI; |
3138 | 0 | state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); |
3139 | 0 | } |
3140 | 0 | state->closePath(); |
3141 | | |
3142 | | // construct and append path for second circle (clockwise) |
3143 | 0 | state->moveTo(xb + rb, yb); |
3144 | 0 | for (k = 1; k < n; ++k) { |
3145 | 0 | angle = -((double)k / (double)n) * 2 * M_PI; |
3146 | 0 | state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); |
3147 | 0 | } |
3148 | 0 | state->closePath(); |
3149 | |
|
3150 | 0 | } else { |
3151 | | |
3152 | | // construct the first subpath (clockwise) |
3153 | 0 | state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), |
3154 | 0 | ya + ra * sin(alpha + theta + 0.5 * M_PI)); |
3155 | 0 | for (k = 0; k < n; ++k) { |
3156 | 0 | angle = alpha + theta + 0.5 * M_PI |
3157 | 0 | - ((double)k / (double)n) * (2 * theta + M_PI); |
3158 | 0 | state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); |
3159 | 0 | } |
3160 | 0 | for (k = 0; k < n; ++k) { |
3161 | 0 | angle = alpha - theta - 0.5 * M_PI |
3162 | 0 | + ((double)k / (double)n) * (2 * theta - M_PI); |
3163 | 0 | state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); |
3164 | 0 | } |
3165 | 0 | state->closePath(); |
3166 | | |
3167 | | // construct the second subpath (counterclockwise) |
3168 | 0 | state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), |
3169 | 0 | ya + ra * sin(alpha + theta + 0.5 * M_PI)); |
3170 | 0 | for (k = 0; k < n; ++k) { |
3171 | 0 | angle = alpha + theta + 0.5 * M_PI |
3172 | 0 | + ((double)k / (double)n) * (-2 * theta + M_PI); |
3173 | 0 | state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); |
3174 | 0 | } |
3175 | 0 | for (k = 0; k < n; ++k) { |
3176 | 0 | angle = alpha - theta - 0.5 * M_PI |
3177 | 0 | + ((double)k / (double)n) * (2 * theta + M_PI); |
3178 | 0 | state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); |
3179 | 0 | } |
3180 | 0 | state->closePath(); |
3181 | 0 | } |
3182 | | |
3183 | | // fill the path |
3184 | 0 | out->fill(state); |
3185 | 0 | state->clearPath(); |
3186 | | |
3187 | | // step to the next value of t |
3188 | 0 | ia = ib; |
3189 | 0 | sa = sb; |
3190 | 0 | ta = tb; |
3191 | 0 | xa = xb; |
3192 | 0 | ya = yb; |
3193 | 0 | ra = rb; |
3194 | 0 | colorA = colorB; |
3195 | 0 | } |
3196 | |
|
3197 | 0 | if (enclosed) { |
3198 | | // extend the smaller circle |
3199 | 0 | if ((shading->getExtend0() && r0 <= r1) || |
3200 | 0 | (shading->getExtend1() && r1 < r0)) { |
3201 | 0 | if (r0 <= r1) { |
3202 | 0 | ta = t0; |
3203 | 0 | ra = r0; |
3204 | 0 | xa = x0; |
3205 | 0 | ya = y0; |
3206 | 0 | } else { |
3207 | 0 | ta = t1; |
3208 | 0 | ra = r1; |
3209 | 0 | xa = x1; |
3210 | 0 | ya = y1; |
3211 | 0 | } |
3212 | 0 | shading->getColor(ta, &colorA); |
3213 | 0 | state->setFillColor(&colorA); |
3214 | 0 | out->updateFillColor(state); |
3215 | 0 | state->moveTo(xa + ra, ya); |
3216 | 0 | for (k = 1; k < n; ++k) { |
3217 | 0 | angle = ((double)k / (double)n) * 2 * M_PI; |
3218 | 0 | state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); |
3219 | 0 | } |
3220 | 0 | state->closePath(); |
3221 | 0 | out->fill(state); |
3222 | 0 | state->clearPath(); |
3223 | 0 | } |
3224 | | |
3225 | | // extend the larger circle |
3226 | 0 | if ((shading->getExtend0() && r0 > r1) || |
3227 | 0 | (shading->getExtend1() && r1 >= r0)) { |
3228 | 0 | if (r0 > r1) { |
3229 | 0 | ta = t0; |
3230 | 0 | ra = r0; |
3231 | 0 | xa = x0; |
3232 | 0 | ya = y0; |
3233 | 0 | } else { |
3234 | 0 | ta = t1; |
3235 | 0 | ra = r1; |
3236 | 0 | xa = x1; |
3237 | 0 | ya = y1; |
3238 | 0 | } |
3239 | 0 | shading->getColor(ta, &colorA); |
3240 | 0 | state->setFillColor(&colorA); |
3241 | 0 | out->updateFillColor(state); |
3242 | 0 | state->moveTo(xMin, yMin); |
3243 | 0 | state->lineTo(xMin, yMax); |
3244 | 0 | state->lineTo(xMax, yMax); |
3245 | 0 | state->lineTo(xMax, yMin); |
3246 | 0 | state->closePath(); |
3247 | 0 | state->moveTo(xa + ra, ya); |
3248 | 0 | for (k = 1; k < n; ++k) { |
3249 | 0 | angle = ((double)k / (double)n) * 2 * M_PI; |
3250 | 0 | state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); |
3251 | 0 | } |
3252 | 0 | state->closePath(); |
3253 | 0 | out->fill(state); |
3254 | 0 | state->clearPath(); |
3255 | 0 | } |
3256 | 0 | } |
3257 | 0 | } |
3258 | | |
3259 | | #if defined(__GNUC__) && !defined(__clang__) |
3260 | | #pragma GCC reset_options |
3261 | | #endif |
3262 | | |
3263 | 0 | void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) { |
3264 | 0 | double x0, y0, x1, y1, x2, y2; |
3265 | 0 | double color0[gfxColorMaxComps]; |
3266 | 0 | double color1[gfxColorMaxComps]; |
3267 | 0 | double color2[gfxColorMaxComps]; |
3268 | 0 | int abortCheckCounter, i; |
3269 | |
|
3270 | 0 | abortCheckCounter = 0; |
3271 | 0 | for (i = 0; i < shading->getNTriangles(); ++i) { |
3272 | 0 | if (abortCheckCbk) { |
3273 | 0 | ++abortCheckCounter; |
3274 | 0 | if (abortCheckCounter > 25) { |
3275 | 0 | if ((*abortCheckCbk)(abortCheckCbkData)) { |
3276 | 0 | break; |
3277 | 0 | } |
3278 | 0 | abortCheckCounter = 0; |
3279 | 0 | } |
3280 | 0 | } |
3281 | 0 | shading->getTriangle(i, &x0, &y0, color0, |
3282 | 0 | &x1, &y1, color1, |
3283 | 0 | &x2, &y2, color2); |
3284 | 0 | gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, |
3285 | 0 | shading, 0); |
3286 | 0 | } |
3287 | 0 | } |
3288 | | |
3289 | | void Gfx::gouraudFillTriangle(double x0, double y0, double *color0, |
3290 | | double x1, double y1, double *color1, |
3291 | | double x2, double y2, double *color2, |
3292 | 0 | GfxGouraudTriangleShading *shading, int depth) { |
3293 | 0 | double dx0, dy0, dx1, dy1, dx2, dy2; |
3294 | 0 | double x01, y01, x12, y12, x20, y20; |
3295 | 0 | double color01[gfxColorMaxComps]; |
3296 | 0 | double color12[gfxColorMaxComps]; |
3297 | 0 | double color20[gfxColorMaxComps]; |
3298 | 0 | GfxColor c0, c1, c2; |
3299 | 0 | int nComps, i; |
3300 | | |
3301 | | // recursion ends when: |
3302 | | // (1) color difference is smaller than gouraudColorDelta; or |
3303 | | // (2) triangles are smaller than 0.5 pixel (note that "device |
3304 | | // space" is 72dpi when generating PostScript); or |
3305 | | // (3) max recursion depth (gouraudMaxDepth) is hit. |
3306 | 0 | nComps = shading->getColorSpace()->getNComps(); |
3307 | 0 | shading->getColor(color0, &c0); |
3308 | 0 | shading->getColor(color1, &c1); |
3309 | 0 | shading->getColor(color2, &c2); |
3310 | 0 | for (i = 0; i < nComps; ++i) { |
3311 | 0 | if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta || |
3312 | 0 | abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) { |
3313 | 0 | break; |
3314 | 0 | } |
3315 | 0 | } |
3316 | 0 | state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0); |
3317 | 0 | state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1); |
3318 | 0 | state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2); |
3319 | 0 | if (i == nComps || |
3320 | 0 | depth == gouraudMaxDepth || |
3321 | 0 | (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 && |
3322 | 0 | fabs(dx1) < 0.5 && fabs(dy1) < 0.5 && |
3323 | 0 | fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) { |
3324 | 0 | state->setFillColor(&c0); |
3325 | 0 | out->updateFillColor(state); |
3326 | 0 | state->moveTo(x0, y0); |
3327 | 0 | state->lineTo(x1, y1); |
3328 | 0 | state->lineTo(x2, y2); |
3329 | 0 | state->closePath(); |
3330 | 0 | out->fill(state); |
3331 | 0 | state->clearPath(); |
3332 | 0 | } else { |
3333 | 0 | x01 = 0.5 * (x0 + x1); |
3334 | 0 | y01 = 0.5 * (y0 + y1); |
3335 | 0 | x12 = 0.5 * (x1 + x2); |
3336 | 0 | y12 = 0.5 * (y1 + y2); |
3337 | 0 | x20 = 0.5 * (x2 + x0); |
3338 | 0 | y20 = 0.5 * (y2 + y0); |
3339 | 0 | for (i = 0; i < shading->getNComps(); ++i) { |
3340 | 0 | color01[i] = 0.5 * (color0[i] + color1[i]); |
3341 | 0 | color12[i] = 0.5 * (color1[i] + color2[i]); |
3342 | 0 | color20[i] = 0.5 * (color2[i] + color0[i]); |
3343 | 0 | } |
3344 | 0 | gouraudFillTriangle(x0, y0, color0, x01, y01, color01, |
3345 | 0 | x20, y20, color20, shading, depth + 1); |
3346 | 0 | gouraudFillTriangle(x01, y01, color01, x1, y1, color1, |
3347 | 0 | x12, y12, color12, shading, depth + 1); |
3348 | 0 | gouraudFillTriangle(x01, y01, color01, x12, y12, color12, |
3349 | 0 | x20, y20, color20, shading, depth + 1); |
3350 | 0 | gouraudFillTriangle(x20, y20, color20, x12, y12, color12, |
3351 | 0 | x2, y2, color2, shading, depth + 1); |
3352 | 0 | } |
3353 | 0 | } |
3354 | | |
3355 | 0 | void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) { |
3356 | 0 | int start, abortCheckCounter, i; |
3357 | |
|
3358 | 0 | if (shading->getNPatches() > 128) { |
3359 | 0 | start = 3; |
3360 | 0 | } else if (shading->getNPatches() > 64) { |
3361 | 0 | start = 2; |
3362 | 0 | } else if (shading->getNPatches() > 16) { |
3363 | 0 | start = 1; |
3364 | 0 | } else { |
3365 | 0 | start = 0; |
3366 | 0 | } |
3367 | 0 | abortCheckCounter = 0; |
3368 | 0 | for (i = 0; i < shading->getNPatches(); ++i) { |
3369 | 0 | if (abortCheckCbk) { |
3370 | 0 | ++abortCheckCounter; |
3371 | 0 | if (abortCheckCounter > 25) { |
3372 | 0 | if ((*abortCheckCbk)(abortCheckCbkData)) { |
3373 | 0 | break; |
3374 | 0 | } |
3375 | 0 | abortCheckCounter = 0; |
3376 | 0 | } |
3377 | 0 | } |
3378 | 0 | fillPatch(shading->getPatch(i), shading, start); |
3379 | 0 | } |
3380 | 0 | } |
3381 | | |
3382 | 0 | void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) { |
3383 | 0 | GfxPatch patch00, patch01, patch10, patch11; |
3384 | 0 | GfxColor c00, c01, c10, c11; |
3385 | 0 | double xx[4][8], yy[4][8]; |
3386 | 0 | double x, y, xMin, yMin, xMax, yMax, xxm, yym; |
3387 | 0 | int nComps, i, j; |
3388 | 0 | GBool stop; |
3389 | |
|
3390 | 0 | shading->getColor(patch->color[0][0], &c00); |
3391 | 0 | stop = gFalse; |
3392 | | |
3393 | | // stop subdivision at max depth |
3394 | 0 | if (depth == patchMaxDepth) { |
3395 | 0 | stop = gTrue; |
3396 | 0 | } |
3397 | | |
3398 | | // stop subdivision if colors are close enough |
3399 | 0 | if (!stop) { |
3400 | 0 | nComps = shading->getColorSpace()->getNComps(); |
3401 | 0 | shading->getColor(patch->color[0][1], &c01); |
3402 | 0 | shading->getColor(patch->color[1][0], &c10); |
3403 | 0 | shading->getColor(patch->color[1][1], &c11); |
3404 | 0 | for (i = 0; i < nComps; ++i) { |
3405 | 0 | if (abs(c00.c[i] - c01.c[i]) > patchColorDelta || |
3406 | 0 | abs(c01.c[i] - c11.c[i]) > patchColorDelta || |
3407 | 0 | abs(c11.c[i] - c10.c[i]) > patchColorDelta || |
3408 | 0 | abs(c10.c[i] - c00.c[i]) > patchColorDelta) { |
3409 | 0 | break; |
3410 | 0 | } |
3411 | 0 | } |
3412 | 0 | if (i == nComps) { |
3413 | 0 | stop = gTrue; |
3414 | 0 | } |
3415 | 0 | } |
3416 | | |
3417 | | // stop subdivision if patch is small enough |
3418 | 0 | if (!stop) { |
3419 | 0 | xMin = yMin = xMax = yMax = 0; |
3420 | 0 | for (j = 0; j < 4; ++j) { |
3421 | 0 | for (i = 0; i < 4; ++i) { |
3422 | 0 | state->transformDelta(patch->x[i][j], patch->y[i][j], &x, &y); |
3423 | 0 | if (i == 0 && j == 0) { |
3424 | 0 | xMin = xMax = x; |
3425 | 0 | yMin = yMax = y; |
3426 | 0 | } else { |
3427 | 0 | if (x < xMin) { |
3428 | 0 | xMin = x; |
3429 | 0 | } else if (x > xMax) { |
3430 | 0 | xMax = x; |
3431 | 0 | } |
3432 | 0 | if (y < yMin) { |
3433 | 0 | yMin = y; |
3434 | 0 | } else if (y > yMax) { |
3435 | 0 | yMax = y; |
3436 | 0 | } |
3437 | 0 | } |
3438 | 0 | } |
3439 | 0 | } |
3440 | 0 | if (xMax - xMin < 1 && yMax - yMin < 1) { |
3441 | 0 | stop = gTrue; |
3442 | 0 | } |
3443 | 0 | } |
3444 | | |
3445 | | // draw the patch |
3446 | 0 | if (stop) { |
3447 | 0 | state->setFillColor(&c00); |
3448 | 0 | out->updateFillColor(state); |
3449 | 0 | state->moveTo(patch->x[0][0], patch->y[0][0]); |
3450 | 0 | state->curveTo(patch->x[0][1], patch->y[0][1], |
3451 | 0 | patch->x[0][2], patch->y[0][2], |
3452 | 0 | patch->x[0][3], patch->y[0][3]); |
3453 | 0 | state->curveTo(patch->x[1][3], patch->y[1][3], |
3454 | 0 | patch->x[2][3], patch->y[2][3], |
3455 | 0 | patch->x[3][3], patch->y[3][3]); |
3456 | 0 | state->curveTo(patch->x[3][2], patch->y[3][2], |
3457 | 0 | patch->x[3][1], patch->y[3][1], |
3458 | 0 | patch->x[3][0], patch->y[3][0]); |
3459 | 0 | state->curveTo(patch->x[2][0], patch->y[2][0], |
3460 | 0 | patch->x[1][0], patch->y[1][0], |
3461 | 0 | patch->x[0][0], patch->y[0][0]); |
3462 | 0 | state->closePath(); |
3463 | 0 | out->fill(state); |
3464 | 0 | state->clearPath(); |
3465 | | |
3466 | | // subdivide the patch |
3467 | 0 | } else { |
3468 | 0 | for (i = 0; i < 4; ++i) { |
3469 | 0 | xx[i][0] = patch->x[i][0]; |
3470 | 0 | yy[i][0] = patch->y[i][0]; |
3471 | 0 | xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]); |
3472 | 0 | yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]); |
3473 | 0 | xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]); |
3474 | 0 | yym = 0.5 * (patch->y[i][1] + patch->y[i][2]); |
3475 | 0 | xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]); |
3476 | 0 | yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]); |
3477 | 0 | xx[i][2] = 0.5 * (xx[i][1] + xxm); |
3478 | 0 | yy[i][2] = 0.5 * (yy[i][1] + yym); |
3479 | 0 | xx[i][5] = 0.5 * (xxm + xx[i][6]); |
3480 | 0 | yy[i][5] = 0.5 * (yym + yy[i][6]); |
3481 | 0 | xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]); |
3482 | 0 | yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]); |
3483 | 0 | xx[i][7] = patch->x[i][3]; |
3484 | 0 | yy[i][7] = patch->y[i][3]; |
3485 | 0 | } |
3486 | 0 | for (i = 0; i < 4; ++i) { |
3487 | 0 | patch00.x[0][i] = xx[0][i]; |
3488 | 0 | patch00.y[0][i] = yy[0][i]; |
3489 | 0 | patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]); |
3490 | 0 | patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]); |
3491 | 0 | xxm = 0.5 * (xx[1][i] + xx[2][i]); |
3492 | 0 | yym = 0.5 * (yy[1][i] + yy[2][i]); |
3493 | 0 | patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]); |
3494 | 0 | patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]); |
3495 | 0 | patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm); |
3496 | 0 | patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym); |
3497 | 0 | patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]); |
3498 | 0 | patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]); |
3499 | 0 | patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]); |
3500 | 0 | patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]); |
3501 | 0 | patch10.x[0][i] = patch00.x[3][i]; |
3502 | 0 | patch10.y[0][i] = patch00.y[3][i]; |
3503 | 0 | patch10.x[3][i] = xx[3][i]; |
3504 | 0 | patch10.y[3][i] = yy[3][i]; |
3505 | 0 | } |
3506 | 0 | for (i = 4; i < 8; ++i) { |
3507 | 0 | patch01.x[0][i-4] = xx[0][i]; |
3508 | 0 | patch01.y[0][i-4] = yy[0][i]; |
3509 | 0 | patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]); |
3510 | 0 | patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]); |
3511 | 0 | xxm = 0.5 * (xx[1][i] + xx[2][i]); |
3512 | 0 | yym = 0.5 * (yy[1][i] + yy[2][i]); |
3513 | 0 | patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]); |
3514 | 0 | patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]); |
3515 | 0 | patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm); |
3516 | 0 | patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym); |
3517 | 0 | patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]); |
3518 | 0 | patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]); |
3519 | 0 | patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]); |
3520 | 0 | patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]); |
3521 | 0 | patch11.x[0][i-4] = patch01.x[3][i-4]; |
3522 | 0 | patch11.y[0][i-4] = patch01.y[3][i-4]; |
3523 | 0 | patch11.x[3][i-4] = xx[3][i]; |
3524 | 0 | patch11.y[3][i-4] = yy[3][i]; |
3525 | 0 | } |
3526 | 0 | for (i = 0; i < shading->getNComps(); ++i) { |
3527 | 0 | patch00.color[0][0][i] = patch->color[0][0][i]; |
3528 | 0 | patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] + |
3529 | 0 | patch->color[0][1][i]); |
3530 | 0 | patch01.color[0][0][i] = patch00.color[0][1][i]; |
3531 | 0 | patch01.color[0][1][i] = patch->color[0][1][i]; |
3532 | 0 | patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] + |
3533 | 0 | patch->color[1][1][i]); |
3534 | 0 | patch11.color[0][1][i] = patch01.color[1][1][i]; |
3535 | 0 | patch11.color[1][1][i] = patch->color[1][1][i]; |
3536 | 0 | patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] + |
3537 | 0 | patch->color[1][0][i]); |
3538 | 0 | patch10.color[1][1][i] = patch11.color[1][0][i]; |
3539 | 0 | patch10.color[1][0][i] = patch->color[1][0][i]; |
3540 | 0 | patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] + |
3541 | 0 | patch->color[0][0][i]); |
3542 | 0 | patch00.color[1][0][i] = patch10.color[0][0][i]; |
3543 | 0 | patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] + |
3544 | 0 | patch01.color[1][1][i]); |
3545 | 0 | patch01.color[1][0][i] = patch00.color[1][1][i]; |
3546 | 0 | patch11.color[0][0][i] = patch00.color[1][1][i]; |
3547 | 0 | patch10.color[0][1][i] = patch00.color[1][1][i]; |
3548 | 0 | } |
3549 | 0 | fillPatch(&patch00, shading, depth + 1); |
3550 | 0 | fillPatch(&patch10, shading, depth + 1); |
3551 | 0 | fillPatch(&patch01, shading, depth + 1); |
3552 | 0 | fillPatch(&patch11, shading, depth + 1); |
3553 | 0 | } |
3554 | 0 | } |
3555 | | |
3556 | 0 | void Gfx::doEndPath() { |
3557 | 0 | if (clip != clipNone) { |
3558 | 0 | if (state->isCurPt()) { |
3559 | 0 | state->clip(); |
3560 | 0 | if (clip == clipNormal) { |
3561 | 0 | out->clip(state); |
3562 | 0 | } else { |
3563 | 0 | out->eoClip(state); |
3564 | 0 | } |
3565 | 0 | } else { |
3566 | 0 | error(errSyntaxError, getPos(), "Empty path in clip"); |
3567 | 0 | } |
3568 | 0 | } |
3569 | 0 | clip = clipNone; |
3570 | 0 | state->clearPath(); |
3571 | 0 | } |
3572 | | |
3573 | | //------------------------------------------------------------------------ |
3574 | | // path clipping operators |
3575 | | //------------------------------------------------------------------------ |
3576 | | |
3577 | 0 | void Gfx::opClip(Object args[], int numArgs) { |
3578 | 0 | clip = clipNormal; |
3579 | 0 | } |
3580 | | |
3581 | 0 | void Gfx::opEOClip(Object args[], int numArgs) { |
3582 | 0 | clip = clipEO; |
3583 | 0 | } |
3584 | | |
3585 | | //------------------------------------------------------------------------ |
3586 | | // text object operators |
3587 | | //------------------------------------------------------------------------ |
3588 | | |
3589 | 0 | void Gfx::opBeginText(Object args[], int numArgs) { |
3590 | 0 | state->setTextMat(1, 0, 0, 1, 0, 0); |
3591 | 0 | state->textMoveTo(0, 0); |
3592 | 0 | out->updateTextMat(state); |
3593 | 0 | out->updateTextPos(state); |
3594 | 0 | fontChanged = gTrue; |
3595 | 0 | haveSavedClipPath = gFalse; |
3596 | 0 | } |
3597 | | |
3598 | 0 | void Gfx::opEndText(Object args[], int numArgs) { |
3599 | 0 | if (haveSavedClipPath) { |
3600 | 0 | out->clipToSavedClipPath(state); |
3601 | 0 | haveSavedClipPath = gFalse; |
3602 | 0 | } |
3603 | |
|
3604 | 0 | out->endTextObject(state); |
3605 | 0 | } |
3606 | | |
3607 | | //------------------------------------------------------------------------ |
3608 | | // text state operators |
3609 | | //------------------------------------------------------------------------ |
3610 | | |
3611 | 0 | void Gfx::opSetCharSpacing(Object args[], int numArgs) { |
3612 | 0 | state->setCharSpace(args[0].getNum()); |
3613 | 0 | out->updateCharSpace(state); |
3614 | 0 | } |
3615 | | |
3616 | 0 | void Gfx::opSetFont(Object args[], int numArgs) { |
3617 | 0 | doSetFont(res->lookupFont(args[0].getName()), args[1].getNum()); |
3618 | 0 | } |
3619 | | |
3620 | 0 | void Gfx::doSetFont(GfxFont *font, double size) { |
3621 | 0 | if (!font) { |
3622 | 0 | if (!defaultFont) { |
3623 | 0 | defaultFont = GfxFont::makeDefaultFont(xref); |
3624 | 0 | } |
3625 | 0 | font = defaultFont; |
3626 | 0 | } |
3627 | 0 | if (printCommands) { |
3628 | 0 | printf(" font: tag=%s name='%s' %g\n", |
3629 | 0 | font->getTag()->getCString(), |
3630 | 0 | font->getName() ? font->getName()->getCString() : "???", |
3631 | 0 | size); |
3632 | 0 | fflush(stdout); |
3633 | 0 | } |
3634 | 0 | state->setFont(font, size); |
3635 | 0 | fontChanged = gTrue; |
3636 | 0 | } |
3637 | | |
3638 | 0 | void Gfx::opSetTextLeading(Object args[], int numArgs) { |
3639 | 0 | state->setLeading(args[0].getNum()); |
3640 | 0 | } |
3641 | | |
3642 | 0 | void Gfx::opSetTextRender(Object args[], int numArgs) { |
3643 | 0 | state->setRender(args[0].getInt()); |
3644 | 0 | out->updateRender(state); |
3645 | 0 | } |
3646 | | |
3647 | 0 | void Gfx::opSetTextRise(Object args[], int numArgs) { |
3648 | 0 | state->setRise(args[0].getNum()); |
3649 | 0 | out->updateRise(state); |
3650 | 0 | } |
3651 | | |
3652 | 0 | void Gfx::opSetWordSpacing(Object args[], int numArgs) { |
3653 | 0 | state->setWordSpace(args[0].getNum()); |
3654 | 0 | out->updateWordSpace(state); |
3655 | 0 | } |
3656 | | |
3657 | 0 | void Gfx::opSetHorizScaling(Object args[], int numArgs) { |
3658 | 0 | state->setHorizScaling(args[0].getNum()); |
3659 | 0 | out->updateHorizScaling(state); |
3660 | 0 | fontChanged = gTrue; |
3661 | 0 | } |
3662 | | |
3663 | | //------------------------------------------------------------------------ |
3664 | | // text positioning operators |
3665 | | //------------------------------------------------------------------------ |
3666 | | |
3667 | 0 | void Gfx::opTextMove(Object args[], int numArgs) { |
3668 | 0 | double tx, ty; |
3669 | |
|
3670 | 0 | tx = state->getLineX() + args[0].getNum(); |
3671 | 0 | ty = state->getLineY() + args[1].getNum(); |
3672 | 0 | state->textMoveTo(tx, ty); |
3673 | 0 | out->updateTextPos(state); |
3674 | 0 | } |
3675 | | |
3676 | 0 | void Gfx::opTextMoveSet(Object args[], int numArgs) { |
3677 | 0 | double tx, ty; |
3678 | |
|
3679 | 0 | tx = state->getLineX() + args[0].getNum(); |
3680 | 0 | ty = args[1].getNum(); |
3681 | 0 | state->setLeading(-ty); |
3682 | 0 | ty += state->getLineY(); |
3683 | 0 | state->textMoveTo(tx, ty); |
3684 | 0 | out->updateTextPos(state); |
3685 | 0 | } |
3686 | | |
3687 | 0 | void Gfx::opSetTextMatrix(Object args[], int numArgs) { |
3688 | 0 | state->setTextMat(args[0].getNum(), args[1].getNum(), |
3689 | 0 | args[2].getNum(), args[3].getNum(), |
3690 | 0 | args[4].getNum(), args[5].getNum()); |
3691 | 0 | state->textMoveTo(0, 0); |
3692 | 0 | out->updateTextMat(state); |
3693 | 0 | out->updateTextPos(state); |
3694 | 0 | fontChanged = gTrue; |
3695 | 0 | } |
3696 | | |
3697 | 0 | void Gfx::opTextNextLine(Object args[], int numArgs) { |
3698 | 0 | double tx, ty; |
3699 | |
|
3700 | 0 | tx = state->getLineX(); |
3701 | 0 | ty = state->getLineY() - state->getLeading(); |
3702 | 0 | state->textMoveTo(tx, ty); |
3703 | 0 | out->updateTextPos(state); |
3704 | 0 | } |
3705 | | |
3706 | | //------------------------------------------------------------------------ |
3707 | | // text string operators |
3708 | | //------------------------------------------------------------------------ |
3709 | | |
3710 | 0 | void Gfx::opShowText(Object args[], int numArgs) { |
3711 | 0 | if (!state->getFont()) { |
3712 | 0 | error(errSyntaxError, getPos(), "No font in show"); |
3713 | 0 | return; |
3714 | 0 | } |
3715 | 0 | if (fontChanged) { |
3716 | 0 | out->updateFont(state); |
3717 | 0 | fontChanged = gFalse; |
3718 | 0 | } |
3719 | 0 | if (ocState) { |
3720 | 0 | out->beginStringOp(state); |
3721 | 0 | doShowText(args[0].getString()); |
3722 | 0 | out->endStringOp(state); |
3723 | 0 | } else { |
3724 | 0 | doIncCharCount(args[0].getString()); |
3725 | 0 | } |
3726 | 0 | } |
3727 | | |
3728 | 0 | void Gfx::opMoveShowText(Object args[], int numArgs) { |
3729 | 0 | double tx, ty; |
3730 | |
|
3731 | 0 | if (!state->getFont()) { |
3732 | 0 | error(errSyntaxError, getPos(), "No font in move/show"); |
3733 | 0 | return; |
3734 | 0 | } |
3735 | 0 | if (fontChanged) { |
3736 | 0 | out->updateFont(state); |
3737 | 0 | fontChanged = gFalse; |
3738 | 0 | } |
3739 | 0 | tx = state->getLineX(); |
3740 | 0 | ty = state->getLineY() - state->getLeading(); |
3741 | 0 | state->textMoveTo(tx, ty); |
3742 | 0 | out->updateTextPos(state); |
3743 | 0 | if (ocState) { |
3744 | 0 | out->beginStringOp(state); |
3745 | 0 | doShowText(args[0].getString()); |
3746 | 0 | out->endStringOp(state); |
3747 | 0 | } else { |
3748 | 0 | doIncCharCount(args[0].getString()); |
3749 | 0 | } |
3750 | 0 | } |
3751 | | |
3752 | 0 | void Gfx::opMoveSetShowText(Object args[], int numArgs) { |
3753 | 0 | double tx, ty; |
3754 | |
|
3755 | 0 | if (!state->getFont()) { |
3756 | 0 | error(errSyntaxError, getPos(), "No font in move/set/show"); |
3757 | 0 | return; |
3758 | 0 | } |
3759 | 0 | if (fontChanged) { |
3760 | 0 | out->updateFont(state); |
3761 | 0 | fontChanged = gFalse; |
3762 | 0 | } |
3763 | 0 | state->setWordSpace(args[0].getNum()); |
3764 | 0 | state->setCharSpace(args[1].getNum()); |
3765 | 0 | tx = state->getLineX(); |
3766 | 0 | ty = state->getLineY() - state->getLeading(); |
3767 | 0 | state->textMoveTo(tx, ty); |
3768 | 0 | out->updateWordSpace(state); |
3769 | 0 | out->updateCharSpace(state); |
3770 | 0 | out->updateTextPos(state); |
3771 | 0 | if (ocState) { |
3772 | 0 | out->beginStringOp(state); |
3773 | 0 | doShowText(args[2].getString()); |
3774 | 0 | out->endStringOp(state); |
3775 | 0 | } else { |
3776 | 0 | doIncCharCount(args[2].getString()); |
3777 | 0 | } |
3778 | 0 | } |
3779 | | |
3780 | 0 | void Gfx::opShowSpaceText(Object args[], int numArgs) { |
3781 | 0 | Array *a; |
3782 | 0 | Object obj; |
3783 | 0 | int wMode; |
3784 | 0 | int i; |
3785 | |
|
3786 | 0 | if (!state->getFont()) { |
3787 | 0 | error(errSyntaxError, getPos(), "No font in show/space"); |
3788 | 0 | return; |
3789 | 0 | } |
3790 | 0 | if (fontChanged) { |
3791 | 0 | out->updateFont(state); |
3792 | 0 | fontChanged = gFalse; |
3793 | 0 | } |
3794 | 0 | if (ocState) { |
3795 | 0 | out->beginStringOp(state); |
3796 | 0 | wMode = state->getFont()->getWMode(); |
3797 | 0 | a = args[0].getArray(); |
3798 | 0 | for (i = 0; i < a->getLength(); ++i) { |
3799 | 0 | a->get(i, &obj); |
3800 | 0 | if (obj.isNum()) { |
3801 | 0 | if (wMode) { |
3802 | 0 | state->textShift(0, -obj.getNum() * 0.001 * |
3803 | 0 | state->getFontSize()); |
3804 | 0 | } else { |
3805 | 0 | state->textShift(-obj.getNum() * 0.001 * |
3806 | 0 | state->getFontSize() * |
3807 | 0 | state->getHorizScaling(), 0); |
3808 | 0 | } |
3809 | 0 | out->updateTextShift(state, obj.getNum()); |
3810 | 0 | } else if (obj.isString()) { |
3811 | 0 | doShowText(obj.getString()); |
3812 | 0 | } else { |
3813 | 0 | error(errSyntaxError, getPos(), |
3814 | 0 | "Element of show/space array must be number or string"); |
3815 | 0 | } |
3816 | 0 | obj.free(); |
3817 | 0 | } |
3818 | 0 | out->endStringOp(state); |
3819 | 0 | } else { |
3820 | 0 | a = args[0].getArray(); |
3821 | 0 | for (i = 0; i < a->getLength(); ++i) { |
3822 | 0 | a->get(i, &obj); |
3823 | 0 | if (obj.isString()) { |
3824 | 0 | doIncCharCount(obj.getString()); |
3825 | 0 | } |
3826 | 0 | obj.free(); |
3827 | 0 | } |
3828 | 0 | } |
3829 | 0 | } |
3830 | | |
3831 | 0 | void Gfx::doShowText(GString *s) { |
3832 | 0 | GfxFont *font = state->getFont(); |
3833 | 0 | int wMode = font->getWMode(); |
3834 | |
|
3835 | 0 | if (globalParams->isDroppedFont(font->getName() |
3836 | 0 | ? font->getName()->getCString() : "")) { |
3837 | 0 | doIncCharCount(s); |
3838 | 0 | return; |
3839 | 0 | } |
3840 | | |
3841 | 0 | if (out->useDrawChar()) { |
3842 | 0 | out->beginString(state, s); |
3843 | 0 | } |
3844 | | |
3845 | | // figure out the drawing mode |
3846 | | // note: clipping, pattern fill, and pattern stroke are all handled |
3847 | | // by saving the path and then performing the appropriate |
3848 | | // actions in opEndText() |
3849 | 0 | GBool doFill = gFalse; |
3850 | 0 | GBool doStroke = gFalse; |
3851 | 0 | GBool doMakePath = gFalse; |
3852 | 0 | int render = state->getRender() & 7; |
3853 | 0 | switch (render & 7) { |
3854 | 0 | case 0: // fill |
3855 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
3856 | 0 | doMakePath = gTrue; |
3857 | 0 | } else { |
3858 | 0 | doFill = gTrue; |
3859 | 0 | } |
3860 | 0 | break; |
3861 | 0 | case 1: // stroke |
3862 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
3863 | 0 | doMakePath = gTrue; |
3864 | 0 | } else { |
3865 | 0 | doStroke = gTrue; |
3866 | 0 | } |
3867 | 0 | break; |
3868 | 0 | case 2: // fill + stroke |
3869 | 0 | if (state->getFillColorSpace()->getMode() == csPattern || |
3870 | 0 | state->getStrokeColorSpace()->getMode() == csPattern) { |
3871 | 0 | doMakePath = gTrue; |
3872 | 0 | } else { |
3873 | 0 | doFill = gTrue; |
3874 | 0 | doStroke = gTrue; |
3875 | 0 | } |
3876 | 0 | break; |
3877 | 0 | case 3: // invisible |
3878 | | // nothing |
3879 | 0 | break; |
3880 | 0 | case 4: // fill + clip |
3881 | 0 | case 5: // stroke + clip |
3882 | 0 | case 6: // fill + stroke + clip |
3883 | 0 | case 7: // clip |
3884 | 0 | doMakePath = gTrue; |
3885 | 0 | break; |
3886 | 0 | } |
3887 | | |
3888 | 0 | double riseX, riseY; |
3889 | 0 | state->textTransformDelta(0, state->getRise(), &riseX, &riseY); |
3890 | 0 | double xMin = state->getCurX() + riseX; |
3891 | 0 | double yMin = state->getCurY() + riseY; |
3892 | | |
3893 | | // handle a Type 3 char |
3894 | 0 | if (font->getType() == fontType3 && out->interpretType3Chars()) { |
3895 | 0 | double *mat = state->getCTM(); |
3896 | 0 | double oldCTM[6]; |
3897 | 0 | for (int i = 0; i < 6; ++i) { |
3898 | 0 | oldCTM[i] = mat[i]; |
3899 | 0 | } |
3900 | 0 | mat = state->getTextMat(); |
3901 | 0 | double newCTM[6]; |
3902 | 0 | newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2]; |
3903 | 0 | newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3]; |
3904 | 0 | newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2]; |
3905 | 0 | newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3]; |
3906 | 0 | mat = font->getFontMatrix(); |
3907 | 0 | newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2]; |
3908 | 0 | newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3]; |
3909 | 0 | newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2]; |
3910 | 0 | newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3]; |
3911 | 0 | newCTM[0] *= state->getFontSize(); |
3912 | 0 | newCTM[1] *= state->getFontSize(); |
3913 | 0 | newCTM[2] *= state->getFontSize(); |
3914 | 0 | newCTM[3] *= state->getFontSize(); |
3915 | 0 | newCTM[0] *= state->getHorizScaling(); |
3916 | 0 | newCTM[1] *= state->getHorizScaling(); |
3917 | 0 | double curX = state->getCurX(); |
3918 | 0 | double curY = state->getCurY(); |
3919 | 0 | Parser *oldParser = parser; |
3920 | 0 | char *p = s->getCString(); |
3921 | 0 | int len = s->getLength(); |
3922 | 0 | while (len > 0) { |
3923 | 0 | CharCode code; |
3924 | 0 | Unicode u[8]; |
3925 | 0 | int uLen; |
3926 | 0 | double dx, dy, originX, originY; |
3927 | 0 | int n = font->getNextChar(p, len, &code, |
3928 | 0 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, |
3929 | 0 | &dx, &dy, &originX, &originY); |
3930 | 0 | dx = dx * state->getFontSize() + state->getCharSpace(); |
3931 | 0 | if (n == 1 && *p == ' ') { |
3932 | 0 | dx += state->getWordSpace(); |
3933 | 0 | } |
3934 | 0 | dx *= state->getHorizScaling(); |
3935 | 0 | dy *= state->getFontSize(); |
3936 | 0 | double tdx, tdy, ddx, ddy, x, y; |
3937 | 0 | state->textTransformDelta(dx, dy, &tdx, &tdy); |
3938 | 0 | state->transform(curX + riseX, curY + riseY, &x, &y); |
3939 | 0 | GfxState *savedState = saveStateStack(); |
3940 | 0 | state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); |
3941 | | //~ the CTM concat values here are wrong (but never used) |
3942 | 0 | out->updateCTM(state, 1, 0, 0, 1, 0, 0); |
3943 | 0 | state->transformDelta(dx, dy, &ddx, &ddy); |
3944 | | #if 0 |
3945 | | // The PDF spec says: "the graphics state shall be inherited |
3946 | | // from the environment of the text-showing operator that caused |
3947 | | // the [Type 3] glyph description to be invoked". However, |
3948 | | // Acrobat apparently copies the fill color to the stroke color. |
3949 | | // It looks like Ghostscript does the same. (This is only |
3950 | | // relevant for uncached Type 3 glyphs.) Uncomment this block |
3951 | | // to make Xpdf mimic Adobe (in violation of the PDF spec). |
3952 | | state->setStrokeColorSpace(state->getFillColorSpace()->copy()); |
3953 | | out->updateStrokeColorSpace(state); |
3954 | | state->setStrokeColor(state->getFillColor()); |
3955 | | out->updateStrokeColor(state); |
3956 | | #endif |
3957 | 0 | if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy, |
3958 | 0 | code, u, uLen)) { |
3959 | 0 | Object charProcRef, charProc; |
3960 | 0 | ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef); |
3961 | 0 | charProcRef.fetch(xref, &charProc); |
3962 | 0 | Dict *resDict = ((Gfx8BitFont *)font)->getResources(); |
3963 | 0 | if (resDict) { |
3964 | 0 | pushResources(resDict); |
3965 | 0 | } |
3966 | 0 | if (charProc.isStream()) { |
3967 | 0 | display(&charProcRef, gFalse); |
3968 | 0 | } else { |
3969 | 0 | error(errSyntaxError, getPos(), |
3970 | 0 | "Missing or bad Type3 CharProc entry"); |
3971 | 0 | } |
3972 | 0 | out->endType3Char(state); |
3973 | 0 | if (resDict) { |
3974 | 0 | popResources(); |
3975 | 0 | } |
3976 | 0 | charProc.free(); |
3977 | 0 | charProcRef.free(); |
3978 | 0 | } |
3979 | 0 | restoreStateStack(savedState); |
3980 | 0 | curX += tdx; |
3981 | 0 | curY += tdy; |
3982 | 0 | state->moveTo(curX, curY); |
3983 | 0 | p += n; |
3984 | 0 | len -= n; |
3985 | 0 | } |
3986 | 0 | parser = oldParser; |
3987 | |
|
3988 | 0 | } else if (out->useDrawChar()) { |
3989 | 0 | char *p = s->getCString(); |
3990 | 0 | int len = s->getLength(); |
3991 | 0 | while (len > 0) { |
3992 | 0 | CharCode code; |
3993 | 0 | Unicode u[8]; |
3994 | 0 | int uLen; |
3995 | 0 | double dx, dy, originX, originY; |
3996 | 0 | int n = font->getNextChar(p, len, &code, |
3997 | 0 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, |
3998 | 0 | &dx, &dy, &originX, &originY); |
3999 | 0 | if (wMode) { |
4000 | 0 | dx *= state->getFontSize(); |
4001 | 0 | dy = dy * state->getFontSize() + state->getCharSpace(); |
4002 | 0 | if (n == 1 && *p == ' ') { |
4003 | 0 | dy += state->getWordSpace(); |
4004 | 0 | } |
4005 | 0 | } else { |
4006 | 0 | dx = dx * state->getFontSize() + state->getCharSpace(); |
4007 | 0 | if (n == 1 && *p == ' ') { |
4008 | 0 | dx += state->getWordSpace(); |
4009 | 0 | } |
4010 | 0 | dx *= state->getHorizScaling(); |
4011 | 0 | dy *= state->getFontSize(); |
4012 | 0 | } |
4013 | 0 | double tdx, tdy, tOriginX, tOriginY; |
4014 | 0 | state->textTransformDelta(dx, dy, &tdx, &tdy); |
4015 | 0 | originX *= state->getFontSize(); |
4016 | 0 | originY *= state->getFontSize(); |
4017 | 0 | state->textTransformDelta(originX, originY, &tOriginX, &tOriginY); |
4018 | 0 | out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY, |
4019 | 0 | tdx, tdy, tOriginX, tOriginY, code, n, u, uLen, |
4020 | 0 | doFill, doStroke, doMakePath); |
4021 | 0 | state->shift(tdx, tdy); |
4022 | 0 | p += n; |
4023 | 0 | len -= n; |
4024 | 0 | } |
4025 | |
|
4026 | 0 | } else { |
4027 | 0 | double dx = 0, dy = 0; |
4028 | 0 | char *p = s->getCString(); |
4029 | 0 | int len = s->getLength(); |
4030 | 0 | int nChars = 0, nSpaces = 0; |
4031 | 0 | while (len > 0) { |
4032 | 0 | CharCode code; |
4033 | 0 | Unicode u[8]; |
4034 | 0 | int uLen; |
4035 | 0 | double dx2, dy2, originX, originY; |
4036 | 0 | int n = font->getNextChar(p, len, &code, |
4037 | 0 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, |
4038 | 0 | &dx2, &dy2, &originX, &originY); |
4039 | 0 | dx += dx2; |
4040 | 0 | dy += dy2; |
4041 | 0 | if (n == 1 && *p == ' ') { |
4042 | 0 | ++nSpaces; |
4043 | 0 | } |
4044 | 0 | ++nChars; |
4045 | 0 | p += n; |
4046 | 0 | len -= n; |
4047 | 0 | } |
4048 | 0 | if (wMode) { |
4049 | 0 | dx *= state->getFontSize(); |
4050 | 0 | dy = dy * state->getFontSize() |
4051 | 0 | + nChars * state->getCharSpace() |
4052 | 0 | + nSpaces * state->getWordSpace(); |
4053 | 0 | } else { |
4054 | 0 | dx = dx * state->getFontSize() |
4055 | 0 | + nChars * state->getCharSpace() |
4056 | 0 | + nSpaces * state->getWordSpace(); |
4057 | 0 | dx *= state->getHorizScaling(); |
4058 | 0 | dy *= state->getFontSize(); |
4059 | 0 | } |
4060 | 0 | double tdx, tdy; |
4061 | 0 | state->textTransformDelta(dx, dy, &tdx, &tdy); |
4062 | 0 | out->drawString(state, s, doFill, doStroke, doMakePath); |
4063 | 0 | state->shift(tdx, tdy); |
4064 | 0 | } |
4065 | |
|
4066 | 0 | if (out->useDrawChar()) { |
4067 | 0 | out->endString(state); |
4068 | 0 | } |
4069 | |
|
4070 | 0 | if (doMakePath) { |
4071 | | // compute the clipping bbox for the saved text path -- assume |
4072 | | // that the text bounding box does not extend past the baseline in |
4073 | | // any direction by more than twice the font size |
4074 | 0 | double xMax = state->getCurX() + riseX; |
4075 | 0 | double yMax = state->getCurY() + riseY; |
4076 | 0 | double t; |
4077 | 0 | if (xMin > xMax) { |
4078 | 0 | t = xMin; xMin = xMax; xMax = t; |
4079 | 0 | } |
4080 | 0 | if (yMin > yMax) { |
4081 | 0 | t = yMin; yMin = yMax; yMax = t; |
4082 | 0 | } |
4083 | 0 | double dx1, dy1, dx2, dy2; |
4084 | 0 | state->textTransformDelta(0, state->getFontSize(), &dx1, &dy1); |
4085 | 0 | state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2); |
4086 | 0 | dx1 = fabs(dx1); |
4087 | 0 | dx2 = fabs(dx2); |
4088 | 0 | if (dx2 > dx1) { |
4089 | 0 | dx1 = dx2; |
4090 | 0 | } |
4091 | 0 | dy1 = fabs(dy1); |
4092 | 0 | dy2 = fabs(dy2); |
4093 | 0 | if (dy2 > dy1) { |
4094 | 0 | dy1 = dy2; |
4095 | 0 | } |
4096 | 0 | xMin -= 2 * dx1; |
4097 | 0 | yMin -= 2 * dy1; |
4098 | 0 | xMax += 2 * dx1; |
4099 | 0 | yMax += 2 * dy1; |
4100 | | |
4101 | | //--- fill |
4102 | 0 | if ((render & 3) == 0 || (render & 3) == 2) { |
4103 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
4104 | 0 | saveState(); |
4105 | 0 | state->clipToRect(xMin, yMin, xMax, yMax); |
4106 | 0 | out->clipToTextPath(state); |
4107 | 0 | doPatternText(gFalse); |
4108 | 0 | restoreState(); |
4109 | 0 | } else { |
4110 | 0 | out->fillTextPath(state); |
4111 | 0 | } |
4112 | 0 | } |
4113 | | |
4114 | | //--- stroke |
4115 | 0 | if ((render & 3) == 1 || (render & 3) == 2) { |
4116 | 0 | if (state->getStrokeColorSpace()->getMode() == csPattern) { |
4117 | 0 | saveState(); |
4118 | 0 | state->clipToRect(xMin, yMin, xMax, yMax); |
4119 | 0 | out->clipToTextStrokePath(state); |
4120 | 0 | doPatternText(gTrue); |
4121 | 0 | restoreState(); |
4122 | 0 | } else { |
4123 | 0 | out->strokeTextPath(state); |
4124 | 0 | } |
4125 | 0 | } |
4126 | | |
4127 | | //--- clip |
4128 | 0 | if (render & 4) { |
4129 | 0 | out->addTextPathToSavedClipPath(state); |
4130 | 0 | haveSavedClipPath = gTrue; |
4131 | 0 | } else { |
4132 | 0 | out->clearTextPath(state); |
4133 | 0 | } |
4134 | 0 | } |
4135 | |
|
4136 | 0 | opCounter += 10 * s->getLength(); |
4137 | 0 | } |
4138 | | |
4139 | | // NB: this is only called when ocState is false. |
4140 | 0 | void Gfx::doIncCharCount(GString *s) { |
4141 | 0 | if (out->needCharCount()) { |
4142 | 0 | out->incCharCount(s->getLength()); |
4143 | 0 | } |
4144 | 0 | } |
4145 | | |
4146 | | //------------------------------------------------------------------------ |
4147 | | // XObject operators |
4148 | | //------------------------------------------------------------------------ |
4149 | | |
4150 | 0 | void Gfx::opXObject(Object args[], int numArgs) { |
4151 | 0 | char *name; |
4152 | 0 | Object xObj, refObj, obj2, obj3; |
4153 | 0 | GBool ocSaved, oc; |
4154 | 0 | #if OPI_SUPPORT |
4155 | 0 | Object opiDict; |
4156 | 0 | #endif |
4157 | |
|
4158 | 0 | if (!ocState && !out->needCharCount()) { |
4159 | 0 | return; |
4160 | 0 | } |
4161 | 0 | name = args[0].getName(); |
4162 | | // NB: we get both the reference and the object here, to make sure |
4163 | | // they refer to the same object. There's a problematic corner |
4164 | | // case: if the resource dict contains an entry for [name] with a |
4165 | | // reference to a nonexistent object ("/name 99999 0 R", where |
4166 | | // object 99999 doesn't exist), and a parent resource dict contains |
4167 | | // a valid entry with the same name, then lookupXObjectNF() will |
4168 | | // return "99999 0 R", but lookupXObject() will return the valid |
4169 | | // entry. This causes problems for doImage() and doForm(). |
4170 | 0 | if (!res->lookupXObjectNF(name, &refObj)) { |
4171 | 0 | return; |
4172 | 0 | } |
4173 | 0 | if (!refObj.fetch(xref, &xObj)) { |
4174 | 0 | refObj.free(); |
4175 | 0 | return; |
4176 | 0 | } |
4177 | 0 | if (!xObj.isStream()) { |
4178 | 0 | error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name); |
4179 | 0 | xObj.free(); |
4180 | 0 | refObj.free(); |
4181 | 0 | return; |
4182 | 0 | } |
4183 | | |
4184 | | // check for optional content key |
4185 | 0 | ocSaved = ocState; |
4186 | 0 | xObj.streamGetDict()->lookupNF("OC", &obj2); |
4187 | 0 | if (doc->getOptionalContent()->evalOCObject(&obj2, &oc)) { |
4188 | 0 | ocState &= oc; |
4189 | 0 | } |
4190 | 0 | obj2.free(); |
4191 | |
|
4192 | 0 | #if USE_EXCEPTIONS |
4193 | 0 | try { |
4194 | 0 | #endif |
4195 | 0 | #if OPI_SUPPORT |
4196 | 0 | xObj.streamGetDict()->lookup("OPI", &opiDict); |
4197 | 0 | if (opiDict.isDict()) { |
4198 | 0 | out->opiBegin(state, opiDict.getDict()); |
4199 | 0 | } |
4200 | 0 | #endif |
4201 | 0 | xObj.streamGetDict()->lookup("Subtype", &obj2); |
4202 | 0 | if (obj2.isName("Image")) { |
4203 | 0 | if (out->needNonText()) { |
4204 | 0 | doImage(&refObj, xObj.getStream(), gFalse); |
4205 | 0 | } |
4206 | 0 | } else if (obj2.isName("Form")) { |
4207 | 0 | if (out->useDrawForm() && refObj.isRef()) { |
4208 | 0 | if (ocState) { |
4209 | 0 | out->drawForm(refObj.getRef()); |
4210 | 0 | } |
4211 | 0 | } else { |
4212 | 0 | doForm(&refObj, &xObj); |
4213 | 0 | } |
4214 | 0 | } else if (obj2.isName("PS")) { |
4215 | 0 | if (ocState) { |
4216 | 0 | xObj.streamGetDict()->lookup("Level1", &obj3); |
4217 | 0 | out->psXObject(xObj.getStream(), |
4218 | 0 | obj3.isStream() ? obj3.getStream() : (Stream *)NULL); |
4219 | 0 | obj3.free(); |
4220 | 0 | } |
4221 | 0 | } else if (obj2.isName()) { |
4222 | 0 | error(errSyntaxError, getPos(), |
4223 | 0 | "Unknown XObject subtype '{0:s}'", obj2.getName()); |
4224 | 0 | } else { |
4225 | 0 | error(errSyntaxError, getPos(), |
4226 | 0 | "XObject subtype is missing or wrong type"); |
4227 | 0 | } |
4228 | 0 | obj2.free(); |
4229 | 0 | #if OPI_SUPPORT |
4230 | 0 | if (opiDict.isDict()) { |
4231 | 0 | out->opiEnd(state, opiDict.getDict()); |
4232 | 0 | } |
4233 | 0 | opiDict.free(); |
4234 | 0 | #endif |
4235 | 0 | #if USE_EXCEPTIONS |
4236 | 0 | } catch (GMemException e) { |
4237 | 0 | xObj.free(); |
4238 | 0 | refObj.free(); |
4239 | 0 | throw; |
4240 | 0 | } |
4241 | 0 | #endif |
4242 | | |
4243 | 0 | ocState = ocSaved; |
4244 | |
|
4245 | 0 | xObj.free(); |
4246 | 0 | refObj.free(); |
4247 | 0 | } |
4248 | | |
4249 | 0 | GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { |
4250 | 0 | Dict *dict, *maskDict; |
4251 | 0 | int width, height; |
4252 | 0 | int bits, maskBits; |
4253 | 0 | StreamColorSpaceMode csMode; |
4254 | 0 | GBool mask, invert; |
4255 | 0 | GfxColorSpace *colorSpace, *maskColorSpace; |
4256 | 0 | GfxImageColorMap *colorMap, *maskColorMap; |
4257 | 0 | Object maskObj, smaskObj, maskRef; |
4258 | 0 | GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte; |
4259 | 0 | int maskColors[2*gfxColorMaxComps]; |
4260 | 0 | int maskWidth, maskHeight; |
4261 | 0 | GBool maskInvert; |
4262 | 0 | Stream *maskStr; |
4263 | 0 | double matte[gfxColorMaxComps]; |
4264 | 0 | GBool interpolate; |
4265 | 0 | GfxRenderingIntent riSaved; |
4266 | 0 | Object obj1, obj2; |
4267 | 0 | int i, n; |
4268 | | |
4269 | | // check for optional content |
4270 | 0 | if (!ocState && !inlineImg) { |
4271 | 0 | return gTrue; |
4272 | 0 | } |
4273 | | |
4274 | | // images can have arbitrarily high compression ratios, but the |
4275 | | // data size is inherently limited |
4276 | 0 | str->disableDecompressionBombChecking(); |
4277 | | |
4278 | | // get info from the stream |
4279 | 0 | bits = 0; |
4280 | 0 | csMode = streamCSNone; |
4281 | 0 | str->getImageParams(&bits, &csMode); |
4282 | | |
4283 | | // get stream dict |
4284 | 0 | dict = str->getDict(); |
4285 | | |
4286 | | // save the current rendering intent |
4287 | 0 | riSaved = state->getRenderingIntent(); |
4288 | | |
4289 | | // get size |
4290 | 0 | dict->lookup("Width", &obj1); |
4291 | 0 | if (obj1.isNull()) { |
4292 | 0 | obj1.free(); |
4293 | 0 | dict->lookup("W", &obj1); |
4294 | 0 | } |
4295 | 0 | if (obj1.isInt()) { |
4296 | 0 | width = obj1.getInt(); |
4297 | 0 | } else if (obj1.isReal()) { |
4298 | 0 | error(errSyntaxWarning, getPos(), "Non-integer image width"); |
4299 | 0 | width = (int)obj1.getReal(); |
4300 | 0 | } else { |
4301 | 0 | goto err2; |
4302 | 0 | } |
4303 | 0 | obj1.free(); |
4304 | 0 | if (width <= 0) { |
4305 | 0 | goto err1; |
4306 | 0 | } |
4307 | 0 | dict->lookup("Height", &obj1); |
4308 | 0 | if (obj1.isNull()) { |
4309 | 0 | obj1.free(); |
4310 | 0 | dict->lookup("H", &obj1); |
4311 | 0 | } |
4312 | 0 | if (obj1.isInt()) { |
4313 | 0 | height = obj1.getInt(); |
4314 | 0 | } else if (obj1.isReal()) { |
4315 | 0 | error(errSyntaxWarning, getPos(), "Non-integer image height"); |
4316 | 0 | height = (int)obj1.getReal(); |
4317 | 0 | } else { |
4318 | 0 | goto err2; |
4319 | 0 | } |
4320 | 0 | obj1.free(); |
4321 | 0 | if (height <= 0) { |
4322 | 0 | goto err1; |
4323 | 0 | } |
4324 | | |
4325 | | // image or mask? |
4326 | 0 | dict->lookup("ImageMask", &obj1); |
4327 | 0 | if (obj1.isNull()) { |
4328 | 0 | obj1.free(); |
4329 | 0 | dict->lookup("IM", &obj1); |
4330 | 0 | } |
4331 | 0 | mask = gFalse; |
4332 | 0 | if (obj1.isBool()) |
4333 | 0 | mask = obj1.getBool(); |
4334 | 0 | else if (!obj1.isNull()) |
4335 | 0 | goto err2; |
4336 | 0 | obj1.free(); |
4337 | | |
4338 | | // bit depth |
4339 | 0 | if (bits == 0) { |
4340 | 0 | dict->lookup("BitsPerComponent", &obj1); |
4341 | 0 | if (obj1.isNull()) { |
4342 | 0 | obj1.free(); |
4343 | 0 | dict->lookup("BPC", &obj1); |
4344 | 0 | } |
4345 | 0 | if (obj1.isNum()) { |
4346 | 0 | if (obj1.isInt()) { |
4347 | 0 | bits = obj1.getInt(); |
4348 | 0 | } else if (obj1.isReal()) { |
4349 | 0 | error(errSyntaxWarning, getPos(), |
4350 | 0 | "Non-integer image bits per component"); |
4351 | 0 | bits = (int)obj1.getReal(); |
4352 | 0 | } |
4353 | 0 | if (bits < 1 || bits > 16) { |
4354 | 0 | goto err2; |
4355 | 0 | } |
4356 | 0 | } else if (mask) { |
4357 | 0 | bits = 1; |
4358 | 0 | } else { |
4359 | 0 | goto err2; |
4360 | 0 | } |
4361 | 0 | obj1.free(); |
4362 | 0 | } |
4363 | | |
4364 | | // interpolate flag |
4365 | 0 | dict->lookup("Interpolate", &obj1); |
4366 | 0 | if (obj1.isNull()) { |
4367 | 0 | obj1.free(); |
4368 | 0 | dict->lookup("I", &obj1); |
4369 | 0 | } |
4370 | 0 | interpolate = obj1.isBool() && obj1.getBool(); |
4371 | 0 | obj1.free(); |
4372 | | |
4373 | | // display a mask |
4374 | 0 | if (mask) { |
4375 | | |
4376 | | // check for inverted mask |
4377 | 0 | if (bits != 1) |
4378 | 0 | goto err1; |
4379 | 0 | invert = gFalse; |
4380 | 0 | dict->lookup("Decode", &obj1); |
4381 | 0 | if (obj1.isNull()) { |
4382 | 0 | obj1.free(); |
4383 | 0 | dict->lookup("D", &obj1); |
4384 | 0 | } |
4385 | 0 | if (obj1.isArray()) { |
4386 | 0 | obj1.arrayGet(0, &obj2); |
4387 | 0 | invert = obj2.isNum() && obj2.getNum() == 1; |
4388 | 0 | obj2.free(); |
4389 | 0 | } else if (!obj1.isNull()) { |
4390 | 0 | goto err2; |
4391 | 0 | } |
4392 | 0 | obj1.free(); |
4393 | | |
4394 | | // if drawing is disabled, skip over inline image data |
4395 | 0 | if (!ocState) { |
4396 | 0 | str->disableDecompressionBombChecking(); |
4397 | 0 | str->reset(); |
4398 | 0 | n = height * ((width + 7) / 8); |
4399 | 0 | for (i = 0; i < n; ++i) { |
4400 | 0 | str->getChar(); |
4401 | 0 | } |
4402 | 0 | str->close(); |
4403 | | |
4404 | | // draw it |
4405 | 0 | } else { |
4406 | 0 | if (state->getFillColorSpace()->getMode() == csPattern) { |
4407 | 0 | doPatternImageMask(ref, str, width, height, invert, inlineImg, |
4408 | 0 | interpolate); |
4409 | 0 | } else { |
4410 | 0 | out->drawImageMask(state, ref, str, width, height, invert, inlineImg, |
4411 | 0 | interpolate); |
4412 | 0 | } |
4413 | 0 | } |
4414 | |
|
4415 | 0 | } else { |
4416 | | |
4417 | | // rendering intent |
4418 | 0 | if (dict->lookup("Intent", &obj1)->isName()) { |
4419 | 0 | opSetRenderingIntent(&obj1, 1); |
4420 | 0 | } |
4421 | 0 | obj1.free(); |
4422 | | |
4423 | | // get color space and color map |
4424 | 0 | dict->lookup("ColorSpace", &obj1); |
4425 | 0 | if (obj1.isNull()) { |
4426 | 0 | obj1.free(); |
4427 | 0 | dict->lookup("CS", &obj1); |
4428 | 0 | } |
4429 | 0 | if (obj1.isName()) { |
4430 | 0 | res->lookupColorSpace(obj1.getName(), &obj2); |
4431 | 0 | if (!obj2.isNull()) { |
4432 | 0 | obj1.free(); |
4433 | 0 | obj1 = obj2; |
4434 | 0 | } else { |
4435 | 0 | obj2.free(); |
4436 | 0 | } |
4437 | 0 | } |
4438 | 0 | if (!obj1.isNull()) { |
4439 | 0 | colorSpace = GfxColorSpace::parse(&obj1 |
4440 | 0 | ); |
4441 | 0 | } else if (csMode == streamCSDeviceGray) { |
4442 | 0 | colorSpace = GfxColorSpace::create(csDeviceGray); |
4443 | 0 | } else if (csMode == streamCSDeviceRGB) { |
4444 | 0 | colorSpace = GfxColorSpace::create(csDeviceRGB); |
4445 | 0 | } else if (csMode == streamCSDeviceCMYK) { |
4446 | 0 | colorSpace = GfxColorSpace::create(csDeviceCMYK); |
4447 | 0 | } else { |
4448 | 0 | colorSpace = NULL; |
4449 | 0 | } |
4450 | 0 | obj1.free(); |
4451 | 0 | if (!colorSpace) { |
4452 | 0 | goto err1; |
4453 | 0 | } |
4454 | 0 | if (colorSpace->getMode() == csPattern) { |
4455 | 0 | error(errSyntaxError, getPos(), "Image with a Pattern color space"); |
4456 | 0 | delete colorSpace; |
4457 | 0 | goto err1; |
4458 | 0 | } |
4459 | 0 | dict->lookup("Decode", &obj1); |
4460 | 0 | if (obj1.isNull()) { |
4461 | 0 | obj1.free(); |
4462 | 0 | dict->lookup("D", &obj1); |
4463 | 0 | } |
4464 | 0 | colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); |
4465 | 0 | obj1.free(); |
4466 | 0 | if (!colorMap->isOk()) { |
4467 | 0 | delete colorMap; |
4468 | 0 | goto err1; |
4469 | 0 | } |
4470 | | |
4471 | | // get the mask |
4472 | 0 | haveColorKeyMask = haveExplicitMask = haveSoftMask = haveMatte = gFalse; |
4473 | 0 | maskStr = NULL; // make gcc happy |
4474 | 0 | maskWidth = maskHeight = 0; // make gcc happy |
4475 | 0 | maskInvert = gFalse; // make gcc happy |
4476 | 0 | maskColorMap = NULL; // make gcc happy |
4477 | 0 | dict->lookup("Mask", &maskObj); |
4478 | 0 | dict->lookup("SMask", &smaskObj); |
4479 | 0 | if (smaskObj.isStream()) { |
4480 | | // soft mask |
4481 | 0 | if (inlineImg) { |
4482 | 0 | delete colorMap; |
4483 | 0 | maskObj.free(); |
4484 | 0 | smaskObj.free(); |
4485 | 0 | goto err1; |
4486 | 0 | } |
4487 | 0 | maskStr = smaskObj.getStream(); |
4488 | 0 | maskStr->disableDecompressionBombChecking(); |
4489 | 0 | maskDict = smaskObj.streamGetDict(); |
4490 | 0 | maskDict->lookup("Width", &obj1); |
4491 | 0 | if (obj1.isNull()) { |
4492 | 0 | obj1.free(); |
4493 | 0 | maskDict->lookup("W", &obj1); |
4494 | 0 | } |
4495 | 0 | if (obj1.isInt()) { |
4496 | 0 | maskWidth = obj1.getInt(); |
4497 | 0 | } else if (obj1.isReal()) { |
4498 | 0 | error(errSyntaxWarning, getPos(), "Non-integer image mask width"); |
4499 | 0 | maskWidth = (int)obj1.getReal(); |
4500 | 0 | } else { |
4501 | 0 | delete colorMap; |
4502 | 0 | maskObj.free(); |
4503 | 0 | smaskObj.free(); |
4504 | 0 | goto err2; |
4505 | 0 | } |
4506 | 0 | obj1.free(); |
4507 | 0 | maskDict->lookup("Height", &obj1); |
4508 | 0 | if (obj1.isNull()) { |
4509 | 0 | obj1.free(); |
4510 | 0 | maskDict->lookup("H", &obj1); |
4511 | 0 | } |
4512 | 0 | if (obj1.isInt()) { |
4513 | 0 | maskHeight = obj1.getInt(); |
4514 | 0 | } else if (obj1.isReal()) { |
4515 | 0 | error(errSyntaxWarning, getPos(), "Non-integer image mask height"); |
4516 | 0 | maskHeight = (int)obj1.getReal(); |
4517 | 0 | } else { |
4518 | 0 | delete colorMap; |
4519 | 0 | maskObj.free(); |
4520 | 0 | smaskObj.free(); |
4521 | 0 | goto err2; |
4522 | 0 | } |
4523 | 0 | obj1.free(); |
4524 | 0 | if (maskWidth <= 0 || maskHeight <= 0) { |
4525 | 0 | delete colorMap; |
4526 | 0 | maskObj.free(); |
4527 | 0 | smaskObj.free(); |
4528 | 0 | goto err1; |
4529 | 0 | } |
4530 | 0 | maskDict->lookup("BitsPerComponent", &obj1); |
4531 | 0 | if (obj1.isNull()) { |
4532 | 0 | obj1.free(); |
4533 | 0 | maskDict->lookup("BPC", &obj1); |
4534 | 0 | } |
4535 | 0 | if (obj1.isInt()) { |
4536 | 0 | maskBits = obj1.getInt(); |
4537 | 0 | } else if (obj1.isReal()) { |
4538 | 0 | error(errSyntaxWarning, getPos(), |
4539 | 0 | "Non-integer image mask bits per component"); |
4540 | 0 | maskBits = (int)obj1.getReal(); |
4541 | 0 | } else { |
4542 | 0 | delete colorMap; |
4543 | 0 | maskObj.free(); |
4544 | 0 | smaskObj.free(); |
4545 | 0 | goto err2; |
4546 | 0 | } |
4547 | 0 | obj1.free(); |
4548 | 0 | if (maskBits < 1 || maskBits > 16) { |
4549 | 0 | delete colorMap; |
4550 | 0 | maskObj.free(); |
4551 | 0 | smaskObj.free(); |
4552 | 0 | goto err1; |
4553 | 0 | } |
4554 | 0 | maskDict->lookup("ColorSpace", &obj1); |
4555 | 0 | if (obj1.isNull()) { |
4556 | 0 | obj1.free(); |
4557 | 0 | maskDict->lookup("CS", &obj1); |
4558 | 0 | } |
4559 | 0 | if (obj1.isName()) { |
4560 | 0 | res->lookupColorSpace(obj1.getName(), &obj2); |
4561 | 0 | if (!obj2.isNull()) { |
4562 | 0 | obj1.free(); |
4563 | 0 | obj1 = obj2; |
4564 | 0 | } else { |
4565 | 0 | obj2.free(); |
4566 | 0 | } |
4567 | 0 | } |
4568 | 0 | if (!obj1.isName("DeviceGray")) { |
4569 | 0 | delete colorMap; |
4570 | 0 | maskObj.free(); |
4571 | 0 | smaskObj.free(); |
4572 | 0 | goto err2; |
4573 | 0 | } |
4574 | 0 | maskColorSpace = new GfxDeviceGrayColorSpace(); |
4575 | 0 | obj1.free(); |
4576 | 0 | maskDict->lookup("Decode", &obj1); |
4577 | 0 | if (obj1.isNull()) { |
4578 | 0 | obj1.free(); |
4579 | 0 | maskDict->lookup("D", &obj1); |
4580 | 0 | } |
4581 | 0 | maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace); |
4582 | 0 | obj1.free(); |
4583 | 0 | if (!maskColorMap->isOk()) { |
4584 | 0 | delete maskColorMap; |
4585 | 0 | delete colorMap; |
4586 | 0 | maskObj.free(); |
4587 | 0 | smaskObj.free(); |
4588 | 0 | goto err1; |
4589 | 0 | } |
4590 | 0 | if (maskDict->lookup("Matte", &obj1)->isArray()) { |
4591 | 0 | if (obj1.arrayGetLength() == colorSpace->getNComps()) { |
4592 | 0 | for (i = 0; i < obj1.arrayGetLength(); ++i) { |
4593 | 0 | if (obj1.arrayGet(i, &obj2)->isNum()) { |
4594 | 0 | matte[i] = obj2.getNum(); |
4595 | 0 | } else { |
4596 | 0 | error(errSyntaxError, getPos(), |
4597 | 0 | "Invalid Matte entry in soft mask"); |
4598 | 0 | matte[i] = 0; |
4599 | 0 | } |
4600 | 0 | obj2.free(); |
4601 | 0 | } |
4602 | 0 | haveMatte = gTrue; |
4603 | 0 | } else { |
4604 | 0 | error(errSyntaxError, getPos(), "Invalid Matte entry in soft mask"); |
4605 | 0 | } |
4606 | 0 | } |
4607 | 0 | obj1.free(); |
4608 | 0 | haveSoftMask = gTrue; |
4609 | 0 | } else if (maskObj.isArray()) { |
4610 | | // color key mask |
4611 | 0 | haveColorKeyMask = gTrue; |
4612 | 0 | for (i = 0; |
4613 | 0 | i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps; |
4614 | 0 | i += 2) { |
4615 | 0 | maskObj.arrayGet(i, &obj1); |
4616 | 0 | if (!obj1.isInt()) { |
4617 | 0 | obj1.free(); |
4618 | 0 | haveColorKeyMask = gFalse; |
4619 | 0 | break; |
4620 | 0 | } |
4621 | 0 | maskColors[i] = obj1.getInt(); |
4622 | 0 | obj1.free(); |
4623 | 0 | if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) { |
4624 | 0 | haveColorKeyMask = gFalse; |
4625 | 0 | break; |
4626 | 0 | } |
4627 | 0 | maskObj.arrayGet(i+1, &obj1); |
4628 | 0 | if (!obj1.isInt()) { |
4629 | 0 | obj1.free(); |
4630 | 0 | haveColorKeyMask = gFalse; |
4631 | 0 | break; |
4632 | 0 | } |
4633 | 0 | maskColors[i+1] = obj1.getInt(); |
4634 | 0 | obj1.free(); |
4635 | 0 | if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) || |
4636 | 0 | maskColors[i] > maskColors[i+1]) { |
4637 | 0 | haveColorKeyMask = gFalse; |
4638 | 0 | break; |
4639 | 0 | } |
4640 | 0 | } |
4641 | 0 | } else if (maskObj.isStream()) { |
4642 | | // explicit mask |
4643 | 0 | haveExplicitMask = gTrue; |
4644 | 0 | if (inlineImg) { |
4645 | 0 | delete colorMap; |
4646 | 0 | maskObj.free(); |
4647 | 0 | smaskObj.free(); |
4648 | 0 | goto err1; |
4649 | 0 | } |
4650 | 0 | maskStr = maskObj.getStream(); |
4651 | 0 | maskStr->disableDecompressionBombChecking(); |
4652 | 0 | maskDict = maskObj.streamGetDict(); |
4653 | 0 | maskDict->lookup("Width", &obj1); |
4654 | 0 | if (obj1.isNull()) { |
4655 | 0 | obj1.free(); |
4656 | 0 | maskDict->lookup("W", &obj1); |
4657 | 0 | } |
4658 | 0 | if (obj1.isInt()) { |
4659 | 0 | maskWidth = obj1.getInt(); |
4660 | 0 | } else if (obj1.isReal()) { |
4661 | 0 | error(errSyntaxWarning, getPos(), "Non-integer image mask width"); |
4662 | 0 | maskWidth = (int)obj1.getReal(); |
4663 | 0 | } else { |
4664 | 0 | haveExplicitMask = gFalse; |
4665 | 0 | } |
4666 | 0 | obj1.free(); |
4667 | 0 | maskDict->lookup("Height", &obj1); |
4668 | 0 | if (obj1.isNull()) { |
4669 | 0 | obj1.free(); |
4670 | 0 | maskDict->lookup("H", &obj1); |
4671 | 0 | } |
4672 | 0 | if (obj1.isInt()) { |
4673 | 0 | maskHeight = obj1.getInt(); |
4674 | 0 | } else if (obj1.isReal()) { |
4675 | 0 | error(errSyntaxWarning, getPos(), "Non-integer image mask height"); |
4676 | 0 | maskHeight = (int)obj1.getReal(); |
4677 | 0 | } else { |
4678 | 0 | haveExplicitMask = gFalse; |
4679 | 0 | } |
4680 | 0 | obj1.free(); |
4681 | 0 | if (maskWidth <= 0 || maskHeight <= 0) { |
4682 | 0 | haveExplicitMask = gFalse; |
4683 | 0 | } |
4684 | 0 | maskDict->lookup("ImageMask", &obj1); |
4685 | 0 | if (obj1.isNull()) { |
4686 | 0 | obj1.free(); |
4687 | 0 | maskDict->lookup("IM", &obj1); |
4688 | 0 | } |
4689 | 0 | if (!obj1.isBool() || !obj1.getBool()) { |
4690 | 0 | haveExplicitMask = gFalse; |
4691 | 0 | } |
4692 | 0 | obj1.free(); |
4693 | 0 | maskInvert = gFalse; |
4694 | 0 | maskDict->lookup("Decode", &obj1); |
4695 | 0 | if (obj1.isNull()) { |
4696 | 0 | obj1.free(); |
4697 | 0 | maskDict->lookup("D", &obj1); |
4698 | 0 | } |
4699 | 0 | if (obj1.isArray()) { |
4700 | 0 | obj1.arrayGet(0, &obj2); |
4701 | 0 | maskInvert = obj2.isNum() && obj2.getNum() == 1; |
4702 | 0 | obj2.free(); |
4703 | 0 | } else if (!obj1.isNull()) { |
4704 | 0 | haveExplicitMask = gFalse; |
4705 | 0 | } |
4706 | 0 | obj1.free(); |
4707 | 0 | if (!haveExplicitMask) { |
4708 | 0 | error(errSyntaxError, getPos(), "Bad image parameters"); |
4709 | 0 | } |
4710 | 0 | } |
4711 | | |
4712 | | // if drawing is disabled, skip over inline image data |
4713 | 0 | if (state->getIgnoreColorOps() || !ocState) { |
4714 | 0 | if (state->getIgnoreColorOps()) { |
4715 | 0 | error(errSyntaxWarning, getPos(), "Ignoring image" |
4716 | 0 | " in uncolored Type 3 char or tiling pattern"); |
4717 | 0 | } |
4718 | 0 | if (inlineImg) { |
4719 | 0 | str->disableDecompressionBombChecking(); |
4720 | 0 | str->reset(); |
4721 | 0 | n = height * ((width * colorMap->getNumPixelComps() * |
4722 | 0 | colorMap->getBits() + 7) / 8); |
4723 | 0 | str->discardChars(n); |
4724 | 0 | str->close(); |
4725 | 0 | } |
4726 | | |
4727 | | // draw it |
4728 | 0 | } else { |
4729 | 0 | if (haveSoftMask) { |
4730 | 0 | dict->lookupNF("SMask", &maskRef); |
4731 | 0 | out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, |
4732 | 0 | &maskRef, maskStr, maskWidth, maskHeight, |
4733 | 0 | maskColorMap, |
4734 | 0 | haveMatte ? matte : (double *)NULL, |
4735 | 0 | interpolate); |
4736 | 0 | maskRef.free(); |
4737 | 0 | delete maskColorMap; |
4738 | 0 | } else if (haveExplicitMask) { |
4739 | 0 | dict->lookupNF("Mask", &maskRef); |
4740 | 0 | out->drawMaskedImage(state, ref, str, width, height, colorMap, |
4741 | 0 | &maskRef, maskStr, maskWidth, maskHeight, |
4742 | 0 | maskInvert, interpolate); |
4743 | 0 | maskRef.free(); |
4744 | 0 | } else { |
4745 | 0 | out->drawImage(state, ref, str, width, height, colorMap, |
4746 | 0 | haveColorKeyMask ? maskColors : (int *)NULL, inlineImg, |
4747 | 0 | interpolate); |
4748 | 0 | } |
4749 | 0 | } |
4750 | |
|
4751 | 0 | delete colorMap; |
4752 | 0 | maskObj.free(); |
4753 | 0 | smaskObj.free(); |
4754 | 0 | } |
4755 | | |
4756 | | // restore rendering intent |
4757 | 0 | if (state->getRenderingIntent() != riSaved) { |
4758 | 0 | state->setRenderingIntent(riSaved); |
4759 | 0 | out->updateRenderingIntent(state); |
4760 | 0 | } |
4761 | |
|
4762 | 0 | if ((i = width * height) > 1000) { |
4763 | 0 | i = 1000; |
4764 | 0 | } |
4765 | 0 | opCounter += i; |
4766 | |
|
4767 | 0 | return gTrue; |
4768 | | |
4769 | 0 | err2: |
4770 | 0 | obj1.free(); |
4771 | 0 | err1: |
4772 | 0 | error(errSyntaxError, getPos(), "Bad image parameters"); |
4773 | | |
4774 | | // restore rendering intent |
4775 | 0 | if (state->getRenderingIntent() != riSaved) { |
4776 | 0 | state->setRenderingIntent(riSaved); |
4777 | 0 | out->updateRenderingIntent(state); |
4778 | 0 | } |
4779 | |
|
4780 | 0 | return gFalse; |
4781 | 0 | } |
4782 | | |
4783 | 0 | void Gfx::doForm(Object *strRef, Object *str) { |
4784 | 0 | Dict *dict; |
4785 | 0 | GBool transpGroup, isolated, knockout; |
4786 | 0 | Object matrixObj, bboxObj; |
4787 | 0 | double m[6], bbox[4]; |
4788 | 0 | Object resObj; |
4789 | 0 | Dict *resDict; |
4790 | 0 | Object obj1, obj2, obj3; |
4791 | 0 | int i; |
4792 | | |
4793 | | // check for excessive recursion |
4794 | 0 | if (formDepth > 100) { |
4795 | 0 | return; |
4796 | 0 | } |
4797 | | |
4798 | | // check for optional content |
4799 | 0 | if (!ocState && !out->needCharCount()) { |
4800 | 0 | return; |
4801 | 0 | } |
4802 | | |
4803 | | // get stream dict |
4804 | 0 | dict = str->streamGetDict(); |
4805 | | |
4806 | | // check form type |
4807 | 0 | dict->lookup("FormType", &obj1); |
4808 | 0 | if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { |
4809 | 0 | error(errSyntaxError, getPos(), "Unknown form type"); |
4810 | 0 | } |
4811 | 0 | obj1.free(); |
4812 | | |
4813 | | // get bounding box |
4814 | 0 | dict->lookup("BBox", &bboxObj); |
4815 | 0 | if (!(bboxObj.isArray() && bboxObj.arrayGetLength() == 4)) { |
4816 | 0 | bboxObj.free(); |
4817 | 0 | error(errSyntaxError, getPos(), "Bad form bounding box"); |
4818 | 0 | return; |
4819 | 0 | } |
4820 | 0 | for (i = 0; i < 4; ++i) { |
4821 | 0 | bboxObj.arrayGet(i, &obj1); |
4822 | 0 | if (!obj1.isNum()) { |
4823 | 0 | bboxObj.free(); |
4824 | 0 | error(errSyntaxError, getPos(), "Bad form bounding box"); |
4825 | 0 | return; |
4826 | 0 | } |
4827 | 0 | bbox[i] = obj1.getNum(); |
4828 | 0 | obj1.free(); |
4829 | 0 | } |
4830 | 0 | bboxObj.free(); |
4831 | | |
4832 | | // get matrix |
4833 | 0 | dict->lookup("Matrix", &matrixObj); |
4834 | 0 | if (matrixObj.isArray() && matrixObj.arrayGetLength() == 6) { |
4835 | 0 | for (i = 0; i < 6; ++i) { |
4836 | 0 | matrixObj.arrayGet(i, &obj1); |
4837 | 0 | if (obj1.isNum()) { |
4838 | 0 | m[i] = obj1.getNum(); |
4839 | 0 | } else { |
4840 | 0 | m[i] = 0; |
4841 | 0 | } |
4842 | 0 | obj1.free(); |
4843 | 0 | } |
4844 | 0 | } else { |
4845 | 0 | m[0] = 1; m[1] = 0; |
4846 | 0 | m[2] = 0; m[3] = 1; |
4847 | 0 | m[4] = 0; m[5] = 0; |
4848 | 0 | } |
4849 | 0 | matrixObj.free(); |
4850 | | |
4851 | | // get resources |
4852 | 0 | dict->lookup("Resources", &resObj); |
4853 | 0 | resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; |
4854 | | |
4855 | | // check for a transparency group |
4856 | 0 | transpGroup = isolated = knockout = gFalse; |
4857 | 0 | if (dict->lookup("Group", &obj1)->isDict()) { |
4858 | 0 | if (obj1.dictLookup("S", &obj2)->isName("Transparency")) { |
4859 | 0 | transpGroup = gTrue; |
4860 | 0 | if (obj1.dictLookup("I", &obj3)->isBool()) { |
4861 | 0 | isolated = obj3.getBool(); |
4862 | 0 | } |
4863 | 0 | obj3.free(); |
4864 | 0 | if (obj1.dictLookup("K", &obj3)->isBool()) { |
4865 | 0 | knockout = obj3.getBool(); |
4866 | 0 | } |
4867 | 0 | obj3.free(); |
4868 | 0 | } |
4869 | 0 | obj2.free(); |
4870 | 0 | } |
4871 | 0 | obj1.free(); |
4872 | | |
4873 | | // draw it |
4874 | 0 | ++formDepth; |
4875 | 0 | drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout); |
4876 | 0 | --formDepth; |
4877 | |
|
4878 | 0 | resObj.free(); |
4879 | 0 | } |
4880 | | |
4881 | | void Gfx::drawForm(Object *strRef, Dict *resDict, |
4882 | | double *matrix, double *bbox, |
4883 | | GBool transpGroup, GBool softMask, |
4884 | | GBool isolated, GBool knockout, |
4885 | | GBool alpha, Function *transferFunc, |
4886 | 0 | Object *backdropColorObj) { |
4887 | 0 | Parser *oldParser; |
4888 | 0 | GfxState *savedState; |
4889 | 0 | GfxColorSpace *blendingColorSpace; |
4890 | 0 | GfxColor backdropColor; |
4891 | 0 | Object strObj, groupAttrsObj, csObj, obj1; |
4892 | 0 | double oldBaseMatrix[6]; |
4893 | 0 | int i; |
4894 | |
|
4895 | 0 | out->startStream(strRef->getRef(), state); |
4896 | | |
4897 | | // push new resources on stack |
4898 | 0 | pushResources(resDict); |
4899 | | |
4900 | | // save current graphics state |
4901 | 0 | saveState(); |
4902 | | |
4903 | | // kill any pre-existing path |
4904 | 0 | state->clearPath(); |
4905 | | |
4906 | | // save current parser |
4907 | 0 | oldParser = parser; |
4908 | | |
4909 | | // set form transformation matrix |
4910 | 0 | state->concatCTM(matrix[0], matrix[1], matrix[2], |
4911 | 0 | matrix[3], matrix[4], matrix[5]); |
4912 | 0 | out->updateCTM(state, matrix[0], matrix[1], matrix[2], |
4913 | 0 | matrix[3], matrix[4], matrix[5]); |
4914 | | |
4915 | | // set form bounding box |
4916 | 0 | state->moveTo(bbox[0], bbox[1]); |
4917 | 0 | state->lineTo(bbox[2], bbox[1]); |
4918 | 0 | state->lineTo(bbox[2], bbox[3]); |
4919 | 0 | state->lineTo(bbox[0], bbox[3]); |
4920 | 0 | state->closePath(); |
4921 | 0 | state->clip(); |
4922 | 0 | out->clip(state); |
4923 | 0 | state->clearPath(); |
4924 | |
|
4925 | 0 | blendingColorSpace = NULL; |
4926 | 0 | if (softMask || transpGroup) { |
4927 | | // get the blending color space |
4928 | | // NB: this must be done AFTER pushing the resource dictionary, |
4929 | | // so that any Default*** color spaces are available |
4930 | 0 | strRef->fetch(xref, &strObj); |
4931 | 0 | if (strObj.streamGetDict()->lookup("Group", &groupAttrsObj)->isDict()) { |
4932 | 0 | if (!groupAttrsObj.dictLookup("CS", &csObj)->isNull()) { |
4933 | 0 | blendingColorSpace = GfxColorSpace::parse(&csObj |
4934 | 0 | ); |
4935 | 0 | } |
4936 | 0 | csObj.free(); |
4937 | 0 | } |
4938 | 0 | groupAttrsObj.free(); |
4939 | 0 | strObj.free(); |
4940 | |
|
4941 | 0 | if (!out->beginTransparencyGroup(state, bbox, blendingColorSpace, |
4942 | 0 | isolated, knockout, softMask)) { |
4943 | 0 | transpGroup = gFalse; |
4944 | 0 | } |
4945 | |
|
4946 | 0 | if (softMask || transpGroup) { |
4947 | 0 | traceBegin(oldBaseMatrix, softMask ? "begin soft mask" : "begin t-group"); |
4948 | 0 | if (state->getBlendMode() != gfxBlendNormal) { |
4949 | 0 | state->setBlendMode(gfxBlendNormal); |
4950 | 0 | out->updateBlendMode(state); |
4951 | 0 | } |
4952 | 0 | if (state->getFillOpacity() != 1) { |
4953 | 0 | state->setFillOpacity(1); |
4954 | 0 | out->updateFillOpacity(state); |
4955 | 0 | } |
4956 | 0 | if (state->getStrokeOpacity() != 1) { |
4957 | 0 | state->setStrokeOpacity(1); |
4958 | 0 | out->updateStrokeOpacity(state); |
4959 | 0 | } |
4960 | 0 | out->clearSoftMask(state); |
4961 | 0 | } |
4962 | 0 | } |
4963 | | |
4964 | | // set new base matrix |
4965 | 0 | for (i = 0; i < 6; ++i) { |
4966 | 0 | oldBaseMatrix[i] = baseMatrix[i]; |
4967 | 0 | baseMatrix[i] = state->getCTM()[i]; |
4968 | 0 | } |
4969 | | |
4970 | | // save the state stack -- this handles the case where the form |
4971 | | // contents have unbalanced q/Q operators |
4972 | 0 | savedState = saveStateStack(); |
4973 | | |
4974 | | // draw the form |
4975 | 0 | display(strRef, gFalse); |
4976 | |
|
4977 | 0 | restoreStateStack(savedState); |
4978 | |
|
4979 | 0 | if (softMask || transpGroup) { |
4980 | 0 | out->endTransparencyGroup(state); |
4981 | 0 | } |
4982 | | |
4983 | | // restore base matrix |
4984 | 0 | for (i = 0; i < 6; ++i) { |
4985 | 0 | baseMatrix[i] = oldBaseMatrix[i]; |
4986 | 0 | } |
4987 | | |
4988 | | // restore parser |
4989 | 0 | parser = oldParser; |
4990 | | |
4991 | | // restore graphics state |
4992 | 0 | restoreState(); |
4993 | | |
4994 | | // pop resource stack |
4995 | 0 | popResources(); |
4996 | |
|
4997 | 0 | if (softMask) { |
4998 | 0 | for (i = 0; i < gfxColorMaxComps; ++i) { |
4999 | 0 | backdropColor.c[i] = 0; |
5000 | 0 | } |
5001 | 0 | if (backdropColorObj->isArray()) { |
5002 | 0 | for (i = 0; |
5003 | 0 | i < backdropColorObj->arrayGetLength() && i < gfxColorMaxComps; |
5004 | 0 | ++i) { |
5005 | 0 | backdropColorObj->arrayGet(i, &obj1); |
5006 | 0 | if (obj1.isNum()) { |
5007 | 0 | backdropColor.c[i] = dblToCol(obj1.getNum()); |
5008 | 0 | } |
5009 | 0 | obj1.free(); |
5010 | 0 | } |
5011 | 0 | } else if (blendingColorSpace) { |
5012 | 0 | blendingColorSpace->getDefaultColor(&backdropColor); |
5013 | 0 | } |
5014 | | //~ else: need to get the parent or default color space (?) |
5015 | 0 | out->setSoftMask(state, bbox, alpha, transferFunc, &backdropColor); |
5016 | 0 | traceEnd(oldBaseMatrix, "end soft mask"); |
5017 | 0 | } else if (transpGroup) { |
5018 | 0 | out->paintTransparencyGroup(state, bbox); |
5019 | 0 | traceEnd(oldBaseMatrix, "end t-group"); |
5020 | 0 | } |
5021 | |
|
5022 | 0 | if (blendingColorSpace) { |
5023 | 0 | delete blendingColorSpace; |
5024 | 0 | } |
5025 | |
|
5026 | 0 | out->endStream(strRef->getRef()); |
5027 | 0 | } |
5028 | | |
5029 | 0 | void Gfx::takeContentStreamStack(Gfx *oldGfx) { |
5030 | 0 | contentStreamStack->append(oldGfx->contentStreamStack); |
5031 | 0 | } |
5032 | | |
5033 | 0 | void Gfx::endOfPage() { |
5034 | 0 | while (state->hasSaves()) { |
5035 | 0 | restoreState(); |
5036 | 0 | } |
5037 | 0 | while (markedContentStack->getLength() > 0) { |
5038 | 0 | opEndMarkedContent(NULL, 0); |
5039 | 0 | } |
5040 | 0 | } |
5041 | | |
5042 | | //------------------------------------------------------------------------ |
5043 | | // in-line image operators |
5044 | | //------------------------------------------------------------------------ |
5045 | | |
5046 | 0 | void Gfx::opBeginImage(Object args[], int numArgs) { |
5047 | 0 | Stream *str; |
5048 | 0 | GBool haveLength; |
5049 | 0 | int c1, c2, c3; |
5050 | | |
5051 | | // NB: this function is run even if ocState is false -- doImage() is |
5052 | | // responsible for skipping over the inline image data |
5053 | | |
5054 | | // build dict/stream |
5055 | 0 | str = buildImageStream(&haveLength); |
5056 | | |
5057 | | // display the image |
5058 | 0 | if (str) { |
5059 | 0 | if (!doImage(NULL, str, gTrue)) { |
5060 | 0 | delete str; |
5061 | | |
5062 | | // if we have the stream length, skip to end-of-stream and then |
5063 | | // skip 'EI' in the original stream |
5064 | 0 | } else if (haveLength) { |
5065 | | // NB: getUndecodedStream() returns the EmbedStream created by |
5066 | | // buildImageStream() |
5067 | 0 | while ((c1 = str->getUndecodedStream()->getChar()) != EOF) ; |
5068 | 0 | delete str; |
5069 | 0 | str = parser->getStream(); |
5070 | 0 | c1 = str->getChar(); |
5071 | 0 | c2 = str->getChar(); |
5072 | 0 | c3 = str->lookChar(); |
5073 | 0 | while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) { |
5074 | 0 | c1 = c2; |
5075 | 0 | c2 = str->getChar(); |
5076 | 0 | c3 = str->lookChar(); |
5077 | 0 | } |
5078 | | |
5079 | | // else, look for the 'EI' tag and skip it |
5080 | 0 | } else { |
5081 | 0 | c1 = str->getUndecodedStream()->getChar(); |
5082 | 0 | c2 = str->getUndecodedStream()->getChar(); |
5083 | 0 | c3 = str->getUndecodedStream()->lookChar(); |
5084 | 0 | while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) { |
5085 | 0 | c1 = c2; |
5086 | 0 | c2 = str->getUndecodedStream()->getChar(); |
5087 | 0 | c3 = str->getUndecodedStream()->lookChar(); |
5088 | 0 | } |
5089 | 0 | delete str; |
5090 | 0 | } |
5091 | 0 | } |
5092 | 0 | } |
5093 | | |
5094 | 0 | Stream *Gfx::buildImageStream(GBool *haveLength) { |
5095 | 0 | Object dict; |
5096 | 0 | Object obj, lengthObj; |
5097 | 0 | char *key; |
5098 | 0 | int length; |
5099 | 0 | Stream *str; |
5100 | | |
5101 | | // build dictionary |
5102 | 0 | dict.initDict(xref); |
5103 | 0 | getContentObj(&obj); |
5104 | 0 | while (!obj.isCmd("ID") && !obj.isEOF()) { |
5105 | 0 | if (!obj.isName()) { |
5106 | 0 | error(errSyntaxError, getPos(), |
5107 | 0 | "Inline image dictionary key must be a name object"); |
5108 | 0 | obj.free(); |
5109 | 0 | } else { |
5110 | 0 | key = copyString(obj.getName()); |
5111 | 0 | obj.free(); |
5112 | 0 | getContentObj(&obj); |
5113 | 0 | if (obj.isEOF()) { |
5114 | 0 | gfree(key); |
5115 | 0 | break; |
5116 | 0 | } |
5117 | 0 | if (obj.isError()) { |
5118 | 0 | gfree(key); |
5119 | 0 | obj.free(); |
5120 | 0 | } else { |
5121 | 0 | dict.dictAdd(key, &obj); |
5122 | 0 | } |
5123 | 0 | } |
5124 | 0 | getContentObj(&obj); |
5125 | 0 | } |
5126 | 0 | if (obj.isEOF()) { |
5127 | 0 | error(errSyntaxError, getPos(), "End of file in inline image"); |
5128 | 0 | obj.free(); |
5129 | 0 | dict.free(); |
5130 | 0 | return NULL; |
5131 | 0 | } |
5132 | 0 | obj.free(); |
5133 | | |
5134 | | // check for length field |
5135 | 0 | length = 0; |
5136 | 0 | *haveLength = gFalse; |
5137 | 0 | if (!dict.dictLookup("Length", &lengthObj)->isInt()) { |
5138 | 0 | lengthObj.free(); |
5139 | 0 | dict.dictLookup("L", &lengthObj); |
5140 | 0 | } |
5141 | 0 | if (lengthObj.isInt()) { |
5142 | 0 | length = lengthObj.getInt(); |
5143 | 0 | *haveLength = gTrue; |
5144 | 0 | } |
5145 | 0 | lengthObj.free(); |
5146 | | |
5147 | | // make stream |
5148 | 0 | if (!(str = parser->getStream())) { |
5149 | 0 | error(errSyntaxError, getPos(), "Invalid inline image data"); |
5150 | 0 | dict.free(); |
5151 | 0 | return NULL; |
5152 | 0 | } |
5153 | 0 | str = new EmbedStream(str, &dict, *haveLength, (GFileOffset)length); |
5154 | 0 | str = str->addFilters(&dict); |
5155 | |
|
5156 | 0 | return str; |
5157 | 0 | } |
5158 | | |
5159 | 0 | void Gfx::opImageData(Object args[], int numArgs) { |
5160 | 0 | error(errInternal, getPos(), "Got 'ID' operator"); |
5161 | 0 | } |
5162 | | |
5163 | 0 | void Gfx::opEndImage(Object args[], int numArgs) { |
5164 | 0 | error(errInternal, getPos(), "Got 'EI' operator"); |
5165 | 0 | } |
5166 | | |
5167 | | //------------------------------------------------------------------------ |
5168 | | // type 3 font operators |
5169 | | //------------------------------------------------------------------------ |
5170 | | |
5171 | 0 | void Gfx::opSetCharWidth(Object args[], int numArgs) { |
5172 | 0 | out->type3D0(state, args[0].getNum(), args[1].getNum()); |
5173 | 0 | } |
5174 | | |
5175 | 0 | void Gfx::opSetCacheDevice(Object args[], int numArgs) { |
5176 | 0 | state->setIgnoreColorOps(gTrue); |
5177 | 0 | out->type3D1(state, args[0].getNum(), args[1].getNum(), |
5178 | 0 | args[2].getNum(), args[3].getNum(), |
5179 | 0 | args[4].getNum(), args[5].getNum()); |
5180 | 0 | } |
5181 | | |
5182 | | //------------------------------------------------------------------------ |
5183 | | // compatibility operators |
5184 | | //------------------------------------------------------------------------ |
5185 | | |
5186 | 0 | void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) { |
5187 | 0 | ++ignoreUndef; |
5188 | 0 | } |
5189 | | |
5190 | 0 | void Gfx::opEndIgnoreUndef(Object args[], int numArgs) { |
5191 | 0 | if (ignoreUndef > 0) |
5192 | 0 | --ignoreUndef; |
5193 | 0 | } |
5194 | | |
5195 | | //------------------------------------------------------------------------ |
5196 | | // marked content operators |
5197 | | //------------------------------------------------------------------------ |
5198 | | |
5199 | 0 | void Gfx::opBeginMarkedContent(Object args[], int numArgs) { |
5200 | 0 | GfxMarkedContent *mc; |
5201 | 0 | Object obj; |
5202 | 0 | GBool ocStateNew; |
5203 | 0 | TextString *s; |
5204 | 0 | GfxMarkedContentKind mcKind; |
5205 | |
|
5206 | 0 | if (printCommands) { |
5207 | 0 | printf(" marked content: %s ", args[0].getName()); |
5208 | 0 | if (numArgs == 2) { |
5209 | 0 | args[1].print(stdout); |
5210 | 0 | } |
5211 | 0 | printf("\n"); |
5212 | 0 | fflush(stdout); |
5213 | 0 | } |
5214 | 0 | mcKind = gfxMCOther; |
5215 | 0 | if (args[0].isName("OC") && numArgs == 2 && args[1].isName() && |
5216 | 0 | res->lookupPropertiesNF(args[1].getName(), &obj)) { |
5217 | 0 | if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) { |
5218 | 0 | ocState &= ocStateNew; |
5219 | 0 | } |
5220 | 0 | obj.free(); |
5221 | 0 | mcKind = gfxMCOptionalContent; |
5222 | 0 | } else if (numArgs == 2 && args[1].isDict()) { |
5223 | 0 | if (args[0].isName("Span")) { |
5224 | 0 | if (args[1].dictLookup("ActualText", &obj)->isString()) { |
5225 | 0 | s = new TextString(obj.getString()); |
5226 | 0 | out->beginActualText(state, s->getUnicode(), s->getLength()); |
5227 | 0 | delete s; |
5228 | 0 | mcKind = gfxMCActualText; |
5229 | 0 | } |
5230 | 0 | obj.free(); |
5231 | 0 | } |
5232 | 0 | if (args[1].dictLookup("MCID", &obj)->isInt()) { |
5233 | 0 | out->beginStructureItem(args[0].getName(), obj.getInt(), |
5234 | 0 | args[1].getDict()); |
5235 | 0 | if (mcKind == gfxMCActualText) { |
5236 | 0 | mcKind = gfxMCStructureItemAndActualText; |
5237 | 0 | } else { |
5238 | 0 | mcKind = gfxMCStructureItem; |
5239 | 0 | } |
5240 | 0 | } |
5241 | 0 | obj.free(); |
5242 | 0 | } |
5243 | |
|
5244 | 0 | mc = new GfxMarkedContent(mcKind, ocState); |
5245 | 0 | markedContentStack->append(mc); |
5246 | 0 | } |
5247 | | |
5248 | 0 | void Gfx::opEndMarkedContent(Object args[], int numArgs) { |
5249 | 0 | GfxMarkedContent *mc; |
5250 | 0 | GfxMarkedContentKind mcKind; |
5251 | |
|
5252 | 0 | if (markedContentStack->getLength() > 0) { |
5253 | 0 | mc = (GfxMarkedContent *) |
5254 | 0 | markedContentStack->del(markedContentStack->getLength() - 1); |
5255 | 0 | mcKind = mc->kind; |
5256 | 0 | delete mc; |
5257 | 0 | if (mcKind == gfxMCOptionalContent) { |
5258 | 0 | if (markedContentStack->getLength() > 0) { |
5259 | 0 | mc = (GfxMarkedContent *) |
5260 | 0 | markedContentStack->get(markedContentStack->getLength() - 1); |
5261 | 0 | ocState = mc->ocState; |
5262 | 0 | } else { |
5263 | 0 | ocState = gTrue; |
5264 | 0 | } |
5265 | 0 | } else if (mcKind == gfxMCActualText) { |
5266 | 0 | out->endActualText(state); |
5267 | 0 | } else if (mcKind == gfxMCStructureItem) { |
5268 | 0 | out->endStructureItem(); |
5269 | 0 | } else if (mcKind == gfxMCStructureItemAndActualText) { |
5270 | 0 | out->endStructureItem(); |
5271 | 0 | out->endActualText(state); |
5272 | 0 | } |
5273 | 0 | } else { |
5274 | 0 | error(errSyntaxWarning, getPos(), "Mismatched EMC operator"); |
5275 | 0 | } |
5276 | 0 | } |
5277 | | |
5278 | 0 | void Gfx::opMarkPoint(Object args[], int numArgs) { |
5279 | 0 | if (printCommands) { |
5280 | 0 | printf(" mark point: %s ", args[0].getName()); |
5281 | 0 | if (numArgs == 2) |
5282 | 0 | args[1].print(stdout); |
5283 | 0 | printf("\n"); |
5284 | 0 | fflush(stdout); |
5285 | 0 | } |
5286 | 0 | } |
5287 | | |
5288 | | //------------------------------------------------------------------------ |
5289 | | // misc |
5290 | | //------------------------------------------------------------------------ |
5291 | | |
5292 | | void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle, |
5293 | 0 | double xMin, double yMin, double xMax, double yMax) { |
5294 | 0 | Dict *dict, *resDict; |
5295 | 0 | Object str, matrixObj, bboxObj, resObj, obj1; |
5296 | 0 | double formXMin, formYMin, formXMax, formYMax; |
5297 | 0 | double x, y, sx, sy, tx, ty; |
5298 | 0 | double m[6], bbox[4]; |
5299 | 0 | double *borderColor; |
5300 | 0 | GfxColor color; |
5301 | 0 | double *dash, *dash2; |
5302 | 0 | int dashLength; |
5303 | 0 | int i; |
5304 | | |
5305 | | // this function assumes that we are in the default user space, |
5306 | | // i.e., baseMatrix = ctm |
5307 | | |
5308 | | // if the bounding box has zero width or height, don't draw anything |
5309 | | // at all |
5310 | 0 | if (xMin == xMax || yMin == yMax) { |
5311 | 0 | return; |
5312 | 0 | } |
5313 | | |
5314 | | // draw the appearance stream (if there is one) |
5315 | 0 | strRef->fetch(xref, &str); |
5316 | 0 | if (str.isStream()) { |
5317 | | |
5318 | | // get stream dict |
5319 | 0 | dict = str.streamGetDict(); |
5320 | | |
5321 | | // get the form bounding box |
5322 | 0 | dict->lookup("BBox", &bboxObj); |
5323 | 0 | if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) { |
5324 | 0 | error(errSyntaxError, getPos(), "Bad form bounding box"); |
5325 | 0 | bboxObj.free(); |
5326 | 0 | str.free(); |
5327 | 0 | return; |
5328 | 0 | } |
5329 | 0 | for (i = 0; i < 4; ++i) { |
5330 | 0 | bboxObj.arrayGet(i, &obj1); |
5331 | 0 | if (obj1.isNum()) { |
5332 | 0 | bbox[i] = obj1.getNum(); |
5333 | 0 | } else { |
5334 | 0 | bbox[i] = 0; |
5335 | 0 | } |
5336 | 0 | obj1.free(); |
5337 | 0 | } |
5338 | 0 | bboxObj.free(); |
5339 | | |
5340 | | // get the form matrix |
5341 | 0 | dict->lookup("Matrix", &matrixObj); |
5342 | 0 | if (matrixObj.isArray()) { |
5343 | 0 | for (i = 0; i < 6; ++i) { |
5344 | 0 | matrixObj.arrayGet(i, &obj1); |
5345 | 0 | m[i] = obj1.getNum(); |
5346 | 0 | obj1.free(); |
5347 | 0 | } |
5348 | 0 | } else { |
5349 | 0 | m[0] = 1; m[1] = 0; |
5350 | 0 | m[2] = 0; m[3] = 1; |
5351 | 0 | m[4] = 0; m[5] = 0; |
5352 | 0 | } |
5353 | 0 | matrixObj.free(); |
5354 | | |
5355 | | // transform the four corners of the form bbox to default user |
5356 | | // space, and construct the transformed bbox |
5357 | 0 | x = bbox[0] * m[0] + bbox[1] * m[2] + m[4]; |
5358 | 0 | y = bbox[0] * m[1] + bbox[1] * m[3] + m[5]; |
5359 | 0 | formXMin = formXMax = x; |
5360 | 0 | formYMin = formYMax = y; |
5361 | 0 | x = bbox[0] * m[0] + bbox[3] * m[2] + m[4]; |
5362 | 0 | y = bbox[0] * m[1] + bbox[3] * m[3] + m[5]; |
5363 | 0 | if (x < formXMin) { |
5364 | 0 | formXMin = x; |
5365 | 0 | } else if (x > formXMax) { |
5366 | 0 | formXMax = x; |
5367 | 0 | } |
5368 | 0 | if (y < formYMin) { |
5369 | 0 | formYMin = y; |
5370 | 0 | } else if (y > formYMax) { |
5371 | 0 | formYMax = y; |
5372 | 0 | } |
5373 | 0 | x = bbox[2] * m[0] + bbox[1] * m[2] + m[4]; |
5374 | 0 | y = bbox[2] * m[1] + bbox[1] * m[3] + m[5]; |
5375 | 0 | if (x < formXMin) { |
5376 | 0 | formXMin = x; |
5377 | 0 | } else if (x > formXMax) { |
5378 | 0 | formXMax = x; |
5379 | 0 | } |
5380 | 0 | if (y < formYMin) { |
5381 | 0 | formYMin = y; |
5382 | 0 | } else if (y > formYMax) { |
5383 | 0 | formYMax = y; |
5384 | 0 | } |
5385 | 0 | x = bbox[2] * m[0] + bbox[3] * m[2] + m[4]; |
5386 | 0 | y = bbox[2] * m[1] + bbox[3] * m[3] + m[5]; |
5387 | 0 | if (x < formXMin) { |
5388 | 0 | formXMin = x; |
5389 | 0 | } else if (x > formXMax) { |
5390 | 0 | formXMax = x; |
5391 | 0 | } |
5392 | 0 | if (y < formYMin) { |
5393 | 0 | formYMin = y; |
5394 | 0 | } else if (y > formYMax) { |
5395 | 0 | formYMax = y; |
5396 | 0 | } |
5397 | | |
5398 | | // construct a mapping matrix, [sx 0 0], which maps the transformed |
5399 | | // [0 sy 0] |
5400 | | // [tx ty 1] |
5401 | | // bbox to the annotation rectangle |
5402 | 0 | if (formXMin == formXMax) { |
5403 | | // this shouldn't happen |
5404 | 0 | sx = 1; |
5405 | 0 | } else { |
5406 | 0 | sx = (xMax - xMin) / (formXMax - formXMin); |
5407 | 0 | } |
5408 | 0 | if (formYMin == formYMax) { |
5409 | | // this shouldn't happen |
5410 | 0 | sy = 1; |
5411 | 0 | } else { |
5412 | 0 | sy = (yMax - yMin) / (formYMax - formYMin); |
5413 | 0 | } |
5414 | 0 | tx = -formXMin * sx + xMin; |
5415 | 0 | ty = -formYMin * sy + yMin; |
5416 | | |
5417 | | // the final transform matrix is (form matrix) * (mapping matrix) |
5418 | 0 | m[0] *= sx; |
5419 | 0 | m[1] *= sy; |
5420 | 0 | m[2] *= sx; |
5421 | 0 | m[3] *= sy; |
5422 | 0 | m[4] = m[4] * sx + tx; |
5423 | 0 | m[5] = m[5] * sy + ty; |
5424 | | |
5425 | | // get the resources |
5426 | 0 | dict->lookup("Resources", &resObj); |
5427 | 0 | resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; |
5428 | | |
5429 | | // draw it |
5430 | 0 | drawForm(strRef, resDict, m, bbox); |
5431 | |
|
5432 | 0 | resObj.free(); |
5433 | 0 | } |
5434 | 0 | str.free(); |
5435 | | |
5436 | | // draw the border |
5437 | 0 | if (borderStyle && borderStyle->getWidth() > 0 && |
5438 | 0 | borderStyle->getNumColorComps() > 0) { |
5439 | 0 | borderColor = borderStyle->getColor(); |
5440 | 0 | switch (borderStyle->getNumColorComps()) { |
5441 | 0 | case 1: |
5442 | 0 | if (state->getStrokeColorSpace()->getMode() != csDeviceGray) { |
5443 | 0 | state->setStrokePattern(NULL); |
5444 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray)); |
5445 | 0 | out->updateStrokeColorSpace(state); |
5446 | 0 | } |
5447 | 0 | break; |
5448 | 0 | case 3: |
5449 | 0 | if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) { |
5450 | 0 | state->setStrokePattern(NULL); |
5451 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB)); |
5452 | 0 | out->updateStrokeColorSpace(state); |
5453 | 0 | } |
5454 | 0 | break; |
5455 | 0 | case 4: |
5456 | 0 | if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) { |
5457 | 0 | state->setStrokePattern(NULL); |
5458 | 0 | state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK)); |
5459 | 0 | out->updateStrokeColorSpace(state); |
5460 | 0 | } |
5461 | 0 | break; |
5462 | 0 | } |
5463 | 0 | color.c[0] = dblToCol(borderColor[0]); |
5464 | 0 | color.c[1] = dblToCol(borderColor[1]); |
5465 | 0 | color.c[2] = dblToCol(borderColor[2]); |
5466 | 0 | color.c[3] = dblToCol(borderColor[3]); |
5467 | 0 | state->setStrokeColor(&color); |
5468 | 0 | out->updateStrokeColor(state); |
5469 | 0 | state->setLineWidth(borderStyle->getWidth()); |
5470 | 0 | out->updateLineWidth(state); |
5471 | 0 | borderStyle->getDash(&dash, &dashLength); |
5472 | 0 | if (borderStyle->getType() == annotBorderDashed && dashLength > 0) { |
5473 | 0 | dash2 = (double *)gmallocn(dashLength, sizeof(double)); |
5474 | 0 | memcpy(dash2, dash, dashLength * sizeof(double)); |
5475 | 0 | state->setLineDash(dash2, dashLength, 0); |
5476 | 0 | out->updateLineDash(state); |
5477 | 0 | } |
5478 | | //~ this doesn't currently handle the beveled and engraved styles |
5479 | 0 | state->clearPath(); |
5480 | 0 | state->moveTo(xMin, yMin); |
5481 | 0 | state->lineTo(xMax, yMin); |
5482 | 0 | if (borderStyle->getType() != annotBorderUnderlined) { |
5483 | 0 | state->lineTo(xMax, yMax); |
5484 | 0 | state->lineTo(xMin, yMax); |
5485 | 0 | state->closePath(); |
5486 | 0 | } |
5487 | 0 | out->stroke(state); |
5488 | 0 | } |
5489 | 0 | } |
5490 | | |
5491 | 0 | void Gfx::saveState() { |
5492 | 0 | out->saveState(state); |
5493 | 0 | state = state->save(); |
5494 | 0 | } |
5495 | | |
5496 | 0 | void Gfx::restoreState() { |
5497 | 0 | state = state->restore(); |
5498 | 0 | out->restoreState(state); |
5499 | 0 | } |
5500 | | |
5501 | | // Create a new state stack, and initialize it with a copy of the |
5502 | | // current state. |
5503 | 0 | GfxState *Gfx::saveStateStack() { |
5504 | 0 | GfxState *oldState; |
5505 | |
|
5506 | 0 | out->saveState(state); |
5507 | 0 | oldState = state; |
5508 | 0 | state = state->copy(gTrue); |
5509 | 0 | return oldState; |
5510 | 0 | } |
5511 | | |
5512 | | // Switch back to the previous state stack. |
5513 | 0 | void Gfx::restoreStateStack(GfxState *oldState) { |
5514 | 0 | while (state->hasSaves()) { |
5515 | 0 | restoreState(); |
5516 | 0 | } |
5517 | 0 | delete state; |
5518 | 0 | state = oldState; |
5519 | 0 | out->restoreState(state); |
5520 | 0 | } |
5521 | | |
5522 | 0 | void Gfx::pushResources(Dict *resDict) { |
5523 | 0 | res = new GfxResources(xref, resDict, res); |
5524 | 0 | } |
5525 | | |
5526 | 0 | void Gfx::popResources() { |
5527 | 0 | GfxResources *resPtr; |
5528 | |
|
5529 | 0 | resPtr = res->getNext(); |
5530 | 0 | delete res; |
5531 | 0 | res = resPtr; |
5532 | 0 | } |