upgrade to xpdf 3.00.
[swftools.git] / pdf2swf / xpdf / Gfx.cc
bloba52aa02ac8dbfe71ca43cf3774e3d2a047f8f7ca
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 <stdio.h>
16 #include <stddef.h>
17 #include <string.h>
18 #include <math.h>
19 #include "gmem.h"
20 #include "GlobalParams.h"
21 #include "CharTypes.h"
22 #include "Object.h"
23 #include "Array.h"
24 #include "Dict.h"
25 #include "Stream.h"
26 #include "Lexer.h"
27 #include "Parser.h"
28 #include "GfxFont.h"
29 #include "GfxState.h"
30 #include "OutputDev.h"
31 #include "Page.h"
32 #include "Error.h"
33 #include "Gfx.h"
35 // the MSVC math.h doesn't define this
36 #ifndef M_PI
37 #define M_PI 3.14159265358979323846
38 #endif
40 //------------------------------------------------------------------------
41 // constants
42 //------------------------------------------------------------------------
44 // Max recursive depth for a function shading fill.
45 #define functionMaxDepth 6
47 // Max delta allowed in any color component for a function shading fill.
48 #define functionColorDelta (1 / 256.0)
50 // Max number of splits along the t axis for an axial shading fill.
51 #define axialMaxSplits 256
53 // Max delta allowed in any color component for an axial shading fill.
54 #define axialColorDelta (1 / 256.0)
56 // Max number of splits along the t axis for a radial shading fill.
57 #define radialMaxSplits 256
59 // Max delta allowed in any color component for a radial shading fill.
60 #define radialColorDelta (1 / 256.0)
62 //------------------------------------------------------------------------
63 // Operator table
64 //------------------------------------------------------------------------
66 #ifdef WIN32 // this works around a bug in the VC7 compiler
67 # pragma optimize("",off)
68 #endif
70 Operator Gfx::opTab[] = {
71 {"\"", 3, {tchkNum, tchkNum, tchkString},
72 &Gfx::opMoveSetShowText},
73 {"'", 1, {tchkString},
74 &Gfx::opMoveShowText},
75 {"B", 0, {tchkNone},
76 &Gfx::opFillStroke},
77 {"B*", 0, {tchkNone},
78 &Gfx::opEOFillStroke},
79 {"BDC", 2, {tchkName, tchkProps},
80 &Gfx::opBeginMarkedContent},
81 {"BI", 0, {tchkNone},
82 &Gfx::opBeginImage},
83 {"BMC", 1, {tchkName},
84 &Gfx::opBeginMarkedContent},
85 {"BT", 0, {tchkNone},
86 &Gfx::opBeginText},
87 {"BX", 0, {tchkNone},
88 &Gfx::opBeginIgnoreUndef},
89 {"CS", 1, {tchkName},
90 &Gfx::opSetStrokeColorSpace},
91 {"DP", 2, {tchkName, tchkProps},
92 &Gfx::opMarkPoint},
93 {"Do", 1, {tchkName},
94 &Gfx::opXObject},
95 {"EI", 0, {tchkNone},
96 &Gfx::opEndImage},
97 {"EMC", 0, {tchkNone},
98 &Gfx::opEndMarkedContent},
99 {"ET", 0, {tchkNone},
100 &Gfx::opEndText},
101 {"EX", 0, {tchkNone},
102 &Gfx::opEndIgnoreUndef},
103 {"F", 0, {tchkNone},
104 &Gfx::opFill},
105 {"G", 1, {tchkNum},
106 &Gfx::opSetStrokeGray},
107 {"ID", 0, {tchkNone},
108 &Gfx::opImageData},
109 {"J", 1, {tchkInt},
110 &Gfx::opSetLineCap},
111 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
112 &Gfx::opSetStrokeCMYKColor},
113 {"M", 1, {tchkNum},
114 &Gfx::opSetMiterLimit},
115 {"MP", 1, {tchkName},
116 &Gfx::opMarkPoint},
117 {"Q", 0, {tchkNone},
118 &Gfx::opRestore},
119 {"RG", 3, {tchkNum, tchkNum, tchkNum},
120 &Gfx::opSetStrokeRGBColor},
121 {"S", 0, {tchkNone},
122 &Gfx::opStroke},
123 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
124 &Gfx::opSetStrokeColor},
125 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
126 tchkSCN},
127 &Gfx::opSetStrokeColorN},
128 {"T*", 0, {tchkNone},
129 &Gfx::opTextNextLine},
130 {"TD", 2, {tchkNum, tchkNum},
131 &Gfx::opTextMoveSet},
132 {"TJ", 1, {tchkArray},
133 &Gfx::opShowSpaceText},
134 {"TL", 1, {tchkNum},
135 &Gfx::opSetTextLeading},
136 {"Tc", 1, {tchkNum},
137 &Gfx::opSetCharSpacing},
138 {"Td", 2, {tchkNum, tchkNum},
139 &Gfx::opTextMove},
140 {"Tf", 2, {tchkName, tchkNum},
141 &Gfx::opSetFont},
142 {"Tj", 1, {tchkString},
143 &Gfx::opShowText},
144 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
145 tchkNum, tchkNum},
146 &Gfx::opSetTextMatrix},
147 {"Tr", 1, {tchkInt},
148 &Gfx::opSetTextRender},
149 {"Ts", 1, {tchkNum},
150 &Gfx::opSetTextRise},
151 {"Tw", 1, {tchkNum},
152 &Gfx::opSetWordSpacing},
153 {"Tz", 1, {tchkNum},
154 &Gfx::opSetHorizScaling},
155 {"W", 0, {tchkNone},
156 &Gfx::opClip},
157 {"W*", 0, {tchkNone},
158 &Gfx::opEOClip},
159 {"b", 0, {tchkNone},
160 &Gfx::opCloseFillStroke},
161 {"b*", 0, {tchkNone},
162 &Gfx::opCloseEOFillStroke},
163 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
164 tchkNum, tchkNum},
165 &Gfx::opCurveTo},
166 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
167 tchkNum, tchkNum},
168 &Gfx::opConcat},
169 {"cs", 1, {tchkName},
170 &Gfx::opSetFillColorSpace},
171 {"d", 2, {tchkArray, tchkNum},
172 &Gfx::opSetDash},
173 {"d0", 2, {tchkNum, tchkNum},
174 &Gfx::opSetCharWidth},
175 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
176 tchkNum, tchkNum},
177 &Gfx::opSetCacheDevice},
178 {"f", 0, {tchkNone},
179 &Gfx::opFill},
180 {"f*", 0, {tchkNone},
181 &Gfx::opEOFill},
182 {"g", 1, {tchkNum},
183 &Gfx::opSetFillGray},
184 {"gs", 1, {tchkName},
185 &Gfx::opSetExtGState},
186 {"h", 0, {tchkNone},
187 &Gfx::opClosePath},
188 {"i", 1, {tchkNum},
189 &Gfx::opSetFlat},
190 {"j", 1, {tchkInt},
191 &Gfx::opSetLineJoin},
192 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
193 &Gfx::opSetFillCMYKColor},
194 {"l", 2, {tchkNum, tchkNum},
195 &Gfx::opLineTo},
196 {"m", 2, {tchkNum, tchkNum},
197 &Gfx::opMoveTo},
198 {"n", 0, {tchkNone},
199 &Gfx::opEndPath},
200 {"q", 0, {tchkNone},
201 &Gfx::opSave},
202 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
203 &Gfx::opRectangle},
204 {"rg", 3, {tchkNum, tchkNum, tchkNum},
205 &Gfx::opSetFillRGBColor},
206 {"ri", 1, {tchkName},
207 &Gfx::opSetRenderingIntent},
208 {"s", 0, {tchkNone},
209 &Gfx::opCloseStroke},
210 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
211 &Gfx::opSetFillColor},
212 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
213 tchkSCN},
214 &Gfx::opSetFillColorN},
215 {"sh", 1, {tchkName},
216 &Gfx::opShFill},
217 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
218 &Gfx::opCurveTo1},
219 {"w", 1, {tchkNum},
220 &Gfx::opSetLineWidth},
221 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
222 &Gfx::opCurveTo2},
225 #ifdef WIN32 // this works around a bug in the VC7 compiler
226 # pragma optimize("",on)
227 #endif
229 #define numOps (sizeof(opTab) / sizeof(Operator))
231 //------------------------------------------------------------------------
232 // GfxResources
233 //------------------------------------------------------------------------
235 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
236 Object obj1, obj2;
237 Ref r;
239 if (resDict) {
241 // build font dictionary
242 fonts = NULL;
243 resDict->lookupNF("Font", &obj1);
244 if (obj1.isRef()) {
245 obj1.fetch(xref, &obj2);
246 if (obj2.isDict()) {
247 r = obj1.getRef();
248 fonts = new GfxFontDict(xref, &r, obj2.getDict());
250 obj2.free();
251 } else if (obj1.isDict()) {
252 fonts = new GfxFontDict(xref, NULL, obj1.getDict());
254 obj1.free();
256 // get XObject dictionary
257 resDict->lookup("XObject", &xObjDict);
259 // get color space dictionary
260 resDict->lookup("ColorSpace", &colorSpaceDict);
262 // get pattern dictionary
263 resDict->lookup("Pattern", &patternDict);
265 // get shading dictionary
266 resDict->lookup("Shading", &shadingDict);
268 // get graphics state parameter dictionary
269 resDict->lookup("ExtGState", &gStateDict);
271 } else {
272 fonts = NULL;
273 xObjDict.initNull();
274 colorSpaceDict.initNull();
275 patternDict.initNull();
276 shadingDict.initNull();
277 gStateDict.initNull();
280 next = nextA;
283 GfxResources::~GfxResources() {
284 if (fonts) {
285 delete fonts;
287 xObjDict.free();
288 colorSpaceDict.free();
289 patternDict.free();
290 shadingDict.free();
291 gStateDict.free();
294 GfxFont *GfxResources::lookupFont(char *name) {
295 GfxFont *font;
296 GfxResources *resPtr;
298 for (resPtr = this; resPtr; resPtr = resPtr->next) {
299 if (resPtr->fonts) {
300 if ((font = resPtr->fonts->lookup(name)))
301 return font;
304 error(-1, "Unknown font tag '%s'", name);
305 return NULL;
308 GBool GfxResources::lookupXObject(char *name, Object *obj) {
309 GfxResources *resPtr;
311 for (resPtr = this; resPtr; resPtr = resPtr->next) {
312 if (resPtr->xObjDict.isDict()) {
313 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
314 return gTrue;
315 obj->free();
318 error(-1, "XObject '%s' is unknown", name);
319 return gFalse;
322 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
323 GfxResources *resPtr;
325 for (resPtr = this; resPtr; resPtr = resPtr->next) {
326 if (resPtr->xObjDict.isDict()) {
327 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
328 return gTrue;
329 obj->free();
332 error(-1, "XObject '%s' is unknown", name);
333 return gFalse;
336 void GfxResources::lookupColorSpace(char *name, Object *obj) {
337 GfxResources *resPtr;
339 for (resPtr = this; resPtr; resPtr = resPtr->next) {
340 if (resPtr->colorSpaceDict.isDict()) {
341 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
342 return;
344 obj->free();
347 obj->initNull();
350 GfxPattern *GfxResources::lookupPattern(char *name) {
351 GfxResources *resPtr;
352 GfxPattern *pattern;
353 Object obj;
355 for (resPtr = this; resPtr; resPtr = resPtr->next) {
356 if (resPtr->patternDict.isDict()) {
357 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
358 pattern = GfxPattern::parse(&obj);
359 obj.free();
360 return pattern;
362 obj.free();
365 error(-1, "Unknown pattern '%s'", name);
366 return NULL;
369 GfxShading *GfxResources::lookupShading(char *name) {
370 GfxResources *resPtr;
371 GfxShading *shading;
372 Object obj;
374 for (resPtr = this; resPtr; resPtr = resPtr->next) {
375 if (resPtr->shadingDict.isDict()) {
376 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
377 shading = GfxShading::parse(&obj);
378 obj.free();
379 return shading;
381 obj.free();
384 error(-1, "Unknown shading '%s'", name);
385 return NULL;
388 GBool GfxResources::lookupGState(char *name, Object *obj) {
389 GfxResources *resPtr;
391 for (resPtr = this; resPtr; resPtr = resPtr->next) {
392 if (resPtr->gStateDict.isDict()) {
393 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
394 return gTrue;
396 obj->free();
399 error(-1, "ExtGState '%s' is unknown", name);
400 return gFalse;
403 //------------------------------------------------------------------------
404 // Gfx
405 //------------------------------------------------------------------------
407 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
408 double hDPI, double vDPI, PDFRectangle *box, GBool crop,
409 PDFRectangle *cropBox, int rotate,
410 GBool (*abortCheckCbkA)(void *data),
411 void *abortCheckCbkDataA) {
412 int i;
414 xref = xrefA;
415 subPage = gFalse;
416 printCommands = globalParams->getPrintCommands();
418 // start the resource stack
419 res = new GfxResources(xref, resDict, NULL);
421 // initialize
422 out = outA;
423 state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
424 fontChanged = gFalse;
425 clip = clipNone;
426 ignoreUndef = 0;
427 out->startPage(pageNum, state);
428 out->setDefaultCTM(state->getCTM());
429 out->updateAll(state);
430 for (i = 0; i < 6; ++i) {
431 baseMatrix[i] = state->getCTM()[i];
433 formDepth = 0;
434 abortCheckCbk = abortCheckCbkA;
435 abortCheckCbkData = abortCheckCbkDataA;
437 // set crop box
438 if (crop) {
439 state->moveTo(cropBox->x1, cropBox->y1);
440 state->lineTo(cropBox->x2, cropBox->y1);
441 state->lineTo(cropBox->x2, cropBox->y2);
442 state->lineTo(cropBox->x1, cropBox->y2);
443 state->closePath();
444 state->clip();
445 out->clip(state);
446 state->clearPath();
450 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
451 PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
452 GBool (*abortCheckCbkA)(void *data),
453 void *abortCheckCbkDataA) {
454 int i;
456 xref = xrefA;
457 subPage = gTrue;
458 printCommands = globalParams->getPrintCommands();
460 // start the resource stack
461 res = new GfxResources(xref, resDict, NULL);
463 // initialize
464 out = outA;
465 state = new GfxState(72, 72, box, 0, gFalse);
466 fontChanged = gFalse;
467 clip = clipNone;
468 ignoreUndef = 0;
469 for (i = 0; i < 6; ++i) {
470 baseMatrix[i] = state->getCTM()[i];
472 formDepth = 0;
473 abortCheckCbk = abortCheckCbkA;
474 abortCheckCbkData = abortCheckCbkDataA;
476 // set crop box
477 if (crop) {
478 state->moveTo(cropBox->x1, cropBox->y1);
479 state->lineTo(cropBox->x2, cropBox->y1);
480 state->lineTo(cropBox->x2, cropBox->y2);
481 state->lineTo(cropBox->x1, cropBox->y2);
482 state->closePath();
483 state->clip();
484 out->clip(state);
485 state->clearPath();
489 Gfx::~Gfx() {
490 while (state->hasSaves()) {
491 restoreState();
493 if (!subPage) {
494 out->endPage();
496 while (res) {
497 popResources();
499 if (state) {
500 delete state;
504 void Gfx::display(Object *obj, GBool topLevel) {
505 Object obj2;
506 int i;
508 if (obj->isArray()) {
509 for (i = 0; i < obj->arrayGetLength(); ++i) {
510 obj->arrayGet(i, &obj2);
511 if (!obj2.isStream()) {
512 error(-1, "Weird page contents");
513 obj2.free();
514 return;
516 obj2.free();
518 } else if (!obj->isStream()) {
519 error(-1, "Weird page contents");
520 return;
522 parser = new Parser(xref, new Lexer(xref, obj));
523 go(topLevel);
524 delete parser;
525 parser = NULL;
528 void Gfx::go(GBool topLevel) {
529 Object obj;
530 Object args[maxArgs];
531 int numArgs, i;
532 int lastAbortCheck;
534 // scan a sequence of objects
535 updateLevel = lastAbortCheck = 0;
536 numArgs = 0;
537 parser->getObj(&obj);
538 while (!obj.isEOF()) {
540 // got a command - execute it
541 if (obj.isCmd()) {
542 if (printCommands) {
543 obj.print(stdout);
544 for (i = 0; i < numArgs; ++i) {
545 printf(" ");
546 args[i].print(stdout);
548 printf("\n");
549 fflush(stdout);
551 execOp(&obj, args, numArgs);
552 obj.free();
553 for (i = 0; i < numArgs; ++i)
554 args[i].free();
555 numArgs = 0;
557 // periodically update display
558 if (++updateLevel >= 20000) {
559 out->dump();
560 updateLevel = 0;
563 // check for an abort
564 if (abortCheckCbk) {
565 if (updateLevel - lastAbortCheck > 10) {
566 if ((*abortCheckCbk)(abortCheckCbkData)) {
567 break;
569 lastAbortCheck = updateLevel;
573 // got an argument - save it
574 } else if (numArgs < maxArgs) {
575 args[numArgs++] = obj;
577 // too many arguments - something is wrong
578 } else {
579 error(getPos(), "Too many args in content stream");
580 if (printCommands) {
581 printf("throwing away arg: ");
582 obj.print(stdout);
583 printf("\n");
584 fflush(stdout);
586 obj.free();
589 // grab the next object
590 parser->getObj(&obj);
592 obj.free();
594 // args at end with no command
595 if (numArgs > 0) {
596 error(getPos(), "Leftover args in content stream");
597 if (printCommands) {
598 printf("%d leftovers:", numArgs);
599 for (i = 0; i < numArgs; ++i) {
600 printf(" ");
601 args[i].print(stdout);
603 printf("\n");
604 fflush(stdout);
606 for (i = 0; i < numArgs; ++i)
607 args[i].free();
610 // update display
611 if (topLevel && updateLevel > 0) {
612 out->dump();
616 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
617 Operator *op;
618 char *name;
619 Object *argPtr;
620 int i;
622 // find operator
623 name = cmd->getCmd();
624 if (!(op = findOp(name))) {
625 if (ignoreUndef == 0)
626 error(getPos(), "Unknown operator '%s'", name);
627 return;
630 // type check args
631 argPtr = args;
632 if (op->numArgs >= 0) {
633 if (numArgs < op->numArgs) {
634 error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
635 return;
637 if (numArgs > op->numArgs) {
638 #if 0
639 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
640 #endif
641 argPtr += numArgs - op->numArgs;
642 numArgs = op->numArgs;
644 } else {
645 if (numArgs > -op->numArgs) {
646 error(getPos(), "Too many (%d) args to '%s' operator",
647 numArgs, name);
648 return;
651 for (i = 0; i < numArgs; ++i) {
652 if (!checkArg(&argPtr[i], op->tchk[i])) {
653 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
654 i, name, argPtr[i].getTypeName());
655 return;
659 // do it
660 (this->*op->func)(argPtr, numArgs);
663 Operator *Gfx::findOp(char *name) {
664 int a, b, m, cmp;
666 a = -1;
667 b = numOps;
668 // invariant: opTab[a] < name < opTab[b]
669 while (b - a > 1) {
670 m = (a + b) / 2;
671 cmp = strcmp(opTab[m].name, name);
672 if (cmp < 0)
673 a = m;
674 else if (cmp > 0)
675 b = m;
676 else
677 a = b = m;
679 if (cmp != 0)
680 return NULL;
681 return &opTab[a];
684 GBool Gfx::checkArg(Object *arg, TchkType type) {
685 switch (type) {
686 case tchkBool: return arg->isBool();
687 case tchkInt: return arg->isInt();
688 case tchkNum: return arg->isNum();
689 case tchkString: return arg->isString();
690 case tchkName: return arg->isName();
691 case tchkArray: return arg->isArray();
692 case tchkProps: return arg->isDict() || arg->isName();
693 case tchkSCN: return arg->isNum() || arg->isName();
694 case tchkNone: return gFalse;
696 return gFalse;
699 int Gfx::getPos() {
700 return parser ? parser->getPos() : -1;
703 //------------------------------------------------------------------------
704 // graphics state operators
705 //------------------------------------------------------------------------
707 void Gfx::opSave(Object args[], int numArgs) {
708 saveState();
711 void Gfx::opRestore(Object args[], int numArgs) {
712 restoreState();
715 void Gfx::opConcat(Object args[], int numArgs) {
716 state->concatCTM(args[0].getNum(), args[1].getNum(),
717 args[2].getNum(), args[3].getNum(),
718 args[4].getNum(), args[5].getNum());
719 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
720 args[2].getNum(), args[3].getNum(),
721 args[4].getNum(), args[5].getNum());
722 fontChanged = gTrue;
725 void Gfx::opSetDash(Object args[], int numArgs) {
726 Array *a;
727 int length;
728 Object obj;
729 double *dash;
730 int i;
732 a = args[0].getArray();
733 length = a->getLength();
734 if (length == 0) {
735 dash = NULL;
736 } else {
737 dash = (double *)gmalloc(length * sizeof(double));
738 for (i = 0; i < length; ++i) {
739 dash[i] = a->get(i, &obj)->getNum();
740 obj.free();
743 state->setLineDash(dash, length, args[1].getNum());
744 out->updateLineDash(state);
747 void Gfx::opSetFlat(Object args[], int numArgs) {
748 state->setFlatness((int)args[0].getNum());
749 out->updateFlatness(state);
752 void Gfx::opSetLineJoin(Object args[], int numArgs) {
753 state->setLineJoin(args[0].getInt());
754 out->updateLineJoin(state);
757 void Gfx::opSetLineCap(Object args[], int numArgs) {
758 state->setLineCap(args[0].getInt());
759 out->updateLineCap(state);
762 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
763 state->setMiterLimit(args[0].getNum());
764 out->updateMiterLimit(state);
767 void Gfx::opSetLineWidth(Object args[], int numArgs) {
768 state->setLineWidth(args[0].getNum());
769 out->updateLineWidth(state);
772 void Gfx::opSetExtGState(Object args[], int numArgs) {
773 Object obj1, obj2;
775 if (!res->lookupGState(args[0].getName(), &obj1)) {
776 return;
778 if (!obj1.isDict()) {
779 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
780 obj1.free();
781 return;
783 if (obj1.dictLookup("ca", &obj2)->isNum()) {
784 state->setFillOpacity(obj2.getNum());
785 out->updateFillOpacity(state);
787 obj2.free();
788 if (obj1.dictLookup("CA", &obj2)->isNum()) {
789 state->setStrokeOpacity(obj2.getNum());
790 out->updateStrokeOpacity(state);
792 obj2.free();
793 obj1.free();
796 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
799 //------------------------------------------------------------------------
800 // color operators
801 //------------------------------------------------------------------------
803 void Gfx::opSetFillGray(Object args[], int numArgs) {
804 GfxColor color;
806 state->setFillPattern(NULL);
807 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
808 color.c[0] = args[0].getNum();
809 state->setFillColor(&color);
810 out->updateFillColor(state);
813 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
814 GfxColor color;
816 state->setStrokePattern(NULL);
817 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
818 color.c[0] = args[0].getNum();
819 state->setStrokeColor(&color);
820 out->updateStrokeColor(state);
823 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
824 GfxColor color;
825 int i;
827 state->setFillPattern(NULL);
828 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
829 for (i = 0; i < 4; ++i) {
830 color.c[i] = args[i].getNum();
832 state->setFillColor(&color);
833 out->updateFillColor(state);
836 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
837 GfxColor color;
838 int i;
840 state->setStrokePattern(NULL);
841 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
842 for (i = 0; i < 4; ++i) {
843 color.c[i] = args[i].getNum();
845 state->setStrokeColor(&color);
846 out->updateStrokeColor(state);
849 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
850 GfxColor color;
851 int i;
853 state->setFillPattern(NULL);
854 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
855 for (i = 0; i < 3; ++i) {
856 color.c[i] = args[i].getNum();
858 state->setFillColor(&color);
859 out->updateFillColor(state);
862 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
863 GfxColor color;
864 int i;
866 state->setStrokePattern(NULL);
867 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
868 for (i = 0; i < 3; ++i) {
869 color.c[i] = args[i].getNum();
871 state->setStrokeColor(&color);
872 out->updateStrokeColor(state);
875 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
876 Object obj;
877 GfxColorSpace *colorSpace;
878 GfxColor color;
879 int i;
881 state->setFillPattern(NULL);
882 res->lookupColorSpace(args[0].getName(), &obj);
883 if (obj.isNull()) {
884 colorSpace = GfxColorSpace::parse(&args[0]);
885 } else {
886 colorSpace = GfxColorSpace::parse(&obj);
888 obj.free();
889 if (colorSpace) {
890 state->setFillColorSpace(colorSpace);
891 } else {
892 error(getPos(), "Bad color space (fill)");
894 for (i = 0; i < gfxColorMaxComps; ++i) {
895 color.c[i] = 0;
897 state->setFillColor(&color);
898 out->updateFillColor(state);
901 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
902 Object obj;
903 GfxColorSpace *colorSpace;
904 GfxColor color;
905 int i;
907 state->setStrokePattern(NULL);
908 res->lookupColorSpace(args[0].getName(), &obj);
909 if (obj.isNull()) {
910 colorSpace = GfxColorSpace::parse(&args[0]);
911 } else {
912 colorSpace = GfxColorSpace::parse(&obj);
914 obj.free();
915 if (colorSpace) {
916 state->setStrokeColorSpace(colorSpace);
917 } else {
918 error(getPos(), "Bad color space (stroke)");
920 for (i = 0; i < gfxColorMaxComps; ++i) {
921 color.c[i] = 0;
923 state->setStrokeColor(&color);
924 out->updateStrokeColor(state);
927 void Gfx::opSetFillColor(Object args[], int numArgs) {
928 GfxColor color;
929 int i;
931 state->setFillPattern(NULL);
932 for (i = 0; i < numArgs; ++i) {
933 color.c[i] = args[i].getNum();
935 state->setFillColor(&color);
936 out->updateFillColor(state);
939 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
940 GfxColor color;
941 int i;
943 state->setStrokePattern(NULL);
944 for (i = 0; i < numArgs; ++i) {
945 color.c[i] = args[i].getNum();
947 state->setStrokeColor(&color);
948 out->updateStrokeColor(state);
951 void Gfx::opSetFillColorN(Object args[], int numArgs) {
952 GfxColor color;
953 GfxPattern *pattern;
954 int i;
956 if (state->getFillColorSpace()->getMode() == csPattern) {
957 if (numArgs > 1) {
958 for (i = 0; i < numArgs && i < 4; ++i) {
959 if (args[i].isNum()) {
960 color.c[i] = args[i].getNum();
963 state->setFillColor(&color);
964 out->updateFillColor(state);
966 if (args[numArgs-1].isName() &&
967 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
968 state->setFillPattern(pattern);
971 } else {
972 state->setFillPattern(NULL);
973 for (i = 0; i < numArgs && i < 4; ++i) {
974 if (args[i].isNum()) {
975 color.c[i] = args[i].getNum();
978 state->setFillColor(&color);
979 out->updateFillColor(state);
983 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
984 GfxColor color;
985 GfxPattern *pattern;
986 int i;
988 if (state->getStrokeColorSpace()->getMode() == csPattern) {
989 if (numArgs > 1) {
990 for (i = 0; i < numArgs && i < 4; ++i) {
991 if (args[i].isNum()) {
992 color.c[i] = args[i].getNum();
995 state->setStrokeColor(&color);
996 out->updateStrokeColor(state);
998 if (args[numArgs-1].isName() &&
999 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1000 state->setStrokePattern(pattern);
1003 } else {
1004 state->setStrokePattern(NULL);
1005 for (i = 0; i < numArgs && i < 4; ++i) {
1006 if (args[i].isNum()) {
1007 color.c[i] = args[i].getNum();
1010 state->setStrokeColor(&color);
1011 out->updateStrokeColor(state);
1015 //------------------------------------------------------------------------
1016 // path segment operators
1017 //------------------------------------------------------------------------
1019 void Gfx::opMoveTo(Object args[], int numArgs) {
1020 state->moveTo(args[0].getNum(), args[1].getNum());
1023 void Gfx::opLineTo(Object args[], int numArgs) {
1024 if (!state->isCurPt()) {
1025 error(getPos(), "No current point in lineto");
1026 return;
1028 state->lineTo(args[0].getNum(), args[1].getNum());
1031 void Gfx::opCurveTo(Object args[], int numArgs) {
1032 double x1, y1, x2, y2, x3, y3;
1034 if (!state->isCurPt()) {
1035 error(getPos(), "No current point in curveto");
1036 return;
1038 x1 = args[0].getNum();
1039 y1 = args[1].getNum();
1040 x2 = args[2].getNum();
1041 y2 = args[3].getNum();
1042 x3 = args[4].getNum();
1043 y3 = args[5].getNum();
1044 state->curveTo(x1, y1, x2, y2, x3, y3);
1047 void Gfx::opCurveTo1(Object args[], int numArgs) {
1048 double x1, y1, x2, y2, x3, y3;
1050 if (!state->isCurPt()) {
1051 error(getPos(), "No current point in curveto1");
1052 return;
1054 x1 = state->getCurX();
1055 y1 = state->getCurY();
1056 x2 = args[0].getNum();
1057 y2 = args[1].getNum();
1058 x3 = args[2].getNum();
1059 y3 = args[3].getNum();
1060 state->curveTo(x1, y1, x2, y2, x3, y3);
1063 void Gfx::opCurveTo2(Object args[], int numArgs) {
1064 double x1, y1, x2, y2, x3, y3;
1066 if (!state->isCurPt()) {
1067 error(getPos(), "No current point in curveto2");
1068 return;
1070 x1 = args[0].getNum();
1071 y1 = args[1].getNum();
1072 x2 = args[2].getNum();
1073 y2 = args[3].getNum();
1074 x3 = x2;
1075 y3 = y2;
1076 state->curveTo(x1, y1, x2, y2, x3, y3);
1079 void Gfx::opRectangle(Object args[], int numArgs) {
1080 double x, y, w, h;
1082 x = args[0].getNum();
1083 y = args[1].getNum();
1084 w = args[2].getNum();
1085 h = args[3].getNum();
1086 state->moveTo(x, y);
1087 state->lineTo(x + w, y);
1088 state->lineTo(x + w, y + h);
1089 state->lineTo(x, y + h);
1090 state->closePath();
1093 void Gfx::opClosePath(Object args[], int numArgs) {
1094 if (!state->isCurPt()) {
1095 error(getPos(), "No current point in closepath");
1096 return;
1098 state->closePath();
1101 //------------------------------------------------------------------------
1102 // path painting operators
1103 //------------------------------------------------------------------------
1105 void Gfx::opEndPath(Object args[], int numArgs) {
1106 doEndPath();
1109 void Gfx::opStroke(Object args[], int numArgs) {
1110 if (!state->isCurPt()) {
1111 //error(getPos(), "No path in stroke");
1112 return;
1114 if (state->isPath())
1115 out->stroke(state);
1116 doEndPath();
1119 void Gfx::opCloseStroke(Object args[], int numArgs) {
1120 if (!state->isCurPt()) {
1121 //error(getPos(), "No path in closepath/stroke");
1122 return;
1124 if (state->isPath()) {
1125 state->closePath();
1126 out->stroke(state);
1128 doEndPath();
1131 void Gfx::opFill(Object args[], int numArgs) {
1132 if (!state->isCurPt()) {
1133 //error(getPos(), "No path in fill");
1134 return;
1136 if (state->isPath()) {
1137 if (state->getFillColorSpace()->getMode() == csPattern) {
1138 doPatternFill(gFalse);
1139 } else {
1140 out->fill(state);
1143 doEndPath();
1146 void Gfx::opEOFill(Object args[], int numArgs) {
1147 if (!state->isCurPt()) {
1148 //error(getPos(), "No path in eofill");
1149 return;
1151 if (state->isPath()) {
1152 if (state->getFillColorSpace()->getMode() == csPattern) {
1153 doPatternFill(gTrue);
1154 } else {
1155 out->eoFill(state);
1158 doEndPath();
1161 void Gfx::opFillStroke(Object args[], int numArgs) {
1162 if (!state->isCurPt()) {
1163 //error(getPos(), "No path in fill/stroke");
1164 return;
1166 if (state->isPath()) {
1167 if (state->getFillColorSpace()->getMode() == csPattern) {
1168 doPatternFill(gFalse);
1169 } else {
1170 out->fill(state);
1172 out->stroke(state);
1174 doEndPath();
1177 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1178 if (!state->isCurPt()) {
1179 //error(getPos(), "No path in closepath/fill/stroke");
1180 return;
1182 if (state->isPath()) {
1183 state->closePath();
1184 if (state->getFillColorSpace()->getMode() == csPattern) {
1185 doPatternFill(gFalse);
1186 } else {
1187 out->fill(state);
1189 out->stroke(state);
1191 doEndPath();
1194 void Gfx::opEOFillStroke(Object args[], int numArgs) {
1195 if (!state->isCurPt()) {
1196 //error(getPos(), "No path in eofill/stroke");
1197 return;
1199 if (state->isPath()) {
1200 if (state->getFillColorSpace()->getMode() == csPattern) {
1201 doPatternFill(gTrue);
1202 } else {
1203 out->eoFill(state);
1205 out->stroke(state);
1207 doEndPath();
1210 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1211 if (!state->isCurPt()) {
1212 //error(getPos(), "No path in closepath/eofill/stroke");
1213 return;
1215 if (state->isPath()) {
1216 state->closePath();
1217 if (state->getFillColorSpace()->getMode() == csPattern) {
1218 doPatternFill(gTrue);
1219 } else {
1220 out->eoFill(state);
1222 out->stroke(state);
1224 doEndPath();
1227 void Gfx::doPatternFill(GBool eoFill) {
1228 GfxPattern *pattern;
1230 // this is a bit of a kludge -- patterns can be really slow, so we
1231 // skip them if we're only doing text extraction, since they almost
1232 // certainly don't contain any text
1233 if (!out->needNonText()) {
1234 return;
1237 if (!(pattern = state->getFillPattern())) {
1238 return;
1240 switch (pattern->getType()) {
1241 case 1:
1242 doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
1243 break;
1244 case 2:
1245 doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
1246 break;
1247 default:
1248 error(getPos(), "Unimplemented pattern type (%d) in fill",
1249 pattern->getType());
1250 break;
1254 void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
1255 GfxPatternColorSpace *patCS;
1256 GfxColorSpace *cs;
1257 GfxPath *savedPath;
1258 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1259 double cxMin, cyMin, cxMax, cyMax;
1260 int xi0, yi0, xi1, yi1, xi, yi;
1261 double *ctm, *btm, *ptm;
1262 double m[6], ictm[6], m1[6], imb[6];
1263 double det;
1264 double xstep, ystep;
1265 int i;
1267 // get color space
1268 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1270 // construct a (pattern space) -> (current space) transform matrix
1271 ctm = state->getCTM();
1272 btm = baseMatrix;
1273 ptm = tPat->getMatrix();
1274 // iCTM = invert CTM
1275 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1276 ictm[0] = ctm[3] * det;
1277 ictm[1] = -ctm[1] * det;
1278 ictm[2] = -ctm[2] * det;
1279 ictm[3] = ctm[0] * det;
1280 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1281 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1282 // m1 = PTM * BTM = PTM * base transform matrix
1283 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1284 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1285 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1286 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1287 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1288 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1289 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1290 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1291 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1292 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1293 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1294 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1295 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1297 // construct a (base space) -> (pattern space) transform matrix
1298 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1299 imb[0] = m1[3] * det;
1300 imb[1] = -m1[1] * det;
1301 imb[2] = -m1[2] * det;
1302 imb[3] = m1[0] * det;
1303 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1304 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1306 // save current graphics state
1307 savedPath = state->getPath()->copy();
1308 saveState();
1310 // set underlying color space (for uncolored tiling patterns); set
1311 // various other parameters (stroke color, line width) to match
1312 // Adobe's behavior
1313 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1314 state->setFillColorSpace(cs->copy());
1315 state->setStrokeColorSpace(cs->copy());
1316 state->setStrokeColor(state->getFillColor());
1317 } else {
1318 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1319 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1321 state->setFillPattern(NULL);
1322 out->updateFillColor(state);
1323 state->setStrokePattern(NULL);
1324 out->updateStrokeColor(state);
1325 state->setLineWidth(0);
1326 out->updateLineWidth(state);
1328 // clip to current path
1329 state->clip();
1330 if (eoFill) {
1331 out->eoClip(state);
1332 } else {
1333 out->clip(state);
1335 state->clearPath();
1337 // transform clip region bbox to pattern space
1338 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1339 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1340 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1341 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1342 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1343 if (x1 < xMin) {
1344 xMin = x1;
1345 } else if (x1 > xMax) {
1346 xMax = x1;
1348 if (y1 < yMin) {
1349 yMin = y1;
1350 } else if (y1 > yMax) {
1351 yMax = y1;
1353 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1354 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1355 if (x1 < xMin) {
1356 xMin = x1;
1357 } else if (x1 > xMax) {
1358 xMax = x1;
1360 if (y1 < yMin) {
1361 yMin = y1;
1362 } else if (y1 > yMax) {
1363 yMax = y1;
1365 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1366 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1367 if (x1 < xMin) {
1368 xMin = x1;
1369 } else if (x1 > xMax) {
1370 xMax = x1;
1372 if (y1 < yMin) {
1373 yMin = y1;
1374 } else if (y1 > yMax) {
1375 yMax = y1;
1378 // draw the pattern
1379 //~ this should treat negative steps differently -- start at right/top
1380 //~ edge instead of left/bottom (?)
1381 xstep = fabs(tPat->getXStep());
1382 ystep = fabs(tPat->getYStep());
1383 xi0 = (int)floor((xMin - tPat->getBBox()[0]) / xstep);
1384 xi1 = (int)ceil((xMax - tPat->getBBox()[0]) / xstep);
1385 yi0 = (int)floor((yMin - tPat->getBBox()[1]) / ystep);
1386 yi1 = (int)ceil((yMax - tPat->getBBox()[1]) / ystep);
1387 for (i = 0; i < 4; ++i) {
1388 m1[i] = m[i];
1390 for (yi = yi0; yi < yi1; ++yi) {
1391 for (xi = xi0; xi < xi1; ++xi) {
1392 x = xi * xstep;
1393 y = yi * ystep;
1394 m1[4] = x * m[0] + y * m[2] + m[4];
1395 m1[5] = x * m[1] + y * m[3] + m[5];
1396 doForm1(tPat->getContentStream(), tPat->getResDict(),
1397 m1, tPat->getBBox());
1401 // restore graphics state
1402 restoreState();
1403 state->setPath(savedPath);
1406 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
1407 GfxShading *shading;
1408 GfxPath *savedPath;
1409 double *ctm, *btm, *ptm;
1410 double m[6], ictm[6], m1[6];
1411 double xMin, yMin, xMax, yMax;
1412 double det;
1414 shading = sPat->getShading();
1416 // save current graphics state
1417 savedPath = state->getPath()->copy();
1418 saveState();
1420 // clip to bbox
1421 if (shading->getHasBBox()) {
1422 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1423 state->moveTo(xMin, yMin);
1424 state->lineTo(xMax, yMin);
1425 state->lineTo(xMax, yMax);
1426 state->lineTo(xMin, yMax);
1427 state->closePath();
1428 state->clip();
1429 out->clip(state);
1430 state->clearPath();
1433 // clip to current path
1434 state->clip();
1435 if (eoFill) {
1436 out->eoClip(state);
1437 } else {
1438 out->clip(state);
1440 state->clearPath();
1442 // construct a (pattern space) -> (current space) transform matrix
1443 ctm = state->getCTM();
1444 btm = baseMatrix;
1445 ptm = sPat->getMatrix();
1446 // iCTM = invert CTM
1447 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1448 ictm[0] = ctm[3] * det;
1449 ictm[1] = -ctm[1] * det;
1450 ictm[2] = -ctm[2] * det;
1451 ictm[3] = ctm[0] * det;
1452 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1453 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1454 // m1 = PTM * BTM = PTM * base transform matrix
1455 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1456 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1457 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1458 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1459 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1460 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1461 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1462 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1463 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1464 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1465 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1466 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1467 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1469 // set the new matrix
1470 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1471 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1473 // set the color space
1474 state->setFillColorSpace(shading->getColorSpace()->copy());
1476 // do shading type-specific operations
1477 switch (shading->getType()) {
1478 case 1:
1479 doFunctionShFill((GfxFunctionShading *)shading);
1480 break;
1481 case 2:
1482 doAxialShFill((GfxAxialShading *)shading);
1483 break;
1484 case 3:
1485 doRadialShFill((GfxRadialShading *)shading);
1486 break;
1489 // restore graphics state
1490 restoreState();
1491 state->setPath(savedPath);
1494 void Gfx::opShFill(Object args[], int numArgs) {
1495 GfxShading *shading;
1496 GfxPath *savedPath;
1497 double xMin, yMin, xMax, yMax;
1499 if (!(shading = res->lookupShading(args[0].getName()))) {
1500 return;
1503 // save current graphics state
1504 savedPath = state->getPath()->copy();
1505 saveState();
1507 // clip to bbox
1508 if (shading->getHasBBox()) {
1509 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1510 state->moveTo(xMin, yMin);
1511 state->lineTo(xMax, yMin);
1512 state->lineTo(xMax, yMax);
1513 state->lineTo(xMin, yMax);
1514 state->closePath();
1515 state->clip();
1516 out->clip(state);
1517 state->clearPath();
1520 // set the color space
1521 state->setFillColorSpace(shading->getColorSpace()->copy());
1523 // do shading type-specific operations
1524 switch (shading->getType()) {
1525 case 1:
1526 doFunctionShFill((GfxFunctionShading *)shading);
1527 break;
1528 case 2:
1529 doAxialShFill((GfxAxialShading *)shading);
1530 break;
1531 case 3:
1532 doRadialShFill((GfxRadialShading *)shading);
1533 break;
1536 // restore graphics state
1537 restoreState();
1538 state->setPath(savedPath);
1540 delete shading;
1543 void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
1544 double x0, y0, x1, y1;
1545 GfxColor colors[4];
1547 shading->getDomain(&x0, &y0, &x1, &y1);
1548 shading->getColor(x0, y0, &colors[0]);
1549 shading->getColor(x0, y1, &colors[1]);
1550 shading->getColor(x1, y0, &colors[2]);
1551 shading->getColor(x1, y1, &colors[3]);
1552 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1555 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
1556 double x0, double y0,
1557 double x1, double y1,
1558 GfxColor *colors, int depth) {
1559 GfxColor fillColor;
1560 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1561 GfxColor colors2[4];
1562 double *matrix;
1563 double xM, yM;
1564 int nComps, i, j;
1566 nComps = shading->getColorSpace()->getNComps();
1567 matrix = shading->getMatrix();
1569 // compare the four corner colors
1570 for (i = 0; i < 4; ++i) {
1571 for (j = 0; j < nComps; ++j) {
1572 if (fabs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1573 break;
1576 if (j < nComps) {
1577 break;
1581 // center of the rectangle
1582 xM = 0.5 * (x0 + x1);
1583 yM = 0.5 * (y0 + y1);
1585 // the four corner colors are close (or we hit the recursive limit)
1586 // -- fill the rectangle; but require at least one subdivision
1587 // (depth==0) to avoid problems when the four outer corners of the
1588 // shaded region are the same color
1589 if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1591 // use the center color
1592 shading->getColor(xM, yM, &fillColor);
1593 state->setFillColor(&fillColor);
1594 out->updateFillColor(state);
1596 // fill the rectangle
1597 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1598 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1599 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1600 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1601 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1602 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1603 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1604 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1605 state->closePath();
1606 out->fill(state);
1607 state->clearPath();
1609 // the four corner colors are not close enough -- subdivide the
1610 // rectangle
1611 } else {
1613 // colors[0] colorM0 colors[2]
1614 // (x0,y0) (xM,y0) (x1,y0)
1615 // +----------+----------+
1616 // | | |
1617 // | UL | UR |
1618 // color0M | colorMM | color1M
1619 // (x0,yM) +----------+----------+ (x1,yM)
1620 // | (xM,yM) |
1621 // | LL | LR |
1622 // | | |
1623 // +----------+----------+
1624 // colors[1] colorM1 colors[3]
1625 // (x0,y1) (xM,y1) (x1,y1)
1627 shading->getColor(x0, yM, &color0M);
1628 shading->getColor(x1, yM, &color1M);
1629 shading->getColor(xM, y0, &colorM0);
1630 shading->getColor(xM, y1, &colorM1);
1631 shading->getColor(xM, yM, &colorMM);
1633 // upper-left sub-rectangle
1634 colors2[0] = colors[0];
1635 colors2[1] = color0M;
1636 colors2[2] = colorM0;
1637 colors2[3] = colorMM;
1638 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1640 // lower-left sub-rectangle
1641 colors2[0] = color0M;
1642 colors2[1] = colors[1];
1643 colors2[2] = colorMM;
1644 colors2[3] = colorM1;
1645 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1647 // upper-right sub-rectangle
1648 colors2[0] = colorM0;
1649 colors2[1] = colorMM;
1650 colors2[2] = colors[2];
1651 colors2[3] = color1M;
1652 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1654 // lower-right sub-rectangle
1655 colors2[0] = colorMM;
1656 colors2[1] = colorM1;
1657 colors2[2] = color1M;
1658 colors2[3] = colors[3];
1659 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1663 void Gfx::doAxialShFill(GfxAxialShading *shading) {
1664 double xMin, yMin, xMax, yMax;
1665 double x0, y0, x1, y1;
1666 double dx, dy, mul;
1667 double tMin, tMax, t, tx, ty;
1668 double s[4], sMin, sMax, tmp;
1669 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1670 double t0, t1, tt;
1671 double ta[axialMaxSplits + 1];
1672 int next[axialMaxSplits + 1];
1673 GfxColor color0, color1;
1674 int nComps;
1675 int i, j, k, kk;
1677 // get the clip region bbox
1678 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1680 // compute min and max t values, based on the four corners of the
1681 // clip region bbox
1682 shading->getCoords(&x0, &y0, &x1, &y1);
1683 dx = x1 - x0;
1684 dy = y1 - y0;
1685 mul = 1 / (dx * dx + dy * dy);
1686 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1687 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1688 if (t < tMin) {
1689 tMin = t;
1690 } else if (t > tMax) {
1691 tMax = t;
1693 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1694 if (t < tMin) {
1695 tMin = t;
1696 } else if (t > tMax) {
1697 tMax = t;
1699 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1700 if (t < tMin) {
1701 tMin = t;
1702 } else if (t > tMax) {
1703 tMax = t;
1705 if (tMin < 0 && !shading->getExtend0()) {
1706 tMin = 0;
1708 if (tMax > 1 && !shading->getExtend1()) {
1709 tMax = 1;
1712 // get the function domain
1713 t0 = shading->getDomain0();
1714 t1 = shading->getDomain1();
1716 // Traverse the t axis and do the shading.
1718 // For each point (tx, ty) on the t axis, consider a line through
1719 // that point perpendicular to the t axis:
1721 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1722 // y(s) = ty + s * dx --> s = (y - ty) / dx
1724 // Then look at the intersection of this line with the bounding box
1725 // (xMin, yMin, xMax, yMax). In the general case, there are four
1726 // intersection points:
1728 // s0 = (xMin - tx) / -dy
1729 // s1 = (xMax - tx) / -dy
1730 // s2 = (yMin - ty) / dx
1731 // s3 = (yMax - ty) / dx
1733 // and we want the middle two s values.
1735 // In the case where dx = 0, take s0 and s1; in the case where dy =
1736 // 0, take s2 and s3.
1738 // Each filled polygon is bounded by two of these line segments
1739 // perpdendicular to the t axis.
1741 // The t axis is bisected into smaller regions until the color
1742 // difference across a region is small enough, and then the region
1743 // is painted with a single color.
1745 // set up: require at least one split to avoid problems when the two
1746 // ends of the t axis have the same color
1747 nComps = shading->getColorSpace()->getNComps();
1748 ta[0] = tMin;
1749 next[0] = axialMaxSplits / 2;
1750 ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
1751 next[axialMaxSplits / 2] = axialMaxSplits;
1752 ta[axialMaxSplits] = tMax;
1754 // compute the color at t = tMin
1755 if (tMin < 0) {
1756 tt = t0;
1757 } else if (tMin > 1) {
1758 tt = t1;
1759 } else {
1760 tt = t0 + (t1 - t0) * tMin;
1762 shading->getColor(tt, &color0);
1764 // compute the coordinates of the point on the t axis at t = tMin;
1765 // then compute the intersection of the perpendicular line with the
1766 // bounding box
1767 tx = x0 + tMin * dx;
1768 ty = y0 + tMin * dy;
1769 if (dx == 0 && dy == 0) {
1770 sMin = sMax = 0;
1771 } if (dx == 0) {
1772 sMin = (xMin - tx) / -dy;
1773 sMax = (xMax - tx) / -dy;
1774 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1775 } else if (dy == 0) {
1776 sMin = (yMin - ty) / dx;
1777 sMax = (yMax - ty) / dx;
1778 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1779 } else {
1780 s[0] = (yMin - ty) / dx;
1781 s[1] = (yMax - ty) / dx;
1782 s[2] = (xMin - tx) / -dy;
1783 s[3] = (xMax - tx) / -dy;
1784 for (j = 0; j < 3; ++j) {
1785 kk = j;
1786 for (k = j + 1; k < 4; ++k) {
1787 if (s[k] < s[kk]) {
1788 kk = k;
1791 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1793 sMin = s[1];
1794 sMax = s[2];
1796 ux0 = tx - sMin * dy;
1797 uy0 = ty + sMin * dx;
1798 vx0 = tx - sMax * dy;
1799 vy0 = ty + sMax * dx;
1801 i = 0;
1802 while (i < axialMaxSplits) {
1804 // bisect until color difference is small enough or we hit the
1805 // bisection limit
1806 j = next[i];
1807 while (j > i + 1) {
1808 if (ta[j] < 0) {
1809 tt = t0;
1810 } else if (ta[j] > 1) {
1811 tt = t1;
1812 } else {
1813 tt = t0 + (t1 - t0) * ta[j];
1815 shading->getColor(tt, &color1);
1816 for (k = 0; k < nComps; ++k) {
1817 if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1818 break;
1821 if (k == nComps) {
1822 break;
1824 k = (i + j) / 2;
1825 ta[k] = 0.5 * (ta[i] + ta[j]);
1826 next[i] = k;
1827 next[k] = j;
1828 j = k;
1831 // use the average of the colors of the two sides of the region
1832 for (k = 0; k < nComps; ++k) {
1833 color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
1836 // compute the coordinates of the point on the t axis; then
1837 // compute the intersection of the perpendicular line with the
1838 // bounding box
1839 tx = x0 + ta[j] * dx;
1840 ty = y0 + ta[j] * dy;
1841 if (dx == 0 && dy == 0) {
1842 sMin = sMax = 0;
1843 } if (dx == 0) {
1844 sMin = (xMin - tx) / -dy;
1845 sMax = (xMax - tx) / -dy;
1846 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1847 } else if (dy == 0) {
1848 sMin = (yMin - ty) / dx;
1849 sMax = (yMax - ty) / dx;
1850 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1851 } else {
1852 s[0] = (yMin - ty) / dx;
1853 s[1] = (yMax - ty) / dx;
1854 s[2] = (xMin - tx) / -dy;
1855 s[3] = (xMax - tx) / -dy;
1856 for (j = 0; j < 3; ++j) {
1857 kk = j;
1858 for (k = j + 1; k < 4; ++k) {
1859 if (s[k] < s[kk]) {
1860 kk = k;
1863 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1865 sMin = s[1];
1866 sMax = s[2];
1868 ux1 = tx - sMin * dy;
1869 uy1 = ty + sMin * dx;
1870 vx1 = tx - sMax * dy;
1871 vy1 = ty + sMax * dx;
1873 // set the color
1874 state->setFillColor(&color0);
1875 out->updateFillColor(state);
1877 // fill the region
1878 state->moveTo(ux0, uy0);
1879 state->lineTo(vx0, vy0);
1880 state->lineTo(vx1, vy1);
1881 state->lineTo(ux1, uy1);
1882 state->closePath();
1883 out->fill(state);
1884 state->clearPath();
1886 // set up for next region
1887 ux0 = ux1;
1888 uy0 = uy1;
1889 vx0 = vx1;
1890 vy0 = vy1;
1891 color0 = color1;
1892 i = next[i];
1896 void Gfx::doRadialShFill(GfxRadialShading *shading) {
1897 double sMin, sMax, xMin, yMin, xMax, yMax;
1898 double x0, y0, r0, x1, y1, r1, t0, t1;
1899 int nComps;
1900 GfxColor colorA, colorB;
1901 double xa, ya, xb, yb, ra, rb;
1902 double ta, tb, sa, sb;
1903 int ia, ib, k, n;
1904 double *ctm;
1905 double angle, t;
1907 // get the shading info
1908 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1909 t0 = shading->getDomain0();
1910 t1 = shading->getDomain1();
1911 nComps = shading->getColorSpace()->getNComps();
1913 // compute the (possibly extended) s range
1914 sMin = 0;
1915 sMax = 1;
1916 if (shading->getExtend0()) {
1917 if (r0 < r1) {
1918 // extend the smaller end
1919 sMin = -r0 / (r1 - r0);
1920 } else {
1921 // extend the larger end
1922 //~ this computes the diagonal of the bounding box -- we should
1923 //~ really compute the intersection of the moving/expanding
1924 //~ circles with each of the four corners and look for the max
1925 //~ radius
1926 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1927 sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
1928 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1929 if (sMin > 0) {
1930 sMin = 0;
1931 } else if (sMin < -20) {
1932 // sanity check
1933 sMin = -20;
1937 if (shading->getExtend1()) {
1938 if (r1 < r0) {
1939 // extend the smaller end
1940 sMax = -r0 / (r1 - r0);
1941 } else if (r1 > r0) {
1942 // extend the larger end
1943 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1944 sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
1945 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1946 if (sMax < 1) {
1947 sMin = 1;
1948 } else if (sMax > 20) {
1949 // sanity check
1950 sMax = 20;
1955 // compute the number of steps into which circles must be divided to
1956 // achieve a curve flatness of 0.1 pixel in device space for the
1957 // largest circle (note that "device space" is 72 dpi when generating
1958 // PostScript, hence the relatively small 0.1 pixel accuracy)
1959 ctm = state->getCTM();
1960 t = fabs(ctm[0]);
1961 if (fabs(ctm[1]) > t) {
1962 t = fabs(ctm[1]);
1964 if (fabs(ctm[2]) > t) {
1965 t = fabs(ctm[2]);
1967 if (fabs(ctm[3]) > t) {
1968 t = fabs(ctm[3]);
1970 if (r0 > r1) {
1971 t *= r0;
1972 } else {
1973 t *= r1;
1975 if (t < 1) {
1976 n = 3;
1977 } else {
1978 n = (int)(M_PI / acos(1 - 0.1 / t));
1979 if (n < 3) {
1980 n = 3;
1981 } else if (n > 200) {
1982 n = 200;
1986 // Traverse the t axis and do the shading.
1988 // This generates and fills a series of rings. Each ring is defined
1989 // by two circles:
1990 // sa, ta, xa, ya, ra, colorA
1991 // sb, tb, xb, yb, rb, colorB
1993 // The s/t axis is divided into radialMaxSplits parts; these parts
1994 // are combined as much as possible while respecting the
1995 // radialColorDelta parameter.
1997 // setup for the start circle
1998 ia = 0;
1999 sa = sMin;
2000 ta = t0 + sa * (t1 - t0);
2001 xa = x0 + sa * (x1 - x0);
2002 ya = y0 + sa * (y1 - y0);
2003 ra = r0 + sa * (r1 - r0);
2004 if (ta < t0) {
2005 shading->getColor(t0, &colorA);
2006 } else if (ta > t1) {
2007 shading->getColor(t1, &colorA);
2008 } else {
2009 shading->getColor(ta, &colorA);
2012 while (ia < radialMaxSplits) {
2014 // go as far along the t axis (toward t1) as we can, such that the
2015 // color difference is within the tolerance (radialColorDelta) --
2016 // this uses bisection (between the current value, t, and t1),
2017 // limited to radialMaxSplits points along the t axis; require at
2018 // least one split to avoid problems when the innermost and
2019 // outermost colors are the same
2020 ib = radialMaxSplits;
2021 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2022 tb = t0 + sb * (t1 - t0);
2023 if (tb < t0) {
2024 shading->getColor(t0, &colorB);
2025 } else if (tb > t1) {
2026 shading->getColor(t1, &colorB);
2027 } else {
2028 shading->getColor(tb, &colorB);
2030 while (ib - ia > 1) {
2031 for (k = 0; k < nComps; ++k) {
2032 if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
2033 break;
2036 if (k == nComps && ib < radialMaxSplits) {
2037 break;
2039 ib = (ia + ib) / 2;
2040 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2041 tb = t0 + sb * (t1 - t0);
2042 if (tb < t0) {
2043 shading->getColor(t0, &colorB);
2044 } else if (tb > t1) {
2045 shading->getColor(t1, &colorB);
2046 } else {
2047 shading->getColor(tb, &colorB);
2051 // compute center and radius of the circle
2052 xb = x0 + sb * (x1 - x0);
2053 yb = y0 + sb * (y1 - y0);
2054 rb = r0 + sb * (r1 - r0);
2056 // use the average of the colors at the two circles
2057 for (k = 0; k < nComps; ++k) {
2058 colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
2060 state->setFillColor(&colorA);
2061 out->updateFillColor(state);
2063 // construct path for first circle
2064 state->moveTo(xa + ra, ya);
2065 for (k = 1; k < n; ++k) {
2066 angle = ((double)k / (double)n) * 2 * M_PI;
2067 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2069 state->closePath();
2071 // construct and append path for second circle
2072 state->moveTo(xb + rb, yb);
2073 for (k = 1; k < n; ++k) {
2074 angle = ((double)k / (double)n) * 2 * M_PI;
2075 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2077 state->closePath();
2079 // fill the ring
2080 out->eoFill(state);
2081 state->clearPath();
2083 // step to the next value of t
2084 ia = ib;
2085 sa = sb;
2086 ta = tb;
2087 xa = xb;
2088 ya = yb;
2089 ra = rb;
2090 colorA = colorB;
2094 void Gfx::doEndPath() {
2095 if (state->isCurPt() && clip != clipNone) {
2096 state->clip();
2097 if (clip == clipNormal) {
2098 out->clip(state);
2099 } else {
2100 out->eoClip(state);
2103 clip = clipNone;
2104 state->clearPath();
2107 //------------------------------------------------------------------------
2108 // path clipping operators
2109 //------------------------------------------------------------------------
2111 void Gfx::opClip(Object args[], int numArgs) {
2112 clip = clipNormal;
2115 void Gfx::opEOClip(Object args[], int numArgs) {
2116 clip = clipEO;
2119 //------------------------------------------------------------------------
2120 // text object operators
2121 //------------------------------------------------------------------------
2123 void Gfx::opBeginText(Object args[], int numArgs) {
2124 state->setTextMat(1, 0, 0, 1, 0, 0);
2125 state->textMoveTo(0, 0);
2126 out->updateTextMat(state);
2127 out->updateTextPos(state);
2128 fontChanged = gTrue;
2131 void Gfx::opEndText(Object args[], int numArgs) {
2132 out->endTextObject(state);
2135 //------------------------------------------------------------------------
2136 // text state operators
2137 //------------------------------------------------------------------------
2139 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
2140 state->setCharSpace(args[0].getNum());
2141 out->updateCharSpace(state);
2144 void Gfx::opSetFont(Object args[], int numArgs) {
2145 GfxFont *font;
2147 if (!(font = res->lookupFont(args[0].getName()))) {
2148 return;
2150 if (printCommands) {
2151 printf(" font: tag=%s name='%s' %g\n",
2152 font->getTag()->getCString(),
2153 font->getName() ? font->getName()->getCString() : "???",
2154 args[1].getNum());
2155 fflush(stdout);
2157 state->setFont(font, args[1].getNum());
2158 fontChanged = gTrue;
2161 void Gfx::opSetTextLeading(Object args[], int numArgs) {
2162 state->setLeading(args[0].getNum());
2165 void Gfx::opSetTextRender(Object args[], int numArgs) {
2166 state->setRender(args[0].getInt());
2167 out->updateRender(state);
2170 void Gfx::opSetTextRise(Object args[], int numArgs) {
2171 state->setRise(args[0].getNum());
2172 out->updateRise(state);
2175 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
2176 state->setWordSpace(args[0].getNum());
2177 out->updateWordSpace(state);
2180 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
2181 state->setHorizScaling(args[0].getNum());
2182 out->updateHorizScaling(state);
2183 fontChanged = gTrue;
2186 //------------------------------------------------------------------------
2187 // text positioning operators
2188 //------------------------------------------------------------------------
2190 void Gfx::opTextMove(Object args[], int numArgs) {
2191 double tx, ty;
2193 tx = state->getLineX() + args[0].getNum();
2194 ty = state->getLineY() + args[1].getNum();
2195 state->textMoveTo(tx, ty);
2196 out->updateTextPos(state);
2199 void Gfx::opTextMoveSet(Object args[], int numArgs) {
2200 double tx, ty;
2202 tx = state->getLineX() + args[0].getNum();
2203 ty = args[1].getNum();
2204 state->setLeading(-ty);
2205 ty += state->getLineY();
2206 state->textMoveTo(tx, ty);
2207 out->updateTextPos(state);
2210 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
2211 state->setTextMat(args[0].getNum(), args[1].getNum(),
2212 args[2].getNum(), args[3].getNum(),
2213 args[4].getNum(), args[5].getNum());
2214 state->textMoveTo(0, 0);
2215 out->updateTextMat(state);
2216 out->updateTextPos(state);
2217 fontChanged = gTrue;
2220 void Gfx::opTextNextLine(Object args[], int numArgs) {
2221 double tx, ty;
2223 tx = state->getLineX();
2224 ty = state->getLineY() - state->getLeading();
2225 state->textMoveTo(tx, ty);
2226 out->updateTextPos(state);
2229 //------------------------------------------------------------------------
2230 // text string operators
2231 //------------------------------------------------------------------------
2233 void Gfx::opShowText(Object args[], int numArgs) {
2234 if (!state->getFont()) {
2235 error(getPos(), "No font in show");
2236 return;
2238 doShowText(args[0].getString());
2241 void Gfx::opMoveShowText(Object args[], int numArgs) {
2242 double tx, ty;
2244 if (!state->getFont()) {
2245 error(getPos(), "No font in move/show");
2246 return;
2248 tx = state->getLineX();
2249 ty = state->getLineY() - state->getLeading();
2250 state->textMoveTo(tx, ty);
2251 out->updateTextPos(state);
2252 doShowText(args[0].getString());
2255 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
2256 double tx, ty;
2258 if (!state->getFont()) {
2259 error(getPos(), "No font in move/set/show");
2260 return;
2262 state->setWordSpace(args[0].getNum());
2263 state->setCharSpace(args[1].getNum());
2264 tx = state->getLineX();
2265 ty = state->getLineY() - state->getLeading();
2266 state->textMoveTo(tx, ty);
2267 out->updateWordSpace(state);
2268 out->updateCharSpace(state);
2269 out->updateTextPos(state);
2270 doShowText(args[2].getString());
2273 void Gfx::opShowSpaceText(Object args[], int numArgs) {
2274 Array *a;
2275 Object obj;
2276 int wMode;
2277 int i;
2279 if (!state->getFont()) {
2280 error(getPos(), "No font in show/space");
2281 return;
2283 wMode = state->getFont()->getWMode();
2284 a = args[0].getArray();
2285 for (i = 0; i < a->getLength(); ++i) {
2286 a->get(i, &obj);
2287 if (obj.isNum()) {
2288 if (wMode) {
2289 state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
2290 } else {
2291 state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
2293 out->updateTextShift(state, obj.getNum());
2294 } else if (obj.isString()) {
2295 doShowText(obj.getString());
2296 } else {
2297 error(getPos(), "Element of show/space array must be number or string");
2299 obj.free();
2303 void Gfx::doShowText(GString *s) {
2304 GfxFont *font;
2305 int wMode;
2306 double riseX, riseY;
2307 CharCode code;
2308 Unicode u[8];
2309 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2310 double originX, originY, tOriginX, tOriginY;
2311 double oldCTM[6], newCTM[6];
2312 double *mat;
2313 Object charProc;
2314 Dict *resDict;
2315 Parser *oldParser;
2316 char *p;
2317 int len, n, uLen, nChars, nSpaces, i;
2319 if (fontChanged) {
2320 out->updateFont(state);
2321 fontChanged = gFalse;
2323 font = state->getFont();
2324 wMode = font->getWMode();
2326 if (out->useDrawChar()) {
2327 out->beginString(state, s);
2330 // handle a Type 3 char
2331 if (font->getType() == fontType3 && out->interpretType3Chars()) {
2332 mat = state->getCTM();
2333 for (i = 0; i < 6; ++i) {
2334 oldCTM[i] = mat[i];
2336 mat = state->getTextMat();
2337 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2338 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2339 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2340 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2341 mat = font->getFontMatrix();
2342 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2343 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2344 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2345 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2346 newCTM[0] *= state->getFontSize();
2347 newCTM[1] *= state->getFontSize();
2348 newCTM[2] *= state->getFontSize();
2349 newCTM[3] *= state->getFontSize();
2350 newCTM[0] *= state->getHorizScaling();
2351 newCTM[2] *= state->getHorizScaling();
2352 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2353 curX = state->getCurX();
2354 curY = state->getCurY();
2355 lineX = state->getLineX();
2356 lineY = state->getLineY();
2357 oldParser = parser;
2358 p = s->getCString();
2359 len = s->getLength();
2360 while (len > 0) {
2361 n = font->getNextChar(p, len, &code,
2362 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2363 &dx, &dy, &originX, &originY);
2364 dx = dx * state->getFontSize() + state->getCharSpace();
2365 if (n == 1 && *p == ' ') {
2366 dx += state->getWordSpace();
2368 dx *= state->getHorizScaling();
2369 dy *= state->getFontSize();
2370 state->textTransformDelta(dx, dy, &tdx, &tdy);
2371 state->transform(curX + riseX, curY + riseY, &x, &y);
2372 saveState();
2373 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2374 //~ out->updateCTM(???)
2375 if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2376 code, u, uLen)) {
2377 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2378 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2379 pushResources(resDict);
2381 if (charProc.isStream()) {
2382 display(&charProc, gFalse);
2383 } else {
2384 error(getPos(), "Missing or bad Type3 CharProc entry");
2386 out->endType3Char(state);
2387 if (resDict) {
2388 popResources();
2390 charProc.free();
2392 restoreState();
2393 // GfxState::restore() does *not* restore the current position,
2394 // so we deal with it here using (curX, curY) and (lineX, lineY)
2395 curX += tdx;
2396 curY += tdy;
2397 state->moveTo(curX, curY);
2398 state->textSetPos(lineX, lineY);
2399 p += n;
2400 len -= n;
2402 parser = oldParser;
2404 } else if (out->useDrawChar()) {
2405 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2406 p = s->getCString();
2407 len = s->getLength();
2408 while (len > 0) {
2409 n = font->getNextChar(p, len, &code,
2410 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2411 &dx, &dy, &originX, &originY);
2412 if (wMode) {
2413 dx *= state->getFontSize();
2414 dy = dy * state->getFontSize() + state->getCharSpace();
2415 if (n == 1 && *p == ' ') {
2416 dy += state->getWordSpace();
2418 } else {
2419 dx = dx * state->getFontSize() + state->getCharSpace();
2420 if (n == 1 && *p == ' ') {
2421 dx += state->getWordSpace();
2423 dx *= state->getHorizScaling();
2424 dy *= state->getFontSize();
2426 state->textTransformDelta(dx, dy, &tdx, &tdy);
2427 originX *= state->getFontSize();
2428 originY *= state->getFontSize();
2429 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2430 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2431 tdx, tdy, tOriginX, tOriginY, code, u, uLen);
2432 state->shift(tdx, tdy);
2433 p += n;
2434 len -= n;
2437 } else {
2438 dx = dy = 0;
2439 p = s->getCString();
2440 len = s->getLength();
2441 nChars = nSpaces = 0;
2442 while (len > 0) {
2443 n = font->getNextChar(p, len, &code,
2444 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2445 &dx2, &dy2, &originX, &originY);
2446 dx += dx2;
2447 dy += dy2;
2448 if (n == 1 && *p == ' ') {
2449 ++nSpaces;
2451 ++nChars;
2452 p += n;
2453 len -= n;
2455 if (wMode) {
2456 dx *= state->getFontSize();
2457 dy = dy * state->getFontSize()
2458 + nChars * state->getCharSpace()
2459 + nSpaces * state->getWordSpace();
2460 } else {
2461 dx = dx * state->getFontSize()
2462 + nChars * state->getCharSpace()
2463 + nSpaces * state->getWordSpace();
2464 dx *= state->getHorizScaling();
2465 dy *= state->getFontSize();
2467 state->textTransformDelta(dx, dy, &tdx, &tdy);
2468 out->drawString(state, s);
2469 state->shift(tdx, tdy);
2472 if (out->useDrawChar()) {
2473 out->endString(state);
2476 updateLevel += 10 * s->getLength();
2479 //------------------------------------------------------------------------
2480 // XObject operators
2481 //------------------------------------------------------------------------
2483 void Gfx::opXObject(Object args[], int numArgs) {
2484 Object obj1, obj2, obj3, refObj;
2485 #if OPI_SUPPORT
2486 Object opiDict;
2487 #endif
2489 if (!res->lookupXObject(args[0].getName(), &obj1)) {
2490 return;
2492 if (!obj1.isStream()) {
2493 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2494 obj1.free();
2495 return;
2497 #if OPI_SUPPORT
2498 obj1.streamGetDict()->lookup("OPI", &opiDict);
2499 if (opiDict.isDict()) {
2500 out->opiBegin(state, opiDict.getDict());
2502 #endif
2503 obj1.streamGetDict()->lookup("Subtype", &obj2);
2504 if (obj2.isName("Image")) {
2505 res->lookupXObjectNF(args[0].getName(), &refObj);
2506 doImage(&refObj, obj1.getStream(), gFalse);
2507 refObj.free();
2508 } else if (obj2.isName("Form")) {
2509 doForm(&obj1);
2510 } else if (obj2.isName("PS")) {
2511 obj1.streamGetDict()->lookup("Level1", &obj3);
2512 out->psXObject(obj1.getStream(),
2513 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2514 } else if (obj2.isName()) {
2515 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2516 } else {
2517 error(getPos(), "XObject subtype is missing or wrong type");
2519 obj2.free();
2520 #if OPI_SUPPORT
2521 if (opiDict.isDict()) {
2522 out->opiEnd(state, opiDict.getDict());
2524 opiDict.free();
2525 #endif
2526 obj1.free();
2529 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2530 Dict *dict;
2531 int width, height;
2532 int bits;
2533 GBool mask;
2534 GBool invert;
2535 GfxColorSpace *colorSpace;
2536 GfxImageColorMap *colorMap;
2537 Object maskObj;
2538 GBool haveMask;
2539 int maskColors[2*gfxColorMaxComps];
2540 Object obj1, obj2;
2541 int i;
2543 // get stream dict
2544 dict = str->getDict();
2546 // get size
2547 dict->lookup("Width", &obj1);
2548 if (obj1.isNull()) {
2549 obj1.free();
2550 dict->lookup("W", &obj1);
2552 if (!obj1.isInt())
2553 goto err2;
2554 width = obj1.getInt();
2555 obj1.free();
2556 dict->lookup("Height", &obj1);
2557 if (obj1.isNull()) {
2558 obj1.free();
2559 dict->lookup("H", &obj1);
2561 if (!obj1.isInt())
2562 goto err2;
2563 height = obj1.getInt();
2564 obj1.free();
2566 // image or mask?
2567 dict->lookup("ImageMask", &obj1);
2568 if (obj1.isNull()) {
2569 obj1.free();
2570 dict->lookup("IM", &obj1);
2572 mask = gFalse;
2573 if (obj1.isBool())
2574 mask = obj1.getBool();
2575 else if (!obj1.isNull())
2576 goto err2;
2577 obj1.free();
2579 // bit depth
2580 dict->lookup("BitsPerComponent", &obj1);
2581 if (obj1.isNull()) {
2582 obj1.free();
2583 dict->lookup("BPC", &obj1);
2585 if (obj1.isInt()) {
2586 bits = obj1.getInt();
2587 } else if (mask) {
2588 bits = 1;
2589 } else {
2590 goto err2;
2592 obj1.free();
2594 // display a mask
2595 if (mask) {
2597 // check for inverted mask
2598 if (bits != 1)
2599 goto err1;
2600 invert = gFalse;
2601 dict->lookup("Decode", &obj1);
2602 if (obj1.isNull()) {
2603 obj1.free();
2604 dict->lookup("D", &obj1);
2606 if (obj1.isArray()) {
2607 obj1.arrayGet(0, &obj2);
2608 if (obj2.isInt() && obj2.getInt() == 1)
2609 invert = gTrue;
2610 obj2.free();
2611 } else if (!obj1.isNull()) {
2612 goto err2;
2614 obj1.free();
2616 // draw it
2617 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2619 } else {
2621 // get color space and color map
2622 dict->lookup("ColorSpace", &obj1);
2623 if (obj1.isNull()) {
2624 obj1.free();
2625 dict->lookup("CS", &obj1);
2627 if (obj1.isName()) {
2628 res->lookupColorSpace(obj1.getName(), &obj2);
2629 if (!obj2.isNull()) {
2630 obj1.free();
2631 obj1 = obj2;
2632 } else {
2633 obj2.free();
2636 colorSpace = GfxColorSpace::parse(&obj1);
2637 obj1.free();
2638 if (!colorSpace) {
2639 goto err1;
2641 dict->lookup("Decode", &obj1);
2642 if (obj1.isNull()) {
2643 obj1.free();
2644 dict->lookup("D", &obj1);
2646 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2647 obj1.free();
2648 if (!colorMap->isOk()) {
2649 delete colorMap;
2650 goto err1;
2653 // get the mask
2654 haveMask = gFalse;
2655 dict->lookup("Mask", &maskObj);
2656 if (maskObj.isArray()) {
2657 for (i = 0; i < maskObj.arrayGetLength(); ++i) {
2658 maskObj.arrayGet(i, &obj1);
2659 maskColors[i] = obj1.getInt();
2660 obj1.free();
2662 haveMask = gTrue;
2665 // draw it
2666 out->drawImage(state, ref, str, width, height, colorMap,
2667 haveMask ? maskColors : (int *)NULL, inlineImg);
2668 delete colorMap;
2670 maskObj.free();
2673 if ((i = width * height) > 1000) {
2674 i = 1000;
2676 updateLevel += i;
2678 return;
2680 err2:
2681 obj1.free();
2682 err1:
2683 error(getPos(), "Bad image parameters");
2686 void Gfx::doForm(Object *str) {
2687 Dict *dict;
2688 Object matrixObj, bboxObj;
2689 double m[6], bbox[6];
2690 Object resObj;
2691 Dict *resDict;
2692 Object obj1;
2693 int i;
2695 // check for excessive recursion
2696 if (formDepth > 20) {
2697 return;
2700 // get stream dict
2701 dict = str->streamGetDict();
2703 // check form type
2704 dict->lookup("FormType", &obj1);
2705 if (!(obj1.isInt() && obj1.getInt() == 1)) {
2706 error(getPos(), "Unknown form type");
2708 obj1.free();
2710 // get bounding box
2711 dict->lookup("BBox", &bboxObj);
2712 if (!bboxObj.isArray()) {
2713 matrixObj.free();
2714 bboxObj.free();
2715 error(getPos(), "Bad form bounding box");
2716 return;
2718 for (i = 0; i < 4; ++i) {
2719 bboxObj.arrayGet(i, &obj1);
2720 bbox[i] = obj1.getNum();
2721 obj1.free();
2723 bboxObj.free();
2725 // get matrix
2726 dict->lookup("Matrix", &matrixObj);
2727 if (matrixObj.isArray()) {
2728 for (i = 0; i < 6; ++i) {
2729 matrixObj.arrayGet(i, &obj1);
2730 m[i] = obj1.getNum();
2731 obj1.free();
2733 } else {
2734 m[0] = 1; m[1] = 0;
2735 m[2] = 0; m[3] = 1;
2736 m[4] = 0; m[5] = 0;
2738 matrixObj.free();
2740 // get resources
2741 dict->lookup("Resources", &resObj);
2742 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2744 // draw it
2745 ++formDepth;
2746 doForm1(str, resDict, m, bbox);
2747 --formDepth;
2749 resObj.free();
2752 void Gfx::doAnnot(Object *str, double xMin, double yMin,
2753 double xMax, double yMax) {
2754 Dict *dict, *resDict;
2755 Object matrixObj, bboxObj, resObj;
2756 Object obj1;
2757 double m[6], bbox[6], ictm[6];
2758 double *ctm;
2759 double formX0, formY0, formX1, formY1;
2760 double annotX0, annotY0, annotX1, annotY1;
2761 double det, x, y, sx, sy;
2762 int i;
2764 // get stream dict
2765 dict = str->streamGetDict();
2767 // get the form bounding box
2768 dict->lookup("BBox", &bboxObj);
2769 if (!bboxObj.isArray()) {
2770 bboxObj.free();
2771 error(getPos(), "Bad form bounding box");
2772 return;
2774 for (i = 0; i < 4; ++i) {
2775 bboxObj.arrayGet(i, &obj1);
2776 bbox[i] = obj1.getNum();
2777 obj1.free();
2779 bboxObj.free();
2781 // get the form matrix
2782 dict->lookup("Matrix", &matrixObj);
2783 if (matrixObj.isArray()) {
2784 for (i = 0; i < 6; ++i) {
2785 matrixObj.arrayGet(i, &obj1);
2786 m[i] = obj1.getNum();
2787 obj1.free();
2789 } else {
2790 m[0] = 1; m[1] = 0;
2791 m[2] = 0; m[3] = 1;
2792 m[4] = 0; m[5] = 0;
2794 matrixObj.free();
2796 // transform the form bbox from form space to user space
2797 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
2798 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
2799 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
2800 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
2802 // transform the annotation bbox from default user space to user
2803 // space: (bbox * baseMatrix) * iCTM
2804 ctm = state->getCTM();
2805 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2806 ictm[0] = ctm[3] * det;
2807 ictm[1] = -ctm[1] * det;
2808 ictm[2] = -ctm[2] * det;
2809 ictm[3] = ctm[0] * det;
2810 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2811 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2812 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
2813 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
2814 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
2815 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
2816 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
2817 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
2818 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
2819 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
2821 // swap min/max coords
2822 if (formX0 > formX1) {
2823 x = formX0; formX0 = formX1; formX1 = x;
2825 if (formY0 > formY1) {
2826 y = formY0; formY0 = formY1; formY1 = y;
2828 if (annotX0 > annotX1) {
2829 x = annotX0; annotX0 = annotX1; annotX1 = x;
2831 if (annotY0 > annotY1) {
2832 y = annotY0; annotY0 = annotY1; annotY1 = y;
2835 // scale the form to fit the annotation bbox
2836 if (formX1 == formX0) {
2837 // this shouldn't happen
2838 sx = 1;
2839 } else {
2840 sx = (annotX1 - annotX0) / (formX1 - formX0);
2842 if (formY1 == formY0) {
2843 // this shouldn't happen
2844 sy = 1;
2845 } else {
2846 sy = (annotY1 - annotY0) / (formY1 - formY0);
2848 m[0] *= sx;
2849 m[2] *= sx;
2850 m[4] = (m[4] - formX0) * sx + annotX0;
2851 m[1] *= sy;
2852 m[3] *= sy;
2853 m[5] = (m[5] - formY0) * sy + annotY0;
2855 // get resources
2856 dict->lookup("Resources", &resObj);
2857 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2859 // draw it
2860 doForm1(str, resDict, m, bbox);
2862 resObj.free();
2863 bboxObj.free();
2866 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
2867 Parser *oldParser;
2868 double oldBaseMatrix[6];
2869 int i;
2871 // push new resources on stack
2872 pushResources(resDict);
2874 // save current graphics state
2875 saveState();
2877 // kill any pre-existing path
2878 state->clearPath();
2880 // save current parser
2881 oldParser = parser;
2883 // set form transformation matrix
2884 state->concatCTM(matrix[0], matrix[1], matrix[2],
2885 matrix[3], matrix[4], matrix[5]);
2886 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2887 matrix[3], matrix[4], matrix[5]);
2889 // set new base matrix
2890 for (i = 0; i < 6; ++i) {
2891 oldBaseMatrix[i] = baseMatrix[i];
2892 baseMatrix[i] = state->getCTM()[i];
2895 // set form bounding box
2896 state->moveTo(bbox[0], bbox[1]);
2897 state->lineTo(bbox[2], bbox[1]);
2898 state->lineTo(bbox[2], bbox[3]);
2899 state->lineTo(bbox[0], bbox[3]);
2900 state->closePath();
2901 state->clip();
2902 out->clip(state);
2903 state->clearPath();
2905 // draw the form
2906 display(str, gFalse);
2908 // restore base matrix
2909 for (i = 0; i < 6; ++i) {
2910 baseMatrix[i] = oldBaseMatrix[i];
2913 // restore parser
2914 parser = oldParser;
2916 // restore graphics state
2917 restoreState();
2919 // pop resource stack
2920 popResources();
2922 return;
2925 //------------------------------------------------------------------------
2926 // in-line image operators
2927 //------------------------------------------------------------------------
2929 void Gfx::opBeginImage(Object args[], int numArgs) {
2930 Stream *str;
2931 int c1, c2;
2933 // build dict/stream
2934 str = buildImageStream();
2936 // display the image
2937 if (str) {
2938 doImage(NULL, str, gTrue);
2940 // skip 'EI' tag
2941 c1 = str->getBaseStream()->getChar();
2942 c2 = str->getBaseStream()->getChar();
2943 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2944 c1 = c2;
2945 c2 = str->getBaseStream()->getChar();
2947 delete str;
2951 Stream *Gfx::buildImageStream() {
2952 Object dict;
2953 Object obj;
2954 char *key;
2955 Stream *str;
2957 // build dictionary
2958 dict.initDict(xref);
2959 parser->getObj(&obj);
2960 while (!obj.isCmd("ID") && !obj.isEOF()) {
2961 if (!obj.isName()) {
2962 error(getPos(), "Inline image dictionary key must be a name object");
2963 obj.free();
2964 } else {
2965 key = copyString(obj.getName());
2966 obj.free();
2967 parser->getObj(&obj);
2968 if (obj.isEOF() || obj.isError()) {
2969 gfree(key);
2970 break;
2972 dict.dictAdd(key, &obj);
2974 parser->getObj(&obj);
2976 if (obj.isEOF()) {
2977 error(getPos(), "End of file in inline image");
2978 obj.free();
2979 dict.free();
2980 return NULL;
2982 obj.free();
2984 // make stream
2985 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2986 str = str->addFilters(&dict);
2988 return str;
2991 void Gfx::opImageData(Object args[], int numArgs) {
2992 error(getPos(), "Internal: got 'ID' operator");
2995 void Gfx::opEndImage(Object args[], int numArgs) {
2996 error(getPos(), "Internal: got 'EI' operator");
2999 //------------------------------------------------------------------------
3000 // type 3 font operators
3001 //------------------------------------------------------------------------
3003 void Gfx::opSetCharWidth(Object args[], int numArgs) {
3004 out->type3D0(state, args[0].getNum(), args[1].getNum());
3007 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
3008 out->type3D1(state, args[0].getNum(), args[1].getNum(),
3009 args[2].getNum(), args[3].getNum(),
3010 args[4].getNum(), args[5].getNum());
3013 //------------------------------------------------------------------------
3014 // compatibility operators
3015 //------------------------------------------------------------------------
3017 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
3018 ++ignoreUndef;
3021 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
3022 if (ignoreUndef > 0)
3023 --ignoreUndef;
3026 //------------------------------------------------------------------------
3027 // marked content operators
3028 //------------------------------------------------------------------------
3030 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
3031 if (printCommands) {
3032 printf(" marked content: %s ", args[0].getName());
3033 if (numArgs == 2)
3034 args[2].print(stdout);
3035 printf("\n");
3036 fflush(stdout);
3040 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
3043 void Gfx::opMarkPoint(Object args[], int numArgs) {
3044 if (printCommands) {
3045 printf(" mark point: %s ", args[0].getName());
3046 if (numArgs == 2)
3047 args[2].print(stdout);
3048 printf("\n");
3049 fflush(stdout);
3053 //------------------------------------------------------------------------
3054 // misc
3055 //------------------------------------------------------------------------
3057 void Gfx::saveState() {
3058 out->saveState(state);
3059 state = state->save();
3062 void Gfx::restoreState() {
3063 state = state->restore();
3064 out->restoreState(state);
3067 void Gfx::pushResources(Dict *resDict) {
3068 res = new GfxResources(xref, resDict, res);
3071 void Gfx::popResources() {
3072 GfxResources *resPtr;
3074 resPtr = res->getNext();
3075 delete res;
3076 res = resPtr;