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