# Correct the needed linklibs in curl-config also.
[AROS-Contrib.git] / arospdf / xpdf / Gfx.cc
blob3ba659b1bea53a7cfb2032e25c091d6b03e184be
1 //========================================================================
2 //
3 // Gfx.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 #include <aconf.h>
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
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 "GlobalParams.h"
22 #include "CharTypes.h"
23 #include "Object.h"
24 #include "Array.h"
25 #include "Dict.h"
26 #include "Stream.h"
27 #include "Lexer.h"
28 #include "Parser.h"
29 #include "GfxFont.h"
30 #include "GfxState.h"
31 #include "OutputDev.h"
32 #include "Page.h"
33 #include "Annot.h"
34 #include "Error.h"
35 #include "Gfx.h"
37 // the MSVC math.h doesn't define this
38 #ifndef M_PI
39 #define M_PI 3.14159265358979323846
40 #endif
42 //------------------------------------------------------------------------
43 // constants
44 //------------------------------------------------------------------------
46 // Max recursive depth for a function shading fill.
47 #define functionMaxDepth 6
49 // Max delta allowed in any color component for a function shading fill.
50 #define functionColorDelta (dblToCol(1 / 256.0))
52 // Max number of splits along the t axis for an axial shading fill.
53 #define axialMaxSplits 256
55 // Max delta allowed in any color component for an axial shading fill.
56 #define axialColorDelta (dblToCol(1 / 256.0))
58 // Max number of splits along the t axis for a radial shading fill.
59 #define radialMaxSplits 256
61 // Max delta allowed in any color component for a radial shading fill.
62 #define radialColorDelta (dblToCol(1 / 256.0))
64 // Max recursive depth for a Gouraud triangle shading fill.
65 #define gouraudMaxDepth 6
67 // Max delta allowed in any color component for a Gouraud triangle
68 // shading fill.
69 #define gouraudColorDelta (dblToCol(1 / 256.0))
71 // Max recursive depth for a patch mesh shading fill.
72 #define patchMaxDepth 6
74 // Max delta allowed in any color component for a patch mesh shading
75 // fill.
76 #define patchColorDelta (dblToCol(1 / 256.0))
78 //------------------------------------------------------------------------
79 // Operator table
80 //------------------------------------------------------------------------
82 #ifdef WIN32 // this works around a bug in the VC7 compiler
83 # pragma optimize("",off)
84 #endif
86 Operator Gfx::opTab[] = {
87 {"\"", 3, {tchkNum, tchkNum, tchkString},
88 &Gfx::opMoveSetShowText},
89 {"'", 1, {tchkString},
90 &Gfx::opMoveShowText},
91 {"B", 0, {tchkNone},
92 &Gfx::opFillStroke},
93 {"B*", 0, {tchkNone},
94 &Gfx::opEOFillStroke},
95 {"BDC", 2, {tchkName, tchkProps},
96 &Gfx::opBeginMarkedContent},
97 {"BI", 0, {tchkNone},
98 &Gfx::opBeginImage},
99 {"BMC", 1, {tchkName},
100 &Gfx::opBeginMarkedContent},
101 {"BT", 0, {tchkNone},
102 &Gfx::opBeginText},
103 {"BX", 0, {tchkNone},
104 &Gfx::opBeginIgnoreUndef},
105 {"CS", 1, {tchkName},
106 &Gfx::opSetStrokeColorSpace},
107 {"DP", 2, {tchkName, tchkProps},
108 &Gfx::opMarkPoint},
109 {"Do", 1, {tchkName},
110 &Gfx::opXObject},
111 {"EI", 0, {tchkNone},
112 &Gfx::opEndImage},
113 {"EMC", 0, {tchkNone},
114 &Gfx::opEndMarkedContent},
115 {"ET", 0, {tchkNone},
116 &Gfx::opEndText},
117 {"EX", 0, {tchkNone},
118 &Gfx::opEndIgnoreUndef},
119 {"F", 0, {tchkNone},
120 &Gfx::opFill},
121 {"G", 1, {tchkNum},
122 &Gfx::opSetStrokeGray},
123 {"ID", 0, {tchkNone},
124 &Gfx::opImageData},
125 {"J", 1, {tchkInt},
126 &Gfx::opSetLineCap},
127 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
128 &Gfx::opSetStrokeCMYKColor},
129 {"M", 1, {tchkNum},
130 &Gfx::opSetMiterLimit},
131 {"MP", 1, {tchkName},
132 &Gfx::opMarkPoint},
133 {"Q", 0, {tchkNone},
134 &Gfx::opRestore},
135 {"RG", 3, {tchkNum, tchkNum, tchkNum},
136 &Gfx::opSetStrokeRGBColor},
137 {"S", 0, {tchkNone},
138 &Gfx::opStroke},
139 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
140 &Gfx::opSetStrokeColor},
141 {"SCN", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
142 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
143 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
144 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
145 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
146 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
147 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
148 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
149 tchkSCN},
150 &Gfx::opSetStrokeColorN},
151 {"T*", 0, {tchkNone},
152 &Gfx::opTextNextLine},
153 {"TD", 2, {tchkNum, tchkNum},
154 &Gfx::opTextMoveSet},
155 {"TJ", 1, {tchkArray},
156 &Gfx::opShowSpaceText},
157 {"TL", 1, {tchkNum},
158 &Gfx::opSetTextLeading},
159 {"Tc", 1, {tchkNum},
160 &Gfx::opSetCharSpacing},
161 {"Td", 2, {tchkNum, tchkNum},
162 &Gfx::opTextMove},
163 {"Tf", 2, {tchkName, tchkNum},
164 &Gfx::opSetFont},
165 {"Tj", 1, {tchkString},
166 &Gfx::opShowText},
167 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
168 tchkNum, tchkNum},
169 &Gfx::opSetTextMatrix},
170 {"Tr", 1, {tchkInt},
171 &Gfx::opSetTextRender},
172 {"Ts", 1, {tchkNum},
173 &Gfx::opSetTextRise},
174 {"Tw", 1, {tchkNum},
175 &Gfx::opSetWordSpacing},
176 {"Tz", 1, {tchkNum},
177 &Gfx::opSetHorizScaling},
178 {"W", 0, {tchkNone},
179 &Gfx::opClip},
180 {"W*", 0, {tchkNone},
181 &Gfx::opEOClip},
182 {"b", 0, {tchkNone},
183 &Gfx::opCloseFillStroke},
184 {"b*", 0, {tchkNone},
185 &Gfx::opCloseEOFillStroke},
186 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
187 tchkNum, tchkNum},
188 &Gfx::opCurveTo},
189 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
190 tchkNum, tchkNum},
191 &Gfx::opConcat},
192 {"cs", 1, {tchkName},
193 &Gfx::opSetFillColorSpace},
194 {"d", 2, {tchkArray, tchkNum},
195 &Gfx::opSetDash},
196 {"d0", 2, {tchkNum, tchkNum},
197 &Gfx::opSetCharWidth},
198 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
199 tchkNum, tchkNum},
200 &Gfx::opSetCacheDevice},
201 {"f", 0, {tchkNone},
202 &Gfx::opFill},
203 {"f*", 0, {tchkNone},
204 &Gfx::opEOFill},
205 {"g", 1, {tchkNum},
206 &Gfx::opSetFillGray},
207 {"gs", 1, {tchkName},
208 &Gfx::opSetExtGState},
209 {"h", 0, {tchkNone},
210 &Gfx::opClosePath},
211 {"i", 1, {tchkNum},
212 &Gfx::opSetFlat},
213 {"j", 1, {tchkInt},
214 &Gfx::opSetLineJoin},
215 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
216 &Gfx::opSetFillCMYKColor},
217 {"l", 2, {tchkNum, tchkNum},
218 &Gfx::opLineTo},
219 {"m", 2, {tchkNum, tchkNum},
220 &Gfx::opMoveTo},
221 {"n", 0, {tchkNone},
222 &Gfx::opEndPath},
223 {"q", 0, {tchkNone},
224 &Gfx::opSave},
225 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
226 &Gfx::opRectangle},
227 {"rg", 3, {tchkNum, tchkNum, tchkNum},
228 &Gfx::opSetFillRGBColor},
229 {"ri", 1, {tchkName},
230 &Gfx::opSetRenderingIntent},
231 {"s", 0, {tchkNone},
232 &Gfx::opCloseStroke},
233 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
234 &Gfx::opSetFillColor},
235 {"scn", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
236 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
237 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
238 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
239 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
240 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
241 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
242 tchkSCN, tchkSCN, tchkSCN, tchkSCN,
243 tchkSCN},
244 &Gfx::opSetFillColorN},
245 {"sh", 1, {tchkName},
246 &Gfx::opShFill},
247 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
248 &Gfx::opCurveTo1},
249 {"w", 1, {tchkNum},
250 &Gfx::opSetLineWidth},
251 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
252 &Gfx::opCurveTo2},
255 #ifdef WIN32 // this works around a bug in the VC7 compiler
256 # pragma optimize("",on)
257 #endif
259 #define numOps (sizeof(opTab) / sizeof(Operator))
261 //------------------------------------------------------------------------
262 // GfxResources
263 //------------------------------------------------------------------------
265 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
266 xObject obj1, obj2;
267 Ref r;
269 if (resDict) {
271 // build font dictionary
272 fonts = NULL;
273 resDict->lookupNF("Font", &obj1);
274 if (obj1.isRef()) {
275 obj1.fetch(xref, &obj2);
276 if (obj2.isDict()) {
277 r = obj1.getRef();
278 fonts = new GfxFontDict(xref, &r, obj2.getDict());
280 obj2.free();
281 } else if (obj1.isDict()) {
282 fonts = new GfxFontDict(xref, NULL, obj1.getDict());
284 obj1.free();
286 // get XObject dictionary
287 resDict->lookup("XObject", &xObjDict);
289 // get color space dictionary
290 resDict->lookup("ColorSpace", &colorSpaceDict);
292 // get pattern dictionary
293 resDict->lookup("Pattern", &patternDict);
295 // get shading dictionary
296 resDict->lookup("Shading", &shadingDict);
298 // get graphics state parameter dictionary
299 resDict->lookup("ExtGState", &gStateDict);
301 } else {
302 fonts = NULL;
303 xObjDict.initNull();
304 colorSpaceDict.initNull();
305 patternDict.initNull();
306 shadingDict.initNull();
307 gStateDict.initNull();
310 next = nextA;
313 GfxResources::~GfxResources() {
314 if (fonts) {
315 delete fonts;
317 xObjDict.free();
318 colorSpaceDict.free();
319 patternDict.free();
320 shadingDict.free();
321 gStateDict.free();
324 GfxFont *GfxResources::lookupFont(char *name) {
325 GfxFont *font;
326 GfxResources *resPtr;
328 for (resPtr = this; resPtr; resPtr = resPtr->next) {
329 if (resPtr->fonts) {
330 if ((font = resPtr->fonts->lookup(name)))
331 return font;
334 error(-1, "Unknown font tag '%s'", name);
335 return NULL;
338 GBool GfxResources::lookupXObject(char *name, xObject *obj) {
339 GfxResources *resPtr;
341 for (resPtr = this; resPtr; resPtr = resPtr->next) {
342 if (resPtr->xObjDict.isDict()) {
343 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
344 return gTrue;
345 obj->free();
348 error(-1, "XObject '%s' is unknown", name);
349 return gFalse;
352 GBool GfxResources::lookupXObjectNF(char *name, xObject *obj) {
353 GfxResources *resPtr;
355 for (resPtr = this; resPtr; resPtr = resPtr->next) {
356 if (resPtr->xObjDict.isDict()) {
357 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
358 return gTrue;
359 obj->free();
362 error(-1, "XObject '%s' is unknown", name);
363 return gFalse;
366 void GfxResources::lookupColorSpace(char *name, xObject *obj) {
367 GfxResources *resPtr;
369 for (resPtr = this; resPtr; resPtr = resPtr->next) {
370 if (resPtr->colorSpaceDict.isDict()) {
371 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
372 return;
374 obj->free();
377 obj->initNull();
380 GfxPattern *GfxResources::lookupPattern(char *name) {
381 GfxResources *resPtr;
382 GfxPattern *pattern;
383 xObject obj;
385 for (resPtr = this; resPtr; resPtr = resPtr->next) {
386 if (resPtr->patternDict.isDict()) {
387 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
388 pattern = GfxPattern::parse(&obj);
389 obj.free();
390 return pattern;
392 obj.free();
395 error(-1, "Unknown pattern '%s'", name);
396 return NULL;
399 GfxShading *GfxResources::lookupShading(char *name) {
400 GfxResources *resPtr;
401 GfxShading *shading;
402 xObject obj;
404 for (resPtr = this; resPtr; resPtr = resPtr->next) {
405 if (resPtr->shadingDict.isDict()) {
406 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
407 shading = GfxShading::parse(&obj);
408 obj.free();
409 return shading;
411 obj.free();
414 error(-1, "Unknown shading '%s'", name);
415 return NULL;
418 GBool GfxResources::lookupGState(char *name, xObject *obj) {
419 GfxResources *resPtr;
421 for (resPtr = this; resPtr; resPtr = resPtr->next) {
422 if (resPtr->gStateDict.isDict()) {
423 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
424 return gTrue;
426 obj->free();
429 error(-1, "ExtGState '%s' is unknown", name);
430 return gFalse;
433 //------------------------------------------------------------------------
434 // Gfx
435 //------------------------------------------------------------------------
437 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
438 double hDPI, double vDPI, PDFRectangle *box,
439 PDFRectangle *cropBox, int rotate,
440 GBool (*abortCheckCbkA)(void *data),
441 void *abortCheckCbkDataA) {
442 int i;
444 xref = xrefA;
445 subPage = gFalse;
446 printCommands = globalParams->getPrintCommands();
448 // start the resource stack
449 res = new GfxResources(xref, resDict, NULL);
451 // initialize
452 out = outA;
453 state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
454 fontChanged = gFalse;
455 clip = clipNone;
456 ignoreUndef = 0;
457 out->startPage(pageNum, state);
458 out->setDefaultCTM(state->getCTM());
459 out->updateAll(state);
460 for (i = 0; i < 6; ++i) {
461 baseMatrix[i] = state->getCTM()[i];
463 formDepth = 0;
464 abortCheckCbk = abortCheckCbkA;
465 abortCheckCbkData = abortCheckCbkDataA;
467 // set crop box
468 if (cropBox) {
469 state->moveTo(cropBox->x1, cropBox->y1);
470 state->lineTo(cropBox->x2, cropBox->y1);
471 state->lineTo(cropBox->x2, cropBox->y2);
472 state->lineTo(cropBox->x1, cropBox->y2);
473 state->closePath();
474 state->clip();
475 out->clip(state);
476 state->clearPath();
480 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
481 PDFRectangle *box, PDFRectangle *cropBox,
482 GBool (*abortCheckCbkA)(void *data),
483 void *abortCheckCbkDataA) {
484 int i;
486 xref = xrefA;
487 subPage = gTrue;
488 printCommands = globalParams->getPrintCommands();
490 // start the resource stack
491 res = new GfxResources(xref, resDict, NULL);
493 // initialize
494 out = outA;
495 state = new GfxState(72, 72, box, 0, gFalse);
496 fontChanged = gFalse;
497 clip = clipNone;
498 ignoreUndef = 0;
499 for (i = 0; i < 6; ++i) {
500 baseMatrix[i] = state->getCTM()[i];
502 formDepth = 0;
503 abortCheckCbk = abortCheckCbkA;
504 abortCheckCbkData = abortCheckCbkDataA;
506 // set crop box
507 if (cropBox) {
508 state->moveTo(cropBox->x1, cropBox->y1);
509 state->lineTo(cropBox->x2, cropBox->y1);
510 state->lineTo(cropBox->x2, cropBox->y2);
511 state->lineTo(cropBox->x1, cropBox->y2);
512 state->closePath();
513 state->clip();
514 out->clip(state);
515 state->clearPath();
519 Gfx::~Gfx() {
520 while (state->hasSaves()) {
521 restoreState();
523 if (!subPage) {
524 out->endPage();
526 while (res) {
527 popResources();
529 if (state) {
530 delete state;
534 void Gfx::display(xObject *obj, GBool topLevel) {
535 xObject obj2;
536 int i;
538 if (obj->isArray()) {
539 for (i = 0; i < obj->arrayGetLength(); ++i) {
540 obj->arrayGet(i, &obj2);
541 if (!obj2.isStream()) {
542 error(-1, "Weird page contents");
543 obj2.free();
544 return;
546 obj2.free();
548 } else if (!obj->isStream()) {
549 error(-1, "Weird page contents");
550 return;
552 parser = new Parser(xref, new Lexer(xref, obj), gFalse);
553 go(topLevel);
554 delete parser;
555 parser = NULL;
558 void Gfx::go(GBool topLevel) {
559 xObject obj;
560 xObject args[maxArgs];
561 int numArgs, i;
562 int lastAbortCheck;
564 // scan a sequence of xObjects
565 updateLevel = lastAbortCheck = 0;
566 numArgs = 0;
567 parser->getObj(&obj);
568 while (!obj.isEOF()) {
570 // got a command - execute it
571 if (obj.isCmd()) {
572 if (printCommands) {
573 obj.print(stdout);
574 for (i = 0; i < numArgs; ++i) {
575 printf(" ");
576 args[i].print(stdout);
578 printf("\n");
579 fflush(stdout);
581 execOp(&obj, args, numArgs);
582 obj.free();
583 for (i = 0; i < numArgs; ++i)
584 args[i].free();
585 numArgs = 0;
587 // periodically update display
588 if (++updateLevel >= 20000) {
589 out->dump();
590 updateLevel = 0;
593 // check for an abort
594 if (abortCheckCbk) {
595 if (updateLevel - lastAbortCheck > 10) {
596 if ((*abortCheckCbk)(abortCheckCbkData)) {
597 break;
599 lastAbortCheck = updateLevel;
603 // got an argument - save it
604 } else if (numArgs < maxArgs) {
605 args[numArgs++] = obj;
607 // too many arguments - something is wrong
608 } else {
609 error(getPos(), "Too many args in content stream");
610 if (printCommands) {
611 printf("throwing away arg: ");
612 obj.print(stdout);
613 printf("\n");
614 fflush(stdout);
616 obj.free();
619 // grab the next xObject
620 parser->getObj(&obj);
622 obj.free();
624 // args at end with no command
625 if (numArgs > 0) {
626 error(getPos(), "Leftover args in content stream");
627 if (printCommands) {
628 printf("%d leftovers:", numArgs);
629 for (i = 0; i < numArgs; ++i) {
630 printf(" ");
631 args[i].print(stdout);
633 printf("\n");
634 fflush(stdout);
636 for (i = 0; i < numArgs; ++i)
637 args[i].free();
640 // update display
641 if (topLevel && updateLevel > 0) {
642 out->dump();
646 void Gfx::execOp(xObject *cmd, xObject args[], int numArgs) {
647 Operator *op;
648 char *name;
649 xObject *argPtr;
650 int i;
652 // find operator
653 name = cmd->getCmd();
654 if (!(op = findOp(name))) {
655 if (ignoreUndef == 0)
656 error(getPos(), "Unknown operator '%s'", name);
657 return;
660 // type check args
661 argPtr = args;
662 if (op->numArgs >= 0) {
663 if (numArgs < op->numArgs) {
664 error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
665 return;
667 if (numArgs > op->numArgs) {
668 #if 0
669 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
670 #endif
671 argPtr += numArgs - op->numArgs;
672 numArgs = op->numArgs;
674 } else {
675 if (numArgs > -op->numArgs) {
676 error(getPos(), "Too many (%d) args to '%s' operator",
677 numArgs, name);
678 return;
681 for (i = 0; i < numArgs; ++i) {
682 if (!checkArg(&argPtr[i], op->tchk[i])) {
683 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
684 i, name, argPtr[i].getTypeName());
685 return;
689 // do it
690 (this->*op->func)(argPtr, numArgs);
693 Operator *Gfx::findOp(char *name) {
694 int a, b, m, cmp;
696 a = -1;
697 b = numOps;
698 // invariant: opTab[a] < name < opTab[b]
699 while (b - a > 1) {
700 m = (a + b) / 2;
701 cmp = strcmp(opTab[m].name, name);
702 if (cmp < 0)
703 a = m;
704 else if (cmp > 0)
705 b = m;
706 else
707 a = b = m;
709 if (cmp != 0)
710 return NULL;
711 return &opTab[a];
714 GBool Gfx::checkArg(xObject *arg, TchkType type) {
715 switch (type) {
716 case tchkBool: return arg->isBool();
717 case tchkInt: return arg->isInt();
718 case tchkNum: return arg->isNum();
719 case tchkString: return arg->isString();
720 case tchkName: return arg->isName();
721 case tchkArray: return arg->isArray();
722 case tchkProps: return arg->isDict() || arg->isName();
723 case tchkSCN: return arg->isNum() || arg->isName();
724 case tchkNone: return gFalse;
726 return gFalse;
729 int Gfx::getPos() {
730 return parser ? parser->getPos() : -1;
733 //------------------------------------------------------------------------
734 // graphics state operators
735 //------------------------------------------------------------------------
737 void Gfx::opSave(xObject args[], int numArgs) {
738 saveState();
741 void Gfx::opRestore(xObject args[], int numArgs) {
742 restoreState();
745 void Gfx::opConcat(xObject args[], int numArgs) {
746 state->concatCTM(args[0].getNum(), args[1].getNum(),
747 args[2].getNum(), args[3].getNum(),
748 args[4].getNum(), args[5].getNum());
749 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
750 args[2].getNum(), args[3].getNum(),
751 args[4].getNum(), args[5].getNum());
752 fontChanged = gTrue;
755 void Gfx::opSetDash(xObject args[], int numArgs) {
756 Array *a;
757 int length;
758 xObject obj;
759 double *dash;
760 int i;
762 a = args[0].getArray();
763 length = a->getLength();
764 if (length == 0) {
765 dash = NULL;
766 } else {
767 dash = (double *)gmallocn(length, sizeof(double));
768 for (i = 0; i < length; ++i) {
769 dash[i] = a->get(i, &obj)->getNum();
770 obj.free();
773 state->setLineDash(dash, length, args[1].getNum());
774 out->updateLineDash(state);
777 void Gfx::opSetFlat(xObject args[], int numArgs) {
778 state->setFlatness((int)args[0].getNum());
779 out->updateFlatness(state);
782 void Gfx::opSetLineJoin(xObject args[], int numArgs) {
783 state->setLineJoin(args[0].getInt());
784 out->updateLineJoin(state);
787 void Gfx::opSetLineCap(xObject args[], int numArgs) {
788 state->setLineCap(args[0].getInt());
789 out->updateLineCap(state);
792 void Gfx::opSetMiterLimit(xObject args[], int numArgs) {
793 state->setMiterLimit(args[0].getNum());
794 out->updateMiterLimit(state);
797 void Gfx::opSetLineWidth(xObject args[], int numArgs) {
798 state->setLineWidth(args[0].getNum());
799 out->updateLineWidth(state);
802 void Gfx::opSetExtGState(xObject args[], int numArgs) {
803 xObject obj1, obj2, obj3, obj4, obj5;
804 GfxBlendMode mode;
805 GBool haveFillOP;
806 Function *funcs[4];
807 GfxColor backdropColor;
808 GBool haveBackdropColor;
809 GfxColorSpace *blendingColorSpace;
810 GBool alpha, isolated, knockout;
811 int i;
813 if (!res->lookupGState(args[0].getName(), &obj1)) {
814 return;
816 if (!obj1.isDict()) {
817 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
818 obj1.free();
819 return;
821 if (printCommands) {
822 printf(" gfx state dict: ");
823 obj1.print();
824 printf("\n");
827 // transparency support: blend mode, fill/stroke opacity
828 if (!obj1.dictLookup("BM", &obj2)->isNull()) {
829 if (state->parseBlendMode(&obj2, &mode)) {
830 state->setBlendMode(mode);
831 out->updateBlendMode(state);
832 } else {
833 error(getPos(), "Invalid blend mode in ExtGState");
836 obj2.free();
837 if (obj1.dictLookup("ca", &obj2)->isNum()) {
838 state->setFillOpacity(obj2.getNum());
839 out->updateFillOpacity(state);
841 obj2.free();
842 if (obj1.dictLookup("CA", &obj2)->isNum()) {
843 state->setStrokeOpacity(obj2.getNum());
844 out->updateStrokeOpacity(state);
846 obj2.free();
848 // fill/stroke overprint
849 if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
850 state->setFillOverprint(obj2.getBool());
851 out->updateFillOverprint(state);
853 obj2.free();
854 if (obj1.dictLookup("OP", &obj2)->isBool()) {
855 state->setStrokeOverprint(obj2.getBool());
856 out->updateStrokeOverprint(state);
857 if (!haveFillOP) {
858 state->setFillOverprint(obj2.getBool());
859 out->updateFillOverprint(state);
862 obj2.free();
864 // stroke adjust
865 if (obj1.dictLookup("SA", &obj2)->isBool()) {
866 state->setStrokeAdjust(obj2.getBool());
867 out->updateStrokeAdjust(state);
869 obj2.free();
871 // transfer function
872 if (obj1.dictLookup("TR2", &obj2)->isNull()) {
873 obj2.free();
874 obj1.dictLookup("TR", &obj2);
876 if (obj2.isName("Default") ||
877 obj2.isName("Identity")) {
878 funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
879 state->setTransfer(funcs);
880 out->updateTransfer(state);
881 } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
882 for (i = 0; i < 4; ++i) {
883 obj2.arrayGet(i, &obj3);
884 funcs[i] = Function::parse(&obj3);
885 obj3.free();
886 if (!funcs[i]) {
887 break;
890 if (i == 4) {
891 state->setTransfer(funcs);
892 out->updateTransfer(state);
894 } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
895 if ((funcs[0] = Function::parse(&obj2))) {
896 funcs[1] = funcs[2] = funcs[3] = NULL;
897 state->setTransfer(funcs);
898 out->updateTransfer(state);
900 } else if (!obj2.isNull()) {
901 error(getPos(), "Invalid transfer function in ExtGState");
903 obj2.free();
905 // soft mask
906 if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
907 if (obj2.isName("None")) {
908 out->clearSoftMask(state);
909 } else if (obj2.isDict()) {
910 if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
911 alpha = gTrue;
912 } else { // "Luminosity"
913 alpha = gFalse;
915 obj3.free();
916 funcs[0] = NULL;
917 if (!obj2.dictLookup("TR", &obj3)->isNull()) {
918 funcs[0] = Function::parse(&obj3);
919 if (funcs[0]->getInputSize() != 1 ||
920 funcs[0]->getOutputSize() != 1) {
921 error(getPos(),
922 "Invalid transfer function in soft mask in ExtGState");
923 delete funcs[0];
924 funcs[0] = NULL;
927 obj3.free();
928 if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
929 for (i = 0; i < gfxColorMaxComps; ++i) {
930 backdropColor.c[i] = 0;
932 for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
933 obj3.arrayGet(i, &obj4);
934 if (obj4.isNum()) {
935 backdropColor.c[i] = dblToCol(obj4.getNum());
937 obj4.free();
940 obj3.free();
941 if (obj2.dictLookup("G", &obj3)->isStream()) {
942 if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
943 blendingColorSpace = NULL;
944 isolated = knockout = gFalse;
945 if (!obj4.dictLookup("CS", &obj5)->isNull()) {
946 blendingColorSpace = GfxColorSpace::parse(&obj5);
948 obj5.free();
949 if (obj4.dictLookup("I", &obj5)->isBool()) {
950 isolated = obj5.getBool();
952 obj5.free();
953 if (obj4.dictLookup("K", &obj5)->isBool()) {
954 knockout = obj5.getBool();
956 obj5.free();
957 if (!haveBackdropColor) {
958 if (blendingColorSpace) {
959 blendingColorSpace->getDefaultColor(&backdropColor);
960 } else {
961 //~ need to get the parent or default color space (?)
962 for (i = 0; i < gfxColorMaxComps; ++i) {
963 backdropColor.c[i] = 0;
967 doSoftMask(&obj3, alpha, blendingColorSpace,
968 isolated, knockout, funcs[0], &backdropColor);
969 if (funcs[0]) {
970 delete funcs[0];
972 } else {
973 error(getPos(), "Invalid soft mask in ExtGState - missing group");
975 obj4.free();
976 } else {
977 error(getPos(), "Invalid soft mask in ExtGState - missing group");
979 obj3.free();
980 } else if (!obj2.isNull()) {
981 error(getPos(), "Invalid soft mask in ExtGState");
984 obj2.free();
986 obj1.free();
989 void Gfx::doSoftMask(xObject *str, GBool alpha,
990 GfxColorSpace *blendingColorSpace,
991 GBool isolated, GBool knockout,
992 Function *transferFunc, GfxColor *backdropColor) {
993 Dict *dict, *resDict;
994 double m[6], bbox[4];
995 xObject obj1, obj2;
996 int i;
998 // check for excessive recursion
999 if (formDepth > 20) {
1000 return;
1003 // get stream dict
1004 dict = str->streamGetDict();
1006 // check form type
1007 dict->lookup("FormType", &obj1);
1008 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
1009 error(getPos(), "Unknown form type");
1011 obj1.free();
1013 // get bounding box
1014 dict->lookup("BBox", &obj1);
1015 if (!obj1.isArray()) {
1016 obj1.free();
1017 error(getPos(), "Bad form bounding box");
1018 return;
1020 for (i = 0; i < 4; ++i) {
1021 obj1.arrayGet(i, &obj2);
1022 bbox[i] = obj2.getNum();
1023 obj2.free();
1025 obj1.free();
1027 // get matrix
1028 dict->lookup("Matrix", &obj1);
1029 if (obj1.isArray()) {
1030 for (i = 0; i < 6; ++i) {
1031 obj1.arrayGet(i, &obj2);
1032 m[i] = obj2.getNum();
1033 obj2.free();
1035 } else {
1036 m[0] = 1; m[1] = 0;
1037 m[2] = 0; m[3] = 1;
1038 m[4] = 0; m[5] = 0;
1040 obj1.free();
1042 // get resources
1043 dict->lookup("Resources", &obj1);
1044 resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
1046 // draw it
1047 ++formDepth;
1048 doForm1(str, resDict, m, bbox, gTrue, gTrue,
1049 blendingColorSpace, isolated, knockout,
1050 alpha, transferFunc, backdropColor);
1051 --formDepth;
1053 if (blendingColorSpace) {
1054 delete blendingColorSpace;
1056 obj1.free();
1059 void Gfx::opSetRenderingIntent(xObject args[], int numArgs) {
1062 //------------------------------------------------------------------------
1063 // color operators
1064 //------------------------------------------------------------------------
1066 void Gfx::opSetFillGray(xObject args[], int numArgs) {
1067 GfxColor color;
1069 state->setFillPattern(NULL);
1070 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1071 out->updateFillColorSpace(state);
1072 color.c[0] = dblToCol(args[0].getNum());
1073 state->setFillColor(&color);
1074 out->updateFillColor(state);
1077 void Gfx::opSetStrokeGray(xObject args[], int numArgs) {
1078 GfxColor color;
1080 state->setStrokePattern(NULL);
1081 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1082 out->updateStrokeColorSpace(state);
1083 color.c[0] = dblToCol(args[0].getNum());
1084 state->setStrokeColor(&color);
1085 out->updateStrokeColor(state);
1088 void Gfx::opSetFillCMYKColor(xObject args[], int numArgs) {
1089 GfxColor color;
1090 int i;
1092 state->setFillPattern(NULL);
1093 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
1094 out->updateFillColorSpace(state);
1095 for (i = 0; i < 4; ++i) {
1096 color.c[i] = dblToCol(args[i].getNum());
1098 state->setFillColor(&color);
1099 out->updateFillColor(state);
1102 void Gfx::opSetStrokeCMYKColor(xObject args[], int numArgs) {
1103 GfxColor color;
1104 int i;
1106 state->setStrokePattern(NULL);
1107 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
1108 out->updateStrokeColorSpace(state);
1109 for (i = 0; i < 4; ++i) {
1110 color.c[i] = dblToCol(args[i].getNum());
1112 state->setStrokeColor(&color);
1113 out->updateStrokeColor(state);
1116 void Gfx::opSetFillRGBColor(xObject args[], int numArgs) {
1117 GfxColor color;
1118 int i;
1120 state->setFillPattern(NULL);
1121 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
1122 out->updateFillColorSpace(state);
1123 for (i = 0; i < 3; ++i) {
1124 color.c[i] = dblToCol(args[i].getNum());
1126 state->setFillColor(&color);
1127 out->updateFillColor(state);
1130 void Gfx::opSetStrokeRGBColor(xObject args[], int numArgs) {
1131 GfxColor color;
1132 int i;
1134 state->setStrokePattern(NULL);
1135 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1136 out->updateStrokeColorSpace(state);
1137 for (i = 0; i < 3; ++i) {
1138 color.c[i] = dblToCol(args[i].getNum());
1140 state->setStrokeColor(&color);
1141 out->updateStrokeColor(state);
1144 void Gfx::opSetFillColorSpace(xObject args[], int numArgs) {
1145 xObject obj;
1146 GfxColorSpace *colorSpace;
1147 GfxColor color;
1149 state->setFillPattern(NULL);
1150 res->lookupColorSpace(args[0].getName(), &obj);
1151 if (obj.isNull()) {
1152 colorSpace = GfxColorSpace::parse(&args[0]);
1153 } else {
1154 colorSpace = GfxColorSpace::parse(&obj);
1156 obj.free();
1157 if (colorSpace) {
1158 state->setFillColorSpace(colorSpace);
1159 out->updateFillColorSpace(state);
1160 colorSpace->getDefaultColor(&color);
1161 state->setFillColor(&color);
1162 out->updateFillColor(state);
1163 } else {
1164 error(getPos(), "Bad color space (fill)");
1168 void Gfx::opSetStrokeColorSpace(xObject args[], int numArgs) {
1169 xObject obj;
1170 GfxColorSpace *colorSpace;
1171 GfxColor color;
1173 state->setStrokePattern(NULL);
1174 res->lookupColorSpace(args[0].getName(), &obj);
1175 if (obj.isNull()) {
1176 colorSpace = GfxColorSpace::parse(&args[0]);
1177 } else {
1178 colorSpace = GfxColorSpace::parse(&obj);
1180 obj.free();
1181 if (colorSpace) {
1182 state->setStrokeColorSpace(colorSpace);
1183 out->updateStrokeColorSpace(state);
1184 colorSpace->getDefaultColor(&color);
1185 state->setStrokeColor(&color);
1186 out->updateStrokeColor(state);
1187 } else {
1188 error(getPos(), "Bad color space (stroke)");
1192 void Gfx::opSetFillColor(xObject args[], int numArgs) {
1193 GfxColor color;
1194 int i;
1196 if (numArgs != state->getFillColorSpace()->getNComps()) {
1197 error(getPos(), "Incorrect number of arguments in 'sc' command");
1198 return;
1200 state->setFillPattern(NULL);
1201 for (i = 0; i < numArgs; ++i) {
1202 color.c[i] = dblToCol(args[i].getNum());
1204 state->setFillColor(&color);
1205 out->updateFillColor(state);
1208 void Gfx::opSetStrokeColor(xObject args[], int numArgs) {
1209 GfxColor color;
1210 int i;
1212 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1213 error(getPos(), "Incorrect number of arguments in 'SC' command");
1214 return;
1216 state->setStrokePattern(NULL);
1217 for (i = 0; i < numArgs; ++i) {
1218 color.c[i] = dblToCol(args[i].getNum());
1220 state->setStrokeColor(&color);
1221 out->updateStrokeColor(state);
1224 void Gfx::opSetFillColorN(xObject args[], int numArgs) {
1225 GfxColor color;
1226 GfxPattern *pattern;
1227 int i;
1229 if (state->getFillColorSpace()->getMode() == csPattern) {
1230 if (numArgs > 1) {
1231 if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1232 numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1233 ->getUnder()->getNComps()) {
1234 error(getPos(), "Incorrect number of arguments in 'scn' command");
1235 return;
1237 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1238 if (args[i].isNum()) {
1239 color.c[i] = dblToCol(args[i].getNum());
1242 state->setFillColor(&color);
1243 out->updateFillColor(state);
1245 if (args[numArgs-1].isName() &&
1246 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1247 state->setFillPattern(pattern);
1250 } else {
1251 if (numArgs != state->getFillColorSpace()->getNComps()) {
1252 error(getPos(), "Incorrect number of arguments in 'scn' command");
1253 return;
1255 state->setFillPattern(NULL);
1256 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1257 if (args[i].isNum()) {
1258 color.c[i] = dblToCol(args[i].getNum());
1261 state->setFillColor(&color);
1262 out->updateFillColor(state);
1266 void Gfx::opSetStrokeColorN(xObject args[], int numArgs) {
1267 GfxColor color;
1268 GfxPattern *pattern;
1269 int i;
1271 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1272 if (numArgs > 1) {
1273 if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1274 ->getUnder() ||
1275 numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1276 ->getUnder()->getNComps()) {
1277 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1278 return;
1280 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1281 if (args[i].isNum()) {
1282 color.c[i] = dblToCol(args[i].getNum());
1285 state->setStrokeColor(&color);
1286 out->updateStrokeColor(state);
1288 if (args[numArgs-1].isName() &&
1289 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1290 state->setStrokePattern(pattern);
1293 } else {
1294 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1295 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1296 return;
1298 state->setStrokePattern(NULL);
1299 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1300 if (args[i].isNum()) {
1301 color.c[i] = dblToCol(args[i].getNum());
1304 state->setStrokeColor(&color);
1305 out->updateStrokeColor(state);
1309 //------------------------------------------------------------------------
1310 // path segment operators
1311 //------------------------------------------------------------------------
1313 void Gfx::opMoveTo(xObject args[], int numArgs) {
1314 state->moveTo(args[0].getNum(), args[1].getNum());
1317 void Gfx::opLineTo(xObject args[], int numArgs) {
1318 if (!state->isCurPt()) {
1319 error(getPos(), "No current point in lineto");
1320 return;
1322 state->lineTo(args[0].getNum(), args[1].getNum());
1325 void Gfx::opCurveTo(xObject args[], int numArgs) {
1326 double x1, y1, x2, y2, x3, y3;
1328 if (!state->isCurPt()) {
1329 error(getPos(), "No current point in curveto");
1330 return;
1332 x1 = args[0].getNum();
1333 y1 = args[1].getNum();
1334 x2 = args[2].getNum();
1335 y2 = args[3].getNum();
1336 x3 = args[4].getNum();
1337 y3 = args[5].getNum();
1338 state->curveTo(x1, y1, x2, y2, x3, y3);
1341 void Gfx::opCurveTo1(xObject args[], int numArgs) {
1342 double x1, y1, x2, y2, x3, y3;
1344 if (!state->isCurPt()) {
1345 error(getPos(), "No current point in curveto1");
1346 return;
1348 x1 = state->getCurX();
1349 y1 = state->getCurY();
1350 x2 = args[0].getNum();
1351 y2 = args[1].getNum();
1352 x3 = args[2].getNum();
1353 y3 = args[3].getNum();
1354 state->curveTo(x1, y1, x2, y2, x3, y3);
1357 void Gfx::opCurveTo2(xObject args[], int numArgs) {
1358 double x1, y1, x2, y2, x3, y3;
1360 if (!state->isCurPt()) {
1361 error(getPos(), "No current point in curveto2");
1362 return;
1364 x1 = args[0].getNum();
1365 y1 = args[1].getNum();
1366 x2 = args[2].getNum();
1367 y2 = args[3].getNum();
1368 x3 = x2;
1369 y3 = y2;
1370 state->curveTo(x1, y1, x2, y2, x3, y3);
1373 void Gfx::opRectangle(xObject args[], int numArgs) {
1374 double x, y, w, h;
1376 x = args[0].getNum();
1377 y = args[1].getNum();
1378 w = args[2].getNum();
1379 h = args[3].getNum();
1380 state->moveTo(x, y);
1381 state->lineTo(x + w, y);
1382 state->lineTo(x + w, y + h);
1383 state->lineTo(x, y + h);
1384 state->closePath();
1387 void Gfx::opClosePath(xObject args[], int numArgs) {
1388 if (!state->isCurPt()) {
1389 error(getPos(), "No current point in closepath");
1390 return;
1392 state->closePath();
1395 //------------------------------------------------------------------------
1396 // path painting operators
1397 //------------------------------------------------------------------------
1399 void Gfx::opEndPath(xObject args[], int numArgs) {
1400 doEndPath();
1403 void Gfx::opStroke(xObject args[], int numArgs) {
1404 if (!state->isCurPt()) {
1405 //error(getPos(), "No path in stroke");
1406 return;
1408 if (state->isPath()) {
1409 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1410 doPatternStroke();
1411 } else {
1412 out->stroke(state);
1415 doEndPath();
1418 void Gfx::opCloseStroke(xObject args[], int numArgs) {
1419 if (!state->isCurPt()) {
1420 //error(getPos(), "No path in closepath/stroke");
1421 return;
1423 if (state->isPath()) {
1424 state->closePath();
1425 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1426 doPatternStroke();
1427 } else {
1428 out->stroke(state);
1431 doEndPath();
1434 void Gfx::opFill(xObject args[], int numArgs) {
1435 if (!state->isCurPt()) {
1436 //error(getPos(), "No path in fill");
1437 return;
1439 if (state->isPath()) {
1440 if (state->getFillColorSpace()->getMode() == csPattern) {
1441 doPatternFill(gFalse);
1442 } else {
1443 out->fill(state);
1446 doEndPath();
1449 void Gfx::opEOFill(xObject args[], int numArgs) {
1450 if (!state->isCurPt()) {
1451 //error(getPos(), "No path in eofill");
1452 return;
1454 if (state->isPath()) {
1455 if (state->getFillColorSpace()->getMode() == csPattern) {
1456 doPatternFill(gTrue);
1457 } else {
1458 out->eoFill(state);
1461 doEndPath();
1464 void Gfx::opFillStroke(xObject args[], int numArgs) {
1465 if (!state->isCurPt()) {
1466 //error(getPos(), "No path in fill/stroke");
1467 return;
1469 if (state->isPath()) {
1470 if (state->getFillColorSpace()->getMode() == csPattern) {
1471 doPatternFill(gFalse);
1472 } else {
1473 out->fill(state);
1475 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1476 doPatternStroke();
1477 } else {
1478 out->stroke(state);
1481 doEndPath();
1484 void Gfx::opCloseFillStroke(xObject args[], int numArgs) {
1485 if (!state->isCurPt()) {
1486 //error(getPos(), "No path in closepath/fill/stroke");
1487 return;
1489 if (state->isPath()) {
1490 state->closePath();
1491 if (state->getFillColorSpace()->getMode() == csPattern) {
1492 doPatternFill(gFalse);
1493 } else {
1494 out->fill(state);
1496 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1497 doPatternStroke();
1498 } else {
1499 out->stroke(state);
1502 doEndPath();
1505 void Gfx::opEOFillStroke(xObject args[], int numArgs) {
1506 if (!state->isCurPt()) {
1507 //error(getPos(), "No path in eofill/stroke");
1508 return;
1510 if (state->isPath()) {
1511 if (state->getFillColorSpace()->getMode() == csPattern) {
1512 doPatternFill(gTrue);
1513 } else {
1514 out->eoFill(state);
1516 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1517 doPatternStroke();
1518 } else {
1519 out->stroke(state);
1522 doEndPath();
1525 void Gfx::opCloseEOFillStroke(xObject args[], int numArgs) {
1526 if (!state->isCurPt()) {
1527 //error(getPos(), "No path in closepath/eofill/stroke");
1528 return;
1530 if (state->isPath()) {
1531 state->closePath();
1532 if (state->getFillColorSpace()->getMode() == csPattern) {
1533 doPatternFill(gTrue);
1534 } else {
1535 out->eoFill(state);
1537 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1538 doPatternStroke();
1539 } else {
1540 out->stroke(state);
1543 doEndPath();
1546 void Gfx::doPatternFill(GBool eoFill) {
1547 GfxPattern *pattern;
1549 // this is a bit of a kludge -- patterns can be really slow, so we
1550 // skip them if we're only doing text extraction, since they almost
1551 // certainly don't contain any text
1552 if (!out->needNonText()) {
1553 return;
1556 if (!(pattern = state->getFillPattern())) {
1557 return;
1559 switch (pattern->getType()) {
1560 case 1:
1561 doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill);
1562 break;
1563 case 2:
1564 doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);
1565 break;
1566 default:
1567 error(getPos(), "Unimplemented pattern type (%d) in fill",
1568 pattern->getType());
1569 break;
1573 void Gfx::doPatternStroke() {
1574 GfxPattern *pattern;
1576 // this is a bit of a kludge -- patterns can be really slow, so we
1577 // skip them if we're only doing text extraction, since they almost
1578 // certainly don't contain any text
1579 if (!out->needNonText()) {
1580 return;
1583 if (!(pattern = state->getStrokePattern())) {
1584 return;
1586 switch (pattern->getType()) {
1587 case 1:
1588 doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse);
1589 break;
1590 case 2:
1591 doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);
1592 break;
1593 default:
1594 error(getPos(), "Unimplemented pattern type (%d) in stroke",
1595 pattern->getType());
1596 break;
1600 void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
1601 GBool stroke, GBool eoFill) {
1602 GfxPatternColorSpace *patCS;
1603 GfxColorSpace *cs;
1604 GfxPath *savedPath;
1605 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1606 double cxMin, cyMin, cxMax, cyMax;
1607 int xi0, yi0, xi1, yi1, xi, yi;
1608 double *ctm, *btm, *ptm;
1609 double m[6], ictm[6], m1[6], imb[6];
1610 double det;
1611 double xstep, ystep;
1612 int i;
1614 // get color space
1615 patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
1616 : state->getFillColorSpace());
1618 // construct a (pattern space) -> (current space) transform matrix
1619 ctm = state->getCTM();
1620 btm = baseMatrix;
1621 ptm = tPat->getMatrix();
1622 // iCTM = invert CTM
1623 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1624 ictm[0] = ctm[3] * det;
1625 ictm[1] = -ctm[1] * det;
1626 ictm[2] = -ctm[2] * det;
1627 ictm[3] = ctm[0] * det;
1628 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1629 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1630 // m1 = PTM * BTM = PTM * base transform matrix
1631 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1632 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1633 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1634 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1635 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1636 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1637 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1638 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1639 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1640 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1641 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1642 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1643 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1645 // construct a (device space) -> (pattern space) transform matrix
1646 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1647 imb[0] = m1[3] * det;
1648 imb[1] = -m1[1] * det;
1649 imb[2] = -m1[2] * det;
1650 imb[3] = m1[0] * det;
1651 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1652 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1654 // save current graphics state
1655 savedPath = state->getPath()->copy();
1656 saveState();
1658 // set underlying color space (for uncolored tiling patterns); set
1659 // various other parameters (stroke color, line width) to match
1660 // Adobe's behavior
1661 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1662 state->setFillColorSpace(cs->copy());
1663 out->updateFillColorSpace(state);
1664 state->setStrokeColorSpace(cs->copy());
1665 out->updateStrokeColorSpace(state);
1666 state->setStrokeColor(state->getFillColor());
1667 } else {
1668 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1669 out->updateFillColorSpace(state);
1670 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1671 out->updateStrokeColorSpace(state);
1673 state->setFillPattern(NULL);
1674 out->updateFillColor(state);
1675 state->setStrokePattern(NULL);
1676 out->updateStrokeColor(state);
1677 if (!stroke) {
1678 state->setLineWidth(0);
1679 out->updateLineWidth(state);
1682 // clip to current path
1683 if (stroke) {
1684 state->clipToStrokePath();
1685 out->clipToStrokePath(state);
1686 } else {
1687 state->clip();
1688 if (eoFill) {
1689 out->eoClip(state);
1690 } else {
1691 out->clip(state);
1694 state->clearPath();
1696 // get the clip region, check for empty
1697 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1698 if (cxMin > cxMax || cyMin > cyMax) {
1699 goto err;
1702 // transform clip region bbox to pattern space
1703 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1704 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1705 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1706 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1707 if (x1 < xMin) {
1708 xMin = x1;
1709 } else if (x1 > xMax) {
1710 xMax = x1;
1712 if (y1 < yMin) {
1713 yMin = y1;
1714 } else if (y1 > yMax) {
1715 yMax = y1;
1717 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1718 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1719 if (x1 < xMin) {
1720 xMin = x1;
1721 } else if (x1 > xMax) {
1722 xMax = x1;
1724 if (y1 < yMin) {
1725 yMin = y1;
1726 } else if (y1 > yMax) {
1727 yMax = y1;
1729 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1730 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1731 if (x1 < xMin) {
1732 xMin = x1;
1733 } else if (x1 > xMax) {
1734 xMax = x1;
1736 if (y1 < yMin) {
1737 yMin = y1;
1738 } else if (y1 > yMax) {
1739 yMax = y1;
1742 // draw the pattern
1743 //~ this should treat negative steps differently -- start at right/top
1744 //~ edge instead of left/bottom (?)
1745 xstep = fabs(tPat->getXStep());
1746 ystep = fabs(tPat->getYStep());
1747 xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);
1748 xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;
1749 yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);
1750 yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1;
1751 for (i = 0; i < 4; ++i) {
1752 m1[i] = m[i];
1754 if (out->useTilingPatternFill()) {
1755 m1[4] = m[4];
1756 m1[5] = m[5];
1757 out->tilingPatternFill(state, tPat->getContentStream(),
1758 tPat->getPaintType(), tPat->getResDict(),
1759 m1, tPat->getBBox(),
1760 xi0, yi0, xi1, yi1, xstep, ystep);
1761 } else {
1762 for (yi = yi0; yi < yi1; ++yi) {
1763 for (xi = xi0; xi < xi1; ++xi) {
1764 x = xi * xstep;
1765 y = yi * ystep;
1766 m1[4] = x * m[0] + y * m[2] + m[4];
1767 m1[5] = x * m[1] + y * m[3] + m[5];
1768 doForm1(tPat->getContentStream(), tPat->getResDict(),
1769 m1, tPat->getBBox());
1774 // restore graphics state
1775 err:
1776 restoreState();
1777 state->setPath(savedPath);
1780 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
1781 GBool stroke, GBool eoFill) {
1782 GfxShading *shading;
1783 GfxPath *savedPath;
1784 double *ctm, *btm, *ptm;
1785 double m[6], ictm[6], m1[6];
1786 double xMin, yMin, xMax, yMax;
1787 double det;
1789 shading = sPat->getShading();
1791 // save current graphics state
1792 savedPath = state->getPath()->copy();
1793 saveState();
1795 // clip to bbox
1796 if (shading->getHasBBox()) {
1797 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1798 state->moveTo(xMin, yMin);
1799 state->lineTo(xMax, yMin);
1800 state->lineTo(xMax, yMax);
1801 state->lineTo(xMin, yMax);
1802 state->closePath();
1803 state->clip();
1804 out->clip(state);
1805 state->setPath(savedPath->copy());
1808 // clip to current path
1809 if (stroke) {
1810 state->clipToStrokePath();
1811 out->clipToStrokePath(state);
1812 } else {
1813 state->clip();
1814 if (eoFill) {
1815 out->eoClip(state);
1816 } else {
1817 out->clip(state);
1821 // set the color space
1822 state->setFillColorSpace(shading->getColorSpace()->copy());
1823 out->updateFillColorSpace(state);
1825 // background color fill
1826 if (shading->getHasBackground()) {
1827 state->setFillColor(shading->getBackground());
1828 out->updateFillColor(state);
1829 out->fill(state);
1831 state->clearPath();
1833 // construct a (pattern space) -> (current space) transform matrix
1834 ctm = state->getCTM();
1835 btm = baseMatrix;
1836 ptm = sPat->getMatrix();
1837 // iCTM = invert CTM
1838 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1839 ictm[0] = ctm[3] * det;
1840 ictm[1] = -ctm[1] * det;
1841 ictm[2] = -ctm[2] * det;
1842 ictm[3] = ctm[0] * det;
1843 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1844 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1845 // m1 = PTM * BTM = PTM * base transform matrix
1846 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1847 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1848 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1849 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1850 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1851 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1852 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1853 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1854 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1855 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1856 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1857 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1858 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1860 // set the new matrix
1861 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1862 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1864 #if 1 //~tmp: turn off anti-aliasing temporarily
1865 GBool vaa = out->getVectorAntialias();
1866 if (vaa) {
1867 out->setVectorAntialias(gFalse);
1869 #endif
1871 // do shading type-specific operations
1872 switch (shading->getType()) {
1873 case 1:
1874 doFunctionShFill((GfxFunctionShading *)shading);
1875 break;
1876 case 2:
1877 doAxialShFill((GfxAxialShading *)shading);
1878 break;
1879 case 3:
1880 doRadialShFill((GfxRadialShading *)shading);
1881 break;
1882 case 4:
1883 case 5:
1884 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1885 break;
1886 case 6:
1887 case 7:
1888 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1889 break;
1892 #if 1 //~tmp: turn off anti-aliasing temporarily
1893 if (vaa) {
1894 out->setVectorAntialias(gTrue);
1896 #endif
1898 // restore graphics state
1899 restoreState();
1900 state->setPath(savedPath);
1903 void Gfx::opShFill(xObject args[], int numArgs) {
1904 GfxShading *shading;
1905 GfxPath *savedPath;
1906 double xMin, yMin, xMax, yMax;
1908 if (!(shading = res->lookupShading(args[0].getName()))) {
1909 return;
1912 // save current graphics state
1913 savedPath = state->getPath()->copy();
1914 saveState();
1916 // clip to bbox
1917 if (shading->getHasBBox()) {
1918 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1919 state->moveTo(xMin, yMin);
1920 state->lineTo(xMax, yMin);
1921 state->lineTo(xMax, yMax);
1922 state->lineTo(xMin, yMax);
1923 state->closePath();
1924 state->clip();
1925 out->clip(state);
1926 state->clearPath();
1929 // set the color space
1930 state->setFillColorSpace(shading->getColorSpace()->copy());
1931 out->updateFillColorSpace(state);
1933 #if 1 //~tmp: turn off anti-aliasing temporarily
1934 GBool vaa = out->getVectorAntialias();
1935 if (vaa) {
1936 out->setVectorAntialias(gFalse);
1938 #endif
1940 // do shading type-specific operations
1941 switch (shading->getType()) {
1942 case 1:
1943 doFunctionShFill((GfxFunctionShading *)shading);
1944 break;
1945 case 2:
1946 doAxialShFill((GfxAxialShading *)shading);
1947 break;
1948 case 3:
1949 doRadialShFill((GfxRadialShading *)shading);
1950 break;
1951 case 4:
1952 case 5:
1953 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1954 break;
1955 case 6:
1956 case 7:
1957 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1958 break;
1961 #if 1 //~tmp: turn off anti-aliasing temporarily
1962 if (vaa) {
1963 out->setVectorAntialias(gTrue);
1965 #endif
1967 // restore graphics state
1968 restoreState();
1969 state->setPath(savedPath);
1971 delete shading;
1974 void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
1975 double x0, y0, x1, y1;
1976 GfxColor colors[4];
1978 if (out->useShadedFills() &&
1979 out->functionShadedFill(state, shading)) {
1980 return;
1983 shading->getDomain(&x0, &y0, &x1, &y1);
1984 shading->getColor(x0, y0, &colors[0]);
1985 shading->getColor(x0, y1, &colors[1]);
1986 shading->getColor(x1, y0, &colors[2]);
1987 shading->getColor(x1, y1, &colors[3]);
1988 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1991 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
1992 double x0, double y0,
1993 double x1, double y1,
1994 GfxColor *colors, int depth) {
1995 GfxColor fillColor;
1996 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1997 GfxColor colors2[4];
1998 double *matrix;
1999 double xM, yM;
2000 int nComps, i, j;
2002 nComps = shading->getColorSpace()->getNComps();
2003 matrix = shading->getMatrix();
2005 // compare the four corner colors
2006 for (i = 0; i < 4; ++i) {
2007 for (j = 0; j < nComps; ++j) {
2008 if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
2009 break;
2012 if (j < nComps) {
2013 break;
2017 // center of the rectangle
2018 xM = 0.5 * (x0 + x1);
2019 yM = 0.5 * (y0 + y1);
2021 // the four corner colors are close (or we hit the recursive limit)
2022 // -- fill the rectangle; but require at least one subdivision
2023 // (depth==0) to avoid problems when the four outer corners of the
2024 // shaded region are the same color
2025 if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
2027 // use the center color
2028 shading->getColor(xM, yM, &fillColor);
2029 state->setFillColor(&fillColor);
2030 out->updateFillColor(state);
2032 // fill the rectangle
2033 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
2034 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
2035 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
2036 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
2037 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
2038 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
2039 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
2040 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
2041 state->closePath();
2042 out->fill(state);
2043 state->clearPath();
2045 // the four corner colors are not close enough -- subdivide the
2046 // rectangle
2047 } else {
2049 // colors[0] colorM0 colors[2]
2050 // (x0,y0) (xM,y0) (x1,y0)
2051 // +----------+----------+
2052 // | | |
2053 // | UL | UR |
2054 // color0M | colorMM | color1M
2055 // (x0,yM) +----------+----------+ (x1,yM)
2056 // | (xM,yM) |
2057 // | LL | LR |
2058 // | | |
2059 // +----------+----------+
2060 // colors[1] colorM1 colors[3]
2061 // (x0,y1) (xM,y1) (x1,y1)
2063 shading->getColor(x0, yM, &color0M);
2064 shading->getColor(x1, yM, &color1M);
2065 shading->getColor(xM, y0, &colorM0);
2066 shading->getColor(xM, y1, &colorM1);
2067 shading->getColor(xM, yM, &colorMM);
2069 // upper-left sub-rectangle
2070 colors2[0] = colors[0];
2071 colors2[1] = color0M;
2072 colors2[2] = colorM0;
2073 colors2[3] = colorMM;
2074 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
2076 // lower-left sub-rectangle
2077 colors2[0] = color0M;
2078 colors2[1] = colors[1];
2079 colors2[2] = colorMM;
2080 colors2[3] = colorM1;
2081 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
2083 // upper-right sub-rectangle
2084 colors2[0] = colorM0;
2085 colors2[1] = colorMM;
2086 colors2[2] = colors[2];
2087 colors2[3] = color1M;
2088 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
2090 // lower-right sub-rectangle
2091 colors2[0] = colorMM;
2092 colors2[1] = colorM1;
2093 colors2[2] = color1M;
2094 colors2[3] = colors[3];
2095 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
2099 void Gfx::doAxialShFill(GfxAxialShading *shading) {
2100 double xMin, yMin, xMax, yMax;
2101 double x0, y0, x1, y1;
2102 double dx, dy, mul;
2103 GBool dxZero, dyZero;
2104 double tMin, tMax, t, tx, ty;
2105 double s[4], sMin, sMax, tmp;
2106 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
2107 double t0, t1, tt;
2108 double ta[axialMaxSplits + 1];
2109 int next[axialMaxSplits + 1];
2110 GfxColor color0, color1;
2111 int nComps;
2112 int i, j, k, kk;
2114 if (out->useShadedFills() &&
2115 out->axialShadedFill(state, shading)) {
2116 return;
2119 // get the clip region bbox
2120 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2122 // compute min and max t values, based on the four corners of the
2123 // clip region bbox
2124 shading->getCoords(&x0, &y0, &x1, &y1);
2125 dx = x1 - x0;
2126 dy = y1 - y0;
2127 dxZero = fabs(dx) < 0.01;
2128 dyZero = fabs(dy) < 0.01;
2129 if (dxZero && dyZero) {
2130 tMin = tMax = 0;
2131 } else {
2132 mul = 1 / (dx * dx + dy * dy);
2133 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
2134 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
2135 if (t < tMin) {
2136 tMin = t;
2137 } else if (t > tMax) {
2138 tMax = t;
2140 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
2141 if (t < tMin) {
2142 tMin = t;
2143 } else if (t > tMax) {
2144 tMax = t;
2146 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
2147 if (t < tMin) {
2148 tMin = t;
2149 } else if (t > tMax) {
2150 tMax = t;
2152 if (tMin < 0 && !shading->getExtend0()) {
2153 tMin = 0;
2155 if (tMax > 1 && !shading->getExtend1()) {
2156 tMax = 1;
2160 // get the function domain
2161 t0 = shading->getDomain0();
2162 t1 = shading->getDomain1();
2164 // Traverse the t axis and do the shading.
2166 // For each point (tx, ty) on the t axis, consider a line through
2167 // that point perpendicular to the t axis:
2169 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
2170 // y(s) = ty + s * dx --> s = (y - ty) / dx
2172 // Then look at the intersection of this line with the bounding box
2173 // (xMin, yMin, xMax, yMax). In the general case, there are four
2174 // intersection points:
2176 // s0 = (xMin - tx) / -dy
2177 // s1 = (xMax - tx) / -dy
2178 // s2 = (yMin - ty) / dx
2179 // s3 = (yMax - ty) / dx
2181 // and we want the middle two s values.
2183 // In the case where dx = 0, take s0 and s1; in the case where dy =
2184 // 0, take s2 and s3.
2186 // Each filled polygon is bounded by two of these line segments
2187 // perpdendicular to the t axis.
2189 // The t axis is bisected into smaller regions until the color
2190 // difference across a region is small enough, and then the region
2191 // is painted with a single color.
2193 // set up: require at least one split to avoid problems when the two
2194 // ends of the t axis have the same color
2195 nComps = shading->getColorSpace()->getNComps();
2196 ta[0] = tMin;
2197 next[0] = axialMaxSplits / 2;
2198 ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
2199 next[axialMaxSplits / 2] = axialMaxSplits;
2200 ta[axialMaxSplits] = tMax;
2202 // compute the color at t = tMin
2203 if (tMin < 0) {
2204 tt = t0;
2205 } else if (tMin > 1) {
2206 tt = t1;
2207 } else {
2208 tt = t0 + (t1 - t0) * tMin;
2210 shading->getColor(tt, &color0);
2212 // compute the coordinates of the point on the t axis at t = tMin;
2213 // then compute the intersection of the perpendicular line with the
2214 // bounding box
2215 tx = x0 + tMin * dx;
2216 ty = y0 + tMin * dy;
2217 if (dxZero && dyZero) {
2218 sMin = sMax = 0;
2219 } else if (dxZero) {
2220 sMin = (xMin - tx) / -dy;
2221 sMax = (xMax - tx) / -dy;
2222 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2223 } else if (dyZero) {
2224 sMin = (yMin - ty) / dx;
2225 sMax = (yMax - ty) / dx;
2226 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2227 } else {
2228 s[0] = (yMin - ty) / dx;
2229 s[1] = (yMax - ty) / dx;
2230 s[2] = (xMin - tx) / -dy;
2231 s[3] = (xMax - tx) / -dy;
2232 for (j = 0; j < 3; ++j) {
2233 kk = j;
2234 for (k = j + 1; k < 4; ++k) {
2235 if (s[k] < s[kk]) {
2236 kk = k;
2239 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
2241 sMin = s[1];
2242 sMax = s[2];
2244 ux0 = tx - sMin * dy;
2245 uy0 = ty + sMin * dx;
2246 vx0 = tx - sMax * dy;
2247 vy0 = ty + sMax * dx;
2249 i = 0;
2250 while (i < axialMaxSplits) {
2252 // bisect until color difference is small enough or we hit the
2253 // bisection limit
2254 j = next[i];
2255 while (j > i + 1) {
2256 if (ta[j] < 0) {
2257 tt = t0;
2258 } else if (ta[j] > 1) {
2259 tt = t1;
2260 } else {
2261 tt = t0 + (t1 - t0) * ta[j];
2263 shading->getColor(tt, &color1);
2264 for (k = 0; k < nComps; ++k) {
2265 if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
2266 break;
2269 if (k == nComps) {
2270 break;
2272 k = (i + j) / 2;
2273 ta[k] = 0.5 * (ta[i] + ta[j]);
2274 next[i] = k;
2275 next[k] = j;
2276 j = k;
2279 // use the average of the colors of the two sides of the region
2280 for (k = 0; k < nComps; ++k) {
2281 color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
2284 // compute the coordinates of the point on the t axis; then
2285 // compute the intersection of the perpendicular line with the
2286 // bounding box
2287 tx = x0 + ta[j] * dx;
2288 ty = y0 + ta[j] * dy;
2289 if (dxZero && dyZero) {
2290 sMin = sMax = 0;
2291 } else if (dxZero) {
2292 sMin = (xMin - tx) / -dy;
2293 sMax = (xMax - tx) / -dy;
2294 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2295 } else if (dyZero) {
2296 sMin = (yMin - ty) / dx;
2297 sMax = (yMax - ty) / dx;
2298 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2299 } else {
2300 s[0] = (yMin - ty) / dx;
2301 s[1] = (yMax - ty) / dx;
2302 s[2] = (xMin - tx) / -dy;
2303 s[3] = (xMax - tx) / -dy;
2304 for (j = 0; j < 3; ++j) {
2305 kk = j;
2306 for (k = j + 1; k < 4; ++k) {
2307 if (s[k] < s[kk]) {
2308 kk = k;
2311 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
2313 sMin = s[1];
2314 sMax = s[2];
2316 ux1 = tx - sMin * dy;
2317 uy1 = ty + sMin * dx;
2318 vx1 = tx - sMax * dy;
2319 vy1 = ty + sMax * dx;
2321 // set the color
2322 state->setFillColor(&color0);
2323 out->updateFillColor(state);
2325 // fill the region
2326 state->moveTo(ux0, uy0);
2327 state->lineTo(vx0, vy0);
2328 state->lineTo(vx1, vy1);
2329 state->lineTo(ux1, uy1);
2330 state->closePath();
2331 out->fill(state);
2332 state->clearPath();
2334 // set up for next region
2335 ux0 = ux1;
2336 uy0 = uy1;
2337 vx0 = vx1;
2338 vy0 = vy1;
2339 color0 = color1;
2340 i = next[i];
2344 void Gfx::doRadialShFill(GfxRadialShading *shading) {
2345 double xMin, yMin, xMax, yMax;
2346 double x0, y0, r0, x1, y1, r1, t0, t1;
2347 int nComps;
2348 GfxColor colorA, colorB;
2349 double xa, ya, xb, yb, ra, rb;
2350 double ta, tb, sa, sb;
2351 double sz, xz, yz, sMin, sMax;
2352 GBool enclosed;
2353 int ia, ib, k, n;
2354 double *ctm;
2355 double theta, alpha, angle, t;
2357 if (out->useShadedFills() &&
2358 out->radialShadedFill(state, shading)) {
2359 return;
2362 // get the shading info
2363 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2364 t0 = shading->getDomain0();
2365 t1 = shading->getDomain1();
2366 nComps = shading->getColorSpace()->getNComps();
2368 // Compute the point at which r(s) = 0; check for the enclosed
2369 // circles case; and compute the angles for the tangent lines.
2370 if (x0 == x1 && y0 == y1) {
2371 enclosed = gTrue;
2372 theta = 0; // make gcc happy
2373 sz = 0; // make gcc happy
2374 } else if (r0 == r1) {
2375 enclosed = gFalse;
2376 theta = 0;
2377 sz = 0; // make gcc happy
2378 } else {
2379 sz = -r0 / (r1 - r0);
2380 xz = x0 + sz * (x1 - x0);
2381 yz = y0 + sz * (y1 - y0);
2382 enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
2383 theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
2384 if (r0 > r1) {
2385 theta = -theta;
2388 if (enclosed) {
2389 alpha = 0;
2390 } else {
2391 alpha = atan2(y1 - y0, x1 - x0);
2394 // compute the (possibly extended) s range
2395 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2396 if (enclosed) {
2397 sMin = 0;
2398 sMax = 1;
2399 } else {
2400 sMin = 1;
2401 sMax = 0;
2402 // solve for x(s) + r(s) = xMin
2403 if ((x1 + r1) - (x0 + r0) != 0) {
2404 sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
2405 if (sa < sMin) {
2406 sMin = sa;
2407 } else if (sa > sMax) {
2408 sMax = sa;
2411 // solve for x(s) - r(s) = xMax
2412 if ((x1 - r1) - (x0 - r0) != 0) {
2413 sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
2414 if (sa < sMin) {
2415 sMin = sa;
2416 } else if (sa > sMax) {
2417 sMax = sa;
2420 // solve for y(s) + r(s) = yMin
2421 if ((y1 + r1) - (y0 + r0) != 0) {
2422 sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
2423 if (sa < sMin) {
2424 sMin = sa;
2425 } else if (sa > sMax) {
2426 sMax = sa;
2429 // solve for y(s) - r(s) = yMax
2430 if ((y1 - r1) - (y0 - r0) != 0) {
2431 sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
2432 if (sa < sMin) {
2433 sMin = sa;
2434 } else if (sa > sMax) {
2435 sMax = sa;
2438 // check against sz
2439 if (r0 < r1) {
2440 if (sMin < sz) {
2441 sMin = sz;
2443 } else if (r0 > r1) {
2444 if (sMax > sz) {
2445 sMax = sz;
2448 // check the 'extend' flags
2449 if (!shading->getExtend0() && sMin < 0) {
2450 sMin = 0;
2452 if (!shading->getExtend1() && sMax > 1) {
2453 sMax = 1;
2457 // compute the number of steps into which circles must be divided to
2458 // achieve a curve flatness of 0.1 pixel in device space for the
2459 // largest circle (note that "device space" is 72 dpi when generating
2460 // PostScript, hence the relatively small 0.1 pixel accuracy)
2461 ctm = state->getCTM();
2462 t = fabs(ctm[0]);
2463 if (fabs(ctm[1]) > t) {
2464 t = fabs(ctm[1]);
2466 if (fabs(ctm[2]) > t) {
2467 t = fabs(ctm[2]);
2469 if (fabs(ctm[3]) > t) {
2470 t = fabs(ctm[3]);
2472 if (r0 > r1) {
2473 t *= r0;
2474 } else {
2475 t *= r1;
2477 if (t < 1) {
2478 n = 3;
2479 } else {
2480 n = (int)(M_PI / acos(1 - 0.1 / t));
2481 if (n < 3) {
2482 n = 3;
2483 } else if (n > 200) {
2484 n = 200;
2488 // setup for the start circle
2489 ia = 0;
2490 sa = sMin;
2491 ta = t0 + sa * (t1 - t0);
2492 xa = x0 + sa * (x1 - x0);
2493 ya = y0 + sa * (y1 - y0);
2494 ra = r0 + sa * (r1 - r0);
2495 if (ta < t0) {
2496 shading->getColor(t0, &colorA);
2497 } else if (ta > t1) {
2498 shading->getColor(t1, &colorA);
2499 } else {
2500 shading->getColor(ta, &colorA);
2503 // fill the circles
2504 while (ia < radialMaxSplits) {
2506 // go as far along the t axis (toward t1) as we can, such that the
2507 // color difference is within the tolerance (radialColorDelta) --
2508 // this uses bisection (between the current value, t, and t1),
2509 // limited to radialMaxSplits points along the t axis; require at
2510 // least one split to avoid problems when the innermost and
2511 // outermost colors are the same
2512 ib = radialMaxSplits;
2513 sb = sMax;
2514 tb = t0 + sb * (t1 - t0);
2515 if (tb < t0) {
2516 shading->getColor(t0, &colorB);
2517 } else if (tb > t1) {
2518 shading->getColor(t1, &colorB);
2519 } else {
2520 shading->getColor(tb, &colorB);
2522 while (ib - ia > 1) {
2523 for (k = 0; k < nComps; ++k) {
2524 if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
2525 break;
2528 if (k == nComps && ib < radialMaxSplits) {
2529 break;
2531 ib = (ia + ib) / 2;
2532 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2533 tb = t0 + sb * (t1 - t0);
2534 if (tb < t0) {
2535 shading->getColor(t0, &colorB);
2536 } else if (tb > t1) {
2537 shading->getColor(t1, &colorB);
2538 } else {
2539 shading->getColor(tb, &colorB);
2543 // compute center and radius of the circle
2544 xb = x0 + sb * (x1 - x0);
2545 yb = y0 + sb * (y1 - y0);
2546 rb = r0 + sb * (r1 - r0);
2548 // use the average of the colors at the two circles
2549 for (k = 0; k < nComps; ++k) {
2550 colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
2552 state->setFillColor(&colorA);
2553 out->updateFillColor(state);
2555 if (enclosed) {
2557 // construct path for first circle (counterclockwise)
2558 state->moveTo(xa + ra, ya);
2559 for (k = 1; k < n; ++k) {
2560 angle = ((double)k / (double)n) * 2 * M_PI;
2561 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2563 state->closePath();
2565 // construct and append path for second circle (clockwise)
2566 state->moveTo(xb + rb, yb);
2567 for (k = 1; k < n; ++k) {
2568 angle = -((double)k / (double)n) * 2 * M_PI;
2569 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2571 state->closePath();
2573 } else {
2575 // construct the first subpath (clockwise)
2576 state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
2577 ya + ra * sin(alpha + theta + 0.5 * M_PI));
2578 for (k = 0; k < n; ++k) {
2579 angle = alpha + theta + 0.5 * M_PI
2580 - ((double)k / (double)n) * (2 * theta + M_PI);
2581 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2583 for (k = 0; k < n; ++k) {
2584 angle = alpha - theta - 0.5 * M_PI
2585 + ((double)k / (double)n) * (2 * theta - M_PI);
2586 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2588 state->closePath();
2590 // construct the second subpath (counterclockwise)
2591 state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
2592 ya + ra * sin(alpha + theta + 0.5 * M_PI));
2593 for (k = 0; k < n; ++k) {
2594 angle = alpha + theta + 0.5 * M_PI
2595 + ((double)k / (double)n) * (-2 * theta + M_PI);
2596 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2598 for (k = 0; k < n; ++k) {
2599 angle = alpha - theta - 0.5 * M_PI
2600 + ((double)k / (double)n) * (2 * theta + M_PI);
2601 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2603 state->closePath();
2606 // fill the path
2607 out->fill(state);
2608 state->clearPath();
2610 // step to the next value of t
2611 ia = ib;
2612 sa = sb;
2613 ta = tb;
2614 xa = xb;
2615 ya = yb;
2616 ra = rb;
2617 colorA = colorB;
2620 if (enclosed) {
2621 // extend the smaller circle
2622 if ((shading->getExtend0() && r0 <= r1) ||
2623 (shading->getExtend1() && r1 < r0)) {
2624 if (r0 <= r1) {
2625 ta = t0;
2626 ra = r0;
2627 xa = x0;
2628 ya = y0;
2629 } else {
2630 ta = t1;
2631 ra = r1;
2632 xa = x1;
2633 ya = y1;
2635 shading->getColor(ta, &colorA);
2636 state->setFillColor(&colorA);
2637 out->updateFillColor(state);
2638 state->moveTo(xa + ra, ya);
2639 for (k = 1; k < n; ++k) {
2640 angle = ((double)k / (double)n) * 2 * M_PI;
2641 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2643 state->closePath();
2644 out->fill(state);
2645 state->clearPath();
2648 // extend the larger circle
2649 if ((shading->getExtend0() && r0 > r1) ||
2650 (shading->getExtend1() && r1 >= r0)) {
2651 if (r0 > r1) {
2652 ta = t0;
2653 ra = r0;
2654 xa = x0;
2655 ya = y0;
2656 } else {
2657 ta = t1;
2658 ra = r1;
2659 xa = x1;
2660 ya = y1;
2662 shading->getColor(ta, &colorA);
2663 state->setFillColor(&colorA);
2664 out->updateFillColor(state);
2665 state->moveTo(xMin, yMin);
2666 state->lineTo(xMin, yMax);
2667 state->lineTo(xMax, yMax);
2668 state->lineTo(xMax, yMin);
2669 state->closePath();
2670 state->moveTo(xa + ra, ya);
2671 for (k = 1; k < n; ++k) {
2672 angle = ((double)k / (double)n) * 2 * M_PI;
2673 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2675 state->closePath();
2676 out->fill(state);
2677 state->clearPath();
2682 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
2683 double x0, y0, x1, y1, x2, y2;
2684 GfxColor color0, color1, color2;
2685 int i;
2687 for (i = 0; i < shading->getNTriangles(); ++i) {
2688 shading->getTriangle(i, &x0, &y0, &color0,
2689 &x1, &y1, &color1,
2690 &x2, &y2, &color2);
2691 gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
2692 shading->getColorSpace()->getNComps(), 0);
2696 void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
2697 double x1, double y1, GfxColor *color1,
2698 double x2, double y2, GfxColor *color2,
2699 int nComps, int depth) {
2700 double x01, y01, x12, y12, x20, y20;
2701 GfxColor color01, color12, color20;
2702 int i;
2704 for (i = 0; i < nComps; ++i) {
2705 if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
2706 abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
2707 break;
2710 if (i == nComps || depth == gouraudMaxDepth) {
2711 state->setFillColor(color0);
2712 out->updateFillColor(state);
2713 state->moveTo(x0, y0);
2714 state->lineTo(x1, y1);
2715 state->lineTo(x2, y2);
2716 state->closePath();
2717 out->fill(state);
2718 state->clearPath();
2719 } else {
2720 x01 = 0.5 * (x0 + x1);
2721 y01 = 0.5 * (y0 + y1);
2722 x12 = 0.5 * (x1 + x2);
2723 y12 = 0.5 * (y1 + y2);
2724 x20 = 0.5 * (x2 + x0);
2725 y20 = 0.5 * (y2 + y0);
2726 //~ if the shading has a Function, this should interpolate on the
2727 //~ function parameter, not on the color components
2728 for (i = 0; i < nComps; ++i) {
2729 color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
2730 color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
2731 color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
2733 gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
2734 x20, y20, &color20, nComps, depth + 1);
2735 gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
2736 x12, y12, &color12, nComps, depth + 1);
2737 gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
2738 x20, y20, &color20, nComps, depth + 1);
2739 gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
2740 x2, y2, color2, nComps, depth + 1);
2744 void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
2745 int start, i;
2747 if (shading->getNPatches() > 128) {
2748 start = 3;
2749 } else if (shading->getNPatches() > 64) {
2750 start = 2;
2751 } else if (shading->getNPatches() > 16) {
2752 start = 1;
2753 } else {
2754 start = 0;
2756 for (i = 0; i < shading->getNPatches(); ++i) {
2757 fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
2758 start);
2762 void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
2763 GfxPatch patch00, patch01, patch10, patch11;
2764 double xx[4][8], yy[4][8];
2765 double xxm, yym;
2766 int i;
2768 for (i = 0; i < nComps; ++i) {
2769 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
2770 > patchColorDelta ||
2771 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
2772 > patchColorDelta ||
2773 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
2774 > patchColorDelta ||
2775 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
2776 > patchColorDelta) {
2777 break;
2780 if (i == nComps || depth == patchMaxDepth) {
2781 state->setFillColor(&patch->color[0][0]);
2782 out->updateFillColor(state);
2783 state->moveTo(patch->x[0][0], patch->y[0][0]);
2784 state->curveTo(patch->x[0][1], patch->y[0][1],
2785 patch->x[0][2], patch->y[0][2],
2786 patch->x[0][3], patch->y[0][3]);
2787 state->curveTo(patch->x[1][3], patch->y[1][3],
2788 patch->x[2][3], patch->y[2][3],
2789 patch->x[3][3], patch->y[3][3]);
2790 state->curveTo(patch->x[3][2], patch->y[3][2],
2791 patch->x[3][1], patch->y[3][1],
2792 patch->x[3][0], patch->y[3][0]);
2793 state->curveTo(patch->x[2][0], patch->y[2][0],
2794 patch->x[1][0], patch->y[1][0],
2795 patch->x[0][0], patch->y[0][0]);
2796 state->closePath();
2797 out->fill(state);
2798 state->clearPath();
2799 } else {
2800 for (i = 0; i < 4; ++i) {
2801 xx[i][0] = patch->x[i][0];
2802 yy[i][0] = patch->y[i][0];
2803 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
2804 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
2805 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
2806 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
2807 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
2808 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
2809 xx[i][2] = 0.5 * (xx[i][1] + xxm);
2810 yy[i][2] = 0.5 * (yy[i][1] + yym);
2811 xx[i][5] = 0.5 * (xxm + xx[i][6]);
2812 yy[i][5] = 0.5 * (yym + yy[i][6]);
2813 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
2814 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
2815 xx[i][7] = patch->x[i][3];
2816 yy[i][7] = patch->y[i][3];
2818 for (i = 0; i < 4; ++i) {
2819 patch00.x[0][i] = xx[0][i];
2820 patch00.y[0][i] = yy[0][i];
2821 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
2822 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
2823 xxm = 0.5 * (xx[1][i] + xx[2][i]);
2824 yym = 0.5 * (yy[1][i] + yy[2][i]);
2825 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
2826 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
2827 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
2828 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
2829 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
2830 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
2831 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
2832 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
2833 patch10.x[0][i] = patch00.x[3][i];
2834 patch10.y[0][i] = patch00.y[3][i];
2835 patch10.x[3][i] = xx[3][i];
2836 patch10.y[3][i] = yy[3][i];
2838 for (i = 4; i < 8; ++i) {
2839 patch01.x[0][i-4] = xx[0][i];
2840 patch01.y[0][i-4] = yy[0][i];
2841 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
2842 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
2843 xxm = 0.5 * (xx[1][i] + xx[2][i]);
2844 yym = 0.5 * (yy[1][i] + yy[2][i]);
2845 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
2846 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
2847 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
2848 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
2849 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
2850 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
2851 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
2852 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
2853 patch11.x[0][i-4] = patch01.x[3][i-4];
2854 patch11.y[0][i-4] = patch01.y[3][i-4];
2855 patch11.x[3][i-4] = xx[3][i];
2856 patch11.y[3][i-4] = yy[3][i];
2858 //~ if the shading has a Function, this should interpolate on the
2859 //~ function parameter, not on the color components
2860 for (i = 0; i < nComps; ++i) {
2861 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
2862 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
2863 patch->color[0][1].c[i]) / 2;
2864 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
2865 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
2866 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
2867 patch->color[1][1].c[i]) / 2;
2868 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
2869 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
2870 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
2871 patch->color[1][0].c[i]) / 2;
2872 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
2873 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
2874 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
2875 patch->color[0][0].c[i]) / 2;
2876 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
2877 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
2878 patch01.color[1][1].c[i]) / 2;
2879 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
2880 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
2881 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
2883 fillPatch(&patch00, nComps, depth + 1);
2884 fillPatch(&patch10, nComps, depth + 1);
2885 fillPatch(&patch01, nComps, depth + 1);
2886 fillPatch(&patch11, nComps, depth + 1);
2890 void Gfx::doEndPath() {
2891 if (state->isCurPt() && clip != clipNone) {
2892 state->clip();
2893 if (clip == clipNormal) {
2894 out->clip(state);
2895 } else {
2896 out->eoClip(state);
2899 clip = clipNone;
2900 state->clearPath();
2903 //------------------------------------------------------------------------
2904 // path clipping operators
2905 //------------------------------------------------------------------------
2907 void Gfx::opClip(xObject args[], int numArgs) {
2908 clip = clipNormal;
2911 void Gfx::opEOClip(xObject args[], int numArgs) {
2912 clip = clipEO;
2915 //------------------------------------------------------------------------
2916 // text xObject operators
2917 //------------------------------------------------------------------------
2919 void Gfx::opBeginText(xObject args[], int numArgs) {
2920 state->setTextMat(1, 0, 0, 1, 0, 0);
2921 state->textMoveTo(0, 0);
2922 out->updateTextMat(state);
2923 out->updateTextPos(state);
2924 fontChanged = gTrue;
2927 void Gfx::opEndText(xObject args[], int numArgs) {
2928 out->endTextObject(state);
2931 //------------------------------------------------------------------------
2932 // text state operators
2933 //------------------------------------------------------------------------
2935 void Gfx::opSetCharSpacing(xObject args[], int numArgs) {
2936 state->setCharSpace(args[0].getNum());
2937 out->updateCharSpace(state);
2940 void Gfx::opSetFont(xObject args[], int numArgs) {
2941 GfxFont *font;
2943 if (!(font = res->lookupFont(args[0].getName()))) {
2944 return;
2946 if (printCommands) {
2947 printf(" font: tag=%s name='%s' %g\n",
2948 font->getTag()->getCString(),
2949 font->getName() ? font->getName()->getCString() : "???",
2950 args[1].getNum());
2951 fflush(stdout);
2953 state->setFont(font, args[1].getNum());
2954 fontChanged = gTrue;
2957 void Gfx::opSetTextLeading(xObject args[], int numArgs) {
2958 state->setLeading(args[0].getNum());
2961 void Gfx::opSetTextRender(xObject args[], int numArgs) {
2962 state->setRender(args[0].getInt());
2963 out->updateRender(state);
2966 void Gfx::opSetTextRise(xObject args[], int numArgs) {
2967 state->setRise(args[0].getNum());
2968 out->updateRise(state);
2971 void Gfx::opSetWordSpacing(xObject args[], int numArgs) {
2972 state->setWordSpace(args[0].getNum());
2973 out->updateWordSpace(state);
2976 void Gfx::opSetHorizScaling(xObject args[], int numArgs) {
2977 state->setHorizScaling(args[0].getNum());
2978 out->updateHorizScaling(state);
2979 fontChanged = gTrue;
2982 //------------------------------------------------------------------------
2983 // text positioning operators
2984 //------------------------------------------------------------------------
2986 void Gfx::opTextMove(xObject args[], int numArgs) {
2987 double tx, ty;
2989 tx = state->getLineX() + args[0].getNum();
2990 ty = state->getLineY() + args[1].getNum();
2991 state->textMoveTo(tx, ty);
2992 out->updateTextPos(state);
2995 void Gfx::opTextMoveSet(xObject args[], int numArgs) {
2996 double tx, ty;
2998 tx = state->getLineX() + args[0].getNum();
2999 ty = args[1].getNum();
3000 state->setLeading(-ty);
3001 ty += state->getLineY();
3002 state->textMoveTo(tx, ty);
3003 out->updateTextPos(state);
3006 void Gfx::opSetTextMatrix(xObject args[], int numArgs) {
3007 state->setTextMat(args[0].getNum(), args[1].getNum(),
3008 args[2].getNum(), args[3].getNum(),
3009 args[4].getNum(), args[5].getNum());
3010 state->textMoveTo(0, 0);
3011 out->updateTextMat(state);
3012 out->updateTextPos(state);
3013 fontChanged = gTrue;
3016 void Gfx::opTextNextLine(xObject args[], int numArgs) {
3017 double tx, ty;
3019 tx = state->getLineX();
3020 ty = state->getLineY() - state->getLeading();
3021 state->textMoveTo(tx, ty);
3022 out->updateTextPos(state);
3025 //------------------------------------------------------------------------
3026 // text string operators
3027 //------------------------------------------------------------------------
3029 void Gfx::opShowText(xObject args[], int numArgs) {
3030 if (!state->getFont()) {
3031 error(getPos(), "No font in show");
3032 return;
3034 if (fontChanged) {
3035 out->updateFont(state);
3036 fontChanged = gFalse;
3038 out->beginStringOp(state);
3039 doShowText(args[0].getString());
3040 out->endStringOp(state);
3043 void Gfx::opMoveShowText(xObject args[], int numArgs) {
3044 double tx, ty;
3046 if (!state->getFont()) {
3047 error(getPos(), "No font in move/show");
3048 return;
3050 if (fontChanged) {
3051 out->updateFont(state);
3052 fontChanged = gFalse;
3054 tx = state->getLineX();
3055 ty = state->getLineY() - state->getLeading();
3056 state->textMoveTo(tx, ty);
3057 out->updateTextPos(state);
3058 out->beginStringOp(state);
3059 doShowText(args[0].getString());
3060 out->endStringOp(state);
3063 void Gfx::opMoveSetShowText(xObject args[], int numArgs) {
3064 double tx, ty;
3066 if (!state->getFont()) {
3067 error(getPos(), "No font in move/set/show");
3068 return;
3070 if (fontChanged) {
3071 out->updateFont(state);
3072 fontChanged = gFalse;
3074 state->setWordSpace(args[0].getNum());
3075 state->setCharSpace(args[1].getNum());
3076 tx = state->getLineX();
3077 ty = state->getLineY() - state->getLeading();
3078 state->textMoveTo(tx, ty);
3079 out->updateWordSpace(state);
3080 out->updateCharSpace(state);
3081 out->updateTextPos(state);
3082 out->beginStringOp(state);
3083 doShowText(args[2].getString());
3084 out->endStringOp(state);
3087 void Gfx::opShowSpaceText(xObject args[], int numArgs) {
3088 Array *a;
3089 xObject obj;
3090 int wMode;
3091 int i;
3093 if (!state->getFont()) {
3094 error(getPos(), "No font in show/space");
3095 return;
3097 if (fontChanged) {
3098 out->updateFont(state);
3099 fontChanged = gFalse;
3101 out->beginStringOp(state);
3102 wMode = state->getFont()->getWMode();
3103 a = args[0].getArray();
3104 for (i = 0; i < a->getLength(); ++i) {
3105 a->get(i, &obj);
3106 if (obj.isNum()) {
3107 // this uses the absolute value of the font size to match
3108 // Acrobat's behavior
3109 if (wMode) {
3110 state->textShift(0, -obj.getNum() * 0.001 *
3111 fabs(state->getFontSize()));
3112 } else {
3113 state->textShift(-obj.getNum() * 0.001 *
3114 fabs(state->getFontSize()), 0);
3116 out->updateTextShift(state, obj.getNum());
3117 } else if (obj.isString()) {
3118 doShowText(obj.getString());
3119 } else {
3120 error(getPos(), "Element of show/space array must be number or string");
3122 obj.free();
3124 out->endStringOp(state);
3127 void Gfx::doShowText(GString *s) {
3128 GfxFont *font;
3129 int wMode;
3130 double riseX, riseY;
3131 CharCode code;
3132 Unicode u[8];
3133 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
3134 double originX, originY, tOriginX, tOriginY;
3135 double oldCTM[6], newCTM[6];
3136 double *mat;
3137 xObject charProc;
3138 Dict *resDict;
3139 Parser *oldParser;
3140 char *p;
3141 int len, n, uLen, nChars, nSpaces, i;
3143 font = state->getFont();
3144 wMode = font->getWMode();
3146 if (out->useDrawChar()) {
3147 out->beginString(state, s);
3150 // handle a Type 3 char
3151 if (font->getType() == fontType3 && out->interpretType3Chars()) {
3152 mat = state->getCTM();
3153 for (i = 0; i < 6; ++i) {
3154 oldCTM[i] = mat[i];
3156 mat = state->getTextMat();
3157 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
3158 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
3159 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
3160 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
3161 mat = font->getFontMatrix();
3162 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
3163 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
3164 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
3165 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
3166 newCTM[0] *= state->getFontSize();
3167 newCTM[1] *= state->getFontSize();
3168 newCTM[2] *= state->getFontSize();
3169 newCTM[3] *= state->getFontSize();
3170 newCTM[0] *= state->getHorizScaling();
3171 newCTM[2] *= state->getHorizScaling();
3172 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3173 curX = state->getCurX();
3174 curY = state->getCurY();
3175 lineX = state->getLineX();
3176 lineY = state->getLineY();
3177 oldParser = parser;
3178 p = s->getCString();
3179 len = s->getLength();
3180 while (len > 0) {
3181 n = font->getNextChar(p, len, &code,
3182 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3183 &dx, &dy, &originX, &originY);
3184 dx = dx * state->getFontSize() + state->getCharSpace();
3185 if (n == 1 && *p == ' ') {
3186 dx += state->getWordSpace();
3188 dx *= state->getHorizScaling();
3189 dy *= state->getFontSize();
3190 state->textTransformDelta(dx, dy, &tdx, &tdy);
3191 state->transform(curX + riseX, curY + riseY, &x, &y);
3192 saveState();
3193 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
3194 //~ the CTM concat values here are wrong (but never used)
3195 out->updateCTM(state, 1, 0, 0, 1, 0, 0);
3196 if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
3197 code, u, uLen)) {
3198 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
3199 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
3200 pushResources(resDict);
3202 if (charProc.isStream()) {
3203 display(&charProc, gFalse);
3204 } else {
3205 error(getPos(), "Missing or bad Type3 CharProc entry");
3207 out->endType3Char(state);
3208 if (resDict) {
3209 popResources();
3211 charProc.free();
3213 restoreState();
3214 // GfxState::restore() does *not* restore the current position,
3215 // so we deal with it here using (curX, curY) and (lineX, lineY)
3216 curX += tdx;
3217 curY += tdy;
3218 state->moveTo(curX, curY);
3219 state->textSetPos(lineX, lineY);
3220 p += n;
3221 len -= n;
3223 parser = oldParser;
3225 } else if (out->useDrawChar()) {
3226 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3227 p = s->getCString();
3228 len = s->getLength();
3229 while (len > 0) {
3230 n = font->getNextChar(p, len, &code,
3231 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3232 &dx, &dy, &originX, &originY);
3233 if (wMode) {
3234 dx *= state->getFontSize();
3235 dy = dy * state->getFontSize() + state->getCharSpace();
3236 if (n == 1 && *p == ' ') {
3237 dy += state->getWordSpace();
3239 } else {
3240 dx = dx * state->getFontSize() + state->getCharSpace();
3241 if (n == 1 && *p == ' ') {
3242 dx += state->getWordSpace();
3244 dx *= state->getHorizScaling();
3245 dy *= state->getFontSize();
3247 state->textTransformDelta(dx, dy, &tdx, &tdy);
3248 originX *= state->getFontSize();
3249 originY *= state->getFontSize();
3250 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
3251 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
3252 tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
3253 state->shift(tdx, tdy);
3254 p += n;
3255 len -= n;
3258 } else {
3259 dx = dy = 0;
3260 p = s->getCString();
3261 len = s->getLength();
3262 nChars = nSpaces = 0;
3263 while (len > 0) {
3264 n = font->getNextChar(p, len, &code,
3265 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3266 &dx2, &dy2, &originX, &originY);
3267 dx += dx2;
3268 dy += dy2;
3269 if (n == 1 && *p == ' ') {
3270 ++nSpaces;
3272 ++nChars;
3273 p += n;
3274 len -= n;
3276 if (wMode) {
3277 dx *= state->getFontSize();
3278 dy = dy * state->getFontSize()
3279 + nChars * state->getCharSpace()
3280 + nSpaces * state->getWordSpace();
3281 } else {
3282 dx = dx * state->getFontSize()
3283 + nChars * state->getCharSpace()
3284 + nSpaces * state->getWordSpace();
3285 dx *= state->getHorizScaling();
3286 dy *= state->getFontSize();
3288 state->textTransformDelta(dx, dy, &tdx, &tdy);
3289 out->drawString(state, s);
3290 state->shift(tdx, tdy);
3293 if (out->useDrawChar()) {
3294 out->endString(state);
3297 updateLevel += 10 * s->getLength();
3300 //------------------------------------------------------------------------
3301 // XObject operators
3302 //------------------------------------------------------------------------
3304 void Gfx::opXObject(xObject args[], int numArgs) {
3305 char *name;
3306 xObject obj1, obj2, obj3, refObj;
3307 #if OPI_SUPPORT
3308 xObject opiDict;
3309 #endif
3311 name = args[0].getName();
3312 if (!res->lookupXObject(name, &obj1)) {
3313 return;
3315 if (!obj1.isStream()) {
3316 error(getPos(), "XObject '%s' is wrong type", name);
3317 obj1.free();
3318 return;
3320 #if OPI_SUPPORT
3321 obj1.streamGetDict()->lookup("OPI", &opiDict);
3322 if (opiDict.isDict()) {
3323 out->opiBegin(state, opiDict.getDict());
3325 #endif
3326 obj1.streamGetDict()->lookup("Subtype", &obj2);
3327 if (obj2.isName("Image")) {
3328 if (out->needNonText()) {
3329 res->lookupXObjectNF(name, &refObj);
3330 doImage(&refObj, obj1.getStream(), gFalse);
3331 refObj.free();
3333 } else if (obj2.isName("Form")) {
3334 res->lookupXObjectNF(name, &refObj);
3335 if (out->useDrawForm() && refObj.isRef()) {
3336 out->drawForm(refObj.getRef());
3337 } else {
3338 doForm(&obj1);
3340 refObj.free();
3341 } else if (obj2.isName("PS")) {
3342 obj1.streamGetDict()->lookup("Level1", &obj3);
3343 out->psXObject(obj1.getStream(),
3344 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
3345 } else if (obj2.isName()) {
3346 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
3347 } else {
3348 error(getPos(), "XObject subtype is missing or wrong type");
3350 obj2.free();
3351 #if OPI_SUPPORT
3352 if (opiDict.isDict()) {
3353 out->opiEnd(state, opiDict.getDict());
3355 opiDict.free();
3356 #endif
3357 obj1.free();
3360 void Gfx::doImage(xObject *ref, Stream *str, GBool inlineImg) {
3361 Dict *dict, *maskDict;
3362 int width, height;
3363 int bits, maskBits;
3364 StreamColorSpaceMode csMode;
3365 GBool mask;
3366 GBool invert;
3367 GfxColorSpace *colorSpace, *maskColorSpace;
3368 GfxImageColorMap *colorMap, *maskColorMap;
3369 xObject maskObj, smaskObj;
3370 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
3371 int maskColors[2*gfxColorMaxComps];
3372 int maskWidth, maskHeight;
3373 GBool maskInvert;
3374 Stream *maskStr;
3375 xObject obj1, obj2;
3376 int i;
3378 // get info from the stream
3379 bits = 0;
3380 csMode = streamCSNone;
3381 str->getImageParams(&bits, &csMode);
3383 // get stream dict
3384 dict = str->getDict();
3386 // get size
3387 dict->lookup("Width", &obj1);
3388 if (obj1.isNull()) {
3389 obj1.free();
3390 dict->lookup("W", &obj1);
3392 if (!obj1.isInt())
3393 goto err2;
3394 width = obj1.getInt();
3395 obj1.free();
3396 dict->lookup("Height", &obj1);
3397 if (obj1.isNull()) {
3398 obj1.free();
3399 dict->lookup("H", &obj1);
3401 if (!obj1.isInt())
3402 goto err2;
3403 height = obj1.getInt();
3404 obj1.free();
3406 // image or mask?
3407 dict->lookup("ImageMask", &obj1);
3408 if (obj1.isNull()) {
3409 obj1.free();
3410 dict->lookup("IM", &obj1);
3412 mask = gFalse;
3413 if (obj1.isBool())
3414 mask = obj1.getBool();
3415 else if (!obj1.isNull())
3416 goto err2;
3417 obj1.free();
3419 // bit depth
3420 if (bits == 0) {
3421 dict->lookup("BitsPerComponent", &obj1);
3422 if (obj1.isNull()) {
3423 obj1.free();
3424 dict->lookup("BPC", &obj1);
3426 if (obj1.isInt()) {
3427 bits = obj1.getInt();
3428 } else if (mask) {
3429 bits = 1;
3430 } else {
3431 goto err2;
3433 obj1.free();
3436 // display a mask
3437 if (mask) {
3439 // check for inverted mask
3440 if (bits != 1)
3441 goto err1;
3442 invert = gFalse;
3443 dict->lookup("Decode", &obj1);
3444 if (obj1.isNull()) {
3445 obj1.free();
3446 dict->lookup("D", &obj1);
3448 if (obj1.isArray()) {
3449 obj1.arrayGet(0, &obj2);
3450 if (obj2.isInt() && obj2.getInt() == 1)
3451 invert = gTrue;
3452 obj2.free();
3453 } else if (!obj1.isNull()) {
3454 goto err2;
3456 obj1.free();
3458 // draw it
3459 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
3461 } else {
3463 // get color space and color map
3464 dict->lookup("ColorSpace", &obj1);
3465 if (obj1.isNull()) {
3466 obj1.free();
3467 dict->lookup("CS", &obj1);
3469 if (obj1.isName()) {
3470 res->lookupColorSpace(obj1.getName(), &obj2);
3471 if (!obj2.isNull()) {
3472 obj1.free();
3473 obj1 = obj2;
3474 } else {
3475 obj2.free();
3478 if (!obj1.isNull()) {
3479 colorSpace = GfxColorSpace::parse(&obj1);
3480 } else if (csMode == streamCSDeviceGray) {
3481 colorSpace = new GfxDeviceGrayColorSpace();
3482 } else if (csMode == streamCSDeviceRGB) {
3483 colorSpace = new GfxDeviceRGBColorSpace();
3484 } else if (csMode == streamCSDeviceCMYK) {
3485 colorSpace = new GfxDeviceCMYKColorSpace();
3486 } else {
3487 colorSpace = NULL;
3489 obj1.free();
3490 if (!colorSpace) {
3491 goto err1;
3493 dict->lookup("Decode", &obj1);
3494 if (obj1.isNull()) {
3495 obj1.free();
3496 dict->lookup("D", &obj1);
3498 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
3499 obj1.free();
3500 if (!colorMap->isOk()) {
3501 delete colorMap;
3502 goto err1;
3505 // get the mask
3506 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
3507 maskStr = NULL; // make gcc happy
3508 maskWidth = maskHeight = 0; // make gcc happy
3509 maskInvert = gFalse; // make gcc happy
3510 maskColorMap = NULL; // make gcc happy
3511 dict->lookup("Mask", &maskObj);
3512 dict->lookup("SMask", &smaskObj);
3513 if (smaskObj.isStream()) {
3514 // soft mask
3515 if (inlineImg) {
3516 goto err1;
3518 maskStr = smaskObj.getStream();
3519 maskDict = smaskObj.streamGetDict();
3520 maskDict->lookup("Width", &obj1);
3521 if (obj1.isNull()) {
3522 obj1.free();
3523 maskDict->lookup("W", &obj1);
3525 if (!obj1.isInt()) {
3526 goto err2;
3528 maskWidth = obj1.getInt();
3529 obj1.free();
3530 maskDict->lookup("Height", &obj1);
3531 if (obj1.isNull()) {
3532 obj1.free();
3533 maskDict->lookup("H", &obj1);
3535 if (!obj1.isInt()) {
3536 goto err2;
3538 maskHeight = obj1.getInt();
3539 obj1.free();
3540 maskDict->lookup("BitsPerComponent", &obj1);
3541 if (obj1.isNull()) {
3542 obj1.free();
3543 maskDict->lookup("BPC", &obj1);
3545 if (!obj1.isInt()) {
3546 goto err2;
3548 maskBits = obj1.getInt();
3549 obj1.free();
3550 maskDict->lookup("ColorSpace", &obj1);
3551 if (obj1.isNull()) {
3552 obj1.free();
3553 maskDict->lookup("CS", &obj1);
3555 if (obj1.isName()) {
3556 res->lookupColorSpace(obj1.getName(), &obj2);
3557 if (!obj2.isNull()) {
3558 obj1.free();
3559 obj1 = obj2;
3560 } else {
3561 obj2.free();
3564 maskColorSpace = GfxColorSpace::parse(&obj1);
3565 obj1.free();
3566 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
3567 goto err1;
3569 maskDict->lookup("Decode", &obj1);
3570 if (obj1.isNull()) {
3571 obj1.free();
3572 maskDict->lookup("D", &obj1);
3574 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
3575 obj1.free();
3576 if (!maskColorMap->isOk()) {
3577 delete maskColorMap;
3578 goto err1;
3580 //~ handle the Matte entry
3581 haveSoftMask = gTrue;
3582 } else if (maskObj.isArray()) {
3583 // color key mask
3584 for (i = 0;
3585 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
3586 ++i) {
3587 maskObj.arrayGet(i, &obj1);
3588 maskColors[i] = obj1.getInt();
3589 obj1.free();
3591 haveColorKeyMask = gTrue;
3592 } else if (maskObj.isStream()) {
3593 // explicit mask
3594 if (inlineImg) {
3595 goto err1;
3597 maskStr = maskObj.getStream();
3598 maskDict = maskObj.streamGetDict();
3599 maskDict->lookup("Width", &obj1);
3600 if (obj1.isNull()) {
3601 obj1.free();
3602 maskDict->lookup("W", &obj1);
3604 if (!obj1.isInt()) {
3605 goto err2;
3607 maskWidth = obj1.getInt();
3608 obj1.free();
3609 maskDict->lookup("Height", &obj1);
3610 if (obj1.isNull()) {
3611 obj1.free();
3612 maskDict->lookup("H", &obj1);
3614 if (!obj1.isInt()) {
3615 goto err2;
3617 maskHeight = obj1.getInt();
3618 obj1.free();
3619 maskDict->lookup("ImageMask", &obj1);
3620 if (obj1.isNull()) {
3621 obj1.free();
3622 maskDict->lookup("IM", &obj1);
3624 if (!obj1.isBool() || !obj1.getBool()) {
3625 goto err2;
3627 obj1.free();
3628 maskInvert = gFalse;
3629 maskDict->lookup("Decode", &obj1);
3630 if (obj1.isNull()) {
3631 obj1.free();
3632 maskDict->lookup("D", &obj1);
3634 if (obj1.isArray()) {
3635 obj1.arrayGet(0, &obj2);
3636 if (obj2.isInt() && obj2.getInt() == 1) {
3637 maskInvert = gTrue;
3639 obj2.free();
3640 } else if (!obj1.isNull()) {
3641 goto err2;
3643 obj1.free();
3644 haveExplicitMask = gTrue;
3647 // draw it
3648 if (haveSoftMask) {
3649 out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
3650 maskStr, maskWidth, maskHeight, maskColorMap);
3651 delete maskColorMap;
3652 } else if (haveExplicitMask) {
3653 out->drawMaskedImage(state, ref, str, width, height, colorMap,
3654 maskStr, maskWidth, maskHeight, maskInvert);
3655 } else {
3656 out->drawImage(state, ref, str, width, height, colorMap,
3657 haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
3659 delete colorMap;
3661 maskObj.free();
3662 smaskObj.free();
3665 if ((i = width * height) > 1000) {
3666 i = 1000;
3668 updateLevel += i;
3670 return;
3672 err2:
3673 obj1.free();
3674 err1:
3675 error(getPos(), "Bad image parameters");
3678 void Gfx::doForm(xObject *str) {
3679 Dict *dict;
3680 GBool transpGroup, isolated, knockout;
3681 GfxColorSpace *blendingColorSpace;
3682 xObject matrixObj, bboxObj;
3683 double m[6], bbox[4];
3684 xObject resObj;
3685 Dict *resDict;
3686 xObject obj1, obj2, obj3;
3687 int i;
3689 // check for excessive recursion
3690 if (formDepth > 20) {
3691 return;
3694 // get stream dict
3695 dict = str->streamGetDict();
3697 // check form type
3698 dict->lookup("FormType", &obj1);
3699 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
3700 error(getPos(), "Unknown form type");
3702 obj1.free();
3704 // get bounding box
3705 dict->lookup("BBox", &bboxObj);
3706 if (!bboxObj.isArray()) {
3707 bboxObj.free();
3708 error(getPos(), "Bad form bounding box");
3709 return;
3711 for (i = 0; i < 4; ++i) {
3712 bboxObj.arrayGet(i, &obj1);
3713 bbox[i] = obj1.getNum();
3714 obj1.free();
3716 bboxObj.free();
3718 // get matrix
3719 dict->lookup("Matrix", &matrixObj);
3720 if (matrixObj.isArray()) {
3721 for (i = 0; i < 6; ++i) {
3722 matrixObj.arrayGet(i, &obj1);
3723 m[i] = obj1.getNum();
3724 obj1.free();
3726 } else {
3727 m[0] = 1; m[1] = 0;
3728 m[2] = 0; m[3] = 1;
3729 m[4] = 0; m[5] = 0;
3731 matrixObj.free();
3733 // get resources
3734 dict->lookup("Resources", &resObj);
3735 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
3737 // check for a transparency group
3738 transpGroup = isolated = knockout = gFalse;
3739 blendingColorSpace = NULL;
3740 if (dict->lookup("Group", &obj1)->isDict()) {
3741 if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
3742 transpGroup = gTrue;
3743 if (!obj1.dictLookup("CS", &obj3)->isNull()) {
3744 blendingColorSpace = GfxColorSpace::parse(&obj3);
3746 obj3.free();
3747 if (obj1.dictLookup("I", &obj3)->isBool()) {
3748 isolated = obj3.getBool();
3750 obj3.free();
3751 if (obj1.dictLookup("K", &obj3)->isBool()) {
3752 knockout = obj3.getBool();
3754 obj3.free();
3756 obj2.free();
3758 obj1.free();
3760 // draw it
3761 ++formDepth;
3762 doForm1(str, resDict, m, bbox,
3763 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
3764 --formDepth;
3766 if (blendingColorSpace) {
3767 delete blendingColorSpace;
3769 resObj.free();
3772 void Gfx::doForm1(xObject *str, Dict *resDict, double *matrix, double *bbox,
3773 GBool transpGroup, GBool softMask,
3774 GfxColorSpace *blendingColorSpace,
3775 GBool isolated, GBool knockout,
3776 GBool alpha, Function *transferFunc,
3777 GfxColor *backdropColor) {
3778 Parser *oldParser;
3779 double oldBaseMatrix[6];
3780 int i;
3782 // push new resources on stack
3783 pushResources(resDict);
3785 // save current graphics state
3786 saveState();
3788 // kill any pre-existing path
3789 state->clearPath();
3791 // save current parser
3792 oldParser = parser;
3794 // set form transformation matrix
3795 state->concatCTM(matrix[0], matrix[1], matrix[2],
3796 matrix[3], matrix[4], matrix[5]);
3797 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
3798 matrix[3], matrix[4], matrix[5]);
3800 // set form bounding box
3801 state->moveTo(bbox[0], bbox[1]);
3802 state->lineTo(bbox[2], bbox[1]);
3803 state->lineTo(bbox[2], bbox[3]);
3804 state->lineTo(bbox[0], bbox[3]);
3805 state->closePath();
3806 state->clip();
3807 out->clip(state);
3808 state->clearPath();
3810 if (softMask || transpGroup) {
3811 if (state->getBlendMode() != gfxBlendNormal) {
3812 state->setBlendMode(gfxBlendNormal);
3813 out->updateBlendMode(state);
3815 if (state->getFillOpacity() != 1) {
3816 state->setFillOpacity(1);
3817 out->updateFillOpacity(state);
3819 if (state->getStrokeOpacity() != 1) {
3820 state->setStrokeOpacity(1);
3821 out->updateStrokeOpacity(state);
3823 out->clearSoftMask(state);
3824 out->beginTransparencyGroup(state, bbox, blendingColorSpace,
3825 isolated, knockout, softMask);
3828 // set new base matrix
3829 for (i = 0; i < 6; ++i) {
3830 oldBaseMatrix[i] = baseMatrix[i];
3831 baseMatrix[i] = state->getCTM()[i];
3834 // draw the form
3835 display(str, gFalse);
3837 if (softMask || transpGroup) {
3838 out->endTransparencyGroup(state);
3841 // restore base matrix
3842 for (i = 0; i < 6; ++i) {
3843 baseMatrix[i] = oldBaseMatrix[i];
3846 // restore parser
3847 parser = oldParser;
3849 // restore graphics state
3850 restoreState();
3852 // pop resource stack
3853 popResources();
3855 if (softMask) {
3856 out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
3857 } else if (transpGroup) {
3858 out->paintTransparencyGroup(state, bbox);
3861 return;
3864 //------------------------------------------------------------------------
3865 // in-line image operators
3866 //------------------------------------------------------------------------
3868 void Gfx::opBeginImage(xObject args[], int numArgs) {
3869 Stream *str;
3870 int c1, c2;
3872 // build dict/stream
3873 str = buildImageStream();
3875 // display the image
3876 if (str) {
3877 doImage(NULL, str, gTrue);
3879 // skip 'EI' tag
3880 c1 = str->getUndecodedStream()->getChar();
3881 c2 = str->getUndecodedStream()->getChar();
3882 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
3883 c1 = c2;
3884 c2 = str->getUndecodedStream()->getChar();
3886 delete str;
3890 Stream *Gfx::buildImageStream() {
3891 xObject dict;
3892 xObject obj;
3893 char *key;
3894 Stream *str;
3896 // build dictionary
3897 dict.initDict(xref);
3898 parser->getObj(&obj);
3899 while (!obj.isCmd("ID") && !obj.isEOF()) {
3900 if (!obj.isName()) {
3901 error(getPos(), "Inline image dictionary key must be a name xObject");
3902 obj.free();
3903 } else {
3904 key = copyString(obj.getName());
3905 obj.free();
3906 parser->getObj(&obj);
3907 if (obj.isEOF() || obj.isError()) {
3908 gfree(key);
3909 break;
3911 dict.dictAdd(key, &obj);
3913 parser->getObj(&obj);
3915 if (obj.isEOF()) {
3916 error(getPos(), "End of file in inline image");
3917 obj.free();
3918 dict.free();
3919 return NULL;
3921 obj.free();
3923 // make stream
3924 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3925 str = str->addFilters(&dict);
3927 return str;
3930 void Gfx::opImageData(xObject args[], int numArgs) {
3931 error(getPos(), "Internal: got 'ID' operator");
3934 void Gfx::opEndImage(xObject args[], int numArgs) {
3935 error(getPos(), "Internal: got 'EI' operator");
3938 //------------------------------------------------------------------------
3939 // type 3 font operators
3940 //------------------------------------------------------------------------
3942 void Gfx::opSetCharWidth(xObject args[], int numArgs) {
3943 out->type3D0(state, args[0].getNum(), args[1].getNum());
3946 void Gfx::opSetCacheDevice(xObject args[], int numArgs) {
3947 out->type3D1(state, args[0].getNum(), args[1].getNum(),
3948 args[2].getNum(), args[3].getNum(),
3949 args[4].getNum(), args[5].getNum());
3952 //------------------------------------------------------------------------
3953 // compatibility operators
3954 //------------------------------------------------------------------------
3956 void Gfx::opBeginIgnoreUndef(xObject args[], int numArgs) {
3957 ++ignoreUndef;
3960 void Gfx::opEndIgnoreUndef(xObject args[], int numArgs) {
3961 if (ignoreUndef > 0)
3962 --ignoreUndef;
3965 //------------------------------------------------------------------------
3966 // marked content operators
3967 //------------------------------------------------------------------------
3969 void Gfx::opBeginMarkedContent(xObject args[], int numArgs) {
3970 if (printCommands) {
3971 printf(" marked content: %s ", args[0].getName());
3972 if (numArgs == 2)
3973 args[2].print(stdout);
3974 printf("\n");
3975 fflush(stdout);
3979 void Gfx::opEndMarkedContent(xObject args[], int numArgs) {
3982 void Gfx::opMarkPoint(xObject args[], int numArgs) {
3983 if (printCommands) {
3984 printf(" mark point: %s ", args[0].getName());
3985 if (numArgs == 2)
3986 args[2].print(stdout);
3987 printf("\n");
3988 fflush(stdout);
3992 //------------------------------------------------------------------------
3993 // misc
3994 //------------------------------------------------------------------------
3996 void Gfx::drawAnnot(xObject *str, AnnotBorderStyle *borderStyle,
3997 double xMin, double yMin, double xMax, double yMax) {
3998 Dict *dict, *resDict;
3999 xObject matrixObj, bboxObj, resObj;
4000 xObject obj1;
4001 double m[6], bbox[4], ictm[6];
4002 double *ctm;
4003 double formX0, formY0, formX1, formY1;
4004 double annotX0, annotY0, annotX1, annotY1;
4005 double det, x, y, sx, sy;
4006 double r, g, b;
4007 GfxColor color;
4008 double *dash, *dash2;
4009 int dashLength;
4010 int i;
4012 //~ can we assume that we're in default user space?
4013 //~ (i.e., baseMatrix = ctm)
4015 // transform the annotation bbox from default user space to user
4016 // space: (bbox * baseMatrix) * iCTM
4017 ctm = state->getCTM();
4018 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
4019 ictm[0] = ctm[3] * det;
4020 ictm[1] = -ctm[1] * det;
4021 ictm[2] = -ctm[2] * det;
4022 ictm[3] = ctm[0] * det;
4023 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
4024 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
4025 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
4026 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
4027 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
4028 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
4029 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
4030 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
4031 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
4032 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
4033 if (annotX0 > annotX1) {
4034 x = annotX0; annotX0 = annotX1; annotX1 = x;
4036 if (annotY0 > annotY1) {
4037 y = annotY0; annotY0 = annotY1; annotY1 = y;
4040 // draw the appearance stream (if there is one)
4041 if (str->isStream()) {
4043 // get stream dict
4044 dict = str->streamGetDict();
4046 // get the form bounding box
4047 dict->lookup("BBox", &bboxObj);
4048 if (!bboxObj.isArray()) {
4049 bboxObj.free();
4050 error(getPos(), "Bad form bounding box");
4051 return;
4053 for (i = 0; i < 4; ++i) {
4054 bboxObj.arrayGet(i, &obj1);
4055 bbox[i] = obj1.getNum();
4056 obj1.free();
4058 bboxObj.free();
4060 // get the form matrix
4061 dict->lookup("Matrix", &matrixObj);
4062 if (matrixObj.isArray()) {
4063 for (i = 0; i < 6; ++i) {
4064 matrixObj.arrayGet(i, &obj1);
4065 m[i] = obj1.getNum();
4066 obj1.free();
4068 } else {
4069 m[0] = 1; m[1] = 0;
4070 m[2] = 0; m[3] = 1;
4071 m[4] = 0; m[5] = 0;
4073 matrixObj.free();
4075 // transform the form bbox from form space to user space
4076 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
4077 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
4078 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
4079 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
4080 if (formX0 > formX1) {
4081 x = formX0; formX0 = formX1; formX1 = x;
4083 if (formY0 > formY1) {
4084 y = formY0; formY0 = formY1; formY1 = y;
4087 // scale the form to fit the annotation bbox
4088 if (formX1 == formX0) {
4089 // this shouldn't happen
4090 sx = 1;
4091 } else {
4092 sx = (annotX1 - annotX0) / (formX1 - formX0);
4094 if (formY1 == formY0) {
4095 // this shouldn't happen
4096 sy = 1;
4097 } else {
4098 sy = (annotY1 - annotY0) / (formY1 - formY0);
4100 m[0] *= sx;
4101 m[2] *= sx;
4102 m[4] = (m[4] - formX0) * sx + annotX0;
4103 m[1] *= sy;
4104 m[3] *= sy;
4105 m[5] = (m[5] - formY0) * sy + annotY0;
4107 // get resources
4108 dict->lookup("Resources", &resObj);
4109 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4111 // draw it
4112 doForm1(str, resDict, m, bbox);
4114 resObj.free();
4117 // draw the border
4118 if (borderStyle && borderStyle->getWidth() > 0) {
4119 if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
4120 state->setStrokePattern(NULL);
4121 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
4122 out->updateStrokeColorSpace(state);
4124 borderStyle->getColor(&r, &g, &b);
4125 color.c[0] = dblToCol(r);
4126 color.c[1] = dblToCol(g);
4127 color.c[2] = dblToCol(b);
4128 state->setStrokeColor(&color);
4129 out->updateStrokeColor(state);
4130 // compute the width scale factor when going from default user
4131 // space to user space
4132 x = (baseMatrix[0] + baseMatrix[2]) * ictm[0] +
4133 (baseMatrix[1] + baseMatrix[3]) * ictm[2];
4134 y = (baseMatrix[0] + baseMatrix[2]) * ictm[1] +
4135 (baseMatrix[1] + baseMatrix[3]) * ictm[3];
4136 x = sqrt(0.5 * (x * x + y * y));
4137 state->setLineWidth(x * borderStyle->getWidth());
4138 out->updateLineWidth(state);
4139 borderStyle->getDash(&dash, &dashLength);
4140 if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
4141 dash2 = (double *)gmallocn(dashLength, sizeof(double));
4142 for (i = 0; i < dashLength; ++i) {
4143 dash2[i] = x * dash[i];
4145 state->setLineDash(dash2, dashLength, 0);
4146 out->updateLineDash(state);
4148 //~ this doesn't currently handle the beveled and engraved styles
4149 state->clearPath();
4150 state->moveTo(annotX0, out->upsideDown() ? annotY1 : annotY0);
4151 state->lineTo(annotX1, out->upsideDown() ? annotY1 : annotY0);
4152 if (borderStyle->getType() != annotBorderUnderlined) {
4153 state->lineTo(annotX1, out->upsideDown() ? annotY0 : annotY1);
4154 state->lineTo(annotX0, out->upsideDown() ? annotY0 : annotY1);
4155 state->closePath();
4157 out->stroke(state);
4161 void Gfx::saveState() {
4162 out->saveState(state);
4163 state = state->save();
4166 void Gfx::restoreState() {
4167 state = state->restore();
4168 out->restoreState(state);
4171 void Gfx::pushResources(Dict *resDict) {
4172 res = new GfxResources(xref, resDict, res);
4175 void Gfx::popResources() {
4176 GfxResources *resPtr;
4178 resPtr = res->getNext();
4179 delete res;
4180 res = resPtr;