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