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