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