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