1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
21 #include "GlobalParams.h"
22 #include "CharTypes.h"
31 #include "OutputDev.h"
36 // the MSVC math.h doesn't define this
38 #define M_PI 3.14159265358979323846
41 //------------------------------------------------------------------------
43 //------------------------------------------------------------------------
45 // Max recursive depth for a function shading fill.
46 #define functionMaxDepth 6
48 // Max delta allowed in any color component for a function shading fill.
49 #define functionColorDelta (dblToCol(1 / 256.0))
51 // Max number of splits along the t axis for an axial shading fill.
52 #define axialMaxSplits 256
54 // Max delta allowed in any color component for an axial shading fill.
55 #define axialColorDelta (dblToCol(1 / 256.0))
57 // Max number of splits along the t axis for a radial shading fill.
58 #define radialMaxSplits 256
60 // Max delta allowed in any color component for a radial shading fill.
61 #define radialColorDelta (dblToCol(1 / 256.0))
63 // Max recursive depth for a Gouraud triangle shading fill.
64 #define gouraudMaxDepth 4
66 // Max delta allowed in any color component for a Gouraud triangle
68 #define gouraudColorDelta (dblToCol(1 / 256.0))
70 // Max recursive depth for a patch mesh shading fill.
71 #define patchMaxDepth 6
73 // Max delta allowed in any color component for a patch mesh shading
75 #define patchColorDelta (dblToCol(1 / 256.0))
77 //------------------------------------------------------------------------
79 //------------------------------------------------------------------------
81 #ifdef WIN32 // this works around a bug in the VC7 compiler
82 # pragma optimize("",off)
85 Operator Gfx::opTab[] = {
86 {"\"", 3, {tchkNum, tchkNum, tchkString},
87 &Gfx::opMoveSetShowText},
88 {"'", 1, {tchkString},
89 &Gfx::opMoveShowText},
93 &Gfx::opEOFillStroke},
94 {"BDC", 2, {tchkName, tchkProps},
95 &Gfx::opBeginMarkedContent},
98 {"BMC", 1, {tchkName},
99 &Gfx::opBeginMarkedContent},
100 {"BT", 0, {tchkNone},
102 {"BX", 0, {tchkNone},
103 &Gfx::opBeginIgnoreUndef},
104 {"CS", 1, {tchkName},
105 &Gfx::opSetStrokeColorSpace},
106 {"DP", 2, {tchkName, tchkProps},
108 {"Do", 1, {tchkName},
110 {"EI", 0, {tchkNone},
112 {"EMC", 0, {tchkNone},
113 &Gfx::opEndMarkedContent},
114 {"ET", 0, {tchkNone},
116 {"EX", 0, {tchkNone},
117 &Gfx::opEndIgnoreUndef},
121 &Gfx::opSetStrokeGray},
122 {"ID", 0, {tchkNone},
126 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
127 &Gfx::opSetStrokeCMYKColor},
129 &Gfx::opSetMiterLimit},
130 {"MP", 1, {tchkName},
134 {"RG", 3, {tchkNum, tchkNum, tchkNum},
135 &Gfx::opSetStrokeRGBColor},
138 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
139 &Gfx::opSetStrokeColor},
140 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
142 &Gfx::opSetStrokeColorN},
143 {"T*", 0, {tchkNone},
144 &Gfx::opTextNextLine},
145 {"TD", 2, {tchkNum, tchkNum},
146 &Gfx::opTextMoveSet},
147 {"TJ", 1, {tchkArray},
148 &Gfx::opShowSpaceText},
150 &Gfx::opSetTextLeading},
152 &Gfx::opSetCharSpacing},
153 {"Td", 2, {tchkNum, tchkNum},
155 {"Tf", 2, {tchkName, tchkNum},
157 {"Tj", 1, {tchkString},
159 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
161 &Gfx::opSetTextMatrix},
163 &Gfx::opSetTextRender},
165 &Gfx::opSetTextRise},
167 &Gfx::opSetWordSpacing},
169 &Gfx::opSetHorizScaling},
172 {"W*", 0, {tchkNone},
175 &Gfx::opCloseFillStroke},
176 {"b*", 0, {tchkNone},
177 &Gfx::opCloseEOFillStroke},
178 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
181 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
184 {"cs", 1, {tchkName},
185 &Gfx::opSetFillColorSpace},
186 {"d", 2, {tchkArray, tchkNum},
188 {"d0", 2, {tchkNum, tchkNum},
189 &Gfx::opSetCharWidth},
190 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
192 &Gfx::opSetCacheDevice},
195 {"f*", 0, {tchkNone},
198 &Gfx::opSetFillGray},
199 {"gs", 1, {tchkName},
200 &Gfx::opSetExtGState},
206 &Gfx::opSetLineJoin},
207 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
208 &Gfx::opSetFillCMYKColor},
209 {"l", 2, {tchkNum, tchkNum},
211 {"m", 2, {tchkNum, tchkNum},
217 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
219 {"rg", 3, {tchkNum, tchkNum, tchkNum},
220 &Gfx::opSetFillRGBColor},
221 {"ri", 1, {tchkName},
222 &Gfx::opSetRenderingIntent},
224 &Gfx::opCloseStroke},
225 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
226 &Gfx::opSetFillColor},
227 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
229 &Gfx::opSetFillColorN},
230 {"sh", 1, {tchkName},
232 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
235 &Gfx::opSetLineWidth},
236 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
240 #ifdef WIN32 // this works around a bug in the VC7 compiler
241 # pragma optimize("",on)
244 #define numOps (sizeof(opTab) / sizeof(Operator))
246 //------------------------------------------------------------------------
248 //------------------------------------------------------------------------
250 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
256 // build font dictionary
258 resDict->lookupNF("Font", &obj1);
260 obj1.fetch(xref, &obj2);
263 fonts = new GfxFontDict(xref, &r, obj2.getDict());
266 } else if (obj1.isDict()) {
267 fonts = new GfxFontDict(xref, NULL, obj1.getDict());
271 // get XObject dictionary
272 resDict->lookup("XObject", &xObjDict);
274 // get color space dictionary
275 resDict->lookup("ColorSpace", &colorSpaceDict);
277 // get pattern dictionary
278 resDict->lookup("Pattern", &patternDict);
280 // get shading dictionary
281 resDict->lookup("Shading", &shadingDict);
283 // get graphics state parameter dictionary
284 resDict->lookup("ExtGState", &gStateDict);
289 colorSpaceDict.initNull();
290 patternDict.initNull();
291 shadingDict.initNull();
292 gStateDict.initNull();
298 GfxResources::~GfxResources() {
303 colorSpaceDict.free();
309 GfxFont *GfxResources::lookupFont(char *name) {
311 GfxResources *resPtr;
313 for (resPtr = this; resPtr; resPtr = resPtr->next) {
315 if ((font = resPtr->fonts->lookup(name)))
319 error(-1, "Unknown font tag '%s'", name);
323 GBool GfxResources::lookupXObject(char *name, Object *obj) {
324 GfxResources *resPtr;
326 for (resPtr = this; resPtr; resPtr = resPtr->next) {
327 if (resPtr->xObjDict.isDict()) {
328 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
333 error(-1, "XObject '%s' is unknown", name);
337 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
338 GfxResources *resPtr;
340 for (resPtr = this; resPtr; resPtr = resPtr->next) {
341 if (resPtr->xObjDict.isDict()) {
342 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
347 error(-1, "XObject '%s' is unknown", name);
351 void GfxResources::lookupColorSpace(char *name, Object *obj) {
352 GfxResources *resPtr;
354 for (resPtr = this; resPtr; resPtr = resPtr->next) {
355 if (resPtr->colorSpaceDict.isDict()) {
356 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
365 GfxPattern *GfxResources::lookupPattern(char *name) {
366 GfxResources *resPtr;
370 for (resPtr = this; resPtr; resPtr = resPtr->next) {
371 if (resPtr->patternDict.isDict()) {
372 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
373 pattern = GfxPattern::parse(&obj);
380 error(-1, "Unknown pattern '%s'", name);
384 GfxShading *GfxResources::lookupShading(char *name) {
385 GfxResources *resPtr;
389 for (resPtr = this; resPtr; resPtr = resPtr->next) {
390 if (resPtr->shadingDict.isDict()) {
391 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
392 shading = GfxShading::parse(&obj);
399 error(-1, "Unknown shading '%s'", name);
403 GBool GfxResources::lookupGState(char *name, Object *obj) {
404 GfxResources *resPtr;
406 for (resPtr = this; resPtr; resPtr = resPtr->next) {
407 if (resPtr->gStateDict.isDict()) {
408 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
414 error(-1, "ExtGState '%s' is unknown", name);
418 //------------------------------------------------------------------------
420 //------------------------------------------------------------------------
422 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
423 double hDPI, double vDPI, PDFRectangle *box,
424 PDFRectangle *cropBox, int rotate,
425 GBool (*abortCheckCbkA)(void *data),
426 void *abortCheckCbkDataA) {
431 printCommands = globalParams->getPrintCommands();
433 // start the resource stack
434 res = new GfxResources(xref, resDict, NULL);
438 state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
439 fontChanged = gFalse;
442 out->startPage(pageNum, state);
443 out->setDefaultCTM(state->getCTM());
444 out->updateAll(state);
445 for (i = 0; i < 6; ++i) {
446 baseMatrix[i] = state->getCTM()[i];
449 abortCheckCbk = abortCheckCbkA;
450 abortCheckCbkData = abortCheckCbkDataA;
454 state->moveTo(cropBox->x1, cropBox->y1);
455 state->lineTo(cropBox->x2, cropBox->y1);
456 state->lineTo(cropBox->x2, cropBox->y2);
457 state->lineTo(cropBox->x1, cropBox->y2);
465 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
466 PDFRectangle *box, PDFRectangle *cropBox,
467 GBool (*abortCheckCbkA)(void *data),
468 void *abortCheckCbkDataA) {
473 printCommands = globalParams->getPrintCommands();
475 // start the resource stack
476 res = new GfxResources(xref, resDict, NULL);
480 state = new GfxState(72, 72, box, 0, gFalse);
481 fontChanged = gFalse;
484 for (i = 0; i < 6; ++i) {
485 baseMatrix[i] = state->getCTM()[i];
488 abortCheckCbk = abortCheckCbkA;
489 abortCheckCbkData = abortCheckCbkDataA;
493 state->moveTo(cropBox->x1, cropBox->y1);
494 state->lineTo(cropBox->x2, cropBox->y1);
495 state->lineTo(cropBox->x2, cropBox->y2);
496 state->lineTo(cropBox->x1, cropBox->y2);
505 while (state->hasSaves()) {
519 void Gfx::display(Object *obj, GBool topLevel) {
523 if (obj->isArray()) {
524 for (i = 0; i < obj->arrayGetLength(); ++i) {
525 obj->arrayGet(i, &obj2);
526 if (!obj2.isStream()) {
527 error(-1, "Weird page contents");
533 } else if (!obj->isStream()) {
534 error(-1, "Weird page contents");
537 parser = new Parser(xref, new Lexer(xref, obj));
543 void Gfx::go(GBool topLevel) {
545 Object args[maxArgs];
549 // scan a sequence of objects
550 updateLevel = lastAbortCheck = 0;
552 parser->getObj(&obj);
553 while (!obj.isEOF()) {
555 // got a command - execute it
559 for (i = 0; i < numArgs; ++i) {
561 args[i].print(stdout);
566 execOp(&obj, args, numArgs);
568 for (i = 0; i < numArgs; ++i)
572 // periodically update display
573 if (++updateLevel >= 20000) {
578 // check for an abort
580 if (updateLevel - lastAbortCheck > 10) {
581 if ((*abortCheckCbk)(abortCheckCbkData)) {
584 lastAbortCheck = updateLevel;
588 // got an argument - save it
589 } else if (numArgs < maxArgs) {
590 args[numArgs++] = obj;
592 // too many arguments - something is wrong
594 error(getPos(), "Too many args in content stream");
596 printf("throwing away arg: ");
604 // grab the next object
605 parser->getObj(&obj);
609 // args at end with no command
611 error(getPos(), "Leftover args in content stream");
613 printf("%d leftovers:", numArgs);
614 for (i = 0; i < numArgs; ++i) {
616 args[i].print(stdout);
621 for (i = 0; i < numArgs; ++i)
626 if (topLevel && updateLevel > 0) {
631 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
638 name = cmd->getCmd();
639 if (!(op = findOp(name))) {
640 if (ignoreUndef == 0)
641 error(getPos(), "Unknown operator '%s'", name);
647 if (op->numArgs >= 0) {
648 if (numArgs < op->numArgs) {
649 error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
652 if (numArgs > op->numArgs) {
654 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
656 argPtr += numArgs - op->numArgs;
657 numArgs = op->numArgs;
660 if (numArgs > -op->numArgs) {
661 error(getPos(), "Too many (%d) args to '%s' operator",
666 for (i = 0; i < numArgs; ++i) {
667 if (!checkArg(&argPtr[i], op->tchk[i])) {
668 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
669 i, name, argPtr[i].getTypeName());
675 (this->*op->func)(argPtr, numArgs);
678 Operator *Gfx::findOp(char *name) {
683 // invariant: opTab[a] < name < opTab[b]
686 cmp = strcmp(opTab[m].name, name);
699 GBool Gfx::checkArg(Object *arg, TchkType type) {
701 case tchkBool: return arg->isBool();
702 case tchkInt: return arg->isInt();
703 case tchkNum: return arg->isNum();
704 case tchkString: return arg->isString();
705 case tchkName: return arg->isName();
706 case tchkArray: return arg->isArray();
707 case tchkProps: return arg->isDict() || arg->isName();
708 case tchkSCN: return arg->isNum() || arg->isName();
709 case tchkNone: return gFalse;
715 return parser ? parser->getPos() : -1;
718 //------------------------------------------------------------------------
719 // graphics state operators
720 //------------------------------------------------------------------------
722 void Gfx::opSave(Object args[], int numArgs) {
726 void Gfx::opRestore(Object args[], int numArgs) {
730 void Gfx::opConcat(Object args[], int numArgs) {
731 state->concatCTM(args[0].getNum(), args[1].getNum(),
732 args[2].getNum(), args[3].getNum(),
733 args[4].getNum(), args[5].getNum());
734 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
735 args[2].getNum(), args[3].getNum(),
736 args[4].getNum(), args[5].getNum());
740 void Gfx::opSetDash(Object args[], int numArgs) {
747 a = args[0].getArray();
748 length = a->getLength();
752 dash = (double *)gmallocn(length, sizeof(double));
753 for (i = 0; i < length; ++i) {
754 dash[i] = a->get(i, &obj)->getNum();
758 state->setLineDash(dash, length, args[1].getNum());
759 out->updateLineDash(state);
762 void Gfx::opSetFlat(Object args[], int numArgs) {
763 state->setFlatness((int)args[0].getNum());
764 out->updateFlatness(state);
767 void Gfx::opSetLineJoin(Object args[], int numArgs) {
768 state->setLineJoin(args[0].getInt());
769 out->updateLineJoin(state);
772 void Gfx::opSetLineCap(Object args[], int numArgs) {
773 state->setLineCap(args[0].getInt());
774 out->updateLineCap(state);
777 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
778 state->setMiterLimit(args[0].getNum());
779 out->updateMiterLimit(state);
782 void Gfx::opSetLineWidth(Object args[], int numArgs) {
783 state->setLineWidth(args[0].getNum());
784 out->updateLineWidth(state);
787 void Gfx::opSetExtGState(Object args[], int numArgs) {
792 if (!res->lookupGState(args[0].getName(), &obj1)) {
795 if (!obj1.isDict()) {
796 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
801 // transparency support: blend mode, fill/stroke opacity
802 if (!obj1.dictLookup("BM", &obj2)->isNull()) {
803 if (state->parseBlendMode(&obj2, &mode)) {
804 state->setBlendMode(mode);
805 out->updateBlendMode(state);
807 error(getPos(), "Invalid blend mode in ExtGState");
811 if (obj1.dictLookup("ca", &obj2)->isNum()) {
812 state->setFillOpacity(obj2.getNum());
813 out->updateFillOpacity(state);
816 if (obj1.dictLookup("CA", &obj2)->isNum()) {
817 state->setStrokeOpacity(obj2.getNum());
818 out->updateStrokeOpacity(state);
822 // fill/stroke overprint
823 if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
824 state->setFillOverprint(obj2.getBool());
825 out->updateFillOverprint(state);
828 if (obj1.dictLookup("OP", &obj2)->isBool()) {
829 state->setStrokeOverprint(obj2.getBool());
830 out->updateStrokeOverprint(state);
832 state->setFillOverprint(obj2.getBool());
833 out->updateFillOverprint(state);
841 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
844 //------------------------------------------------------------------------
846 //------------------------------------------------------------------------
848 void Gfx::opSetFillGray(Object args[], int numArgs) {
851 state->setFillPattern(NULL);
852 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
853 out->updateFillColorSpace(state);
854 color.c[0] = dblToCol(args[0].getNum());
855 state->setFillColor(&color);
856 out->updateFillColor(state);
859 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
862 state->setStrokePattern(NULL);
863 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
864 out->updateStrokeColorSpace(state);
865 color.c[0] = dblToCol(args[0].getNum());
866 state->setStrokeColor(&color);
867 out->updateStrokeColor(state);
870 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
874 state->setFillPattern(NULL);
875 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
876 out->updateFillColorSpace(state);
877 for (i = 0; i < 4; ++i) {
878 color.c[i] = dblToCol(args[i].getNum());
880 state->setFillColor(&color);
881 out->updateFillColor(state);
884 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
888 state->setStrokePattern(NULL);
889 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
890 out->updateStrokeColorSpace(state);
891 for (i = 0; i < 4; ++i) {
892 color.c[i] = dblToCol(args[i].getNum());
894 state->setStrokeColor(&color);
895 out->updateStrokeColor(state);
898 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
902 state->setFillPattern(NULL);
903 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
904 out->updateFillColorSpace(state);
905 for (i = 0; i < 3; ++i) {
906 color.c[i] = dblToCol(args[i].getNum());
908 state->setFillColor(&color);
909 out->updateFillColor(state);
912 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
916 state->setStrokePattern(NULL);
917 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
918 out->updateStrokeColorSpace(state);
919 for (i = 0; i < 3; ++i) {
920 color.c[i] = dblToCol(args[i].getNum());
922 state->setStrokeColor(&color);
923 out->updateStrokeColor(state);
926 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
928 GfxColorSpace *colorSpace;
932 state->setFillPattern(NULL);
933 res->lookupColorSpace(args[0].getName(), &obj);
935 colorSpace = GfxColorSpace::parse(&args[0]);
937 colorSpace = GfxColorSpace::parse(&obj);
941 state->setFillColorSpace(colorSpace);
942 out->updateFillColorSpace(state);
944 error(getPos(), "Bad color space (fill)");
946 for (i = 0; i < gfxColorMaxComps; ++i) {
949 state->setFillColor(&color);
950 out->updateFillColor(state);
953 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
955 GfxColorSpace *colorSpace;
959 state->setStrokePattern(NULL);
960 res->lookupColorSpace(args[0].getName(), &obj);
962 colorSpace = GfxColorSpace::parse(&args[0]);
964 colorSpace = GfxColorSpace::parse(&obj);
968 state->setStrokeColorSpace(colorSpace);
969 out->updateStrokeColorSpace(state);
971 error(getPos(), "Bad color space (stroke)");
973 for (i = 0; i < gfxColorMaxComps; ++i) {
976 state->setStrokeColor(&color);
977 out->updateStrokeColor(state);
980 void Gfx::opSetFillColor(Object args[], int numArgs) {
984 state->setFillPattern(NULL);
985 for (i = 0; i < numArgs; ++i) {
986 color.c[i] = dblToCol(args[i].getNum());
988 state->setFillColor(&color);
989 out->updateFillColor(state);
992 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
996 state->setStrokePattern(NULL);
997 for (i = 0; i < numArgs; ++i) {
998 color.c[i] = dblToCol(args[i].getNum());
1000 state->setStrokeColor(&color);
1001 out->updateStrokeColor(state);
1004 void Gfx::opSetFillColorN(Object args[], int numArgs) {
1006 GfxPattern *pattern;
1009 if (state->getFillColorSpace()->getMode() == csPattern) {
1011 for (i = 0; i < numArgs && i < 4; ++i) {
1012 if (args[i].isNum()) {
1013 color.c[i] = dblToCol(args[i].getNum());
1016 state->setFillColor(&color);
1017 out->updateFillColor(state);
1019 if (args[numArgs-1].isName() &&
1020 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1021 state->setFillPattern(pattern);
1025 state->setFillPattern(NULL);
1026 for (i = 0; i < numArgs && i < 4; ++i) {
1027 if (args[i].isNum()) {
1028 color.c[i] = dblToCol(args[i].getNum());
1031 state->setFillColor(&color);
1032 out->updateFillColor(state);
1036 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
1038 GfxPattern *pattern;
1041 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1043 for (i = 0; i < numArgs && i < 4; ++i) {
1044 if (args[i].isNum()) {
1045 color.c[i] = dblToCol(args[i].getNum());
1048 state->setStrokeColor(&color);
1049 out->updateStrokeColor(state);
1051 if (args[numArgs-1].isName() &&
1052 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1053 state->setStrokePattern(pattern);
1057 state->setStrokePattern(NULL);
1058 for (i = 0; i < numArgs && i < 4; ++i) {
1059 if (args[i].isNum()) {
1060 color.c[i] = dblToCol(args[i].getNum());
1063 state->setStrokeColor(&color);
1064 out->updateStrokeColor(state);
1068 //------------------------------------------------------------------------
1069 // path segment operators
1070 //------------------------------------------------------------------------
1072 void Gfx::opMoveTo(Object args[], int numArgs) {
1073 state->moveTo(args[0].getNum(), args[1].getNum());
1076 void Gfx::opLineTo(Object args[], int numArgs) {
1077 if (!state->isCurPt()) {
1078 error(getPos(), "No current point in lineto");
1081 state->lineTo(args[0].getNum(), args[1].getNum());
1084 void Gfx::opCurveTo(Object args[], int numArgs) {
1085 double x1, y1, x2, y2, x3, y3;
1087 if (!state->isCurPt()) {
1088 error(getPos(), "No current point in curveto");
1091 x1 = args[0].getNum();
1092 y1 = args[1].getNum();
1093 x2 = args[2].getNum();
1094 y2 = args[3].getNum();
1095 x3 = args[4].getNum();
1096 y3 = args[5].getNum();
1097 state->curveTo(x1, y1, x2, y2, x3, y3);
1100 void Gfx::opCurveTo1(Object args[], int numArgs) {
1101 double x1, y1, x2, y2, x3, y3;
1103 if (!state->isCurPt()) {
1104 error(getPos(), "No current point in curveto1");
1107 x1 = state->getCurX();
1108 y1 = state->getCurY();
1109 x2 = args[0].getNum();
1110 y2 = args[1].getNum();
1111 x3 = args[2].getNum();
1112 y3 = args[3].getNum();
1113 state->curveTo(x1, y1, x2, y2, x3, y3);
1116 void Gfx::opCurveTo2(Object args[], int numArgs) {
1117 double x1, y1, x2, y2, x3, y3;
1119 if (!state->isCurPt()) {
1120 error(getPos(), "No current point in curveto2");
1123 x1 = args[0].getNum();
1124 y1 = args[1].getNum();
1125 x2 = args[2].getNum();
1126 y2 = args[3].getNum();
1129 state->curveTo(x1, y1, x2, y2, x3, y3);
1132 void Gfx::opRectangle(Object args[], int numArgs) {
1135 x = args[0].getNum();
1136 y = args[1].getNum();
1137 w = args[2].getNum();
1138 h = args[3].getNum();
1139 state->moveTo(x, y);
1140 state->lineTo(x + w, y);
1141 state->lineTo(x + w, y + h);
1142 state->lineTo(x, y + h);
1146 void Gfx::opClosePath(Object args[], int numArgs) {
1147 if (!state->isCurPt()) {
1148 error(getPos(), "No current point in closepath");
1154 //------------------------------------------------------------------------
1155 // path painting operators
1156 //------------------------------------------------------------------------
1158 void Gfx::opEndPath(Object args[], int numArgs) {
1162 void Gfx::opStroke(Object args[], int numArgs) {
1163 if (!state->isCurPt()) {
1164 //error(getPos(), "No path in stroke");
1167 if (state->isPath())
1172 void Gfx::opCloseStroke(Object args[], int numArgs) {
1173 if (!state->isCurPt()) {
1174 //error(getPos(), "No path in closepath/stroke");
1177 if (state->isPath()) {
1184 void Gfx::opFill(Object args[], int numArgs) {
1185 if (!state->isCurPt()) {
1186 //error(getPos(), "No path in fill");
1189 if (state->isPath()) {
1190 if (state->getFillColorSpace()->getMode() == csPattern) {
1191 doPatternFill(gFalse);
1199 void Gfx::opEOFill(Object args[], int numArgs) {
1200 if (!state->isCurPt()) {
1201 //error(getPos(), "No path in eofill");
1204 if (state->isPath()) {
1205 if (state->getFillColorSpace()->getMode() == csPattern) {
1206 doPatternFill(gTrue);
1214 void Gfx::opFillStroke(Object args[], int numArgs) {
1215 if (!state->isCurPt()) {
1216 //error(getPos(), "No path in fill/stroke");
1219 if (state->isPath()) {
1220 if (state->getFillColorSpace()->getMode() == csPattern) {
1221 doPatternFill(gFalse);
1230 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1231 if (!state->isCurPt()) {
1232 //error(getPos(), "No path in closepath/fill/stroke");
1235 if (state->isPath()) {
1237 if (state->getFillColorSpace()->getMode() == csPattern) {
1238 doPatternFill(gFalse);
1247 void Gfx::opEOFillStroke(Object args[], int numArgs) {
1248 if (!state->isCurPt()) {
1249 //error(getPos(), "No path in eofill/stroke");
1252 if (state->isPath()) {
1253 if (state->getFillColorSpace()->getMode() == csPattern) {
1254 doPatternFill(gTrue);
1263 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1264 if (!state->isCurPt()) {
1265 //error(getPos(), "No path in closepath/eofill/stroke");
1268 if (state->isPath()) {
1270 if (state->getFillColorSpace()->getMode() == csPattern) {
1271 doPatternFill(gTrue);
1280 void Gfx::doPatternFill(GBool eoFill) {
1281 GfxPattern *pattern;
1283 // this is a bit of a kludge -- patterns can be really slow, so we
1284 // skip them if we're only doing text extraction, since they almost
1285 // certainly don't contain any text
1286 if (!out->needNonText()) {
1290 if (!(pattern = state->getFillPattern())) {
1293 switch (pattern->getType()) {
1295 doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
1298 doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
1301 error(getPos(), "Unimplemented pattern type (%d) in fill",
1302 pattern->getType());
1307 void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
1308 GfxPatternColorSpace *patCS;
1311 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1312 double cxMin, cyMin, cxMax, cyMax;
1313 int xi0, yi0, xi1, yi1, xi, yi;
1314 double *ctm, *btm, *ptm;
1315 double m[6], ictm[6], m1[6], imb[6];
1317 double xstep, ystep;
1321 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1323 // construct a (pattern space) -> (current space) transform matrix
1324 ctm = state->getCTM();
1326 ptm = tPat->getMatrix();
1327 // iCTM = invert CTM
1328 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1329 ictm[0] = ctm[3] * det;
1330 ictm[1] = -ctm[1] * det;
1331 ictm[2] = -ctm[2] * det;
1332 ictm[3] = ctm[0] * det;
1333 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1334 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1335 // m1 = PTM * BTM = PTM * base transform matrix
1336 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1337 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1338 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1339 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1340 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1341 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1342 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1343 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1344 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1345 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1346 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1347 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1348 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1350 // construct a (device space) -> (pattern space) transform matrix
1351 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1352 imb[0] = m1[3] * det;
1353 imb[1] = -m1[1] * det;
1354 imb[2] = -m1[2] * det;
1355 imb[3] = m1[0] * det;
1356 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1357 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1359 // save current graphics state
1360 savedPath = state->getPath()->copy();
1363 // set underlying color space (for uncolored tiling patterns); set
1364 // various other parameters (stroke color, line width) to match
1366 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1367 state->setFillColorSpace(cs->copy());
1368 out->updateFillColorSpace(state);
1369 state->setStrokeColorSpace(cs->copy());
1370 out->updateStrokeColorSpace(state);
1371 state->setStrokeColor(state->getFillColor());
1373 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1374 out->updateFillColorSpace(state);
1375 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1376 out->updateStrokeColorSpace(state);
1378 state->setFillPattern(NULL);
1379 out->updateFillColor(state);
1380 state->setStrokePattern(NULL);
1381 out->updateStrokeColor(state);
1382 state->setLineWidth(0);
1383 out->updateLineWidth(state);
1385 // clip to current path
1394 // get the clip region, check for empty
1395 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1396 if (cxMin > cxMax || cyMin > cyMax) {
1400 // transform clip region bbox to pattern space
1401 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1402 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1403 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1404 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1407 } else if (x1 > xMax) {
1412 } else if (y1 > yMax) {
1415 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1416 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1419 } else if (x1 > xMax) {
1424 } else if (y1 > yMax) {
1427 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1428 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1431 } else if (x1 > xMax) {
1436 } else if (y1 > yMax) {
1441 //~ this should treat negative steps differently -- start at right/top
1442 //~ edge instead of left/bottom (?)
1443 xstep = fabs(tPat->getXStep());
1444 ystep = fabs(tPat->getYStep());
1445 xi0 = (int)floor((xMin - tPat->getBBox()[0]) / xstep);
1446 xi1 = (int)ceil((xMax - tPat->getBBox()[0]) / xstep);
1447 yi0 = (int)floor((yMin - tPat->getBBox()[1]) / ystep);
1448 yi1 = (int)ceil((yMax - tPat->getBBox()[1]) / ystep);
1449 for (i = 0; i < 4; ++i) {
1452 if (out->useTilingPatternFill()) {
1455 out->tilingPatternFill(state, tPat->getContentStream(),
1456 tPat->getPaintType(), tPat->getResDict(),
1457 m1, tPat->getBBox(),
1458 xi0, yi0, xi1, yi1, xstep, ystep);
1460 for (yi = yi0; yi < yi1; ++yi) {
1461 for (xi = xi0; xi < xi1; ++xi) {
1464 m1[4] = x * m[0] + y * m[2] + m[4];
1465 m1[5] = x * m[1] + y * m[3] + m[5];
1466 doForm1(tPat->getContentStream(), tPat->getResDict(),
1467 m1, tPat->getBBox());
1472 // restore graphics state
1475 state->setPath(savedPath);
1478 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
1479 GfxShading *shading;
1481 double *ctm, *btm, *ptm;
1482 double m[6], ictm[6], m1[6];
1483 double xMin, yMin, xMax, yMax;
1486 shading = sPat->getShading();
1488 // save current graphics state
1489 savedPath = state->getPath()->copy();
1493 if (shading->getHasBBox()) {
1494 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1495 state->moveTo(xMin, yMin);
1496 state->lineTo(xMax, yMin);
1497 state->lineTo(xMax, yMax);
1498 state->lineTo(xMin, yMax);
1502 state->setPath(savedPath->copy());
1505 // clip to current path
1513 // set the color space
1514 state->setFillColorSpace(shading->getColorSpace()->copy());
1515 out->updateFillColorSpace(state);
1517 // background color fill
1518 if (shading->getHasBackground()) {
1519 state->setFillColor(shading->getBackground());
1520 out->updateFillColor(state);
1525 // construct a (pattern space) -> (current space) transform matrix
1526 ctm = state->getCTM();
1528 ptm = sPat->getMatrix();
1529 // iCTM = invert CTM
1530 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1531 ictm[0] = ctm[3] * det;
1532 ictm[1] = -ctm[1] * det;
1533 ictm[2] = -ctm[2] * det;
1534 ictm[3] = ctm[0] * det;
1535 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1536 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1537 // m1 = PTM * BTM = PTM * base transform matrix
1538 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1539 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1540 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1541 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1542 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1543 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1544 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1545 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1546 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1547 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1548 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1549 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1550 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1552 // set the new matrix
1553 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1554 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1556 // do shading type-specific operations
1557 switch (shading->getType()) {
1559 doFunctionShFill((GfxFunctionShading *)shading);
1562 doAxialShFill((GfxAxialShading *)shading);
1565 doRadialShFill((GfxRadialShading *)shading);
1569 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1573 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1577 // restore graphics state
1579 state->setPath(savedPath);
1582 void Gfx::opShFill(Object args[], int numArgs) {
1583 GfxShading *shading;
1585 double xMin, yMin, xMax, yMax;
1587 if (!(shading = res->lookupShading(args[0].getName()))) {
1591 // save current graphics state
1592 savedPath = state->getPath()->copy();
1596 if (shading->getHasBBox()) {
1597 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1598 state->moveTo(xMin, yMin);
1599 state->lineTo(xMax, yMin);
1600 state->lineTo(xMax, yMax);
1601 state->lineTo(xMin, yMax);
1608 // set the color space
1609 state->setFillColorSpace(shading->getColorSpace()->copy());
1610 out->updateFillColorSpace(state);
1612 // do shading type-specific operations
1613 switch (shading->getType()) {
1615 doFunctionShFill((GfxFunctionShading *)shading);
1618 doAxialShFill((GfxAxialShading *)shading);
1621 doRadialShFill((GfxRadialShading *)shading);
1625 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1629 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1633 // restore graphics state
1635 state->setPath(savedPath);
1640 void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
1641 double x0, y0, x1, y1;
1644 if (out->useShadedFills()) {
1645 out->functionShadedFill(state, shading);
1647 shading->getDomain(&x0, &y0, &x1, &y1);
1648 shading->getColor(x0, y0, &colors[0]);
1649 shading->getColor(x0, y1, &colors[1]);
1650 shading->getColor(x1, y0, &colors[2]);
1651 shading->getColor(x1, y1, &colors[3]);
1652 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1656 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
1657 double x0, double y0,
1658 double x1, double y1,
1659 GfxColor *colors, int depth) {
1661 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1662 GfxColor colors2[4];
1667 nComps = shading->getColorSpace()->getNComps();
1668 matrix = shading->getMatrix();
1670 // compare the four corner colors
1671 for (i = 0; i < 4; ++i) {
1672 for (j = 0; j < nComps; ++j) {
1673 if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1682 // center of the rectangle
1683 xM = 0.5 * (x0 + x1);
1684 yM = 0.5 * (y0 + y1);
1686 // the four corner colors are close (or we hit the recursive limit)
1687 // -- fill the rectangle; but require at least one subdivision
1688 // (depth==0) to avoid problems when the four outer corners of the
1689 // shaded region are the same color
1690 if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1692 // use the center color
1693 shading->getColor(xM, yM, &fillColor);
1694 state->setFillColor(&fillColor);
1695 out->updateFillColor(state);
1697 // fill the rectangle
1698 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1699 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1700 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1701 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1702 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1703 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1704 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1705 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1710 // the four corner colors are not close enough -- subdivide the
1714 // colors[0] colorM0 colors[2]
1715 // (x0,y0) (xM,y0) (x1,y0)
1716 // +----------+----------+
1719 // color0M | colorMM | color1M
1720 // (x0,yM) +----------+----------+ (x1,yM)
1724 // +----------+----------+
1725 // colors[1] colorM1 colors[3]
1726 // (x0,y1) (xM,y1) (x1,y1)
1728 shading->getColor(x0, yM, &color0M);
1729 shading->getColor(x1, yM, &color1M);
1730 shading->getColor(xM, y0, &colorM0);
1731 shading->getColor(xM, y1, &colorM1);
1732 shading->getColor(xM, yM, &colorMM);
1734 // upper-left sub-rectangle
1735 colors2[0] = colors[0];
1736 colors2[1] = color0M;
1737 colors2[2] = colorM0;
1738 colors2[3] = colorMM;
1739 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1741 // lower-left sub-rectangle
1742 colors2[0] = color0M;
1743 colors2[1] = colors[1];
1744 colors2[2] = colorMM;
1745 colors2[3] = colorM1;
1746 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1748 // upper-right sub-rectangle
1749 colors2[0] = colorM0;
1750 colors2[1] = colorMM;
1751 colors2[2] = colors[2];
1752 colors2[3] = color1M;
1753 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1755 // lower-right sub-rectangle
1756 colors2[0] = colorMM;
1757 colors2[1] = colorM1;
1758 colors2[2] = color1M;
1759 colors2[3] = colors[3];
1760 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1764 void Gfx::doAxialShFill(GfxAxialShading *shading) {
1765 double xMin, yMin, xMax, yMax;
1766 double x0, y0, x1, y1;
1768 GBool dxZero, dyZero;
1769 double tMin, tMax, t, tx, ty;
1770 double s[4], sMin, sMax, tmp;
1771 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1773 double ta[axialMaxSplits + 1];
1774 int next[axialMaxSplits + 1];
1775 GfxColor color0, color1;
1779 if (out->useShadedFills()) {
1781 out->axialShadedFill(state, shading);
1785 // get the clip region bbox
1786 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1788 // compute min and max t values, based on the four corners of the
1790 shading->getCoords(&x0, &y0, &x1, &y1);
1793 dxZero = fabs(dx) < 0.001;
1794 dyZero = fabs(dy) < 0.001;
1795 mul = 1 / (dx * dx + dy * dy);
1796 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1797 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1800 } else if (t > tMax) {
1803 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1806 } else if (t > tMax) {
1809 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1812 } else if (t > tMax) {
1815 if (tMin < 0 && !shading->getExtend0()) {
1818 if (tMax > 1 && !shading->getExtend1()) {
1822 // get the function domain
1823 t0 = shading->getDomain0();
1824 t1 = shading->getDomain1();
1826 // Traverse the t axis and do the shading.
1828 // For each point (tx, ty) on the t axis, consider a line through
1829 // that point perpendicular to the t axis:
1831 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1832 // y(s) = ty + s * dx --> s = (y - ty) / dx
1834 // Then look at the intersection of this line with the bounding box
1835 // (xMin, yMin, xMax, yMax). In the general case, there are four
1836 // intersection points:
1838 // s0 = (xMin - tx) / -dy
1839 // s1 = (xMax - tx) / -dy
1840 // s2 = (yMin - ty) / dx
1841 // s3 = (yMax - ty) / dx
1843 // and we want the middle two s values.
1845 // In the case where dx = 0, take s0 and s1; in the case where dy =
1846 // 0, take s2 and s3.
1848 // Each filled polygon is bounded by two of these line segments
1849 // perpdendicular to the t axis.
1851 // The t axis is bisected into smaller regions until the color
1852 // difference across a region is small enough, and then the region
1853 // is painted with a single color.
1855 // set up: require at least one split to avoid problems when the two
1856 // ends of the t axis have the same color
1857 nComps = shading->getColorSpace()->getNComps();
1859 next[0] = axialMaxSplits / 2;
1860 ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
1861 next[axialMaxSplits / 2] = axialMaxSplits;
1862 ta[axialMaxSplits] = tMax;
1864 // compute the color at t = tMin
1867 } else if (tMin > 1) {
1870 tt = t0 + (t1 - t0) * tMin;
1872 shading->getColor(tt, &color0);
1874 // compute the coordinates of the point on the t axis at t = tMin;
1875 // then compute the intersection of the perpendicular line with the
1877 tx = x0 + tMin * dx;
1878 ty = y0 + tMin * dy;
1879 if (dxZero && dyZero) {
1882 sMin = (xMin - tx) / -dy;
1883 sMax = (xMax - tx) / -dy;
1884 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1885 } else if (dyZero) {
1886 sMin = (yMin - ty) / dx;
1887 sMax = (yMax - ty) / dx;
1888 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1890 s[0] = (yMin - ty) / dx;
1891 s[1] = (yMax - ty) / dx;
1892 s[2] = (xMin - tx) / -dy;
1893 s[3] = (xMax - tx) / -dy;
1894 for (j = 0; j < 3; ++j) {
1896 for (k = j + 1; k < 4; ++k) {
1901 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1906 ux0 = tx - sMin * dy;
1907 uy0 = ty + sMin * dx;
1908 vx0 = tx - sMax * dy;
1909 vy0 = ty + sMax * dx;
1912 while (i < axialMaxSplits) {
1914 // bisect until color difference is small enough or we hit the
1920 } else if (ta[j] > 1) {
1923 tt = t0 + (t1 - t0) * ta[j];
1925 shading->getColor(tt, &color1);
1926 for (k = 0; k < nComps; ++k) {
1927 if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1935 ta[k] = 0.5 * (ta[i] + ta[j]);
1941 // use the average of the colors of the two sides of the region
1942 for (k = 0; k < nComps; ++k) {
1943 color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
1946 // compute the coordinates of the point on the t axis; then
1947 // compute the intersection of the perpendicular line with the
1949 tx = x0 + ta[j] * dx;
1950 ty = y0 + ta[j] * dy;
1951 if (dxZero && dyZero) {
1954 sMin = (xMin - tx) / -dy;
1955 sMax = (xMax - tx) / -dy;
1956 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1957 } else if (dyZero) {
1958 sMin = (yMin - ty) / dx;
1959 sMax = (yMax - ty) / dx;
1960 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1962 s[0] = (yMin - ty) / dx;
1963 s[1] = (yMax - ty) / dx;
1964 s[2] = (xMin - tx) / -dy;
1965 s[3] = (xMax - tx) / -dy;
1966 for (j = 0; j < 3; ++j) {
1968 for (k = j + 1; k < 4; ++k) {
1973 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1978 ux1 = tx - sMin * dy;
1979 uy1 = ty + sMin * dx;
1980 vx1 = tx - sMax * dy;
1981 vy1 = ty + sMax * dx;
1984 state->setFillColor(&color0);
1985 out->updateFillColor(state);
1988 state->moveTo(ux0, uy0);
1989 state->lineTo(vx0, vy0);
1990 state->lineTo(vx1, vy1);
1991 state->lineTo(ux1, uy1);
1996 // set up for next region
2007 void Gfx::doRadialShFill(GfxRadialShading *shading) {
2008 double sMin, sMax, xMin, yMin, xMax, yMax;
2009 double x0, y0, r0, x1, y1, r1, t0, t1;
2011 GfxColor colorA, colorB;
2012 double xa, ya, xb, yb, ra, rb;
2013 double ta, tb, sa, sb;
2016 double angle, t, d0, d1;
2018 if (out->useShadedFills()) {
2020 out->radialShadedFill(state, shading);
2024 // get the shading info
2025 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2026 t0 = shading->getDomain0();
2027 t1 = shading->getDomain1();
2028 nComps = shading->getColorSpace()->getNComps();
2030 // compute the (possibly extended) s range
2033 if (shading->getExtend0()) {
2035 // extend the smaller end
2036 sMin = -r0 / (r1 - r0);
2038 // extend the larger end
2039 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2040 d0 = (x0 - xMin) * (x0 - xMin);
2041 d1 = (x0 - xMax) * (x0 - xMax);
2042 sMin = d0 > d1 ? d0 : d1;
2043 d0 = (y0 - yMin) * (y0 - yMin);
2044 d1 = (y0 - yMax) * (y0 - yMax);
2045 sMin += d0 > d1 ? d0 : d1;
2046 sMin = (sqrt(sMin) - r0) / (r1 - r0);
2049 } else if (sMin < -20) {
2055 if (shading->getExtend1()) {
2057 // extend the smaller end
2058 sMax = -r0 / (r1 - r0);
2059 } else if (r1 > r0) {
2060 // extend the larger end
2061 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2062 d0 = (x1 - xMin) * (x1 - xMin);
2063 d1 = (x1 - xMax) * (x1 - xMax);
2064 sMax = d0 > d1 ? d0 : d1;
2065 d0 = (y1 - yMin) * (y1 - yMin);
2066 d1 = (y1 - yMax) * (y1 - yMax);
2067 sMax += d0 > d1 ? d0 : d1;
2068 sMax = (sqrt(sMax) - r0) / (r1 - r0);
2071 } else if (sMax > 20) {
2078 // compute the number of steps into which circles must be divided to
2079 // achieve a curve flatness of 0.1 pixel in device space for the
2080 // largest circle (note that "device space" is 72 dpi when generating
2081 // PostScript, hence the relatively small 0.1 pixel accuracy)
2082 ctm = state->getCTM();
2084 if (fabs(ctm[1]) > t) {
2087 if (fabs(ctm[2]) > t) {
2090 if (fabs(ctm[3]) > t) {
2101 n = (int)(M_PI / acos(1 - 0.1 / t));
2104 } else if (n > 200) {
2109 // Traverse the t axis and do the shading.
2111 // This generates and fills a series of rings. Each ring is defined
2113 // sa, ta, xa, ya, ra, colorA
2114 // sb, tb, xb, yb, rb, colorB
2116 // The s/t axis is divided into radialMaxSplits parts; these parts
2117 // are combined as much as possible while respecting the
2118 // radialColorDelta parameter.
2120 // setup for the start circle
2123 ta = t0 + sa * (t1 - t0);
2124 xa = x0 + sa * (x1 - x0);
2125 ya = y0 + sa * (y1 - y0);
2126 ra = r0 + sa * (r1 - r0);
2128 shading->getColor(t0, &colorA);
2129 } else if (ta > t1) {
2130 shading->getColor(t1, &colorA);
2132 shading->getColor(ta, &colorA);
2135 while (ia < radialMaxSplits) {
2137 // go as far along the t axis (toward t1) as we can, such that the
2138 // color difference is within the tolerance (radialColorDelta) --
2139 // this uses bisection (between the current value, t, and t1),
2140 // limited to radialMaxSplits points along the t axis; require at
2141 // least one split to avoid problems when the innermost and
2142 // outermost colors are the same
2143 ib = radialMaxSplits;
2144 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2145 tb = t0 + sb * (t1 - t0);
2147 shading->getColor(t0, &colorB);
2148 } else if (tb > t1) {
2149 shading->getColor(t1, &colorB);
2151 shading->getColor(tb, &colorB);
2153 while (ib - ia > 1) {
2154 for (k = 0; k < nComps; ++k) {
2155 if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
2159 if (k == nComps && ib < radialMaxSplits) {
2163 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2164 tb = t0 + sb * (t1 - t0);
2166 shading->getColor(t0, &colorB);
2167 } else if (tb > t1) {
2168 shading->getColor(t1, &colorB);
2170 shading->getColor(tb, &colorB);
2174 // compute center and radius of the circle
2175 xb = x0 + sb * (x1 - x0);
2176 yb = y0 + sb * (y1 - y0);
2177 rb = r0 + sb * (r1 - r0);
2179 // use the average of the colors at the two circles
2180 for (k = 0; k < nComps; ++k) {
2181 colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
2183 state->setFillColor(&colorA);
2184 out->updateFillColor(state);
2186 // construct path for first circle
2187 state->moveTo(xa + ra, ya);
2188 for (k = 1; k < n; ++k) {
2189 angle = ((double)k / (double)n) * 2 * M_PI;
2190 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2194 // construct and append path for second circle
2195 state->moveTo(xb + rb, yb);
2196 for (k = 1; k < n; ++k) {
2197 angle = ((double)k / (double)n) * 2 * M_PI;
2198 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2206 // step to the next value of t
2218 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
2219 double x0, y0, x1, y1, x2, y2;
2220 GfxColor color0, color1, color2;
2223 for (i = 0; i < shading->getNTriangles(); ++i) {
2224 shading->getTriangle(i, &x0, &y0, &color0,
2227 gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
2228 shading->getColorSpace()->getNComps(), 0);
2232 void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
2233 double x1, double y1, GfxColor *color1,
2234 double x2, double y2, GfxColor *color2,
2235 int nComps, int depth) {
2236 double x01, y01, x12, y12, x20, y20;
2237 GfxColor color01, color12, color20;
2240 for (i = 0; i < nComps; ++i) {
2241 if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
2242 abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
2246 if (i == nComps || depth == gouraudMaxDepth) {
2247 state->setFillColor(color0);
2248 out->updateFillColor(state);
2249 state->moveTo(x0, y0);
2250 state->lineTo(x1, y1);
2251 state->lineTo(x2, y2);
2256 x01 = 0.5 * (x0 + x1);
2257 y01 = 0.5 * (y0 + y1);
2258 x12 = 0.5 * (x1 + x2);
2259 y12 = 0.5 * (y1 + y2);
2260 x20 = 0.5 * (x2 + x0);
2261 y20 = 0.5 * (y2 + y0);
2262 //~ if the shading has a Function, this should interpolate on the
2263 //~ function parameter, not on the color components
2264 for (i = 0; i < nComps; ++i) {
2265 color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
2266 color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
2267 color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
2269 gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
2270 x20, y20, &color20, nComps, depth + 1);
2271 gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
2272 x12, y12, &color12, nComps, depth + 1);
2273 gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
2274 x20, y20, &color20, nComps, depth + 1);
2275 gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
2276 x2, y2, color2, nComps, depth + 1);
2280 void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
2283 if (shading->getNPatches() > 128) {
2285 } else if (shading->getNPatches() > 64) {
2287 } else if (shading->getNPatches() > 16) {
2292 for (i = 0; i < shading->getNPatches(); ++i) {
2293 fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
2298 void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
2299 GfxPatch patch00, patch01, patch10, patch11;
2300 double xx[4][8], yy[4][8];
2304 for (i = 0; i < nComps; ++i) {
2305 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
2306 > patchColorDelta ||
2307 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
2308 > patchColorDelta ||
2309 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
2310 > patchColorDelta ||
2311 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
2312 > patchColorDelta) {
2316 if (i == nComps || depth == patchMaxDepth) {
2317 state->setFillColor(&patch->color[0][0]);
2318 out->updateFillColor(state);
2319 state->moveTo(patch->x[0][0], patch->y[0][0]);
2320 state->curveTo(patch->x[0][1], patch->y[0][1],
2321 patch->x[0][2], patch->y[0][2],
2322 patch->x[0][3], patch->y[0][3]);
2323 state->curveTo(patch->x[1][3], patch->y[1][3],
2324 patch->x[2][3], patch->y[2][3],
2325 patch->x[3][3], patch->y[3][3]);
2326 state->curveTo(patch->x[3][2], patch->y[3][2],
2327 patch->x[3][1], patch->y[3][1],
2328 patch->x[3][0], patch->y[3][0]);
2329 state->curveTo(patch->x[2][0], patch->y[2][0],
2330 patch->x[1][0], patch->y[1][0],
2331 patch->x[0][0], patch->y[0][0]);
2336 for (i = 0; i < 4; ++i) {
2337 xx[i][0] = patch->x[i][0];
2338 yy[i][0] = patch->y[i][0];
2339 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
2340 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
2341 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
2342 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
2343 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
2344 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
2345 xx[i][2] = 0.5 * (xx[i][1] + xxm);
2346 yy[i][2] = 0.5 * (yy[i][1] + yym);
2347 xx[i][5] = 0.5 * (xxm + xx[i][6]);
2348 yy[i][5] = 0.5 * (yym + yy[i][6]);
2349 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
2350 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
2351 xx[i][7] = patch->x[i][3];
2352 yy[i][7] = patch->y[i][3];
2354 for (i = 0; i < 4; ++i) {
2355 patch00.x[0][i] = xx[0][i];
2356 patch00.y[0][i] = yy[0][i];
2357 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
2358 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
2359 xxm = 0.5 * (xx[1][i] + xx[2][i]);
2360 yym = 0.5 * (yy[1][i] + yy[2][i]);
2361 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
2362 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
2363 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
2364 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
2365 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
2366 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
2367 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
2368 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
2369 patch10.x[0][i] = patch00.x[3][i];
2370 patch10.y[0][i] = patch00.y[3][i];
2371 patch10.x[3][i] = xx[3][i];
2372 patch10.y[3][i] = yy[3][i];
2374 for (i = 4; i < 8; ++i) {
2375 patch01.x[0][i-4] = xx[0][i];
2376 patch01.y[0][i-4] = yy[0][i];
2377 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
2378 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
2379 xxm = 0.5 * (xx[1][i] + xx[2][i]);
2380 yym = 0.5 * (yy[1][i] + yy[2][i]);
2381 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
2382 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
2383 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
2384 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
2385 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
2386 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
2387 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
2388 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
2389 patch11.x[0][i-4] = patch01.x[3][i-4];
2390 patch11.y[0][i-4] = patch01.y[3][i-4];
2391 patch11.x[3][i-4] = xx[3][i];
2392 patch11.y[3][i-4] = yy[3][i];
2394 //~ if the shading has a Function, this should interpolate on the
2395 //~ function parameter, not on the color components
2396 for (i = 0; i < nComps; ++i) {
2397 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
2398 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
2399 patch->color[0][1].c[i]) / 2;
2400 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
2401 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
2402 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
2403 patch->color[1][1].c[i]) / 2;
2404 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
2405 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
2406 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
2407 patch->color[1][0].c[i]) / 2;
2408 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
2409 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
2410 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
2411 patch->color[0][0].c[i]) / 2;
2412 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
2413 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
2414 patch01.color[1][1].c[i]) / 2;
2415 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
2416 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
2417 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
2419 fillPatch(&patch00, nComps, depth + 1);
2420 fillPatch(&patch10, nComps, depth + 1);
2421 fillPatch(&patch01, nComps, depth + 1);
2422 fillPatch(&patch11, nComps, depth + 1);
2426 void Gfx::doEndPath() {
2427 if (state->isCurPt() && clip != clipNone) {
2429 if (clip == clipNormal) {
2439 //------------------------------------------------------------------------
2440 // path clipping operators
2441 //------------------------------------------------------------------------
2443 void Gfx::opClip(Object args[], int numArgs) {
2447 void Gfx::opEOClip(Object args[], int numArgs) {
2451 //------------------------------------------------------------------------
2452 // text object operators
2453 //------------------------------------------------------------------------
2455 void Gfx::opBeginText(Object args[], int numArgs) {
2456 state->setTextMat(1, 0, 0, 1, 0, 0);
2457 state->textMoveTo(0, 0);
2458 out->updateTextMat(state);
2459 out->updateTextPos(state);
2460 fontChanged = gTrue;
2463 void Gfx::opEndText(Object args[], int numArgs) {
2464 out->endTextObject(state);
2467 //------------------------------------------------------------------------
2468 // text state operators
2469 //------------------------------------------------------------------------
2471 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
2472 state->setCharSpace(args[0].getNum());
2473 out->updateCharSpace(state);
2476 void Gfx::opSetFont(Object args[], int numArgs) {
2479 if (!(font = res->lookupFont(args[0].getName()))) {
2482 if (printCommands) {
2483 printf(" font: tag=%s name='%s' %g\n",
2484 font->getTag()->getCString(),
2485 font->getName() ? font->getName()->getCString() : "???",
2489 state->setFont(font, args[1].getNum());
2490 fontChanged = gTrue;
2493 void Gfx::opSetTextLeading(Object args[], int numArgs) {
2494 state->setLeading(args[0].getNum());
2497 void Gfx::opSetTextRender(Object args[], int numArgs) {
2498 state->setRender(args[0].getInt());
2499 out->updateRender(state);
2502 void Gfx::opSetTextRise(Object args[], int numArgs) {
2503 state->setRise(args[0].getNum());
2504 out->updateRise(state);
2507 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
2508 state->setWordSpace(args[0].getNum());
2509 out->updateWordSpace(state);
2512 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
2513 state->setHorizScaling(args[0].getNum());
2514 out->updateHorizScaling(state);
2515 fontChanged = gTrue;
2518 //------------------------------------------------------------------------
2519 // text positioning operators
2520 //------------------------------------------------------------------------
2522 void Gfx::opTextMove(Object args[], int numArgs) {
2525 tx = state->getLineX() + args[0].getNum();
2526 ty = state->getLineY() + args[1].getNum();
2527 state->textMoveTo(tx, ty);
2528 out->updateTextPos(state);
2531 void Gfx::opTextMoveSet(Object args[], int numArgs) {
2534 tx = state->getLineX() + args[0].getNum();
2535 ty = args[1].getNum();
2536 state->setLeading(-ty);
2537 ty += state->getLineY();
2538 state->textMoveTo(tx, ty);
2539 out->updateTextPos(state);
2542 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
2543 state->setTextMat(args[0].getNum(), args[1].getNum(),
2544 args[2].getNum(), args[3].getNum(),
2545 args[4].getNum(), args[5].getNum());
2546 state->textMoveTo(0, 0);
2547 out->updateTextMat(state);
2548 out->updateTextPos(state);
2549 fontChanged = gTrue;
2552 void Gfx::opTextNextLine(Object args[], int numArgs) {
2555 tx = state->getLineX();
2556 ty = state->getLineY() - state->getLeading();
2557 state->textMoveTo(tx, ty);
2558 out->updateTextPos(state);
2561 //------------------------------------------------------------------------
2562 // text string operators
2563 //------------------------------------------------------------------------
2565 void Gfx::opShowText(Object args[], int numArgs) {
2566 if (!state->getFont()) {
2567 error(getPos(), "No font in show");
2571 out->updateFont(state);
2572 fontChanged = gFalse;
2574 out->beginStringOp(state);
2575 doShowText(args[0].getString());
2576 out->endStringOp(state);
2579 void Gfx::opMoveShowText(Object args[], int numArgs) {
2582 if (!state->getFont()) {
2583 error(getPos(), "No font in move/show");
2587 out->updateFont(state);
2588 fontChanged = gFalse;
2590 tx = state->getLineX();
2591 ty = state->getLineY() - state->getLeading();
2592 state->textMoveTo(tx, ty);
2593 out->updateTextPos(state);
2594 out->beginStringOp(state);
2595 doShowText(args[0].getString());
2596 out->endStringOp(state);
2599 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
2602 if (!state->getFont()) {
2603 error(getPos(), "No font in move/set/show");
2607 out->updateFont(state);
2608 fontChanged = gFalse;
2610 state->setWordSpace(args[0].getNum());
2611 state->setCharSpace(args[1].getNum());
2612 tx = state->getLineX();
2613 ty = state->getLineY() - state->getLeading();
2614 state->textMoveTo(tx, ty);
2615 out->updateWordSpace(state);
2616 out->updateCharSpace(state);
2617 out->updateTextPos(state);
2618 out->beginStringOp(state);
2619 doShowText(args[2].getString());
2620 out->endStringOp(state);
2623 void Gfx::opShowSpaceText(Object args[], int numArgs) {
2629 if (!state->getFont()) {
2630 error(getPos(), "No font in show/space");
2634 out->updateFont(state);
2635 fontChanged = gFalse;
2637 out->beginStringOp(state);
2638 wMode = state->getFont()->getWMode();
2639 a = args[0].getArray();
2640 for (i = 0; i < a->getLength(); ++i) {
2643 // this uses the absolute value of the font size to match
2644 // Acrobat's behavior
2646 state->textShift(0, -obj.getNum() * 0.001 *
2647 fabs(state->getFontSize()));
2649 state->textShift(-obj.getNum() * 0.001 *
2650 fabs(state->getFontSize()), 0);
2652 out->updateTextShift(state, obj.getNum());
2653 } else if (obj.isString()) {
2654 doShowText(obj.getString());
2656 error(getPos(), "Element of show/space array must be number or string");
2660 out->endStringOp(state);
2663 void Gfx::doShowText(GString *s) {
2666 double riseX, riseY;
2669 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2670 double originX, originY, tOriginX, tOriginY;
2671 double oldCTM[6], newCTM[6];
2677 int len, n, uLen, nChars, nSpaces, i;
2679 font = state->getFont();
2680 wMode = font->getWMode();
2682 if (out->useDrawChar()) {
2683 out->beginString(state, s);
2686 // handle a Type 3 char
2687 if (font->getType() == fontType3 && out->interpretType3Chars()) {
2688 mat = state->getCTM();
2689 for (i = 0; i < 6; ++i) {
2692 mat = state->getTextMat();
2693 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2694 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2695 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2696 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2697 mat = font->getFontMatrix();
2698 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2699 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2700 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2701 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2702 newCTM[0] *= state->getFontSize();
2703 newCTM[1] *= state->getFontSize();
2704 newCTM[2] *= state->getFontSize();
2705 newCTM[3] *= state->getFontSize();
2706 newCTM[0] *= state->getHorizScaling();
2707 newCTM[2] *= state->getHorizScaling();
2708 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2709 curX = state->getCurX();
2710 curY = state->getCurY();
2711 lineX = state->getLineX();
2712 lineY = state->getLineY();
2714 p = s->getCString();
2715 len = s->getLength();
2717 n = font->getNextChar(p, len, &code,
2718 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2719 &dx, &dy, &originX, &originY);
2720 dx = dx * state->getFontSize() + state->getCharSpace();
2721 if (n == 1 && *p == ' ') {
2722 dx += state->getWordSpace();
2724 dx *= state->getHorizScaling();
2725 dy *= state->getFontSize();
2726 state->textTransformDelta(dx, dy, &tdx, &tdy);
2727 state->transform(curX + riseX, curY + riseY, &x, &y);
2729 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2730 //~ out->updateCTM(???)
2731 if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2733 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2734 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2735 pushResources(resDict);
2737 if (charProc.isStream()) {
2738 display(&charProc, gFalse);
2740 error(getPos(), "Missing or bad Type3 CharProc entry");
2742 out->endType3Char(state);
2749 // GfxState::restore() does *not* restore the current position,
2750 // so we deal with it here using (curX, curY) and (lineX, lineY)
2753 state->moveTo(curX, curY);
2754 state->textSetPos(lineX, lineY);
2760 } else if (out->useDrawChar()) {
2761 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2762 p = s->getCString();
2763 len = s->getLength();
2765 n = font->getNextChar(p, len, &code,
2766 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2767 &dx, &dy, &originX, &originY);
2769 dx *= state->getFontSize();
2770 dy = dy * state->getFontSize() + state->getCharSpace();
2771 if (n == 1 && *p == ' ') {
2772 dy += state->getWordSpace();
2775 dx = dx * state->getFontSize() + state->getCharSpace();
2776 if (n == 1 && *p == ' ') {
2777 dx += state->getWordSpace();
2779 dx *= state->getHorizScaling();
2780 dy *= state->getFontSize();
2782 state->textTransformDelta(dx, dy, &tdx, &tdy);
2783 originX *= state->getFontSize();
2784 originY *= state->getFontSize();
2785 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2786 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2787 tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
2788 state->shift(tdx, tdy);
2795 p = s->getCString();
2796 len = s->getLength();
2797 nChars = nSpaces = 0;
2799 n = font->getNextChar(p, len, &code,
2800 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2801 &dx2, &dy2, &originX, &originY);
2804 if (n == 1 && *p == ' ') {
2812 dx *= state->getFontSize();
2813 dy = dy * state->getFontSize()
2814 + nChars * state->getCharSpace()
2815 + nSpaces * state->getWordSpace();
2817 dx = dx * state->getFontSize()
2818 + nChars * state->getCharSpace()
2819 + nSpaces * state->getWordSpace();
2820 dx *= state->getHorizScaling();
2821 dy *= state->getFontSize();
2823 state->textTransformDelta(dx, dy, &tdx, &tdy);
2824 out->drawString(state, s);
2825 state->shift(tdx, tdy);
2828 if (out->useDrawChar()) {
2829 out->endString(state);
2832 updateLevel += 10 * s->getLength();
2835 //------------------------------------------------------------------------
2836 // XObject operators
2837 //------------------------------------------------------------------------
2839 void Gfx::opXObject(Object args[], int numArgs) {
2840 Object obj1, obj2, obj3, refObj;
2845 if (!res->lookupXObject(args[0].getName(), &obj1)) {
2848 if (!obj1.isStream()) {
2849 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2854 obj1.streamGetDict()->lookup("OPI", &opiDict);
2855 if (opiDict.isDict()) {
2856 out->opiBegin(state, opiDict.getDict());
2859 obj1.streamGetDict()->lookup("Subtype", &obj2);
2860 if (obj2.isName("Image")) {
2861 if (out->needNonText()) {
2862 res->lookupXObjectNF(args[0].getName(), &refObj);
2863 doImage(&refObj, obj1.getStream(), gFalse);
2866 } else if (obj2.isName("Form")) {
2868 } else if (obj2.isName("PS")) {
2869 obj1.streamGetDict()->lookup("Level1", &obj3);
2870 out->psXObject(obj1.getStream(),
2871 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2872 } else if (obj2.isName()) {
2873 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2875 error(getPos(), "XObject subtype is missing or wrong type");
2879 if (opiDict.isDict()) {
2880 out->opiEnd(state, opiDict.getDict());
2887 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2888 Dict *dict, *maskDict;
2891 StreamColorSpaceMode csMode;
2894 GfxColorSpace *colorSpace, *maskColorSpace;
2895 GfxImageColorMap *colorMap, *maskColorMap;
2896 Object maskObj, smaskObj;
2897 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2898 int maskColors[2*gfxColorMaxComps];
2899 int maskWidth, maskHeight;
2905 // get info from the stream
2907 csMode = streamCSNone;
2908 str->getImageParams(&bits, &csMode);
2911 dict = str->getDict();
2914 dict->lookup("Width", &obj1);
2915 if (obj1.isNull()) {
2917 dict->lookup("W", &obj1);
2921 width = obj1.getInt();
2923 dict->lookup("Height", &obj1);
2924 if (obj1.isNull()) {
2926 dict->lookup("H", &obj1);
2930 height = obj1.getInt();
2934 dict->lookup("ImageMask", &obj1);
2935 if (obj1.isNull()) {
2937 dict->lookup("IM", &obj1);
2941 mask = obj1.getBool();
2942 else if (!obj1.isNull())
2948 dict->lookup("BitsPerComponent", &obj1);
2949 if (obj1.isNull()) {
2951 dict->lookup("BPC", &obj1);
2954 bits = obj1.getInt();
2966 // check for inverted mask
2970 dict->lookup("Decode", &obj1);
2971 if (obj1.isNull()) {
2973 dict->lookup("D", &obj1);
2975 if (obj1.isArray()) {
2976 obj1.arrayGet(0, &obj2);
2977 if (obj2.isInt() && obj2.getInt() == 1)
2980 } else if (!obj1.isNull()) {
2986 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2990 // get color space and color map
2991 dict->lookup("ColorSpace", &obj1);
2992 if (obj1.isNull()) {
2994 dict->lookup("CS", &obj1);
2996 if (obj1.isName()) {
2997 res->lookupColorSpace(obj1.getName(), &obj2);
2998 if (!obj2.isNull()) {
3005 if (!obj1.isNull()) {
3006 colorSpace = GfxColorSpace::parse(&obj1);
3007 } else if (csMode == streamCSDeviceGray) {
3008 colorSpace = new GfxDeviceGrayColorSpace();
3009 } else if (csMode == streamCSDeviceRGB) {
3010 colorSpace = new GfxDeviceRGBColorSpace();
3011 } else if (csMode == streamCSDeviceCMYK) {
3012 colorSpace = new GfxDeviceCMYKColorSpace();
3020 dict->lookup("Decode", &obj1);
3021 if (obj1.isNull()) {
3023 dict->lookup("D", &obj1);
3025 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
3027 if (!colorMap->isOk()) {
3033 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
3034 maskStr = NULL; // make gcc happy
3035 maskWidth = maskHeight = 0; // make gcc happy
3036 maskInvert = gFalse; // make gcc happy
3037 maskColorMap = NULL; // make gcc happy
3038 dict->lookup("Mask", &maskObj);
3039 dict->lookup("SMask", &smaskObj);
3040 if (smaskObj.isStream()) {
3045 maskStr = smaskObj.getStream();
3046 maskDict = smaskObj.streamGetDict();
3047 maskDict->lookup("Width", &obj1);
3048 if (obj1.isNull()) {
3050 maskDict->lookup("W", &obj1);
3052 if (!obj1.isInt()) {
3055 maskWidth = obj1.getInt();
3057 maskDict->lookup("Height", &obj1);
3058 if (obj1.isNull()) {
3060 maskDict->lookup("H", &obj1);
3062 if (!obj1.isInt()) {
3065 maskHeight = obj1.getInt();
3067 maskDict->lookup("BitsPerComponent", &obj1);
3068 if (obj1.isNull()) {
3070 maskDict->lookup("BPC", &obj1);
3072 if (!obj1.isInt()) {
3075 maskBits = obj1.getInt();
3077 maskDict->lookup("ColorSpace", &obj1);
3078 if (obj1.isNull()) {
3080 maskDict->lookup("CS", &obj1);
3082 if (obj1.isName()) {
3083 res->lookupColorSpace(obj1.getName(), &obj2);
3084 if (!obj2.isNull()) {
3091 maskColorSpace = GfxColorSpace::parse(&obj1);
3093 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
3096 maskDict->lookup("Decode", &obj1);
3097 if (obj1.isNull()) {
3099 maskDict->lookup("D", &obj1);
3101 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
3103 if (!maskColorMap->isOk()) {
3104 delete maskColorMap;
3107 //~ handle the Matte entry
3108 haveSoftMask = gTrue;
3109 } else if (maskObj.isArray()) {
3112 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
3114 maskObj.arrayGet(i, &obj1);
3115 maskColors[i] = obj1.getInt();
3118 haveColorKeyMask = gTrue;
3119 } else if (maskObj.isStream()) {
3124 maskStr = maskObj.getStream();
3125 maskDict = maskObj.streamGetDict();
3126 maskDict->lookup("Width", &obj1);
3127 if (obj1.isNull()) {
3129 maskDict->lookup("W", &obj1);
3131 if (!obj1.isInt()) {
3134 maskWidth = obj1.getInt();
3136 maskDict->lookup("Height", &obj1);
3137 if (obj1.isNull()) {
3139 maskDict->lookup("H", &obj1);
3141 if (!obj1.isInt()) {
3144 maskHeight = obj1.getInt();
3146 maskDict->lookup("ImageMask", &obj1);
3147 if (obj1.isNull()) {
3149 maskDict->lookup("IM", &obj1);
3151 if (!obj1.isBool() || !obj1.getBool()) {
3155 maskInvert = gFalse;
3156 maskDict->lookup("Decode", &obj1);
3157 if (obj1.isNull()) {
3159 maskDict->lookup("D", &obj1);
3161 if (obj1.isArray()) {
3162 obj1.arrayGet(0, &obj2);
3163 if (obj2.isInt() && obj2.getInt() == 1) {
3167 } else if (!obj1.isNull()) {
3171 haveExplicitMask = gTrue;
3176 out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
3177 maskStr, maskWidth, maskHeight, maskColorMap);
3178 delete maskColorMap;
3179 } else if (haveExplicitMask) {
3180 out->drawMaskedImage(state, ref, str, width, height, colorMap,
3181 maskStr, maskWidth, maskHeight, maskInvert);
3183 out->drawImage(state, ref, str, width, height, colorMap,
3184 haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
3192 if ((i = width * height) > 1000) {
3202 error(getPos(), "Bad image parameters");
3205 void Gfx::doForm(Object *str) {
3207 Object matrixObj, bboxObj;
3208 double m[6], bbox[6];
3214 // check for excessive recursion
3215 if (formDepth > 20) {
3220 dict = str->streamGetDict();
3223 dict->lookup("FormType", &obj1);
3224 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
3225 error(getPos(), "Unknown form type");
3230 dict->lookup("BBox", &bboxObj);
3231 if (!bboxObj.isArray()) {
3234 error(getPos(), "Bad form bounding box");
3237 for (i = 0; i < 4; ++i) {
3238 bboxObj.arrayGet(i, &obj1);
3239 bbox[i] = obj1.getNum();
3245 dict->lookup("Matrix", &matrixObj);
3246 if (matrixObj.isArray()) {
3247 for (i = 0; i < 6; ++i) {
3248 matrixObj.arrayGet(i, &obj1);
3249 m[i] = obj1.getNum();
3260 dict->lookup("Resources", &resObj);
3261 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
3265 doForm1(str, resDict, m, bbox);
3271 void Gfx::doAnnot(Object *str, double xMin, double yMin,
3272 double xMax, double yMax) {
3273 Dict *dict, *resDict;
3274 Object matrixObj, bboxObj, resObj;
3276 double m[6], bbox[6], ictm[6];
3278 double formX0, formY0, formX1, formY1;
3279 double annotX0, annotY0, annotX1, annotY1;
3280 double det, x, y, sx, sy;
3284 dict = str->streamGetDict();
3286 // get the form bounding box
3287 dict->lookup("BBox", &bboxObj);
3288 if (!bboxObj.isArray()) {
3290 error(getPos(), "Bad form bounding box");
3293 for (i = 0; i < 4; ++i) {
3294 bboxObj.arrayGet(i, &obj1);
3295 bbox[i] = obj1.getNum();
3300 // get the form matrix
3301 dict->lookup("Matrix", &matrixObj);
3302 if (matrixObj.isArray()) {
3303 for (i = 0; i < 6; ++i) {
3304 matrixObj.arrayGet(i, &obj1);
3305 m[i] = obj1.getNum();
3315 // transform the form bbox from form space to user space
3316 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
3317 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
3318 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
3319 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
3321 // transform the annotation bbox from default user space to user
3322 // space: (bbox * baseMatrix) * iCTM
3323 ctm = state->getCTM();
3324 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
3325 ictm[0] = ctm[3] * det;
3326 ictm[1] = -ctm[1] * det;
3327 ictm[2] = -ctm[2] * det;
3328 ictm[3] = ctm[0] * det;
3329 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
3330 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
3331 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
3332 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
3333 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
3334 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
3335 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
3336 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
3337 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
3338 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
3340 // swap min/max coords
3341 if (formX0 > formX1) {
3342 x = formX0; formX0 = formX1; formX1 = x;
3344 if (formY0 > formY1) {
3345 y = formY0; formY0 = formY1; formY1 = y;
3347 if (annotX0 > annotX1) {
3348 x = annotX0; annotX0 = annotX1; annotX1 = x;
3350 if (annotY0 > annotY1) {
3351 y = annotY0; annotY0 = annotY1; annotY1 = y;
3354 // scale the form to fit the annotation bbox
3355 if (formX1 == formX0) {
3356 // this shouldn't happen
3359 sx = (annotX1 - annotX0) / (formX1 - formX0);
3361 if (formY1 == formY0) {
3362 // this shouldn't happen
3365 sy = (annotY1 - annotY0) / (formY1 - formY0);
3369 m[4] = (m[4] - formX0) * sx + annotX0;
3372 m[5] = (m[5] - formY0) * sy + annotY0;
3375 dict->lookup("Resources", &resObj);
3376 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
3379 doForm1(str, resDict, m, bbox);
3385 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
3387 double oldBaseMatrix[6];
3390 // push new resources on stack
3391 pushResources(resDict);
3393 // save current graphics state
3396 // kill any pre-existing path
3399 // save current parser
3402 // set form transformation matrix
3403 state->concatCTM(matrix[0], matrix[1], matrix[2],
3404 matrix[3], matrix[4], matrix[5]);
3405 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
3406 matrix[3], matrix[4], matrix[5]);
3408 // set new base matrix
3409 for (i = 0; i < 6; ++i) {
3410 oldBaseMatrix[i] = baseMatrix[i];
3411 baseMatrix[i] = state->getCTM()[i];
3414 // set form bounding box
3415 state->moveTo(bbox[0], bbox[1]);
3416 state->lineTo(bbox[2], bbox[1]);
3417 state->lineTo(bbox[2], bbox[3]);
3418 state->lineTo(bbox[0], bbox[3]);
3425 display(str, gFalse);
3427 // restore base matrix
3428 for (i = 0; i < 6; ++i) {
3429 baseMatrix[i] = oldBaseMatrix[i];
3435 // restore graphics state
3438 // pop resource stack
3444 //------------------------------------------------------------------------
3445 // in-line image operators
3446 //------------------------------------------------------------------------
3448 void Gfx::opBeginImage(Object args[], int numArgs) {
3452 // build dict/stream
3453 str = buildImageStream();
3455 // display the image
3457 doImage(NULL, str, gTrue);
3460 c1 = str->getBaseStream()->getChar();
3461 c2 = str->getBaseStream()->getChar();
3462 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
3464 c2 = str->getBaseStream()->getChar();
3470 Stream *Gfx::buildImageStream() {
3477 dict.initDict(xref);
3478 parser->getObj(&obj);
3479 while (!obj.isCmd("ID") && !obj.isEOF()) {
3480 if (!obj.isName()) {
3481 error(getPos(), "Inline image dictionary key must be a name object");
3484 key = copyString(obj.getName());
3486 parser->getObj(&obj);
3487 if (obj.isEOF() || obj.isError()) {
3491 dict.dictAdd(key, &obj);
3493 parser->getObj(&obj);
3496 error(getPos(), "End of file in inline image");
3504 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3505 str = str->addFilters(&dict);
3510 void Gfx::opImageData(Object args[], int numArgs) {
3511 error(getPos(), "Internal: got 'ID' operator");
3514 void Gfx::opEndImage(Object args[], int numArgs) {
3515 error(getPos(), "Internal: got 'EI' operator");
3518 //------------------------------------------------------------------------
3519 // type 3 font operators
3520 //------------------------------------------------------------------------
3522 void Gfx::opSetCharWidth(Object args[], int numArgs) {
3523 out->type3D0(state, args[0].getNum(), args[1].getNum());
3526 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
3527 out->type3D1(state, args[0].getNum(), args[1].getNum(),
3528 args[2].getNum(), args[3].getNum(),
3529 args[4].getNum(), args[5].getNum());
3532 //------------------------------------------------------------------------
3533 // compatibility operators
3534 //------------------------------------------------------------------------
3536 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
3540 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
3541 if (ignoreUndef > 0)
3545 //------------------------------------------------------------------------
3546 // marked content operators
3547 //------------------------------------------------------------------------
3549 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
3550 if (printCommands) {
3551 printf(" marked content: %s ", args[0].getName());
3553 args[2].print(stdout);
3559 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
3562 void Gfx::opMarkPoint(Object args[], int numArgs) {
3563 if (printCommands) {
3564 printf(" mark point: %s ", args[0].getName());
3566 args[2].print(stdout);
3572 //------------------------------------------------------------------------
3574 //------------------------------------------------------------------------
3576 void Gfx::saveState() {
3577 out->saveState(state);
3578 state = state->save();
3581 void Gfx::restoreState() {
3582 state = state->restore();
3583 out->restoreState(state);
3586 void Gfx::pushResources(Dict *resDict) {
3587 res = new GfxResources(xref, resDict, res);
3590 void Gfx::popResources() {
3591 GfxResources *resPtr;
3593 resPtr = res->getNext();