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