1 //========================================================================
2 //
3 // Gfx.cc
4 //
5 // Copyright 1996-2013 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, 2016 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-2016 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 = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2130   if (fabs(det) < 0.000001) {
2131     error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2132     return;
2133   }
2134   det = 1 / det;
2135   ictm[0] = ctm[3] * det;
2136   ictm[1] = -ctm[1] * det;
2137   ictm[2] = -ctm[2] * det;
2138   ictm[3] = ctm[0] * det;
2139   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2140   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2141   // m1 = PTM * BTM = PTM * base transform matrix
2142   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2143   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2144   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2145   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2146   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2147   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2148   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2149   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2150   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2151   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2152   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2153   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2154   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2155 
2156   // construct a (device space) -> (pattern space) transform matrix
2157   det = m1[0] * m1[3] - m1[1] * m1[2];
2158   if (fabs(det) < 0.000001) {
2159     error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2160     return;
2161   }
2162   det = 1 / det;
2163   imb[0] = m1[3] * det;
2164   imb[1] = -m1[1] * det;
2165   imb[2] = -m1[2] * det;
2166   imb[3] = m1[0] * det;
2167   imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
2168   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
2169 
2170   // save current graphics state
2171   savedState = saveStateStack();
2172 
2173   // set underlying color space (for uncolored tiling patterns); set
2174   // various other parameters (stroke color, line width) to match
2175   // Adobe's behavior
2176   state->setFillPattern(NULL);
2177   state->setStrokePattern(NULL);
2178   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
2179     state->setFillColorSpace(cs->copy());
2180     out->updateFillColorSpace(state);
2181     state->setStrokeColorSpace(cs->copy());
2182     out->updateStrokeColorSpace(state);
2183     if (stroke) {
2184 	state->setFillColor(state->getStrokeColor());
2185     } else {
2186 	state->setStrokeColor(state->getFillColor());
2187     }
2188     out->updateFillColor(state);
2189     out->updateStrokeColor(state);
2190   } else {
2191     cs = new GfxDeviceGrayColorSpace();
2192     state->setFillColorSpace(cs);
2193     cs->getDefaultColor(&color);
2194     state->setFillColor(&color);
2195     out->updateFillColorSpace(state);
2196     state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
2197     state->setStrokeColor(&color);
2198     out->updateStrokeColorSpace(state);
2199   }
2200   if (!stroke) {
2201     state->setLineWidth(0);
2202     out->updateLineWidth(state);
2203   }
2204 
2205   // clip to current path
2206   if (stroke) {
2207     state->clipToStrokePath();
2208     out->clipToStrokePath(state);
2209   } else if (!text) {
2210     state->clip();
2211     if (eoFill) {
2212       out->eoClip(state);
2213     } else {
2214       out->clip(state);
2215     }
2216   }
2217   state->clearPath();
2218 
2219   // get the clip region, check for empty
2220   state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
2221   if (cxMin > cxMax || cyMin > cyMax) {
2222     goto restore;
2223   }
2224 
2225   // transform clip region bbox to pattern space
2226   xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
2227   yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
2228   x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
2229   y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
2230   if (x1 < xMin) {
2231     xMin = x1;
2232   } else if (x1 > xMax) {
2233     xMax = x1;
2234   }
2235   if (y1 < yMin) {
2236     yMin = y1;
2237   } else if (y1 > yMax) {
2238     yMax = y1;
2239   }
2240   x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
2241   y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
2242   if (x1 < xMin) {
2243     xMin = x1;
2244   } else if (x1 > xMax) {
2245     xMax = x1;
2246   }
2247   if (y1 < yMin) {
2248     yMin = y1;
2249   } else if (y1 > yMax) {
2250     yMax = y1;
2251   }
2252   x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
2253   y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
2254   if (x1 < xMin) {
2255     xMin = x1;
2256   } else if (x1 > xMax) {
2257     xMax = x1;
2258   }
2259   if (y1 < yMin) {
2260     yMin = y1;
2261   } else if (y1 > yMax) {
2262     yMax = y1;
2263   }
2264 
2265   // draw the pattern
2266   //~ this should treat negative steps differently -- start at right/top
2267   //~ edge instead of left/bottom (?)
2268   xstep = fabs(tPat->getXStep());
2269   ystep = fabs(tPat->getYStep());
2270   if (tPat->getBBox()[0] < tPat->getBBox()[2]) {
2271     xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);
2272     xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;
2273   } else {
2274     xi0 = (int)ceil((xMin - tPat->getBBox()[0]) / xstep);
2275     xi1 = (int)floor((xMax - tPat->getBBox()[2]) / xstep) + 1;
2276   }
2277   if (tPat->getBBox()[1] < tPat->getBBox()[3]) {
2278     yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);
2279     yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1;
2280   } else {
2281     yi0 = (int)ceil((yMin - tPat->getBBox()[1]) / ystep);
2282     yi1 = (int)floor((yMax - tPat->getBBox()[3]) / ystep) + 1;
2283   }
2284   for (i = 0; i < 4; ++i) {
2285     m1[i] = m[i];
2286   }
2287   m1[4] = m[4];
2288   m1[5] = m[5];
2289   if (out->useTilingPatternFill() &&
2290 	out->tilingPatternFill(state, this, catalog, tPat->getContentStream(),
2291 		       tPat->getMatrix(), tPat->getPaintType(), tPat->getTilingType(),
2292 		       tPat->getResDict(), m1, tPat->getBBox(),
2293 		       xi0, yi0, xi1, yi1, xstep, ystep)) {
2294     goto restore;
2295   } else {
2296     out->updatePatternOpacity(state);
2297     for (yi = yi0; yi < yi1; ++yi) {
2298       for (xi = xi0; xi < xi1; ++xi) {
2299         x = xi * xstep;
2300         y = yi * ystep;
2301         m1[4] = x * m[0] + y * m[2] + m[4];
2302         m1[5] = x * m[1] + y * m[3] + m[5];
2303         drawForm(tPat->getContentStream(), tPat->getResDict(),
2304         	  m1, tPat->getBBox());
2305       }
2306     }
2307     out->clearPatternOpacity(state);
2308   }
2309 
2310   // restore graphics state
2311  restore:
2312   restoreStateStack(savedState);
2313 }
2314 
doShadingPatternFill(GfxShadingPattern * sPat,GBool stroke,GBool eoFill,GBool text)2315 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
2316 			       GBool stroke, GBool eoFill, GBool text) {
2317   GfxShading *shading;
2318   GfxState *savedState;
2319   double *ctm, *btm, *ptm;
2320   double m[6], ictm[6], m1[6];
2321   double xMin, yMin, xMax, yMax;
2322   double det;
2323 
2324   shading = sPat->getShading();
2325 
2326   // save current graphics state
2327   savedState = saveStateStack();
2328 
2329   // clip to current path
2330   if (stroke) {
2331     state->clipToStrokePath();
2332     out->clipToStrokePath(state);
2333   } else if (!text) {
2334     state->clip();
2335     if (eoFill) {
2336       out->eoClip(state);
2337     } else {
2338       out->clip(state);
2339     }
2340   }
2341   state->clearPath();
2342 
2343   // construct a (pattern space) -> (current space) transform matrix
2344   ctm = state->getCTM();
2345   btm = baseMatrix;
2346   ptm = sPat->getMatrix();
2347   // iCTM = invert CTM
2348   det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2349   if (fabs(det) < 0.000001) {
2350     error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
2351     return;
2352   }
2353   det = 1 / det;
2354   ictm[0] = ctm[3] * det;
2355   ictm[1] = -ctm[1] * det;
2356   ictm[2] = -ctm[2] * det;
2357   ictm[3] = ctm[0] * det;
2358   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2359   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2360   // m1 = PTM * BTM = PTM * base transform matrix
2361   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2362   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2363   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2364   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2365   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2366   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2367   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2368   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2369   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2370   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2371   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2372   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2373   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2374 
2375   // set the new matrix
2376   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
2377   out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
2378 
2379   // clip to bbox
2380   if (shading->getHasBBox()) {
2381     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2382     state->moveTo(xMin, yMin);
2383     state->lineTo(xMax, yMin);
2384     state->lineTo(xMax, yMax);
2385     state->lineTo(xMin, yMax);
2386     state->closePath();
2387     state->clip();
2388     out->clip(state);
2389     state->clearPath();
2390   }
2391 
2392   // set the color space
2393   state->setFillColorSpace(shading->getColorSpace()->copy());
2394   out->updateFillColorSpace(state);
2395 
2396   // background color fill
2397   if (shading->getHasBackground()) {
2398     state->setFillColor(shading->getBackground());
2399     out->updateFillColor(state);
2400     state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2401     state->moveTo(xMin, yMin);
2402     state->lineTo(xMax, yMin);
2403     state->lineTo(xMax, yMax);
2404     state->lineTo(xMin, yMax);
2405     state->closePath();
2406     out->fill(state);
2407     state->clearPath();
2408   }
2409 
2410 #if 1 //~tmp: turn off anti-aliasing temporarily
2411   GBool vaa = out->getVectorAntialias();
2412   if (vaa) {
2413     out->setVectorAntialias(gFalse);
2414   }
2415 #endif
2416 
2417   // do shading type-specific operations
2418   switch (shading->getType()) {
2419   case 1:
2420     doFunctionShFill((GfxFunctionShading *)shading);
2421     break;
2422   case 2:
2423     doAxialShFill((GfxAxialShading *)shading);
2424     break;
2425   case 3:
2426     doRadialShFill((GfxRadialShading *)shading);
2427     break;
2428   case 4:
2429   case 5:
2430     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
2431     break;
2432   case 6:
2433   case 7:
2434     doPatchMeshShFill((GfxPatchMeshShading *)shading);
2435     break;
2436   }
2437 
2438 #if 1 //~tmp: turn off anti-aliasing temporarily
2439   if (vaa) {
2440     out->setVectorAntialias(gTrue);
2441   }
2442 #endif
2443 
2444   // restore graphics state
2445   restoreStateStack(savedState);
2446 }
2447 
opShFill(Object args[],int numArgs)2448 void Gfx::opShFill(Object args[], int numArgs) {
2449   GfxShading *shading;
2450   GfxState *savedState;
2451   double xMin, yMin, xMax, yMax;
2452 
2453   if (!ocState) {
2454     return;
2455   }
2456 
2457   if (!(shading = res->lookupShading(args[0].getName(), out, state))) {
2458     return;
2459   }
2460 
2461   // save current graphics state
2462   savedState = saveStateStack();
2463 
2464   // clip to bbox
2465   if (shading->getHasBBox()) {
2466     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2467     state->moveTo(xMin, yMin);
2468     state->lineTo(xMax, yMin);
2469     state->lineTo(xMax, yMax);
2470     state->lineTo(xMin, yMax);
2471     state->closePath();
2472     state->clip();
2473     out->clip(state);
2474     state->clearPath();
2475   }
2476 
2477   // set the color space
2478   state->setFillColorSpace(shading->getColorSpace()->copy());
2479   out->updateFillColorSpace(state);
2480 
2481 #if 1 //~tmp: turn off anti-aliasing temporarily
2482   GBool vaa = out->getVectorAntialias();
2483   if (vaa) {
2484     out->setVectorAntialias(gFalse);
2485   }
2486 #endif
2487 
2488   // do shading type-specific operations
2489   switch (shading->getType()) {
2490   case 1:
2491     doFunctionShFill((GfxFunctionShading *)shading);
2492     break;
2493   case 2:
2494     doAxialShFill((GfxAxialShading *)shading);
2495     break;
2496   case 3:
2497     doRadialShFill((GfxRadialShading *)shading);
2498     break;
2499   case 4:
2500   case 5:
2501     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
2502     break;
2503   case 6:
2504   case 7:
2505     doPatchMeshShFill((GfxPatchMeshShading *)shading);
2506     break;
2507   }
2508 
2509 #if 1 //~tmp: turn off anti-aliasing temporarily
2510   if (vaa) {
2511     out->setVectorAntialias(gTrue);
2512   }
2513 #endif
2514 
2515   // restore graphics state
2516   restoreStateStack(savedState);
2517 
2518   delete shading;
2519 }
2520 
doFunctionShFill(GfxFunctionShading * shading)2521 void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
2522   double x0, y0, x1, y1;
2523   GfxColor colors[4];
2524 
2525   if (out->useShadedFills( shading->getType() ) &&
2526       out->functionShadedFill(state, shading)) {
2527     return;
2528   }
2529 
2530   shading->getDomain(&x0, &y0, &x1, &y1);
2531   shading->getColor(x0, y0, &colors[0]);
2532   shading->getColor(x0, y1, &colors[1]);
2533   shading->getColor(x1, y0, &colors[2]);
2534   shading->getColor(x1, y1, &colors[3]);
2535   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
2536 }
2537 
doFunctionShFill1(GfxFunctionShading * shading,double x0,double y0,double x1,double y1,GfxColor * colors,int depth)2538 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
2539 			    double x0, double y0,
2540 			    double x1, double y1,
2541 			    GfxColor *colors, int depth) {
2542   GfxColor fillColor;
2543   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
2544   GfxColor colors2[4];
2545   double *matrix;
2546   double xM, yM;
2547   int nComps, i, j;
2548 
2549   nComps = shading->getColorSpace()->getNComps();
2550   matrix = shading->getMatrix();
2551 
2552   // compare the four corner colors
2553   for (i = 0; i < 4; ++i) {
2554     for (j = 0; j < nComps; ++j) {
2555       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
2556 	break;
2557       }
2558     }
2559     if (j < nComps) {
2560       break;
2561     }
2562   }
2563 
2564   // center of the rectangle
2565   xM = 0.5 * (x0 + x1);
2566   yM = 0.5 * (y0 + y1);
2567 
2568   // the four corner colors are close (or we hit the recursive limit)
2569   // -- fill the rectangle; but require at least one subdivision
2570   // (depth==0) to avoid problems when the four outer corners of the
2571   // shaded region are the same color
2572   if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
2573 
2574     // use the center color
2575     shading->getColor(xM, yM, &fillColor);
2576     state->setFillColor(&fillColor);
2577     out->updateFillColor(state);
2578 
2579     // fill the rectangle
2580     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
2581 		  x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
2582     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
2583 		  x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
2584     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
2585 		  x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
2586     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
2587 		  x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
2588     state->closePath();
2589     out->fill(state);
2590     state->clearPath();
2591 
2592   // the four corner colors are not close enough -- subdivide the
2593   // rectangle
2594   } else {
2595 
2596     // colors[0]       colorM0       colors[2]
2597     //   (x0,y0)       (xM,y0)       (x1,y0)
2598     //         +----------+----------+
2599     //         |          |          |
2600     //         |    UL    |    UR    |
2601     // color0M |       colorMM       | color1M
2602     // (x0,yM) +----------+----------+ (x1,yM)
2603     //         |       (xM,yM)       |
2604     //         |    LL    |    LR    |
2605     //         |          |          |
2606     //         +----------+----------+
2607     // colors[1]       colorM1       colors[3]
2608     //   (x0,y1)       (xM,y1)       (x1,y1)
2609 
2610     shading->getColor(x0, yM, &color0M);
2611     shading->getColor(x1, yM, &color1M);
2612     shading->getColor(xM, y0, &colorM0);
2613     shading->getColor(xM, y1, &colorM1);
2614     shading->getColor(xM, yM, &colorMM);
2615 
2616     // upper-left sub-rectangle
2617     colors2[0] = colors[0];
2618     colors2[1] = color0M;
2619     colors2[2] = colorM0;
2620     colors2[3] = colorMM;
2621     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
2622 
2623     // lower-left sub-rectangle
2624     colors2[0] = color0M;
2625     colors2[1] = colors[1];
2626     colors2[2] = colorMM;
2627     colors2[3] = colorM1;
2628     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
2629 
2630     // upper-right sub-rectangle
2631     colors2[0] = colorM0;
2632     colors2[1] = colorMM;
2633     colors2[2] = colors[2];
2634     colors2[3] = color1M;
2635     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
2636 
2637     // lower-right sub-rectangle
2638     colors2[0] = colorMM;
2639     colors2[1] = colorM1;
2640     colors2[2] = color1M;
2641     colors2[3] = colors[3];
2642     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
2643   }
2644 }
2645 
bubbleSort(double array[])2646 static void bubbleSort(double array[])
2647 {
2648   for (int j = 0; j < 3; ++j) {
2649     int kk = j;
2650     for (int k = j + 1; k < 4; ++k) {
2651       if (array[k] < array[kk]) {
2652         kk = k;
2653       }
2654     }
2655     double tmp = array[j];
2656     array[j] = array[kk];
2657     array[kk] = tmp;
2658   }
2659 }
2660 
doAxialShFill(GfxAxialShading * shading)2661 void Gfx::doAxialShFill(GfxAxialShading *shading) {
2662   double xMin, yMin, xMax, yMax;
2663   double x0, y0, x1, y1;
2664   double dx, dy, mul;
2665   GBool dxZero, dyZero;
2666   double bboxIntersections[4];
2667   double tMin, tMax, tx, ty;
2668   double s[4], sMin, sMax, tmp;
2669   double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
2670   double t0, t1, tt;
2671   double ta[axialMaxSplits + 1];
2672   int next[axialMaxSplits + 1];
2673   GfxColor color0, color1;
2674   int nComps;
2675   int i, j, k;
2676   GBool needExtend = gTrue;
2677 
2678   // get the clip region bbox
2679   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2680 
2681   // compute min and max t values, based on the four corners of the
2682   // clip region bbox
2683   shading->getCoords(&x0, &y0, &x1, &y1);
2684   dx = x1 - x0;
2685   dy = y1 - y0;
2686   dxZero = fabs(dx) < 0.01;
2687   dyZero = fabs(dy) < 0.01;
2688   if (dxZero && dyZero) {
2689     tMin = tMax = 0;
2690   } else {
2691     mul = 1 / (dx * dx + dy * dy);
2692     bboxIntersections[0] = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
2693     bboxIntersections[1] = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
2694     bboxIntersections[2] = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
2695     bboxIntersections[3] = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
2696     bubbleSort(bboxIntersections);
2697     tMin = bboxIntersections[0];
2698     tMax = bboxIntersections[3];
2699     if (tMin < 0 && !shading->getExtend0()) {
2700       tMin = 0;
2701     }
2702     if (tMax > 1 && !shading->getExtend1()) {
2703       tMax = 1;
2704     }
2705   }
2706 
2707   if (out->useShadedFills( shading->getType() ) &&
2708       out->axialShadedFill(state, shading, tMin, tMax)) {
2709 	  return;
2710   }
2711 
2712   // get the function domain
2713   t0 = shading->getDomain0();
2714   t1 = shading->getDomain1();
2715 
2716   // Traverse the t axis and do the shading.
2717   //
2718   // For each point (tx, ty) on the t axis, consider a line through
2719   // that point perpendicular to the t axis:
2720   //
2721   //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
2722   //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
2723   //
2724   // Then look at the intersection of this line with the bounding box
2725   // (xMin, yMin, xMax, yMax).  In the general case, there are four
2726   // intersection points:
2727   //
2728   //     s0 = (xMin - tx) / -dy
2729   //     s1 = (xMax - tx) / -dy
2730   //     s2 = (yMin - ty) / dx
2731   //     s3 = (yMax - ty) / dx
2732   //
2733   // and we want the middle two s values.
2734   //
2735   // In the case where dx = 0, take s0 and s1; in the case where dy =
2736   // 0, take s2 and s3.
2737   //
2738   // Each filled polygon is bounded by two of these line segments
2739   // perpdendicular to the t axis.
2740   //
2741   // The t axis is bisected into smaller regions until the color
2742   // difference across a region is small enough, and then the region
2743   // is painted with a single color.
2744 
2745   // set up: require at least one split to avoid problems when the two
2746   // ends of the t axis have the same color
2747   nComps = shading->getColorSpace()->getNComps();
2748   ta[0] = tMin;
2749   next[0] = axialMaxSplits / 2;
2750   ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
2751   next[axialMaxSplits / 2] = axialMaxSplits;
2752   ta[axialMaxSplits] = tMax;
2753 
2754   // compute the color at t = tMin
2755   if (tMin < 0) {
2756     tt = t0;
2757   } else if (tMin > 1) {
2758     tt = t1;
2759   } else {
2760     tt = t0 + (t1 - t0) * tMin;
2761   }
2762   shading->getColor(tt, &color0);
2763 
2764   if (out->useFillColorStop()) {
2765     // make sure we add stop color when t = tMin
2766     state->setFillColor(&color0);
2767     out->updateFillColorStop(state, 0);
2768   }
2769 
2770   // compute the coordinates of the point on the t axis at t = tMin;
2771   // then compute the intersection of the perpendicular line with the
2772   // bounding box
2773   tx = x0 + tMin * dx;
2774   ty = y0 + tMin * dy;
2775   if (dxZero && dyZero) {
2776     sMin = sMax = 0;
2777   } else if (dxZero) {
2778     sMin = (xMin - tx) / -dy;
2779     sMax = (xMax - tx) / -dy;
2780     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2781   } else if (dyZero) {
2782     sMin = (yMin - ty) / dx;
2783     sMax = (yMax - ty) / dx;
2784     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2785   } else {
2786     s[0] = (yMin - ty) / dx;
2787     s[1] = (yMax - ty) / dx;
2788     s[2] = (xMin - tx) / -dy;
2789     s[3] = (xMax - tx) / -dy;
2790     bubbleSort(s);
2791     sMin = s[1];
2792     sMax = s[2];
2793   }
2794   ux0 = tx - sMin * dy;
2795   uy0 = ty + sMin * dx;
2796   vx0 = tx - sMax * dy;
2797   vy0 = ty + sMax * dx;
2798 
2799   i = 0;
2800   bool doneBBox1, doneBBox2;
2801   if (dxZero && dyZero) {
2802     doneBBox1 = doneBBox2 = true;
2803   } else {
2804     doneBBox1 = bboxIntersections[1] < tMin;
2805     doneBBox2 = bboxIntersections[2] > tMax;
2806   }
2807 
2808   // If output device doesn't support the extended mode required
2809   // we have to do it here
2810   needExtend = !out->axialShadedSupportExtend(state, shading);
2811 
2812   while (i < axialMaxSplits) {
2813 
2814     // bisect until color difference is small enough or we hit the
2815     // bisection limit
2816     j = next[i];
2817     while (j > i + 1) {
2818       if (ta[j] < 0) {
2819 	tt = t0;
2820       } else if (ta[j] > 1) {
2821 	tt = t1;
2822       } else {
2823 	tt = t0 + (t1 - t0) * ta[j];
2824       }
2825       shading->getColor(tt, &color1);
2826       if (isSameGfxColor(color1, color0, nComps, axialColorDelta)) {
2827          // in these two if what we guarantee is that if we are skipping lots of
2828          // positions because the colors are the same, we still create a region
2829          // with vertexs passing by bboxIntersections[1] and bboxIntersections[2]
2830          // otherwise we can have empty regions that should really be painted
2831          // like happened in bug 19896
2832          // What we do to ensure that we pass a line through this points
2833          // is making sure use the exact bboxIntersections[] value as one of the used ta[] values
2834          if (!doneBBox1 && ta[i] < bboxIntersections[1] && ta[j] > bboxIntersections[1]) {
2835            int teoricalj = (int) ((bboxIntersections[1] - tMin) * axialMaxSplits / (tMax - tMin));
2836            if (teoricalj <= i) teoricalj = i + 1;
2837            if (teoricalj < j) {
2838              next[i] = teoricalj;
2839              next[teoricalj] = j;
2840            }
2841            else {
2842              teoricalj = j;
2843            }
2844            ta[teoricalj] = bboxIntersections[1];
2845            j = teoricalj;
2846            doneBBox1 = true;
2847          }
2848          if (!doneBBox2 && ta[i] < bboxIntersections[2] && ta[j] > bboxIntersections[2]) {
2849            int teoricalj = (int) ((bboxIntersections[2] - tMin) * axialMaxSplits / (tMax - tMin));
2850            if (teoricalj <= i) teoricalj = i + 1;
2851            if (teoricalj < j) {
2852              next[i] = teoricalj;
2853              next[teoricalj] = j;
2854            }
2855            else {
2856              teoricalj = j;
2857            }
2858            ta[teoricalj] = bboxIntersections[2];
2859            j = teoricalj;
2860            doneBBox2 = true;
2861          }
2862          break;
2863       }
2864       k = (i + j) / 2;
2865       ta[k] = 0.5 * (ta[i] + ta[j]);
2866       next[i] = k;
2867       next[k] = j;
2868       j = k;
2869     }
2870 
2871     // use the average of the colors of the two sides of the region
2872     for (k = 0; k < nComps; ++k) {
2873       color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
2874     }
2875 
2876     // compute the coordinates of the point on the t axis; then
2877     // compute the intersection of the perpendicular line with the
2878     // bounding box
2879     tx = x0 + ta[j] * dx;
2880     ty = y0 + ta[j] * dy;
2881     if (dxZero && dyZero) {
2882       sMin = sMax = 0;
2883     } else if (dxZero) {
2884       sMin = (xMin - tx) / -dy;
2885       sMax = (xMax - tx) / -dy;
2886       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2887     } else if (dyZero) {
2888       sMin = (yMin - ty) / dx;
2889       sMax = (yMax - ty) / dx;
2890       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2891     } else {
2892       s[0] = (yMin - ty) / dx;
2893       s[1] = (yMax - ty) / dx;
2894       s[2] = (xMin - tx) / -dy;
2895       s[3] = (xMax - tx) / -dy;
2896       bubbleSort(s);
2897       sMin = s[1];
2898       sMax = s[2];
2899     }
2900     ux1 = tx - sMin * dy;
2901     uy1 = ty + sMin * dx;
2902     vx1 = tx - sMax * dy;
2903     vy1 = ty + sMax * dx;
2904 
2905     // set the color
2906     state->setFillColor(&color0);
2907     if (out->useFillColorStop())
2908       out->updateFillColorStop(state, (ta[j] - tMin)/(tMax - tMin));
2909     else
2910       out->updateFillColor(state);
2911 
2912     if (needExtend) {
2913       // fill the region
2914       state->moveTo(ux0, uy0);
2915       state->lineTo(vx0, vy0);
2916       state->lineTo(vx1, vy1);
2917       state->lineTo(ux1, uy1);
2918       state->closePath();
2919     }
2920 
2921     if (!out->useFillColorStop()) {
2922       out->fill(state);
2923       state->clearPath();
2924     }
2925 
2926     // set up for next region
2927     ux0 = ux1;
2928     uy0 = uy1;
2929     vx0 = vx1;
2930     vy0 = vy1;
2931     color0 = color1;
2932     i = next[i];
2933   }
2934 
2935   if (out->useFillColorStop()) {
2936     if (!needExtend) {
2937       state->moveTo(xMin, yMin);
2938       state->lineTo(xMin, yMax);
2939       state->lineTo(xMax, yMax);
2940       state->lineTo(xMax, yMin);
2941       state->closePath();
2942     }
2943     out->fill(state);
2944     state->clearPath();
2945   }
2946 }
2947 
getShadingColorRadialHelper(double t0,double t1,double t,GfxRadialShading * shading,GfxColor * color)2948 static inline void getShadingColorRadialHelper(double t0, double t1, double t, GfxRadialShading *shading, GfxColor *color)
2949 {
2950   if (t0 < t1) {
2951     if (t < t0) {
2952       shading->getColor(t0, color);
2953     } else if (t > t1) {
2954       shading->getColor(t1, color);
2955     } else {
2956       shading->getColor(t, color);
2957     }
2958   } else {
2959     if (t > t0) {
2960       shading->getColor(t0, color);
2961     } else if (t < t1) {
2962       shading->getColor(t1, color);
2963     } else {
2964       shading->getColor(t, color);
2965     }
2966   }
2967 }
2968 
doRadialShFill(GfxRadialShading * shading)2969 void Gfx::doRadialShFill(GfxRadialShading *shading) {
2970   double xMin, yMin, xMax, yMax;
2971   double x0, y0, r0, x1, y1, r1, t0, t1;
2972   int nComps;
2973   GfxColor colorA, colorB;
2974   double xa, ya, xb, yb, ra, rb;
2975   double ta, tb, sa, sb;
2976   double sz, xz, yz, sMin, sMax;
2977   GBool enclosed;
2978   int ia, ib, k, n;
2979   double *ctm;
2980   double theta, alpha, angle, t;
2981   GBool needExtend = gTrue;
2982 
2983   // get the shading info
2984   shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2985   t0 = shading->getDomain0();
2986   t1 = shading->getDomain1();
2987   nComps = shading->getColorSpace()->getNComps();
2988 
2989   // Compute the point at which r(s) = 0; check for the enclosed
2990   // circles case; and compute the angles for the tangent lines.
2991   if (x0 == x1 && y0 == y1) {
2992     enclosed = gTrue;
2993     theta = 0; // make gcc happy
2994     sz = 0; // make gcc happy
2995   } else if (r0 == r1) {
2996     enclosed = gFalse;
2997     theta = 0;
2998     sz = 0; // make gcc happy
2999   } else {
3000     sz = (r1 > r0) ? -r0 / (r1 - r0) : -r1 / (r0 - r1);
3001     xz = x0 + sz * (x1 - x0);
3002     yz = y0 + sz * (y1 - y0);
3003     enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
3004     theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
3005     if (r0 > r1) {
3006       theta = -theta;
3007     }
3008   }
3009   if (enclosed) {
3010     alpha = 0;
3011   } else {
3012     alpha = atan2(y1 - y0, x1 - x0);
3013   }
3014 
3015   // compute the (possibly extended) s range
3016   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3017   if (enclosed) {
3018     sMin = 0;
3019     sMax = 1;
3020   } else {
3021     sMin = 1;
3022     sMax = 0;
3023     // solve for x(s) + r(s) = xMin
3024     if ((x1 + r1) - (x0 + r0) != 0) {
3025       sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
3026       if (sa < sMin) {
3027 	sMin = sa;
3028       } else if (sa > sMax) {
3029 	sMax = sa;
3030       }
3031     }
3032     // solve for x(s) - r(s) = xMax
3033     if ((x1 - r1) - (x0 - r0) != 0) {
3034       sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
3035       if (sa < sMin) {
3036 	sMin = sa;
3037       } else if (sa > sMax) {
3038 	sMax = sa;
3039       }
3040     }
3041     // solve for y(s) + r(s) = yMin
3042     if ((y1 + r1) - (y0 + r0) != 0) {
3043       sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
3044       if (sa < sMin) {
3045 	sMin = sa;
3046       } else if (sa > sMax) {
3047 	sMax = sa;
3048       }
3049     }
3050     // solve for y(s) - r(s) = yMax
3051     if ((y1 - r1) - (y0 - r0) != 0) {
3052       sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
3053       if (sa < sMin) {
3054 	sMin = sa;
3055       } else if (sa > sMax) {
3056 	sMax = sa;
3057       }
3058     }
3059     // check against sz
3060     if (r0 < r1) {
3061       if (sMin < sz) {
3062 	sMin = sz;
3063       }
3064     } else if (r0 > r1) {
3065       if (sMax > sz) {
3066 	sMax = sz;
3067       }
3068     }
3069     // check the 'extend' flags
3070     if (!shading->getExtend0() && sMin < 0) {
3071       sMin = 0;
3072     }
3073     if (!shading->getExtend1() && sMax > 1) {
3074       sMax = 1;
3075     }
3076   }
3077 
3078   if (out->useShadedFills( shading->getType() ) &&
3079       out->radialShadedFill(state, shading, sMin, sMax)) {
3080     return;
3081   }
3082 
3083   // compute the number of steps into which circles must be divided to
3084   // achieve a curve flatness of 0.1 pixel in device space for the
3085   // largest circle (note that "device space" is 72 dpi when generating
3086   // PostScript, hence the relatively small 0.1 pixel accuracy)
3087   ctm = state->getCTM();
3088   t = fabs(ctm[0]);
3089   if (fabs(ctm[1]) > t) {
3090     t = fabs(ctm[1]);
3091   }
3092   if (fabs(ctm[2]) > t) {
3093     t = fabs(ctm[2]);
3094   }
3095   if (fabs(ctm[3]) > t) {
3096     t = fabs(ctm[3]);
3097   }
3098   if (r0 > r1) {
3099     t *= r0;
3100   } else {
3101     t *= r1;
3102   }
3103   if (t < 1) {
3104     n = 3;
3105   } else {
3106     n = (int)(M_PI / acos(1 - 0.1 / t));
3107     if (n < 3) {
3108       n = 3;
3109     } else if (n > 200) {
3110       n = 200;
3111     }
3112   }
3113 
3114   // setup for the start circle
3115   ia = 0;
3116   sa = sMin;
3117   ta = t0 + sa * (t1 - t0);
3118   xa = x0 + sa * (x1 - x0);
3119   ya = y0 + sa * (y1 - y0);
3120   ra = r0 + sa * (r1 - r0);
3121   getShadingColorRadialHelper(t0, t1, ta, shading, &colorA);
3122 
3123   needExtend = !out->radialShadedSupportExtend(state, shading);
3124 
3125   // fill the circles
3126   while (ia < radialMaxSplits) {
3127 
3128     // go as far along the t axis (toward t1) as we can, such that the
3129     // color difference is within the tolerance (radialColorDelta) --
3130     // this uses bisection (between the current value, t, and t1),
3131     // limited to radialMaxSplits points along the t axis; require at
3132     // least one split to avoid problems when the innermost and
3133     // outermost colors are the same
3134     ib = radialMaxSplits;
3135     sb = sMax;
3136     tb = t0 + sb * (t1 - t0);
3137     getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
3138     while (ib - ia > 1) {
3139       if (isSameGfxColor(colorB, colorA, nComps, radialColorDelta)) {
3140         // The shading is not necessarily lineal so having two points with the
3141         // same color does not mean all the areas in between have the same color too
3142         int ic = ia + 1;
3143         for (; ic <= ib; ic++) {
3144           GfxColor colorC;
3145           const double sc = sMin + ((double)ic / (double)radialMaxSplits) * (sMax - sMin);
3146           const double tc = t0 + sc * (t1 - t0);
3147           getShadingColorRadialHelper(t0, t1, tc, shading, &colorC);
3148           if (!isSameGfxColor(colorC, colorA, nComps, radialColorDelta)) {
3149             break;
3150           }
3151         }
3152         ib = (ic > ia + 1) ? ic - 1 : ia + 1;
3153         sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
3154         tb = t0 + sb * (t1 - t0);
3155         getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
3156         break;
3157       }
3158       ib = (ia + ib) / 2;
3159       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
3160       tb = t0 + sb * (t1 - t0);
3161       getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
3162     }
3163 
3164     // compute center and radius of the circle
3165     xb = x0 + sb * (x1 - x0);
3166     yb = y0 + sb * (y1 - y0);
3167     rb = r0 + sb * (r1 - r0);
3168 
3169     // use the average of the colors at the two circles
3170     for (k = 0; k < nComps; ++k) {
3171       colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
3172     }
3173     state->setFillColor(&colorA);
3174     if (out->useFillColorStop())
3175       out->updateFillColorStop(state, (sa - sMin)/(sMax - sMin));
3176     else
3177       out->updateFillColor(state);
3178 
3179     if (needExtend) {
3180       if (enclosed) {
3181         // construct path for first circle (counterclockwise)
3182         state->moveTo(xa + ra, ya);
3183         for (k = 1; k < n; ++k) {
3184 	  angle = ((double)k / (double)n) * 2 * M_PI;
3185 	  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3186         }
3187         state->closePath();
3188 
3189         // construct and append path for second circle (clockwise)
3190         state->moveTo(xb + rb, yb);
3191         for (k = 1; k < n; ++k) {
3192 	  angle = -((double)k / (double)n) * 2 * M_PI;
3193 	  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3194         }
3195         state->closePath();
3196       } else {
3197         // construct the first subpath (clockwise)
3198         state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3199 		      ya + ra * sin(alpha + theta + 0.5 * M_PI));
3200         for (k = 0; k < n; ++k) {
3201 	  angle = alpha + theta + 0.5 * M_PI
3202 		  - ((double)k / (double)n) * (2 * theta + M_PI);
3203 	  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3204         }
3205 	for (k = 0; k < n; ++k) {
3206 	  angle = alpha - theta - 0.5 * M_PI
3207 		  + ((double)k / (double)n) * (2 * theta - M_PI);
3208 	  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3209 	}
3210 	state->closePath();
3211 
3212         // construct the second subpath (counterclockwise)
3213 	state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3214 		      ya + ra * sin(alpha + theta + 0.5 * M_PI));
3215 	for (k = 0; k < n; ++k) {
3216 	  angle = alpha + theta + 0.5 * M_PI
3217 		  + ((double)k / (double)n) * (-2 * theta + M_PI);
3218 	  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3219 	}
3220 	for (k = 0; k < n; ++k) {
3221 	  angle = alpha - theta - 0.5 * M_PI
3222 		  + ((double)k / (double)n) * (2 * theta + M_PI);
3223 	  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3224 	}
3225 	state->closePath();
3226       }
3227     }
3228 
3229     if (!out->useFillColorStop()) {
3230       // fill the path
3231       out->fill(state);
3232       state->clearPath();
3233     }
3234 
3235     // step to the next value of t
3236     ia = ib;
3237     sa = sb;
3238     ta = tb;
3239     xa = xb;
3240     ya = yb;
3241     ra = rb;
3242     colorA = colorB;
3243   }
3244 
3245   if (out->useFillColorStop()) {
3246     // make sure we add stop color when sb = sMax
3247     state->setFillColor(&colorA);
3248     out->updateFillColorStop(state, (sb - sMin)/(sMax - sMin));
3249 
3250     // fill the path
3251     state->moveTo(xMin, yMin);
3252     state->lineTo(xMin, yMax);
3253     state->lineTo(xMax, yMax);
3254     state->lineTo(xMax, yMin);
3255     state->closePath();
3256 
3257     out->fill(state);
3258     state->clearPath();
3259   }
3260 
3261   if (!needExtend)
3262     return;
3263 
3264   if (enclosed) {
3265     // extend the smaller circle
3266     if ((shading->getExtend0() && r0 <= r1) ||
3267 	(shading->getExtend1() && r1 < r0)) {
3268       if (r0 <= r1) {
3269 	ta = t0;
3270 	ra = r0;
3271 	xa = x0;
3272 	ya = y0;
3273       } else {
3274 	ta = t1;
3275 	ra = r1;
3276 	xa = x1;
3277 	ya = y1;
3278       }
3279       shading->getColor(ta, &colorA);
3280       state->setFillColor(&colorA);
3281       out->updateFillColor(state);
3282       state->moveTo(xa + ra, ya);
3283       for (k = 1; k < n; ++k) {
3284 	angle = ((double)k / (double)n) * 2 * M_PI;
3285 	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3286       }
3287       state->closePath();
3288       out->fill(state);
3289       state->clearPath();
3290     }
3291 
3292     // extend the larger circle
3293     if ((shading->getExtend0() && r0 > r1) ||
3294 	(shading->getExtend1() && r1 >= r0)) {
3295       if (r0 > r1) {
3296 	ta = t0;
3297 	ra = r0;
3298 	xa = x0;
3299 	ya = y0;
3300       } else {
3301 	ta = t1;
3302 	ra = r1;
3303 	xa = x1;
3304 	ya = y1;
3305       }
3306       shading->getColor(ta, &colorA);
3307       state->setFillColor(&colorA);
3308       out->updateFillColor(state);
3309       state->moveTo(xMin, yMin);
3310       state->lineTo(xMin, yMax);
3311       state->lineTo(xMax, yMax);
3312       state->lineTo(xMax, yMin);
3313       state->closePath();
3314       state->moveTo(xa + ra, ya);
3315       for (k = 1; k < n; ++k) {
3316 	angle = ((double)k / (double)n) * 2 * M_PI;
3317 	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3318       }
3319       state->closePath();
3320       out->fill(state);
3321       state->clearPath();
3322     }
3323   }
3324 }
3325 
doGouraudTriangleShFill(GfxGouraudTriangleShading * shading)3326 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
3327   double x0, y0, x1, y1, x2, y2;
3328   int i;
3329 
3330   if (out->useShadedFills( shading->getType())) {
3331     if (out->gouraudTriangleShadedFill( state, shading))
3332       return;
3333   }
3334 
3335   // preallocate a path (speed improvements)
3336   state->moveTo(0., 0.);
3337   state->lineTo(1., 0.);
3338   state->lineTo(0., 1.);
3339   state->closePath();
3340 
3341   GfxState::ReusablePathIterator *reusablePath = state->getReusablePath();
3342 
3343   if (shading->isParameterized()) {
3344     // work with parameterized values:
3345     double color0, color1, color2;
3346     // a relative threshold:
3347     const double refineColorThreshold = gouraudParameterizedColorDelta *
3348                                         (shading->getParameterDomainMax() - shading->getParameterDomainMin());
3349     for (i = 0; i < shading->getNTriangles(); ++i) {
3350       shading->getTriangle(i, &x0, &y0, &color0,
3351                               &x1, &y1, &color1,
3352                               &x2, &y2, &color2);
3353       gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath);
3354     }
3355 
3356   } else {
3357     // this always produces output -- even for parameterized ranges.
3358     // But it ignores the parameterized color map (the function).
3359     //
3360     // Note that using this code in for parameterized shadings might be
3361     // correct in circumstances (namely if the function is linear in the actual
3362     // triangle), but in general, it will simply be wrong.
3363     GfxColor color0, color1, color2;
3364     for (i = 0; i < shading->getNTriangles(); ++i) {
3365       shading->getTriangle(i, &x0, &y0, &color0,
3366                               &x1, &y1, &color1,
3367                               &x2, &y2, &color2);
3368       gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath);
3369     }
3370   }
3371 
3372   delete reusablePath;
3373 }
3374 
checkTrue(bool b,const char * message)3375 static inline void checkTrue(bool b, const char *message) {
3376   if (unlikely(!b)) {
3377     error(errSyntaxError, -1, message);
3378   }
3379 }
3380 
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)3381 void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
3382 			      double x1, double y1, GfxColor *color1,
3383 			      double x2, double y2, GfxColor *color2,
3384 			      int nComps, int depth, GfxState::ReusablePathIterator *path) {
3385   double x01, y01, x12, y12, x20, y20;
3386   GfxColor color01, color12, color20;
3387   int i;
3388 
3389   for (i = 0; i < nComps; ++i) {
3390     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
3391 	abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
3392       break;
3393     }
3394   }
3395   if (i == nComps || depth == gouraudMaxDepth) {
3396     state->setFillColor(color0);
3397     out->updateFillColor(state);
3398 
3399     path->reset();                         checkTrue(!path->isEnd(), "Path should not be at end");
3400     path->setCoord(x0,y0);  path->next();  checkTrue(!path->isEnd(), "Path should not be at end");
3401     path->setCoord(x1,y1);  path->next();  checkTrue(!path->isEnd(), "Path should not be at end");
3402     path->setCoord(x2,y2);  path->next();  checkTrue(!path->isEnd(), "Path should not be at end");
3403     path->setCoord(x0,y0);  path->next();  checkTrue( path->isEnd(), "Path should be at end");
3404     out->fill(state);
3405 
3406   } else {
3407     x01 = 0.5 * (x0 + x1);
3408     y01 = 0.5 * (y0 + y1);
3409     x12 = 0.5 * (x1 + x2);
3410     y12 = 0.5 * (y1 + y2);
3411     x20 = 0.5 * (x2 + x0);
3412     y20 = 0.5 * (y2 + y0);
3413     for (i = 0; i < nComps; ++i) {
3414       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
3415       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
3416       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
3417     }
3418     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
3419 			x20, y20, &color20, nComps, depth + 1, path);
3420     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
3421 			x12, y12, &color12, nComps, depth + 1, path);
3422     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
3423 			x20, y20, &color20, nComps, depth + 1, path);
3424     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
3425 			x2, y2, color2, nComps, depth + 1, path);
3426   }
3427 }
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)3428 void Gfx::gouraudFillTriangle(double x0, double y0, double color0,
3429                               double x1, double y1, double color1,
3430                               double x2, double y2, double color2,
3431                               double refineColorThreshold, int depth, GfxGouraudTriangleShading *shading, GfxState::ReusablePathIterator *path) {
3432   const double meanColor = (color0 + color1 + color2) / 3;
3433 
3434   const bool isFineEnough =
3435        fabs(color0 - meanColor) < refineColorThreshold &&
3436        fabs(color1 - meanColor) < refineColorThreshold &&
3437        fabs(color2 - meanColor) < refineColorThreshold;
3438 
3439   if (isFineEnough || depth == gouraudMaxDepth) {
3440     GfxColor color;
3441 
3442     shading->getParameterizedColor(meanColor, &color);
3443     state->setFillColor(&color);
3444     out->updateFillColor(state);
3445 
3446     path->reset();                         checkTrue(!path->isEnd(), "Path should not be at end");
3447     path->setCoord(x0,y0);  path->next();  checkTrue(!path->isEnd(), "Path should not be at end");
3448     path->setCoord(x1,y1);  path->next();  checkTrue(!path->isEnd(), "Path should not be at end");
3449     path->setCoord(x2,y2);  path->next();  checkTrue(!path->isEnd(), "Path should not be at end");
3450     path->setCoord(x0,y0);  path->next();  checkTrue( path->isEnd(), "Path should be at end");
3451     out->fill(state);
3452 
3453   } else {
3454     const double x01 = 0.5 * (x0 + x1);
3455     const double y01 = 0.5 * (y0 + y1);
3456     const double x12 = 0.5 * (x1 + x2);
3457     const double y12 = 0.5 * (y1 + y2);
3458     const double x20 = 0.5 * (x2 + x0);
3459     const double y20 = 0.5 * (y2 + y0);
3460     const double color01 = (color0 + color1) / 2.;
3461     const double color12 = (color1 + color2) / 2.;
3462     const double color20 = (color2 + color0) / 2.;
3463     ++depth;
3464     gouraudFillTriangle(x0, y0, color0,
3465                         x01, y01, color01,
3466                         x20, y20, color20,
3467                         refineColorThreshold, depth, shading, path);
3468     gouraudFillTriangle(x01, y01, color01,
3469                         x1, y1, color1,
3470                         x12, y12, color12,
3471                         refineColorThreshold, depth, shading, path);
3472     gouraudFillTriangle(x01, y01, color01,
3473                         x12, y12, color12,
3474                         x20, y20, color20,
3475                         refineColorThreshold, depth, shading, path);
3476     gouraudFillTriangle(x20, y20, color20,
3477                         x12, y12, color12,
3478                         x2, y2, color2,
3479                         refineColorThreshold, depth, shading, path);
3480   }
3481 }
3482 
doPatchMeshShFill(GfxPatchMeshShading * shading)3483 void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
3484   int start, i;
3485 
3486   if (out->useShadedFills( shading->getType())) {
3487     if (out->patchMeshShadedFill( state, shading))
3488       return;
3489   }
3490 
3491   if (shading->getNPatches() > 128) {
3492     start = 3;
3493   } else if (shading->getNPatches() > 64) {
3494     start = 2;
3495   } else if (shading->getNPatches() > 16) {
3496     start = 1;
3497   } else {
3498     start = 0;
3499   }
3500   /*
3501    * Parameterized shadings take one parameter [t_0,t_e]
3502    * and map it into the color space.
3503    *
3504    * Consequently, all color values are stored as doubles.
3505    *
3506    * These color values are interpreted as parameters for parameterized
3507    * shadings and as colorspace entities otherwise.
3508    *
3509    * The only difference is that color space entities are stored into
3510    * DOUBLE arrays, not into arrays of type GfxColorComp.
3511    */
3512   const int colorComps = shading->getColorSpace()->getNComps();
3513   double refineColorThreshold;
3514   if( shading->isParameterized() ) {
3515 	  refineColorThreshold = gouraudParameterizedColorDelta *
3516 		  (shading->getParameterDomainMax() - shading->getParameterDomainMin());
3517 
3518   } else {
3519 	  refineColorThreshold = patchColorDelta;
3520   }
3521 
3522   for (i = 0; i < shading->getNPatches(); ++i) {
3523     fillPatch(shading->getPatch(i),
3524              colorComps,
3525              shading->isParameterized() ? 1 : colorComps,
3526              refineColorThreshold,
3527              start,
3528              shading);
3529   }
3530 }
3531 
3532 
fillPatch(GfxPatch * patch,int colorComps,int patchColorComps,double refineColorThreshold,int depth,GfxPatchMeshShading * shading)3533 void Gfx::fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, GfxPatchMeshShading *shading) {
3534   GfxPatch patch00, patch01, patch10, patch11;
3535   double xx[4][8], yy[4][8];
3536   double xxm, yym;
3537   int i;
3538 
3539   for (i = 0; i < patchColorComps; ++i) {
3540     // these comparisons are done in double arithmetics.
3541     //
3542     // For non-parameterized shadings, they are done in color space
3543     // components.
3544     if (fabs(patch->color[0][0].c[i] - patch->color[0][1].c[i]) > refineColorThreshold ||
3545         fabs(patch->color[0][1].c[i] - patch->color[1][1].c[i]) > refineColorThreshold ||
3546         fabs(patch->color[1][1].c[i] - patch->color[1][0].c[i]) > refineColorThreshold ||
3547         fabs(patch->color[1][0].c[i] - patch->color[0][0].c[i]) > refineColorThreshold) {
3548       break;
3549     }
3550   }
3551   if (i == patchColorComps || depth == patchMaxDepth) {
3552     GfxColor flatColor;
3553     if( shading->isParameterized() ) {
3554       shading->getParameterizedColor( patch->color[0][0].c[0], &flatColor );
3555     } else {
3556       for( i = 0; i<colorComps; ++i ) {
3557         // simply cast to the desired type; that's all what is needed.
3558         flatColor.c[i] = GfxColorComp(patch->color[0][0].c[i]);
3559       }
3560     }
3561     state->setFillColor(&flatColor);
3562     out->updateFillColor(state);
3563     state->moveTo(patch->x[0][0], patch->y[0][0]);
3564     state->curveTo(patch->x[0][1], patch->y[0][1],
3565 		   patch->x[0][2], patch->y[0][2],
3566 		   patch->x[0][3], patch->y[0][3]);
3567     state->curveTo(patch->x[1][3], patch->y[1][3],
3568 		   patch->x[2][3], patch->y[2][3],
3569 		   patch->x[3][3], patch->y[3][3]);
3570     state->curveTo(patch->x[3][2], patch->y[3][2],
3571 		   patch->x[3][1], patch->y[3][1],
3572 		   patch->x[3][0], patch->y[3][0]);
3573     state->curveTo(patch->x[2][0], patch->y[2][0],
3574 		   patch->x[1][0], patch->y[1][0],
3575 		   patch->x[0][0], patch->y[0][0]);
3576     state->closePath();
3577     out->fill(state);
3578     state->clearPath();
3579   } else {
3580     for (i = 0; i < 4; ++i) {
3581       xx[i][0] = patch->x[i][0];
3582       yy[i][0] = patch->y[i][0];
3583       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
3584       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
3585       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
3586       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
3587       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
3588       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
3589       xx[i][2] = 0.5 * (xx[i][1] + xxm);
3590       yy[i][2] = 0.5 * (yy[i][1] + yym);
3591       xx[i][5] = 0.5 * (xxm + xx[i][6]);
3592       yy[i][5] = 0.5 * (yym + yy[i][6]);
3593       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
3594       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
3595       xx[i][7] = patch->x[i][3];
3596       yy[i][7] = patch->y[i][3];
3597     }
3598     for (i = 0; i < 4; ++i) {
3599       patch00.x[0][i] = xx[0][i];
3600       patch00.y[0][i] = yy[0][i];
3601       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
3602       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
3603       xxm = 0.5 * (xx[1][i] + xx[2][i]);
3604       yym = 0.5 * (yy[1][i] + yy[2][i]);
3605       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
3606       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
3607       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
3608       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
3609       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
3610       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
3611       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
3612       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
3613       patch10.x[0][i] = patch00.x[3][i];
3614       patch10.y[0][i] = patch00.y[3][i];
3615       patch10.x[3][i] = xx[3][i];
3616       patch10.y[3][i] = yy[3][i];
3617     }
3618     for (i = 4; i < 8; ++i) {
3619       patch01.x[0][i-4] = xx[0][i];
3620       patch01.y[0][i-4] = yy[0][i];
3621       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
3622       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
3623       xxm = 0.5 * (xx[1][i] + xx[2][i]);
3624       yym = 0.5 * (yy[1][i] + yy[2][i]);
3625       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
3626       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
3627       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
3628       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
3629       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
3630       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
3631       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
3632       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
3633       patch11.x[0][i-4] = patch01.x[3][i-4];
3634       patch11.y[0][i-4] = patch01.y[3][i-4];
3635       patch11.x[3][i-4] = xx[3][i];
3636       patch11.y[3][i-4] = yy[3][i];
3637     }
3638     for (i = 0; i < patchColorComps; ++i) {
3639       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
3640       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
3641 				  patch->color[0][1].c[i]) / 2;
3642       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
3643       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
3644       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
3645 				  patch->color[1][1].c[i]) / 2;
3646       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
3647       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
3648       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
3649 				  patch->color[1][0].c[i]) / 2;
3650       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
3651       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
3652       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
3653 				  patch->color[0][0].c[i]) / 2;
3654       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
3655       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
3656 				  patch01.color[1][1].c[i]) / 2;
3657       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
3658       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
3659       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
3660     }
3661     fillPatch(&patch00, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3662     fillPatch(&patch10, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3663     fillPatch(&patch01, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3664     fillPatch(&patch11, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3665   }
3666 }
3667 
doEndPath()3668 void Gfx::doEndPath() {
3669   if (state->isCurPt() && clip != clipNone) {
3670     state->clip();
3671     if (clip == clipNormal) {
3672       out->clip(state);
3673     } else {
3674       out->eoClip(state);
3675     }
3676   }
3677   clip = clipNone;
3678   state->clearPath();
3679 }
3680 
3681 //------------------------------------------------------------------------
3682 // path clipping operators
3683 //------------------------------------------------------------------------
3684 
opClip(Object args[],int numArgs)3685 void Gfx::opClip(Object args[], int numArgs) {
3686   clip = clipNormal;
3687 }
3688 
opEOClip(Object args[],int numArgs)3689 void Gfx::opEOClip(Object args[], int numArgs) {
3690   clip = clipEO;
3691 }
3692 
3693 //------------------------------------------------------------------------
3694 // text object operators
3695 //------------------------------------------------------------------------
3696 
opBeginText(Object args[],int numArgs)3697 void Gfx::opBeginText(Object args[], int numArgs) {
3698   out->beginTextObject(state);
3699   state->setTextMat(1, 0, 0, 1, 0, 0);
3700   state->textMoveTo(0, 0);
3701   out->updateTextMat(state);
3702   out->updateTextPos(state);
3703   fontChanged = gTrue;
3704 }
3705 
opEndText(Object args[],int numArgs)3706 void Gfx::opEndText(Object args[], int numArgs) {
3707   out->endTextObject(state);
3708 }
3709 
3710 //------------------------------------------------------------------------
3711 // text state operators
3712 //------------------------------------------------------------------------
3713 
opSetCharSpacing(Object args[],int numArgs)3714 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
3715   state->setCharSpace(args[0].getNum());
3716   out->updateCharSpace(state);
3717 }
3718 
opSetFont(Object args[],int numArgs)3719 void Gfx::opSetFont(Object args[], int numArgs) {
3720   GfxFont *font;
3721 
3722   if (!(font = res->lookupFont(args[0].getName()))) {
3723     // unsetting the font (drawing no text) is better than using the
3724     // previous one and drawing random glyphs from it
3725     state->setFont(NULL, args[1].getNum());
3726     fontChanged = gTrue;
3727     return;
3728   }
3729   if (printCommands) {
3730     printf("  font: tag=%s name='%s' %g\n",
3731 	   font->getTag()->getCString(),
3732 	   font->getName() ? font->getName()->getCString() : "???",
3733 	   args[1].getNum());
3734     fflush(stdout);
3735   }
3736 
3737   font->incRefCnt();
3738   state->setFont(font, args[1].getNum());
3739   fontChanged = gTrue;
3740 }
3741 
opSetTextLeading(Object args[],int numArgs)3742 void Gfx::opSetTextLeading(Object args[], int numArgs) {
3743   state->setLeading(args[0].getNum());
3744 }
3745 
opSetTextRender(Object args[],int numArgs)3746 void Gfx::opSetTextRender(Object args[], int numArgs) {
3747   state->setRender(args[0].getInt());
3748   out->updateRender(state);
3749 }
3750 
opSetTextRise(Object args[],int numArgs)3751 void Gfx::opSetTextRise(Object args[], int numArgs) {
3752   state->setRise(args[0].getNum());
3753   out->updateRise(state);
3754 }
3755 
opSetWordSpacing(Object args[],int numArgs)3756 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
3757   state->setWordSpace(args[0].getNum());
3758   out->updateWordSpace(state);
3759 }
3760 
opSetHorizScaling(Object args[],int numArgs)3761 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
3762   state->setHorizScaling(args[0].getNum());
3763   out->updateHorizScaling(state);
3764   fontChanged = gTrue;
3765 }
3766 
3767 //------------------------------------------------------------------------
3768 // text positioning operators
3769 //------------------------------------------------------------------------
3770 
opTextMove(Object args[],int numArgs)3771 void Gfx::opTextMove(Object args[], int numArgs) {
3772   double tx, ty;
3773 
3774   tx = state->getLineX() + args[0].getNum();
3775   ty = state->getLineY() + args[1].getNum();
3776   state->textMoveTo(tx, ty);
3777   out->updateTextPos(state);
3778 }
3779 
opTextMoveSet(Object args[],int numArgs)3780 void Gfx::opTextMoveSet(Object args[], int numArgs) {
3781   double tx, ty;
3782 
3783   tx = state->getLineX() + args[0].getNum();
3784   ty = args[1].getNum();
3785   state->setLeading(-ty);
3786   ty += state->getLineY();
3787   state->textMoveTo(tx, ty);
3788   out->updateTextPos(state);
3789 }
3790 
opSetTextMatrix(Object args[],int numArgs)3791 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
3792   state->setTextMat(args[0].getNum(), args[1].getNum(),
3793 		    args[2].getNum(), args[3].getNum(),
3794 		    args[4].getNum(), args[5].getNum());
3795   state->textMoveTo(0, 0);
3796   out->updateTextMat(state);
3797   out->updateTextPos(state);
3798   fontChanged = gTrue;
3799 }
3800 
opTextNextLine(Object args[],int numArgs)3801 void Gfx::opTextNextLine(Object args[], int numArgs) {
3802   double tx, ty;
3803 
3804   tx = state->getLineX();
3805   ty = state->getLineY() - state->getLeading();
3806   state->textMoveTo(tx, ty);
3807   out->updateTextPos(state);
3808 }
3809 
3810 //------------------------------------------------------------------------
3811 // text string operators
3812 //------------------------------------------------------------------------
3813 
opShowText(Object args[],int numArgs)3814 void Gfx::opShowText(Object args[], int numArgs) {
3815   if (!state->getFont()) {
3816     error(errSyntaxError, getPos(), "No font in show");
3817     return;
3818   }
3819   if (fontChanged) {
3820     out->updateFont(state);
3821     fontChanged = gFalse;
3822   }
3823   out->beginStringOp(state);
3824   doShowText(args[0].getString());
3825   out->endStringOp(state);
3826   if (!ocState) {
3827     doIncCharCount(args[0].getString());
3828   }
3829 }
3830 
opMoveShowText(Object args[],int numArgs)3831 void Gfx::opMoveShowText(Object args[], int numArgs) {
3832   double tx, ty;
3833 
3834   if (!state->getFont()) {
3835     error(errSyntaxError, getPos(), "No font in move/show");
3836     return;
3837   }
3838   if (fontChanged) {
3839     out->updateFont(state);
3840     fontChanged = gFalse;
3841   }
3842   tx = state->getLineX();
3843   ty = state->getLineY() - state->getLeading();
3844   state->textMoveTo(tx, ty);
3845   out->updateTextPos(state);
3846   out->beginStringOp(state);
3847   doShowText(args[0].getString());
3848   out->endStringOp(state);
3849   if (!ocState) {
3850     doIncCharCount(args[0].getString());
3851   }
3852 }
3853 
opMoveSetShowText(Object args[],int numArgs)3854 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
3855   double tx, ty;
3856 
3857   if (!state->getFont()) {
3858     error(errSyntaxError, getPos(), "No font in move/set/show");
3859     return;
3860   }
3861   if (fontChanged) {
3862     out->updateFont(state);
3863     fontChanged = gFalse;
3864   }
3865   state->setWordSpace(args[0].getNum());
3866   state->setCharSpace(args[1].getNum());
3867   tx = state->getLineX();
3868   ty = state->getLineY() - state->getLeading();
3869   state->textMoveTo(tx, ty);
3870   out->updateWordSpace(state);
3871   out->updateCharSpace(state);
3872   out->updateTextPos(state);
3873   out->beginStringOp(state);
3874   doShowText(args[2].getString());
3875   out->endStringOp(state);
3876   if (ocState) {
3877     doIncCharCount(args[2].getString());
3878   }
3879 }
3880 
opShowSpaceText(Object args[],int numArgs)3881 void Gfx::opShowSpaceText(Object args[], int numArgs) {
3882   Array *a;
3883   Object obj;
3884   int wMode;
3885   int i;
3886 
3887   if (!state->getFont()) {
3888     error(errSyntaxError, getPos(), "No font in show/space");
3889     return;
3890   }
3891   if (fontChanged) {
3892     out->updateFont(state);
3893     fontChanged = gFalse;
3894   }
3895   out->beginStringOp(state);
3896   wMode = state->getFont()->getWMode();
3897   a = args[0].getArray();
3898   for (i = 0; i < a->getLength(); ++i) {
3899     a->get(i, &obj);
3900     if (obj.isNum()) {
3901       // this uses the absolute value of the font size to match
3902       // Acrobat's behavior
3903       if (wMode) {
3904         state->textShift(0, -obj.getNum() * 0.001 *
3905           state->getFontSize());
3906       } else {
3907         state->textShift(-obj.getNum() * 0.001 *
3908           state->getFontSize() *
3909           state->getHorizScaling(), 0);
3910       }
3911       out->updateTextShift(state, obj.getNum());
3912     } else if (obj.isString()) {
3913       doShowText(obj.getString());
3914     } else {
3915       error(errSyntaxError, getPos(),
3916         "Element of show/space array must be number or string");
3917     }
3918     obj.free();
3919   }
3920   out->endStringOp(state);
3921   if (!ocState) {
3922     a = args[0].getArray();
3923     for (i = 0; i < a->getLength(); ++i) {
3924       a->get(i, &obj);
3925       if (obj.isString()) {
3926 	doIncCharCount(obj.getString());
3927       }
3928       obj.free();
3929     }
3930   }
3931 }
3932 
doShowText(GooString * s)3933 void Gfx::doShowText(GooString *s) {
3934   GfxFont *font;
3935   int wMode;
3936   double riseX, riseY;
3937   CharCode code;
3938   Unicode *u = NULL;
3939   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy;
3940   double originX, originY, tOriginX, tOriginY;
3941   double x0, y0, x1, y1;
3942   double oldCTM[6], newCTM[6];
3943   double *mat;
3944   Object charProc;
3945   Dict *resDict;
3946   Parser *oldParser;
3947   GfxState *savedState;
3948   char *p;
3949   int render;
3950   GBool patternFill;
3951   int len, n, uLen, nChars, nSpaces, i;
3952 
3953   font = state->getFont();
3954   wMode = font->getWMode();
3955 
3956   if (out->useDrawChar()) {
3957     out->beginString(state, s);
3958   }
3959 
3960   // if we're doing a pattern fill, set up clipping
3961   render = state->getRender();
3962   if (!(render & 1) &&
3963       state->getFillColorSpace()->getMode() == csPattern) {
3964     patternFill = gTrue;
3965     saveState();
3966     // disable fill, enable clipping, leave stroke unchanged
3967     if ((render ^ (render >> 1)) & 1) {
3968       render = 5;
3969     } else {
3970       render = 7;
3971     }
3972     state->setRender(render);
3973     out->updateRender(state);
3974   } else {
3975     patternFill = gFalse;
3976   }
3977 
3978   state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3979   x0 = state->getCurX() + riseX;
3980   y0 = state->getCurY() + riseY;
3981 
3982   // handle a Type 3 char
3983   if (font->getType() == fontType3 && out->interpretType3Chars()) {
3984     mat = state->getCTM();
3985     for (i = 0; i < 6; ++i) {
3986       oldCTM[i] = mat[i];
3987     }
3988     mat = state->getTextMat();
3989     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
3990     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
3991     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
3992     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
3993     mat = font->getFontMatrix();
3994     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
3995     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
3996     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
3997     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
3998     newCTM[0] *= state->getFontSize();
3999     newCTM[1] *= state->getFontSize();
4000     newCTM[2] *= state->getFontSize();
4001     newCTM[3] *= state->getFontSize();
4002     newCTM[0] *= state->getHorizScaling();
4003     newCTM[2] *= state->getHorizScaling();
4004     curX = state->getCurX();
4005     curY = state->getCurY();
4006     oldParser = parser;
4007     p = s->getCString();
4008     len = s->getLength();
4009     while (len > 0) {
4010       n = font->getNextChar(p, len, &code,
4011 			    &u, &uLen,
4012 			    &dx, &dy, &originX, &originY);
4013       dx = dx * state->getFontSize() + state->getCharSpace();
4014       if (n == 1 && *p == ' ') {
4015 	dx += state->getWordSpace();
4016       }
4017       dx *= state->getHorizScaling();
4018       dy *= state->getFontSize();
4019       state->textTransformDelta(dx, dy, &tdx, &tdy);
4020       state->transform(curX + riseX, curY + riseY, &x, &y);
4021       savedState = saveStateStack();
4022       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
4023       //~ the CTM concat values here are wrong (but never used)
4024       out->updateCTM(state, 1, 0, 0, 1, 0, 0);
4025       state->transformDelta(dx, dy, &ddx, &ddy);
4026       if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
4027 			       code, u, uLen)) {
4028 	((Gfx8BitFont *)font)->getCharProc(code, &charProc);
4029 	if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
4030 	  pushResources(resDict);
4031 	}
4032 	if (charProc.isStream()) {
4033 	  display(&charProc, gFalse);
4034 	} else {
4035 	  error(errSyntaxError, getPos(), "Missing or bad Type3 CharProc entry");
4036 	}
4037 	out->endType3Char(state);
4038 	if (resDict) {
4039 	  popResources();
4040 	}
4041 	charProc.free();
4042       }
4043       restoreStateStack(savedState);
4044       // GfxState::restore() does *not* restore the current position,
4045       // so we deal with it here using (curX, curY) and (lineX, lineY)
4046       curX += tdx;
4047       curY += tdy;
4048       state->moveTo(curX, curY);
4049       out->updateCTM(state, 0, 0, 0, 0, 0, 0);
4050       p += n;
4051       len -= n;
4052     }
4053     parser = oldParser;
4054 
4055   } else if (out->useDrawChar()) {
4056     p = s->getCString();
4057     len = s->getLength();
4058     while (len > 0) {
4059       n = font->getNextChar(p, len, &code,
4060 			    &u, &uLen,
4061 			    &dx, &dy, &originX, &originY);
4062       if (wMode) {
4063 	dx *= state->getFontSize();
4064 	dy = dy * state->getFontSize() + state->getCharSpace();
4065 	if (n == 1 && *p == ' ') {
4066 	  dy += state->getWordSpace();
4067 	}
4068       } else {
4069 	dx = dx * state->getFontSize() + state->getCharSpace();
4070 	if (n == 1 && *p == ' ') {
4071 	  dx += state->getWordSpace();
4072 	}
4073 	dx *= state->getHorizScaling();
4074 	dy *= state->getFontSize();
4075       }
4076       state->textTransformDelta(dx, dy, &tdx, &tdy);
4077       originX *= state->getFontSize();
4078       originY *= state->getFontSize();
4079       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
4080       if (ocState)
4081         out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
4082 		      tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
4083       state->shift(tdx, tdy);
4084       p += n;
4085       len -= n;
4086     }
4087   } else {
4088     dx = dy = 0;
4089     p = s->getCString();
4090     len = s->getLength();
4091     nChars = nSpaces = 0;
4092     while (len > 0) {
4093       n = font->getNextChar(p, len, &code,
4094 			    &u, &uLen,
4095 			    &dx2, &dy2, &originX, &originY);
4096       dx += dx2;
4097       dy += dy2;
4098       if (n == 1 && *p == ' ') {
4099 	++nSpaces;
4100       }
4101       ++nChars;
4102       p += n;
4103       len -= n;
4104     }
4105     if (wMode) {
4106       dx *= state->getFontSize();
4107       dy = dy * state->getFontSize()
4108 	   + nChars * state->getCharSpace()
4109 	   + nSpaces * state->getWordSpace();
4110     } else {
4111       dx = dx * state->getFontSize()
4112 	   + nChars * state->getCharSpace()
4113 	   + nSpaces * state->getWordSpace();
4114       dx *= state->getHorizScaling();
4115       dy *= state->getFontSize();
4116     }
4117     state->textTransformDelta(dx, dy, &tdx, &tdy);
4118     if (ocState)
4119       out->drawString(state, s);
4120     state->shift(tdx, tdy);
4121   }
4122 
4123   if (out->useDrawChar()) {
4124     out->endString(state);
4125   }
4126 
4127   if (patternFill && ocState) {
4128     out->saveTextPos(state);
4129     // tell the OutputDev to do the clipping
4130     out->endTextObject(state);
4131     // set up a clipping bbox so doPatternText will work -- assume
4132     // that the text bounding box does not extend past the baseline in
4133     // any direction by more than twice the font size
4134     x1 = state->getCurX() + riseX;
4135     y1 = state->getCurY() + riseY;
4136     if (x0 > x1) {
4137       x = x0; x0 = x1; x1 = x;
4138     }
4139     if (y0 > y1) {
4140       y = y0; y0 = y1; y1 = y;
4141     }
4142     state->textTransformDelta(0, state->getFontSize(), &dx, &dy);
4143     state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2);
4144     dx = fabs(dx);
4145     dx2 = fabs(dx2);
4146     if (dx2 > dx) {
4147       dx = dx2;
4148     }
4149     dy = fabs(dy);
4150     dy2 = fabs(dy2);
4151     if (dy2 > dy) {
4152       dy = dy2;
4153     }
4154     state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy);
4155     // set render mode to fill-only
4156     state->setRender(0);
4157     out->updateRender(state);
4158     doPatternText();
4159     restoreState();
4160     out->restoreTextPos(state);
4161   }
4162 
4163   updateLevel += 10 * s->getLength();
4164 }
4165 
4166 // NB: this is only called when ocState is false.
doIncCharCount(GooString * s)4167 void Gfx::doIncCharCount(GooString *s) {
4168   if (out->needCharCount()) {
4169     out->incCharCount(s->getLength());
4170   }
4171 }
4172 
4173 //------------------------------------------------------------------------
4174 // XObject operators
4175 //------------------------------------------------------------------------
4176 
opXObject(Object args[],int numArgs)4177 void Gfx::opXObject(Object args[], int numArgs) {
4178   char *name;
4179   Object obj1, obj2, obj3, refObj;
4180 #if OPI_SUPPORT
4181   Object opiDict;
4182 #endif
4183 
4184   if (!ocState && !out->needCharCount()) {
4185     return;
4186   }
4187   name = args[0].getName();
4188   if (!res->lookupXObject(name, &obj1)) {
4189     return;
4190   }
4191   if (!obj1.isStream()) {
4192       error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name);
4193     obj1.free();
4194     return;
4195   }
4196 
4197 #if OPI_SUPPORT
4198   obj1.streamGetDict()->lookup("OPI", &opiDict);
4199   if (opiDict.isDict()) {
4200     out->opiBegin(state, opiDict.getDict());
4201   }
4202 #endif
4203   obj1.streamGetDict()->lookup("Subtype", &obj2);
4204   if (obj2.isName("Image")) {
4205     if (out->needNonText()) {
4206       res->lookupXObjectNF(name, &refObj);
4207       doImage(&refObj, obj1.getStream(), gFalse);
4208       refObj.free();
4209     }
4210   } else if (obj2.isName("Form")) {
4211     res->lookupXObjectNF(name, &refObj);
4212     GBool shouldDoForm = gTrue;
4213     std::set<int>::iterator drawingFormIt;
4214     if (refObj.isRef()) {
4215       const int num = refObj.getRef().num;
4216       if (formsDrawing.find(num) == formsDrawing.end()) {
4217 	drawingFormIt = formsDrawing.insert(num).first;
4218       } else {
4219 	shouldDoForm = gFalse;
4220       }
4221     }
4222     if (shouldDoForm) {
4223       if (out->useDrawForm() && refObj.isRef()) {
4224 	out->drawForm(refObj.getRef());
4225       } else {
4226 	doForm(&obj1);
4227       }
4228     }
4229     if (refObj.isRef() && shouldDoForm) {
4230       formsDrawing.erase(drawingFormIt);
4231     }
4232     refObj.free();
4233   } else if (obj2.isName("PS")) {
4234     obj1.streamGetDict()->lookup("Level1", &obj3);
4235     out->psXObject(obj1.getStream(),
4236 		   obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
4237   } else if (obj2.isName()) {
4238     error(errSyntaxError, getPos(), "Unknown XObject subtype '{0:s}'", obj2.getName());
4239   } else {
4240     error(errSyntaxError, getPos(), "XObject subtype is missing or wrong type");
4241   }
4242   obj2.free();
4243 #if OPI_SUPPORT
4244   if (opiDict.isDict()) {
4245     out->opiEnd(state, opiDict.getDict());
4246   }
4247   opiDict.free();
4248 #endif
4249   obj1.free();
4250 }
4251 
doImage(Object * ref,Stream * str,GBool inlineImg)4252 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
4253   Dict *dict, *maskDict;
4254   int width, height;
4255   int bits, maskBits;
4256   GBool interpolate;
4257   StreamColorSpaceMode csMode;
4258   GBool mask;
4259   GBool invert;
4260   GfxColorSpace *colorSpace, *maskColorSpace;
4261   GfxImageColorMap *colorMap, *maskColorMap;
4262   Object maskObj, smaskObj;
4263   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
4264   int maskColors[2*gfxColorMaxComps];
4265   int maskWidth, maskHeight;
4266   GBool maskInvert;
4267   GBool maskInterpolate;
4268   Stream *maskStr;
4269   Object obj1, obj2;
4270   int i, n;
4271 
4272   // get info from the stream
4273   bits = 0;
4274   csMode = streamCSNone;
4275   str->getImageParams(&bits, &csMode);
4276 
4277   // get stream dict
4278   dict = str->getDict();
4279 
4280   // check for optional content key
4281   if (ref) {
4282     dict->lookupNF("OC", &obj1);
4283     if (catalog->getOptContentConfig() && !catalog->getOptContentConfig()->optContentIsVisible(&obj1)) {
4284       obj1.free();
4285       return;
4286     }
4287     obj1.free();
4288   }
4289 
4290   // get size
4291   dict->lookup("Width", &obj1);
4292   if (obj1.isNull()) {
4293     obj1.free();
4294     dict->lookup("W", &obj1);
4295   }
4296   if (obj1.isInt())
4297     width = obj1.getInt();
4298   else if (obj1.isReal())
4299     width = (int)obj1.getReal();
4300   else
4301     goto err2;
4302   obj1.free();
4303   dict->lookup("Height", &obj1);
4304   if (obj1.isNull()) {
4305     obj1.free();
4306     dict->lookup("H", &obj1);
4307   }
4308   if (obj1.isInt())
4309     height = obj1.getInt();
4310   else if (obj1.isReal())
4311     height = (int)obj1.getReal();
4312   else
4313     goto err2;
4314   obj1.free();
4315 
4316   if (width < 1 || height < 1)
4317     goto err1;
4318 
4319   // image interpolation
4320   dict->lookup("Interpolate", &obj1);
4321   if (obj1.isNull()) {
4322     obj1.free();
4323     dict->lookup("I", &obj1);
4324   }
4325   if (obj1.isBool())
4326     interpolate = obj1.getBool();
4327   else
4328     interpolate = gFalse;
4329   obj1.free();
4330   maskInterpolate = gFalse;
4331 
4332   // image or mask?
4333   dict->lookup("ImageMask", &obj1);
4334   if (obj1.isNull()) {
4335     obj1.free();
4336     dict->lookup("IM", &obj1);
4337   }
4338   mask = gFalse;
4339   if (obj1.isBool())
4340     mask = obj1.getBool();
4341   else if (!obj1.isNull())
4342     goto err2;
4343   obj1.free();
4344 
4345   // bit depth
4346   if (bits == 0) {
4347     dict->lookup("BitsPerComponent", &obj1);
4348     if (obj1.isNull()) {
4349       obj1.free();
4350       dict->lookup("BPC", &obj1);
4351     }
4352     if (obj1.isInt()) {
4353       bits = obj1.getInt();
4354     } else if (mask) {
4355       bits = 1;
4356     } else {
4357       goto err2;
4358     }
4359     obj1.free();
4360   }
4361 
4362   // display a mask
4363   if (mask) {
4364 
4365     // check for inverted mask
4366     if (bits != 1)
4367       goto err1;
4368     invert = gFalse;
4369     dict->lookup("Decode", &obj1);
4370     if (obj1.isNull()) {
4371       obj1.free();
4372       dict->lookup("D", &obj1);
4373     }
4374     if (obj1.isArray()) {
4375       obj1.arrayGet(0, &obj2);
4376       // Table 4.39 says /Decode must be [1 0] or [0 1]. Adobe
4377       // accepts [1.0 0.0] as well.
4378       if (obj2.isNum() && obj2.getNum() >= 0.9)
4379 	invert = gTrue;
4380       obj2.free();
4381     } else if (!obj1.isNull()) {
4382       goto err2;
4383     }
4384     obj1.free();
4385 
4386     // if drawing is disabled, skip over inline image data
4387     if (!ocState || !out->needNonText()) {
4388       str->reset();
4389       n = height * ((width + 7) / 8);
4390       for (i = 0; i < n; ++i) {
4391 	str->getChar();
4392       }
4393       str->close();
4394 
4395     // draw it
4396     } else {
4397       if (state->getFillColorSpace()->getMode() == csPattern) {
4398 	doPatternImageMask(ref, str, width, height, invert, inlineImg);
4399       } else {
4400 	out->drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
4401       }
4402     }
4403   } else {
4404 
4405     // get color space and color map
4406     dict->lookup("ColorSpace", &obj1);
4407     if (obj1.isNull()) {
4408       obj1.free();
4409       dict->lookup("CS", &obj1);
4410     }
4411     if (obj1.isName() && inlineImg) {
4412       res->lookupColorSpace(obj1.getName(), &obj2);
4413       if (!obj2.isNull()) {
4414 	obj1.free();
4415 	obj1 = obj2;
4416       } else {
4417 	obj2.free();
4418       }
4419     }
4420     if (!obj1.isNull()) {
4421       Object objIntent;
4422       char *tempIntent = NULL;
4423       dict->lookup("Intent", &objIntent);
4424       if (objIntent.isName()) {
4425         tempIntent = state->getRenderingIntent();
4426         if (tempIntent != NULL) {
4427           tempIntent = strdup(tempIntent);
4428         }
4429         state->setRenderingIntent(objIntent.getName());
4430       }
4431       colorSpace = GfxColorSpace::parse(res, &obj1, out, state);
4432       if (objIntent.isName()) {
4433         state->setRenderingIntent(tempIntent);
4434         free(tempIntent);
4435       }
4436       objIntent.free();
4437     } else if (csMode == streamCSDeviceGray) {
4438       Object objCS;
4439       res->lookupColorSpace("DefaultGray", &objCS);
4440       if (objCS.isNull()) {
4441         colorSpace = new GfxDeviceGrayColorSpace();
4442       } else {
4443         colorSpace = GfxColorSpace::parse(res, &objCS, out, state);
4444       }
4445       objCS.free();
4446     } else if (csMode == streamCSDeviceRGB) {
4447       Object objCS;
4448       res->lookupColorSpace("DefaultRGB", &objCS);
4449       if (objCS.isNull()) {
4450         colorSpace = new GfxDeviceRGBColorSpace();
4451       } else {
4452         colorSpace = GfxColorSpace::parse(res, &objCS, out, state);
4453       }
4454       objCS.free();
4455     } else if (csMode == streamCSDeviceCMYK) {
4456       Object objCS;
4457       res->lookupColorSpace("DefaultCMYK", &objCS);
4458       if (objCS.isNull()) {
4459         colorSpace = new GfxDeviceCMYKColorSpace();
4460       } else {
4461         colorSpace = GfxColorSpace::parse(res, &objCS, out, state);
4462       }
4463       objCS.free();
4464     } else {
4465       colorSpace = NULL;
4466     }
4467     obj1.free();
4468     if (!colorSpace) {
4469       goto err1;
4470     }
4471     dict->lookup("Decode", &obj1);
4472     if (obj1.isNull()) {
4473       obj1.free();
4474       dict->lookup("D", &obj1);
4475     }
4476     if (bits == 0) {
4477       delete colorSpace;
4478       goto err2;
4479     }
4480     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
4481     obj1.free();
4482     if (!colorMap->isOk()) {
4483       delete colorMap;
4484       goto err1;
4485     }
4486 
4487     // get the mask
4488     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
4489     maskStr = NULL; // make gcc happy
4490     maskWidth = maskHeight = 0; // make gcc happy
4491     maskInvert = gFalse; // make gcc happy
4492     maskColorMap = NULL; // make gcc happy
4493     dict->lookup("Mask", &maskObj);
4494     dict->lookup("SMask", &smaskObj);
4495     if (smaskObj.isStream()) {
4496       // soft mask
4497       if (inlineImg) {
4498 	goto err1;
4499       }
4500       maskStr = smaskObj.getStream();
4501       maskDict = smaskObj.streamGetDict();
4502       maskDict->lookup("Width", &obj1);
4503       if (obj1.isNull()) {
4504 	obj1.free();
4505 	maskDict->lookup("W", &obj1);
4506       }
4507       if (!obj1.isInt()) {
4508 	goto err2;
4509       }
4510       maskWidth = obj1.getInt();
4511       obj1.free();
4512       maskDict->lookup("Height", &obj1);
4513       if (obj1.isNull()) {
4514 	obj1.free();
4515 	maskDict->lookup("H", &obj1);
4516       }
4517       if (!obj1.isInt()) {
4518 	goto err2;
4519       }
4520       maskHeight = obj1.getInt();
4521       obj1.free();
4522       maskDict->lookup("Interpolate", &obj1);
4523       if (obj1.isNull()) {
4524         obj1.free();
4525         maskDict->lookup("I", &obj1);
4526       }
4527       if (obj1.isBool())
4528         maskInterpolate = obj1.getBool();
4529       else
4530         maskInterpolate = gFalse;
4531       obj1.free();
4532       maskDict->lookup("BitsPerComponent", &obj1);
4533       if (obj1.isNull()) {
4534 	obj1.free();
4535 	maskDict->lookup("BPC", &obj1);
4536       }
4537       if (!obj1.isInt()) {
4538 	goto err2;
4539       }
4540       maskBits = obj1.getInt();
4541       obj1.free();
4542       maskDict->lookup("ColorSpace", &obj1);
4543       if (obj1.isNull()) {
4544 	obj1.free();
4545 	maskDict->lookup("CS", &obj1);
4546       }
4547       if (obj1.isName()) {
4548 	res->lookupColorSpace(obj1.getName(), &obj2);
4549 	if (!obj2.isNull()) {
4550 	  obj1.free();
4551 	  obj1 = obj2;
4552 	} else {
4553 	  obj2.free();
4554 	}
4555       }
4556       maskColorSpace = GfxColorSpace::parse(NULL, &obj1, out, state);
4557       obj1.free();
4558       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
4559 	goto err1;
4560       }
4561       maskDict->lookup("Decode", &obj1);
4562       if (obj1.isNull()) {
4563 	obj1.free();
4564 	maskDict->lookup("D", &obj1);
4565       }
4566       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
4567       obj1.free();
4568       if (!maskColorMap->isOk()) {
4569 	delete maskColorMap;
4570 	goto err1;
4571       }
4572       // handle the Matte entry
4573       maskDict->lookup("Matte", &obj1);
4574       if (obj1.isArray()) {
4575         if (obj1.getArray()->getLength() != colorSpace->getNComps()) {
4576           error(errSyntaxError, -1, "Matte entry should have {0:d} components but has {1:d}",
4577             colorSpace->getNComps(), obj1.getArray()->getLength());
4578         } else if (maskWidth != width || maskHeight != height) {
4579           error(errSyntaxError, -1, "Softmask with matte entry {0:d} x {1:d} must have same geometry as the image {2:d} x {3:d}",
4580             maskWidth, maskHeight, width, height);
4581         } else {
4582           GfxColor matteColor;
4583           for (i = 0; i < colorSpace->getNComps(); i++) {
4584             obj1.getArray()->get(i, &obj2);
4585             if (!obj2.isNum()) {
4586               obj2.free();
4587               error(errSyntaxError, -1, "Matte entry {0:d} should be a number but it's of type {1:d}", i, obj2.getType());
4588 
4589               break;
4590             }
4591             matteColor.c[i] = dblToCol(obj2.getNum());
4592             obj2.free();
4593           }
4594           if (i == colorSpace->getNComps()) {
4595             maskColorMap->setMatteColor(&matteColor);
4596           }
4597         }
4598       }
4599       obj1.free();
4600       haveSoftMask = gTrue;
4601     } else if (maskObj.isArray()) {
4602       // color key mask
4603       for (i = 0;
4604 	   i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
4605 	   ++i) {
4606 	maskObj.arrayGet(i, &obj1);
4607 	if (obj1.isInt()) {
4608 	  maskColors[i] = obj1.getInt();
4609 	} else if (obj1.isReal()) {
4610 	  error(errSyntaxError, -1, "Mask entry should be an integer but it's a real, trying to use it");
4611 	  maskColors[i] = (int) obj1.getReal();
4612 	} else {
4613 	  error(errSyntaxError, -1, "Mask entry should be an integer but it's of type {0:d}", obj1.getType());
4614 	  obj1.free();
4615 	  goto err1;
4616 	}
4617 	obj1.free();
4618       }
4619       haveColorKeyMask = gTrue;
4620     } else if (maskObj.isStream()) {
4621       // explicit mask
4622       if (inlineImg) {
4623 	goto err1;
4624       }
4625       maskStr = maskObj.getStream();
4626       maskDict = maskObj.streamGetDict();
4627       maskDict->lookup("Width", &obj1);
4628       if (obj1.isNull()) {
4629 	obj1.free();
4630 	maskDict->lookup("W", &obj1);
4631       }
4632       if (!obj1.isInt()) {
4633 	goto err2;
4634       }
4635       maskWidth = obj1.getInt();
4636       obj1.free();
4637       maskDict->lookup("Height", &obj1);
4638       if (obj1.isNull()) {
4639 	obj1.free();
4640 	maskDict->lookup("H", &obj1);
4641       }
4642       if (!obj1.isInt()) {
4643 	goto err2;
4644       }
4645       maskHeight = obj1.getInt();
4646       obj1.free();
4647       maskDict->lookup("Interpolate", &obj1);
4648       if (obj1.isNull()) {
4649         obj1.free();
4650 	maskDict->lookup("I", &obj1);
4651       }
4652       if (obj1.isBool())
4653         maskInterpolate = obj1.getBool();
4654       else
4655         maskInterpolate = gFalse;
4656       obj1.free();
4657       maskDict->lookup("ImageMask", &obj1);
4658       if (obj1.isNull()) {
4659 	obj1.free();
4660 	maskDict->lookup("IM", &obj1);
4661       }
4662       if (!obj1.isBool() || !obj1.getBool()) {
4663 	goto err2;
4664       }
4665       obj1.free();
4666       maskInvert = gFalse;
4667       maskDict->lookup("Decode", &obj1);
4668       if (obj1.isNull()) {
4669 	obj1.free();
4670 	maskDict->lookup("D", &obj1);
4671       }
4672       if (obj1.isArray()) {
4673 	obj1.arrayGet(0, &obj2);
4674 	// Table 4.39 says /Decode must be [1 0] or [0 1]. Adobe
4675 	// accepts [1.0 0.0] as well.
4676 	if (obj2.isNum() && obj2.getNum() >= 0.9) {
4677 	  maskInvert = gTrue;
4678 	}
4679 	obj2.free();
4680       } else if (!obj1.isNull()) {
4681 	goto err2;
4682       }
4683       obj1.free();
4684       haveExplicitMask = gTrue;
4685     }
4686 
4687     // if drawing is disabled, skip over inline image data
4688     if (!ocState || !out->needNonText()) {
4689       str->reset();
4690       n = height * ((width * colorMap->getNumPixelComps() *
4691 		     colorMap->getBits() + 7) / 8);
4692       for (i = 0; i < n; ++i) {
4693 	str->getChar();
4694       }
4695       str->close();
4696 
4697     // draw it
4698     } else {
4699       if (haveSoftMask) {
4700 	out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
4701 				 maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
4702 	delete maskColorMap;
4703       } else if (haveExplicitMask) {
4704 	out->drawMaskedImage(state, ref, str, width, height, colorMap, interpolate,
4705 			     maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate);
4706       } else {
4707 	out->drawImage(state, ref, str, width, height, colorMap, interpolate,
4708 		       haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
4709       }
4710     }
4711     delete colorMap;
4712 
4713     maskObj.free();
4714     smaskObj.free();
4715   }
4716 
4717   if ((i = width * height) > 1000) {
4718     i = 1000;
4719   }
4720   updateLevel += i;
4721 
4722   return;
4723 
4724  err2:
4725   obj1.free();
4726  err1:
4727   error(errSyntaxError, getPos(), "Bad image parameters");
4728 }
4729 
checkTransparencyGroup(Dict * resDict)4730 GBool Gfx::checkTransparencyGroup(Dict *resDict) {
4731   // check the effect of compositing objects as a group:
4732   // look for ExtGState entries with ca != 1 or CA != 1 or BM != normal
4733   Object extGStates;
4734   GBool transpGroup = gFalse;
4735   double opac;
4736 
4737   if (resDict == NULL)
4738     return gFalse;
4739   pushResources(resDict);
4740   resDict->lookup("ExtGState", &extGStates);
4741   if (extGStates.isDict()) {
4742     Dict *dict = extGStates.getDict();
4743     for (int i = 0; i < dict->getLength() && !transpGroup; i++) {
4744       Object obj1, obj2;
4745       GfxBlendMode mode;
4746 
4747       if (res->lookupGState(dict->getKey(i), &obj1) && obj1.isDict()) {
4748         if (!obj1.dictLookup("BM", &obj2)->isNull()) {
4749           if (state->parseBlendMode(&obj2, &mode)) {
4750             if (mode != gfxBlendNormal)
4751               transpGroup = gTrue;
4752           } else {
4753             error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState");
4754           }
4755         }
4756         obj2.free();
4757         if (obj1.dictLookup("ca", &obj2)->isNum()) {
4758           opac = obj2.getNum();
4759           opac = opac < 0 ? 0 : opac > 1 ? 1 : opac;
4760           if (opac != 1)
4761             transpGroup = gTrue;
4762         }
4763         obj2.free();
4764         if (obj1.dictLookup("CA", &obj2)->isNum()) {
4765           opac = obj2.getNum();
4766           opac = opac < 0 ? 0 : opac > 1 ? 1 : opac;
4767           if (opac != 1)
4768             transpGroup = gTrue;
4769         }
4770         obj2.free();
4771         // alpha is shape
4772         if (!transpGroup && obj1.dictLookup("AIS", &obj2)->isBool()) {
4773           transpGroup = obj2.getBool();
4774         }
4775         obj2.free();
4776         // soft mask
4777         if (!transpGroup && !obj1.dictLookup("SMask", &obj2)->isNull()) {
4778           if (!obj2.isName("None")) {
4779             transpGroup = gTrue;
4780           }
4781         }
4782         obj2.free();
4783       }
4784       obj1.free();
4785     }
4786   }
4787   extGStates.free();
4788   popResources();
4789   return transpGroup;
4790 }
4791 
doForm(Object * str)4792 void Gfx::doForm(Object *str) {
4793   Dict *dict;
4794   GBool transpGroup, isolated, knockout;
4795   GfxColorSpace *blendingColorSpace;
4796   Object matrixObj, bboxObj;
4797   double m[6], bbox[4];
4798   Object resObj;
4799   Dict *resDict;
4800   GBool ocSaved;
4801   Object obj1, obj2, obj3;
4802   int i;
4803 
4804   // check for excessive recursion
4805   if (formDepth > 100) {
4806     return;
4807   }
4808 
4809   // get stream dict
4810   dict = str->streamGetDict();
4811 
4812   // check form type
4813   dict->lookup("FormType", &obj1);
4814   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
4815     error(errSyntaxError, getPos(), "Unknown form type");
4816   }
4817   obj1.free();
4818 
4819   // check for optional content key
4820   ocSaved = ocState;
4821   dict->lookupNF("OC", &obj1);
4822   if (catalog->getOptContentConfig() && !catalog->getOptContentConfig()->optContentIsVisible(&obj1)) {
4823     obj1.free();
4824     if (out->needCharCount()) {
4825       ocState = gFalse;
4826     } else {
4827       return;
4828     }
4829   }
4830   obj1.free();
4831 
4832   // get bounding box
4833   dict->lookup("BBox", &bboxObj);
4834   if (!bboxObj.isArray()) {
4835     bboxObj.free();
4836     error(errSyntaxError, getPos(), "Bad form bounding box");
4837     ocState = ocSaved;
4838     return;
4839   }
4840   for (i = 0; i < 4; ++i) {
4841     bboxObj.arrayGet(i, &obj1);
4842     if (likely(obj1.isNum())) {
4843       bbox[i] = obj1.getNum();
4844       obj1.free();
4845     } else {
4846       obj1.free();
4847       error(errSyntaxError, getPos(), "Bad form bounding box value");
4848       return;
4849     }
4850   }
4851   bboxObj.free();
4852 
4853   // get matrix
4854   dict->lookup("Matrix", &matrixObj);
4855   if (matrixObj.isArray()) {
4856     for (i = 0; i < 6; ++i) {
4857       matrixObj.arrayGet(i, &obj1);
4858       if (likely(obj1.isNum())) m[i] = obj1.getNum();
4859       else m[i] = 0;
4860       obj1.free();
4861     }
4862   } else {
4863     m[0] = 1; m[1] = 0;
4864     m[2] = 0; m[3] = 1;
4865     m[4] = 0; m[5] = 0;
4866   }
4867   matrixObj.free();
4868 
4869   // get resources
4870   dict->lookup("Resources", &resObj);
4871   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4872 
4873   // check for a transparency group
4874   transpGroup = isolated = knockout = gFalse;
4875   blendingColorSpace = NULL;
4876   if (dict->lookup("Group", &obj1)->isDict()) {
4877     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
4878       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
4879 	blendingColorSpace = GfxColorSpace::parse(res, &obj3, out, state);
4880       }
4881       obj3.free();
4882       if (obj1.dictLookup("I", &obj3)->isBool()) {
4883 	isolated = obj3.getBool();
4884       }
4885       obj3.free();
4886       if (obj1.dictLookup("K", &obj3)->isBool()) {
4887 	knockout = obj3.getBool();
4888       }
4889       obj3.free();
4890       transpGroup = isolated || out->checkTransparencyGroup(state, knockout) || checkTransparencyGroup(resDict);
4891     }
4892     obj2.free();
4893   }
4894   obj1.free();
4895 
4896   // draw it
4897   ++formDepth;
4898   drawForm(str, resDict, m, bbox,
4899 	  transpGroup, gFalse, blendingColorSpace, isolated, knockout);
4900   --formDepth;
4901 
4902   if (blendingColorSpace) {
4903     delete blendingColorSpace;
4904   }
4905   resObj.free();
4906 
4907   ocState = ocSaved;
4908 }
4909 
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)4910 void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
4911 		  GBool transpGroup, GBool softMask,
4912 		  GfxColorSpace *blendingColorSpace,
4913 		  GBool isolated, GBool knockout,
4914 		  GBool alpha, Function *transferFunc,
4915 		  GfxColor *backdropColor) {
4916   Parser *oldParser;
4917   GfxState *savedState;
4918   double oldBaseMatrix[6];
4919   int i;
4920 
4921   // push new resources on stack
4922   pushResources(resDict);
4923 
4924   // save current graphics state
4925   savedState = saveStateStack();
4926 
4927   // kill any pre-existing path
4928   state->clearPath();
4929 
4930   // save current parser
4931   oldParser = parser;
4932 
4933   // set form transformation matrix
4934   state->concatCTM(matrix[0], matrix[1], matrix[2],
4935 		   matrix[3], matrix[4], matrix[5]);
4936   out->updateCTM(state, matrix[0], matrix[1], matrix[2],
4937 		 matrix[3], matrix[4], matrix[5]);
4938 
4939   // set form bounding box
4940   state->moveTo(bbox[0], bbox[1]);
4941   state->lineTo(bbox[2], bbox[1]);
4942   state->lineTo(bbox[2], bbox[3]);
4943   state->lineTo(bbox[0], bbox[3]);
4944   state->closePath();
4945   state->clip();
4946   out->clip(state);
4947   state->clearPath();
4948 
4949   if (softMask || transpGroup) {
4950     if (state->getBlendMode() != gfxBlendNormal) {
4951       state->setBlendMode(gfxBlendNormal);
4952       out->updateBlendMode(state);
4953     }
4954     if (state->getFillOpacity() != 1) {
4955       state->setFillOpacity(1);
4956       out->updateFillOpacity(state);
4957     }
4958     if (state->getStrokeOpacity() != 1) {
4959       state->setStrokeOpacity(1);
4960       out->updateStrokeOpacity(state);
4961     }
4962     out->clearSoftMask(state);
4963     out->beginTransparencyGroup(state, bbox, blendingColorSpace,
4964 				isolated, knockout, softMask);
4965   }
4966 
4967   // set new base matrix
4968   for (i = 0; i < 6; ++i) {
4969     oldBaseMatrix[i] = baseMatrix[i];
4970     baseMatrix[i] = state->getCTM()[i];
4971   }
4972 
4973   GfxState *stateBefore = state;
4974 
4975   // draw the form
4976   display(str, gFalse);
4977 
4978   if (stateBefore != state) {
4979     if (state->isParentState(stateBefore)) {
4980       error(errSyntaxError, -1, "There's a form with more q than Q, trying to fix");
4981       while (stateBefore != state) {
4982         restoreState();
4983       }
4984     } else {
4985       error(errSyntaxError, -1, "There's a form with more Q than q");
4986     }
4987   }
4988 
4989   if (softMask || transpGroup) {
4990     out->endTransparencyGroup(state);
4991   }
4992 
4993   // restore base matrix
4994   for (i = 0; i < 6; ++i) {
4995     baseMatrix[i] = oldBaseMatrix[i];
4996   }
4997 
4998   // restore parser
4999   parser = oldParser;
5000 
5001   // restore graphics state
5002   restoreStateStack(savedState);
5003 
5004   // pop resource stack
5005   popResources();
5006 
5007   if (softMask) {
5008     out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
5009   } else if (transpGroup) {
5010     out->paintTransparencyGroup(state, bbox);
5011   }
5012 
5013   return;
5014 }
5015 
5016 //------------------------------------------------------------------------
5017 // in-line image operators
5018 //------------------------------------------------------------------------
5019 
opBeginImage(Object args[],int numArgs)5020 void Gfx::opBeginImage(Object args[], int numArgs) {
5021   Stream *str;
5022   int c1, c2;
5023 
5024   // NB: this function is run even if ocState is false -- doImage() is
5025   // responsible for skipping over the inline image data
5026 
5027   // build dict/stream
5028   str = buildImageStream();
5029 
5030   // display the image
5031   if (str) {
5032     doImage(NULL, str, gTrue);
5033 
5034     // skip 'EI' tag
5035     c1 = str->getUndecodedStream()->getChar();
5036     c2 = str->getUndecodedStream()->getChar();
5037     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
5038       c1 = c2;
5039       c2 = str->getUndecodedStream()->getChar();
5040     }
5041     delete str;
5042   }
5043 }
5044 
buildImageStream()5045 Stream *Gfx::buildImageStream() {
5046   Object dict;
5047   Object obj;
5048   char *key;
5049   Stream *str;
5050 
5051   // build dictionary
5052   dict.initDict(xref);
5053   parser->getObj(&obj);
5054   while (!obj.isCmd("ID") && !obj.isEOF()) {
5055     if (!obj.isName()) {
5056       error(errSyntaxError, getPos(), "Inline image dictionary key must be a name object");
5057       obj.free();
5058     } else {
5059       key = copyString(obj.getName());
5060       obj.free();
5061       parser->getObj(&obj);
5062       if (obj.isEOF() || obj.isError()) {
5063 	gfree(key);
5064 	break;
5065       }
5066       dict.dictAdd(key, &obj);
5067     }
5068     parser->getObj(&obj);
5069   }
5070   if (obj.isEOF()) {
5071     error(errSyntaxError, getPos(), "End of file in inline image");
5072     obj.free();
5073     dict.free();
5074     return NULL;
5075   }
5076   obj.free();
5077 
5078   // make stream
5079   if (parser->getStream()) {
5080     str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
5081     str = str->addFilters(&dict);
5082   } else {
5083     str = NULL;
5084     dict.free();
5085   }
5086 
5087   return str;
5088 }
5089 
opImageData(Object args[],int numArgs)5090 void Gfx::opImageData(Object args[], int numArgs) {
5091   error(errInternal, getPos(), "Got 'ID' operator");
5092 }
5093 
opEndImage(Object args[],int numArgs)5094 void Gfx::opEndImage(Object args[], int numArgs) {
5095   error(errInternal, getPos(), "Got 'EI' operator");
5096 }
5097 
5098 //------------------------------------------------------------------------
5099 // type 3 font operators
5100 //------------------------------------------------------------------------
5101 
opSetCharWidth(Object args[],int numArgs)5102 void Gfx::opSetCharWidth(Object args[], int numArgs) {
5103   out->type3D0(state, args[0].getNum(), args[1].getNum());
5104 }
5105 
opSetCacheDevice(Object args[],int numArgs)5106 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
5107   out->type3D1(state, args[0].getNum(), args[1].getNum(),
5108 	       args[2].getNum(), args[3].getNum(),
5109 	       args[4].getNum(), args[5].getNum());
5110 }
5111 
5112 //------------------------------------------------------------------------
5113 // compatibility operators
5114 //------------------------------------------------------------------------
5115 
opBeginIgnoreUndef(Object args[],int numArgs)5116 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
5117   ++ignoreUndef;
5118 }
5119 
opEndIgnoreUndef(Object args[],int numArgs)5120 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
5121   if (ignoreUndef > 0)
5122     --ignoreUndef;
5123 }
5124 
5125 //------------------------------------------------------------------------
5126 // marked content operators
5127 //------------------------------------------------------------------------
5128 
5129 enum GfxMarkedContentKind {
5130   gfxMCOptionalContent,
5131   gfxMCActualText,
5132   gfxMCOther
5133 };
5134 
5135 struct MarkedContentStack {
5136   GfxMarkedContentKind kind;
5137   GBool ocSuppressed;       // are we ignoring content based on OptionalContent?
5138   MarkedContentStack *next; // next object on stack
5139 };
5140 
popMarkedContent()5141 void Gfx::popMarkedContent() {
5142   MarkedContentStack *mc = mcStack;
5143   mcStack = mc->next;
5144   delete mc;
5145 }
5146 
pushMarkedContent()5147 void Gfx::pushMarkedContent() {
5148   MarkedContentStack *mc = new MarkedContentStack();
5149   mc->ocSuppressed = gFalse;
5150   mc->kind = gfxMCOther;
5151   mc->next = mcStack;
5152   mcStack = mc;
5153 }
5154 
contentIsHidden()5155 GBool Gfx::contentIsHidden() {
5156   MarkedContentStack *mc = mcStack;
5157   bool hidden = mc && mc->ocSuppressed;
5158   while (!hidden && mc && mc->next) {
5159     mc = mc->next;
5160     hidden = mc->ocSuppressed;
5161   }
5162   return hidden;
5163 }
5164 
opBeginMarkedContent(Object args[],int numArgs)5165 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
5166   // push a new stack entry
5167   pushMarkedContent();
5168 
5169   OCGs *contentConfig = catalog->getOptContentConfig();
5170   char* name0 = args[0].getName();
5171   if ( strncmp( name0, "OC", 2) == 0 && contentConfig) {
5172     if ( numArgs >= 2 ) {
5173       if (!args[1].isName()) {
5174 	error(errSyntaxError, getPos(), "Unexpected MC Type: {0:d}", args[1].getType());
5175       }
5176       char* name1 = args[1].getName();
5177       Object markedContent;
5178       MarkedContentStack *mc = mcStack;
5179       mc->kind = gfxMCOptionalContent;
5180       if ( res->lookupMarkedContentNF( name1, &markedContent ) ) {
5181         bool visible = contentConfig->optContentIsVisible(&markedContent);
5182         mc->ocSuppressed = !(visible);
5183       } else {
5184 	error(errSyntaxError, getPos(), "DID NOT find {0:s}", name1);
5185       }
5186       markedContent.free();
5187     } else {
5188       error(errSyntaxError, getPos(), "insufficient arguments for Marked Content");
5189     }
5190   } else if (args[0].isName("Span") && numArgs == 2 && args[1].isDict()) {
5191     Object obj;
5192     if (args[1].dictLookup("ActualText", &obj)->isString()) {
5193       out->beginActualText(state, obj.getString());
5194       MarkedContentStack *mc = mcStack;
5195       mc->kind = gfxMCActualText;
5196     }
5197     obj.free();
5198   }
5199 
5200   if (printCommands) {
5201     printf("  marked content: %s ", args[0].getName());
5202     if (numArgs == 2)
5203       args[1].print(stdout);
5204     printf("\n");
5205     fflush(stdout);
5206   }
5207   ocState = !contentIsHidden();
5208 
5209   if (numArgs == 2 && args[1].isDict()) {
5210     out->beginMarkedContent(args[0].getName(), args[1].getDict());
5211   } else if(numArgs == 1) {
5212     out->beginMarkedContent(args[0].getName(), NULL);
5213   }
5214 }
5215 
opEndMarkedContent(Object args[],int numArgs)5216 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
5217   if (!mcStack) {
5218     error(errSyntaxWarning, getPos(), "Mismatched EMC operator");
5219     return;
5220   }
5221 
5222   MarkedContentStack *mc = mcStack;
5223   GfxMarkedContentKind mcKind = mc->kind;
5224 
5225   // pop the stack
5226   popMarkedContent();
5227 
5228   if (mcKind == gfxMCActualText)
5229     out->endActualText(state);
5230   ocState = !contentIsHidden();
5231 
5232   out->endMarkedContent(state);
5233 }
5234 
opMarkPoint(Object args[],int numArgs)5235 void Gfx::opMarkPoint(Object args[], int numArgs) {
5236   if (printCommands) {
5237     printf("  mark point: %s ", args[0].getName());
5238     if (numArgs == 2)
5239       args[1].print(stdout);
5240     printf("\n");
5241     fflush(stdout);
5242   }
5243 
5244   if(numArgs == 2 && args[1].isDict()) {
5245     out->markPoint(args[0].getName(),args[1].getDict());
5246   } else {
5247     out->markPoint(args[0].getName());
5248   }
5249 
5250 }
5251 
5252 //------------------------------------------------------------------------
5253 // misc
5254 //------------------------------------------------------------------------
5255 
5256 struct GfxStackStateSaver {
GfxStackStateSaverGfxStackStateSaver5257   GfxStackStateSaver(Gfx *gfx) : gfx(gfx) {
5258     gfx->saveState();
5259   }
5260 
~GfxStackStateSaverGfxStackStateSaver5261   ~GfxStackStateSaver() {
5262     gfx->restoreState();
5263   }
5264 
5265   Gfx * const gfx;
5266 };
5267 
drawAnnot(Object * str,AnnotBorder * border,AnnotColor * aColor,double xMin,double yMin,double xMax,double yMax,int rotate)5268 void Gfx::drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor,
5269 		    double xMin, double yMin, double xMax, double yMax, int rotate) {
5270   Dict *dict, *resDict;
5271   Object matrixObj, bboxObj, resObj, obj1;
5272   double formXMin, formYMin, formXMax, formYMax;
5273   double x, y, sx, sy, tx, ty;
5274   double m[6], bbox[4];
5275   double r, g, b;
5276   GfxColor color;
5277   double *dash, *dash2;
5278   int dashLength;
5279   int i;
5280 
5281   // this function assumes that we are in the default user space,
5282   // i.e., baseMatrix = ctm
5283 
5284   // if the bounding box has zero width or height, don't draw anything
5285   // at all
5286   if (xMin == xMax || yMin == yMax) {
5287     return;
5288   }
5289 
5290   // saves gfx state and automatically restores it on return
5291   GfxStackStateSaver stackStateSaver(this);
5292 
5293   // Rotation around the topleft corner (for the NoRotate flag)
5294   if (rotate != 0) {
5295     const double angle_rad = rotate * M_PI / 180;
5296     const double c = cos(angle_rad);
5297     const double s = sin(angle_rad);
5298 
5299     // (xMin, yMax) is the pivot
5300     const double unrotateMTX[6] = {
5301         +c, -s,
5302         +s, +c,
5303         -c*xMin - s*yMax + xMin, -c*yMax + s*xMin + yMax
5304     };
5305 
5306     state->concatCTM(unrotateMTX[0], unrotateMTX[1], unrotateMTX[2],
5307                      unrotateMTX[3], unrotateMTX[4], unrotateMTX[5]);
5308     out->updateCTM(state, unrotateMTX[0], unrotateMTX[1], unrotateMTX[2],
5309                           unrotateMTX[3], unrotateMTX[4], unrotateMTX[5]);
5310   }
5311 
5312   // draw the appearance stream (if there is one)
5313   if (str->isStream()) {
5314 
5315     // get stream dict
5316     dict = str->streamGetDict();
5317 
5318     // get the form bounding box
5319     dict->lookup("BBox", &bboxObj);
5320     if (!bboxObj.isArray()) {
5321       bboxObj.free();
5322       error(errSyntaxError, getPos(), "Bad form bounding box");
5323       return;
5324     }
5325     for (i = 0; i < 4; ++i) {
5326       bboxObj.arrayGet(i, &obj1);
5327       if (likely(obj1.isNum())) {
5328         bbox[i] = obj1.getNum();
5329         obj1.free();
5330       } else {
5331         obj1.free();
5332         bboxObj.free();
5333         error(errSyntaxError, getPos(), "Bad form bounding box value");
5334         return;
5335       }
5336     }
5337     bboxObj.free();
5338 
5339     // get the form matrix
5340     dict->lookup("Matrix", &matrixObj);
5341     if (matrixObj.isArray() && matrixObj.arrayGetLength() >= 6) {
5342       for (i = 0; i < 6; ++i) {
5343 	matrixObj.arrayGet(i, &obj1);
5344 	if (likely(obj1.isNum())) {
5345 	  m[i] = obj1.getNum();
5346 	  obj1.free();
5347 	} else {
5348 	  obj1.free();
5349 	  matrixObj.free();
5350 	  error(errSyntaxError, getPos(), "Bad form matrix");
5351 	  return;
5352 	}
5353       }
5354     } else {
5355       m[0] = 1; m[1] = 0;
5356       m[2] = 0; m[3] = 1;
5357       m[4] = 0; m[5] = 0;
5358     }
5359     matrixObj.free();
5360 
5361     // transform the four corners of the form bbox to default user
5362     // space, and construct the transformed bbox
5363     x = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
5364     y = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
5365     formXMin = formXMax = x;
5366     formYMin = formYMax = y;
5367     x = bbox[0] * m[0] + bbox[3] * m[2] + m[4];
5368     y = bbox[0] * m[1] + bbox[3] * m[3] + m[5];
5369     if (x < formXMin) {
5370       formXMin = x;
5371     } else if (x > formXMax) {
5372       formXMax = x;
5373     }
5374     if (y < formYMin) {
5375       formYMin = y;
5376     } else if (y > formYMax) {
5377       formYMax = y;
5378     }
5379     x = bbox[2] * m[0] + bbox[1] * m[2] + m[4];
5380     y = bbox[2] * m[1] + bbox[1] * m[3] + m[5];
5381     if (x < formXMin) {
5382       formXMin = x;
5383     } else if (x > formXMax) {
5384       formXMax = x;
5385     }
5386     if (y < formYMin) {
5387       formYMin = y;
5388     } else if (y > formYMax) {
5389       formYMax = y;
5390     }
5391     x = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
5392     y = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
5393     if (x < formXMin) {
5394       formXMin = x;
5395     } else if (x > formXMax) {
5396       formXMax = x;
5397     }
5398     if (y < formYMin) {
5399       formYMin = y;
5400     } else if (y > formYMax) {
5401       formYMax = y;
5402     }
5403 
5404     // construct a mapping matrix, [sx 0  0], which maps the transformed
5405     //                             [0  sy 0]
5406     //                             [tx ty 1]
5407     // bbox to the annotation rectangle
5408     if (formXMin == formXMax) {
5409       // this shouldn't happen
5410       sx = 1;
5411     } else {
5412       sx = (xMax - xMin) / (formXMax - formXMin);
5413     }
5414     if (formYMin == formYMax) {
5415       // this shouldn't happen
5416       sy = 1;
5417     } else {
5418       sy = (yMax - yMin) / (formYMax - formYMin);
5419     }
5420     tx = -formXMin * sx + xMin;
5421     ty = -formYMin * sy + yMin;
5422 
5423     // the final transform matrix is (form matrix) * (mapping matrix)
5424     m[0] *= sx;
5425     m[1] *= sy;
5426     m[2] *= sx;
5427     m[3] *= sy;
5428     m[4] = m[4] * sx + tx;
5429     m[5] = m[5] * sy + ty;
5430 
5431     // get the resources
5432     dict->lookup("Resources", &resObj);
5433     resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
5434 
5435     // draw it
5436     drawForm(str, resDict, m, bbox);
5437 
5438     resObj.free();
5439   }
5440 
5441   // draw the border
5442   if (border && border->getWidth() > 0) {
5443     if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
5444       state->setStrokePattern(NULL);
5445       state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
5446       out->updateStrokeColorSpace(state);
5447     }
5448     if (aColor && (aColor->getSpace() == AnnotColor::colorRGB)) {
5449       const double *values = aColor->getValues();
5450       r = values[0];
5451       g = values[1];
5452       b = values[2];
5453     } else {
5454       r = g = b = 0;
5455     };
5456     color.c[0] = dblToCol(r);
5457     color.c[1] = dblToCol(g);
5458     color.c[2] = dblToCol(b);
5459     state->setStrokeColor(&color);
5460     out->updateStrokeColor(state);
5461     state->setLineWidth(border->getWidth());
5462     out->updateLineWidth(state);
5463     dashLength = border->getDashLength();
5464     dash = border->getDash();
5465     if (border->getStyle() == AnnotBorder::borderDashed && dashLength > 0) {
5466       dash2 = (double *)gmallocn(dashLength, sizeof(double));
5467       memcpy(dash2, dash, dashLength * sizeof(double));
5468       state->setLineDash(dash2, dashLength, 0);
5469       out->updateLineDash(state);
5470     }
5471     //~ this doesn't currently handle the beveled and engraved styles
5472     state->clearPath();
5473     state->moveTo(xMin, yMin);
5474     state->lineTo(xMax, yMin);
5475     if (border->getStyle() != AnnotBorder::borderUnderlined) {
5476       state->lineTo(xMax, yMax);
5477       state->lineTo(xMin, yMax);
5478       state->closePath();
5479     }
5480     out->stroke(state);
5481   }
5482 }
5483 
bottomGuard()5484 int Gfx::bottomGuard() {
5485     return stateGuards[stateGuards.size()-1];
5486 }
5487 
pushStateGuard()5488 void Gfx::pushStateGuard() {
5489     stateGuards.push_back(stackHeight);
5490 }
5491 
popStateGuard()5492 void Gfx::popStateGuard() {
5493     while (stackHeight > bottomGuard() && state->hasSaves())
5494 	restoreState();
5495     stateGuards.pop_back();
5496 }
5497 
saveState()5498 void Gfx::saveState() {
5499   out->saveState(state);
5500   state = state->save();
5501   stackHeight++;
5502 }
5503 
restoreState()5504 void Gfx::restoreState() {
5505   if (stackHeight <= bottomGuard() || !state->hasSaves()) {
5506     error(errSyntaxError, -1, "Restoring state when no valid states to pop");
5507     commandAborted = gTrue;
5508     return;
5509   }
5510   state = state->restore();
5511   out->restoreState(state);
5512   stackHeight--;
5513 }
5514 
5515 // Create a new state stack, and initialize it with a copy of the
5516 // current state.
saveStateStack()5517 GfxState *Gfx::saveStateStack() {
5518   GfxState *oldState;
5519 
5520   out->saveState(state);
5521   oldState = state;
5522   state = state->copy(gTrue);
5523   return oldState;
5524 }
5525 
5526 // Switch back to the previous state stack.
restoreStateStack(GfxState * oldState)5527 void Gfx::restoreStateStack(GfxState *oldState) {
5528   while (state->hasSaves()) {
5529     restoreState();
5530   }
5531   delete state;
5532   state = oldState;
5533   out->restoreState(state);
5534 }
5535 
pushResources(Dict * resDict)5536 void Gfx::pushResources(Dict *resDict) {
5537   res = new GfxResources(xref, resDict, res);
5538 }
5539 
popResources()5540 void Gfx::popResources() {
5541   GfxResources *resPtr;
5542 
5543   resPtr = res->getNext();
5544   delete res;
5545   res = resPtr;
5546 }
5547