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