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