1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
20 #include "GlobalParams.h"
21 #include "CharTypes.h"
30 #include "OutputDev.h"
35 // the MSVC math.h doesn't define this
37 #define M_PI 3.14159265358979323846
40 //------------------------------------------------------------------------
42 //------------------------------------------------------------------------
44 // Max number of splits along the t axis for an axial shading fill.
45 #define axialMaxSplits 256
47 // Max delta allowed in any color component for an axial shading fill.
48 #define axialColorDelta (1 / 256.0)
50 // Max number of splits along the t axis for a radial shading fill.
51 #define radialMaxSplits 256
53 // Max delta allowed in any color component for a radial shading fill.
54 #define radialColorDelta (1 / 256.0)
56 //------------------------------------------------------------------------
58 //------------------------------------------------------------------------
60 Operator Gfx::opTab[] = {
61 {"\"", 3, {tchkNum, tchkNum, tchkString},
62 &Gfx::opMoveSetShowText},
63 {"'", 1, {tchkString},
64 &Gfx::opMoveShowText},
68 &Gfx::opEOFillStroke},
69 {"BDC", 2, {tchkName, tchkProps},
70 &Gfx::opBeginMarkedContent},
73 {"BMC", 1, {tchkName},
74 &Gfx::opBeginMarkedContent},
78 &Gfx::opBeginIgnoreUndef},
80 &Gfx::opSetStrokeColorSpace},
81 {"DP", 2, {tchkName, tchkProps},
87 {"EMC", 0, {tchkNone},
88 &Gfx::opEndMarkedContent},
92 &Gfx::opEndIgnoreUndef},
96 &Gfx::opSetStrokeGray},
101 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
102 &Gfx::opSetStrokeCMYKColor},
104 &Gfx::opSetMiterLimit},
105 {"MP", 1, {tchkName},
109 {"RG", 3, {tchkNum, tchkNum, tchkNum},
110 &Gfx::opSetStrokeRGBColor},
113 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
114 &Gfx::opSetStrokeColor},
115 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
117 &Gfx::opSetStrokeColorN},
118 {"T*", 0, {tchkNone},
119 &Gfx::opTextNextLine},
120 {"TD", 2, {tchkNum, tchkNum},
121 &Gfx::opTextMoveSet},
122 {"TJ", 1, {tchkArray},
123 &Gfx::opShowSpaceText},
125 &Gfx::opSetTextLeading},
127 &Gfx::opSetCharSpacing},
128 {"Td", 2, {tchkNum, tchkNum},
130 {"Tf", 2, {tchkName, tchkNum},
132 {"Tj", 1, {tchkString},
134 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
136 &Gfx::opSetTextMatrix},
138 &Gfx::opSetTextRender},
140 &Gfx::opSetTextRise},
142 &Gfx::opSetWordSpacing},
144 &Gfx::opSetHorizScaling},
147 {"W*", 0, {tchkNone},
150 &Gfx::opCloseFillStroke},
151 {"b*", 0, {tchkNone},
152 &Gfx::opCloseEOFillStroke},
153 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
156 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
159 {"cs", 1, {tchkName},
160 &Gfx::opSetFillColorSpace},
161 {"d", 2, {tchkArray, tchkNum},
163 {"d0", 2, {tchkNum, tchkNum},
164 &Gfx::opSetCharWidth},
165 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
167 &Gfx::opSetCacheDevice},
170 {"f*", 0, {tchkNone},
173 &Gfx::opSetFillGray},
174 {"gs", 1, {tchkName},
175 &Gfx::opSetExtGState},
181 &Gfx::opSetLineJoin},
182 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
183 &Gfx::opSetFillCMYKColor},
184 {"l", 2, {tchkNum, tchkNum},
186 {"m", 2, {tchkNum, tchkNum},
192 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
194 {"rg", 3, {tchkNum, tchkNum, tchkNum},
195 &Gfx::opSetFillRGBColor},
196 {"ri", 1, {tchkName},
197 &Gfx::opSetRenderingIntent},
199 &Gfx::opCloseStroke},
200 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
201 &Gfx::opSetFillColor},
202 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
204 &Gfx::opSetFillColorN},
205 {"sh", 1, {tchkName},
207 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
210 &Gfx::opSetLineWidth},
211 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
215 #define numOps (sizeof(opTab) / sizeof(Operator))
217 //------------------------------------------------------------------------
219 //------------------------------------------------------------------------
221 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
226 // build font dictionary
228 resDict->lookup("Font", &obj1);
230 fonts = new GfxFontDict(xref, obj1.getDict());
234 // get XObject dictionary
235 resDict->lookup("XObject", &xObjDict);
237 // get color space dictionary
238 resDict->lookup("ColorSpace", &colorSpaceDict);
240 // get pattern dictionary
241 resDict->lookup("Pattern", &patternDict);
243 // get shading dictionary
244 resDict->lookup("Shading", &shadingDict);
246 // get graphics state parameter dictionary
247 resDict->lookup("ExtGState", &gStateDict);
252 colorSpaceDict.initNull();
253 patternDict.initNull();
254 gStateDict.initNull();
260 GfxResources::~GfxResources() {
265 colorSpaceDict.free();
271 GfxFont *GfxResources::lookupFont(char *name) {
273 GfxResources *resPtr;
275 for (resPtr = this; resPtr; resPtr = resPtr->next) {
277 if ((font = resPtr->fonts->lookup(name)))
281 error(-1, "Unknown font tag '%s'", name);
285 GBool GfxResources::lookupXObject(char *name, Object *obj) {
286 GfxResources *resPtr;
288 for (resPtr = this; resPtr; resPtr = resPtr->next) {
289 if (resPtr->xObjDict.isDict()) {
290 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
295 error(-1, "XObject '%s' is unknown", name);
299 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
300 GfxResources *resPtr;
302 for (resPtr = this; resPtr; resPtr = resPtr->next) {
303 if (resPtr->xObjDict.isDict()) {
304 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
309 error(-1, "XObject '%s' is unknown", name);
313 void GfxResources::lookupColorSpace(char *name, Object *obj) {
314 GfxResources *resPtr;
316 for (resPtr = this; resPtr; resPtr = resPtr->next) {
317 if (resPtr->colorSpaceDict.isDict()) {
318 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
327 GfxPattern *GfxResources::lookupPattern(char *name) {
328 GfxResources *resPtr;
332 for (resPtr = this; resPtr; resPtr = resPtr->next) {
333 if (resPtr->patternDict.isDict()) {
334 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
335 pattern = GfxPattern::parse(&obj);
342 error(-1, "Unknown pattern '%s'", name);
346 GfxShading *GfxResources::lookupShading(char *name) {
347 GfxResources *resPtr;
351 for (resPtr = this; resPtr; resPtr = resPtr->next) {
352 if (resPtr->shadingDict.isDict()) {
353 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
354 shading = GfxShading::parse(&obj);
361 error(-1, "Unknown shading '%s'", name);
365 GBool GfxResources::lookupGState(char *name, Object *obj) {
366 GfxResources *resPtr;
368 for (resPtr = this; resPtr; resPtr = resPtr->next) {
369 if (resPtr->gStateDict.isDict()) {
370 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
376 error(-1, "ExtGState '%s' is unknown", name);
380 //------------------------------------------------------------------------
382 //------------------------------------------------------------------------
384 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
385 PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
386 GBool (*abortCheckCbkA)(void *data),
387 void *abortCheckCbkDataA) {
392 printCommands = globalParams->getPrintCommands();
394 // start the resource stack
395 res = new GfxResources(xref, resDict, NULL);
399 state = new GfxState(dpi, box, rotate, out->upsideDown());
400 fontChanged = gFalse;
403 out->startPage(pageNum, state);
404 out->setDefaultCTM(state->getCTM());
405 out->updateAll(state);
406 for (i = 0; i < 6; ++i) {
407 baseMatrix[i] = state->getCTM()[i];
409 abortCheckCbk = abortCheckCbkA;
410 abortCheckCbkData = abortCheckCbkDataA;
414 state->moveTo(cropBox->x1, cropBox->y1);
415 state->lineTo(cropBox->x2, cropBox->y1);
416 state->lineTo(cropBox->x2, cropBox->y2);
417 state->lineTo(cropBox->x1, cropBox->y2);
425 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
426 PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
427 GBool (*abortCheckCbkA)(void *data),
428 void *abortCheckCbkDataA) {
433 printCommands = globalParams->getPrintCommands();
435 // start the resource stack
436 res = new GfxResources(xref, resDict, NULL);
440 state = new GfxState(72, box, 0, gFalse);
441 fontChanged = gFalse;
444 for (i = 0; i < 6; ++i) {
445 baseMatrix[i] = state->getCTM()[i];
447 abortCheckCbk = abortCheckCbkA;
448 abortCheckCbkData = abortCheckCbkDataA;
452 state->moveTo(cropBox->x1, cropBox->y1);
453 state->lineTo(cropBox->x2, cropBox->y1);
454 state->lineTo(cropBox->x2, cropBox->y2);
455 state->lineTo(cropBox->x1, cropBox->y2);
464 while (state->hasSaves()) {
465 state = state->restore();
466 out->restoreState(state);
479 void Gfx::display(Object *obj, GBool topLevel) {
483 if (obj->isArray()) {
484 for (i = 0; i < obj->arrayGetLength(); ++i) {
485 obj->arrayGet(i, &obj2);
486 if (!obj2.isStream()) {
487 error(-1, "Weird page contents");
493 } else if (!obj->isStream()) {
494 error(-1, "Weird page contents");
497 parser = new Parser(xref, new Lexer(xref, obj));
503 void Gfx::go(GBool topLevel) {
505 Object args[maxArgs];
509 // scan a sequence of objects
510 updateLevel = lastAbortCheck = 0;
512 parser->getObj(&obj);
513 while (!obj.isEOF()) {
515 // got a command - execute it
519 for (i = 0; i < numArgs; ++i) {
521 args[i].print(stdout);
526 execOp(&obj, args, numArgs);
528 for (i = 0; i < numArgs; ++i)
532 // periodically update display
533 if (++updateLevel >= 20000) {
538 // check for an abort
540 if (updateLevel - lastAbortCheck > 10) {
541 if ((*abortCheckCbk)(abortCheckCbkData)) {
544 lastAbortCheck = updateLevel;
548 // got an argument - save it
549 } else if (numArgs < maxArgs) {
550 args[numArgs++] = obj;
552 // too many arguments - something is wrong
554 error(getPos(), "Too many args in content stream");
556 printf("throwing away arg: ");
564 // grab the next object
565 parser->getObj(&obj);
569 // args at end with no command
571 error(getPos(), "Leftover args in content stream");
573 printf("%d leftovers:", numArgs);
574 for (i = 0; i < numArgs; ++i) {
576 args[i].print(stdout);
581 for (i = 0; i < numArgs; ++i)
586 if (topLevel && updateLevel > 0) {
591 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
597 name = cmd->getCmd();
598 if (!(op = findOp(name))) {
599 if (ignoreUndef == 0)
600 error(getPos(), "Unknown operator '%s'", name);
605 if (op->numArgs >= 0) {
606 if (numArgs != op->numArgs) {
607 error(getPos(), "Wrong number (%d) of args to '%s' operator",
612 if (numArgs > -op->numArgs) {
613 error(getPos(), "Too many (%d) args to '%s' operator",
618 for (i = 0; i < numArgs; ++i) {
619 if (!checkArg(&args[i], op->tchk[i])) {
620 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
621 i, name, args[i].getTypeName());
627 (this->*op->func)(args, numArgs);
630 Operator *Gfx::findOp(char *name) {
635 // invariant: opTab[a] < name < opTab[b]
638 cmp = strcmp(opTab[m].name, name);
651 GBool Gfx::checkArg(Object *arg, TchkType type) {
653 case tchkBool: return arg->isBool();
654 case tchkInt: return arg->isInt();
655 case tchkNum: return arg->isNum();
656 case tchkString: return arg->isString();
657 case tchkName: return arg->isName();
658 case tchkArray: return arg->isArray();
659 case tchkProps: return arg->isDict() || arg->isName();
660 case tchkSCN: return arg->isNum() || arg->isName();
661 case tchkNone: return gFalse;
667 return parser ? parser->getPos() : -1;
670 //------------------------------------------------------------------------
671 // graphics state operators
672 //------------------------------------------------------------------------
674 void Gfx::opSave(Object args[], int numArgs) {
675 out->saveState(state);
676 state = state->save();
679 void Gfx::opRestore(Object args[], int numArgs) {
680 state = state->restore();
681 out->restoreState(state);
684 void Gfx::opConcat(Object args[], int numArgs) {
685 state->concatCTM(args[0].getNum(), args[1].getNum(),
686 args[2].getNum(), args[3].getNum(),
687 args[4].getNum(), args[5].getNum());
688 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
689 args[2].getNum(), args[3].getNum(),
690 args[4].getNum(), args[5].getNum());
694 void Gfx::opSetDash(Object args[], int numArgs) {
701 a = args[0].getArray();
702 length = a->getLength();
706 dash = (double *)gmalloc(length * sizeof(double));
707 for (i = 0; i < length; ++i) {
708 dash[i] = a->get(i, &obj)->getNum();
712 state->setLineDash(dash, length, args[1].getNum());
713 out->updateLineDash(state);
716 void Gfx::opSetFlat(Object args[], int numArgs) {
717 state->setFlatness((int)args[0].getNum());
718 out->updateFlatness(state);
721 void Gfx::opSetLineJoin(Object args[], int numArgs) {
722 state->setLineJoin(args[0].getInt());
723 out->updateLineJoin(state);
726 void Gfx::opSetLineCap(Object args[], int numArgs) {
727 state->setLineCap(args[0].getInt());
728 out->updateLineCap(state);
731 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
732 state->setMiterLimit(args[0].getNum());
733 out->updateMiterLimit(state);
736 void Gfx::opSetLineWidth(Object args[], int numArgs) {
737 state->setLineWidth(args[0].getNum());
738 out->updateLineWidth(state);
741 void Gfx::opSetExtGState(Object args[], int numArgs) {
744 if (!res->lookupGState(args[0].getName(), &obj1)) {
747 if (!obj1.isDict()) {
748 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
752 if (obj1.dictLookup("ca", &obj2)->isNum()) {
753 state->setFillOpacity(obj2.getNum());
754 out->updateFillOpacity(state);
757 if (obj1.dictLookup("CA", &obj2)->isNum()) {
758 state->setStrokeOpacity(obj2.getNum());
759 out->updateStrokeOpacity(state);
765 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
768 //------------------------------------------------------------------------
770 //------------------------------------------------------------------------
772 void Gfx::opSetFillGray(Object args[], int numArgs) {
775 state->setFillPattern(NULL);
776 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
777 color.c[0] = args[0].getNum();
778 state->setFillColor(&color);
779 out->updateFillColor(state);
782 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
785 state->setStrokePattern(NULL);
786 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
787 color.c[0] = args[0].getNum();
788 state->setStrokeColor(&color);
789 out->updateStrokeColor(state);
792 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
796 state->setFillPattern(NULL);
797 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
798 for (i = 0; i < 4; ++i) {
799 color.c[i] = args[i].getNum();
801 state->setFillColor(&color);
802 out->updateFillColor(state);
805 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
809 state->setStrokePattern(NULL);
810 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
811 for (i = 0; i < 4; ++i) {
812 color.c[i] = args[i].getNum();
814 state->setStrokeColor(&color);
815 out->updateStrokeColor(state);
818 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
822 state->setFillPattern(NULL);
823 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
824 for (i = 0; i < 3; ++i) {
825 color.c[i] = args[i].getNum();
827 state->setFillColor(&color);
828 out->updateFillColor(state);
831 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
835 state->setStrokePattern(NULL);
836 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
837 for (i = 0; i < 3; ++i) {
838 color.c[i] = args[i].getNum();
840 state->setStrokeColor(&color);
841 out->updateStrokeColor(state);
844 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
846 GfxColorSpace *colorSpace;
850 state->setFillPattern(NULL);
851 res->lookupColorSpace(args[0].getName(), &obj);
853 colorSpace = GfxColorSpace::parse(&args[0]);
855 colorSpace = GfxColorSpace::parse(&obj);
859 state->setFillColorSpace(colorSpace);
861 error(getPos(), "Bad color space (fill)");
863 for (i = 0; i < gfxColorMaxComps; ++i) {
866 state->setFillColor(&color);
867 out->updateFillColor(state);
870 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
872 GfxColorSpace *colorSpace;
876 state->setStrokePattern(NULL);
877 res->lookupColorSpace(args[0].getName(), &obj);
879 colorSpace = GfxColorSpace::parse(&args[0]);
881 colorSpace = GfxColorSpace::parse(&obj);
885 state->setStrokeColorSpace(colorSpace);
887 error(getPos(), "Bad color space (stroke)");
889 for (i = 0; i < gfxColorMaxComps; ++i) {
892 state->setStrokeColor(&color);
893 out->updateStrokeColor(state);
896 void Gfx::opSetFillColor(Object args[], int numArgs) {
900 state->setFillPattern(NULL);
901 for (i = 0; i < numArgs; ++i) {
902 color.c[i] = args[i].getNum();
904 state->setFillColor(&color);
905 out->updateFillColor(state);
908 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
912 state->setStrokePattern(NULL);
913 for (i = 0; i < numArgs; ++i) {
914 color.c[i] = args[i].getNum();
916 state->setStrokeColor(&color);
917 out->updateStrokeColor(state);
920 void Gfx::opSetFillColorN(Object args[], int numArgs) {
925 if (state->getFillColorSpace()->getMode() == csPattern) {
927 for (i = 0; i < numArgs && i < 4; ++i) {
928 if (args[i].isNum()) {
929 color.c[i] = args[i].getNum();
932 state->setFillColor(&color);
933 out->updateFillColor(state);
935 if (args[numArgs-1].isName() &&
936 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
937 state->setFillPattern(pattern);
941 state->setFillPattern(NULL);
942 for (i = 0; i < numArgs && i < 4; ++i) {
943 if (args[i].isNum()) {
944 color.c[i] = args[i].getNum();
947 state->setFillColor(&color);
948 out->updateFillColor(state);
952 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
957 if (state->getStrokeColorSpace()->getMode() == csPattern) {
959 for (i = 0; i < numArgs && i < 4; ++i) {
960 if (args[i].isNum()) {
961 color.c[i] = args[i].getNum();
964 state->setStrokeColor(&color);
965 out->updateStrokeColor(state);
967 if (args[numArgs-1].isName() &&
968 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
969 state->setStrokePattern(pattern);
973 state->setStrokePattern(NULL);
974 for (i = 0; i < numArgs && i < 4; ++i) {
975 if (args[i].isNum()) {
976 color.c[i] = args[i].getNum();
979 state->setStrokeColor(&color);
980 out->updateStrokeColor(state);
984 //------------------------------------------------------------------------
985 // path segment operators
986 //------------------------------------------------------------------------
988 void Gfx::opMoveTo(Object args[], int numArgs) {
989 state->moveTo(args[0].getNum(), args[1].getNum());
992 void Gfx::opLineTo(Object args[], int numArgs) {
993 if (!state->isCurPt()) {
994 error(getPos(), "No current point in lineto");
997 state->lineTo(args[0].getNum(), args[1].getNum());
1000 void Gfx::opCurveTo(Object args[], int numArgs) {
1001 double x1, y1, x2, y2, x3, y3;
1003 if (!state->isCurPt()) {
1004 error(getPos(), "No current point in curveto");
1007 x1 = args[0].getNum();
1008 y1 = args[1].getNum();
1009 x2 = args[2].getNum();
1010 y2 = args[3].getNum();
1011 x3 = args[4].getNum();
1012 y3 = args[5].getNum();
1013 state->curveTo(x1, y1, x2, y2, x3, y3);
1016 void Gfx::opCurveTo1(Object args[], int numArgs) {
1017 double x1, y1, x2, y2, x3, y3;
1019 if (!state->isCurPt()) {
1020 error(getPos(), "No current point in curveto1");
1023 x1 = state->getCurX();
1024 y1 = state->getCurY();
1025 x2 = args[0].getNum();
1026 y2 = args[1].getNum();
1027 x3 = args[2].getNum();
1028 y3 = args[3].getNum();
1029 state->curveTo(x1, y1, x2, y2, x3, y3);
1032 void Gfx::opCurveTo2(Object args[], int numArgs) {
1033 double x1, y1, x2, y2, x3, y3;
1035 if (!state->isCurPt()) {
1036 error(getPos(), "No current point in curveto2");
1039 x1 = args[0].getNum();
1040 y1 = args[1].getNum();
1041 x2 = args[2].getNum();
1042 y2 = args[3].getNum();
1045 state->curveTo(x1, y1, x2, y2, x3, y3);
1048 void Gfx::opRectangle(Object args[], int numArgs) {
1051 x = args[0].getNum();
1052 y = args[1].getNum();
1053 w = args[2].getNum();
1054 h = args[3].getNum();
1055 state->moveTo(x, y);
1056 state->lineTo(x + w, y);
1057 state->lineTo(x + w, y + h);
1058 state->lineTo(x, y + h);
1062 void Gfx::opClosePath(Object args[], int numArgs) {
1063 if (!state->isCurPt()) {
1064 error(getPos(), "No current point in closepath");
1070 //------------------------------------------------------------------------
1071 // path painting operators
1072 //------------------------------------------------------------------------
1074 void Gfx::opEndPath(Object args[], int numArgs) {
1078 void Gfx::opStroke(Object args[], int numArgs) {
1079 if (!state->isCurPt()) {
1080 //error(getPos(), "No path in stroke");
1083 if (state->isPath())
1088 void Gfx::opCloseStroke(Object args[], int numArgs) {
1089 if (!state->isCurPt()) {
1090 //error(getPos(), "No path in closepath/stroke");
1093 if (state->isPath()) {
1100 void Gfx::opFill(Object args[], int numArgs) {
1101 if (!state->isCurPt()) {
1102 //error(getPos(), "No path in fill");
1105 if (state->isPath()) {
1106 if (state->getFillColorSpace()->getMode() == csPattern) {
1107 doPatternFill(gFalse);
1115 void Gfx::opEOFill(Object args[], int numArgs) {
1116 if (!state->isCurPt()) {
1117 //error(getPos(), "No path in eofill");
1120 if (state->isPath()) {
1121 if (state->getFillColorSpace()->getMode() == csPattern) {
1122 doPatternFill(gTrue);
1130 void Gfx::opFillStroke(Object args[], int numArgs) {
1131 if (!state->isCurPt()) {
1132 //error(getPos(), "No path in fill/stroke");
1135 if (state->isPath()) {
1136 if (state->getFillColorSpace()->getMode() == csPattern) {
1137 doPatternFill(gFalse);
1146 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1147 if (!state->isCurPt()) {
1148 //error(getPos(), "No path in closepath/fill/stroke");
1151 if (state->isPath()) {
1153 if (state->getFillColorSpace()->getMode() == csPattern) {
1154 doPatternFill(gFalse);
1163 void Gfx::opEOFillStroke(Object args[], int numArgs) {
1164 if (!state->isCurPt()) {
1165 //error(getPos(), "No path in eofill/stroke");
1168 if (state->isPath()) {
1169 if (state->getFillColorSpace()->getMode() == csPattern) {
1170 doPatternFill(gTrue);
1179 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1180 if (!state->isCurPt()) {
1181 //error(getPos(), "No path in closepath/eofill/stroke");
1184 if (state->isPath()) {
1186 if (state->getFillColorSpace()->getMode() == csPattern) {
1187 doPatternFill(gTrue);
1196 void Gfx::doPatternFill(GBool eoFill) {
1197 GfxPatternColorSpace *patCS;
1198 GfxPattern *pattern;
1199 GfxTilingPattern *tPat;
1201 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1202 double cxMin, cyMin, cxMax, cyMax;
1203 int xi0, yi0, xi1, yi1, xi, yi;
1204 double *ctm, *btm, *ptm;
1205 double m[6], ictm[6], m1[6], imb[6];
1207 double xstep, ystep;
1210 // this is a bit of a kludge -- patterns can be really slow, so we
1211 // skip them if we're only doing text extraction, since they almost
1212 // certainly don't contain any text
1213 if (!out->needNonText()) {
1218 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1221 if (!(pattern = state->getFillPattern())) {
1224 if (pattern->getType() != 1) {
1227 tPat = (GfxTilingPattern *)pattern;
1229 // construct a (pattern space) -> (current space) transform matrix
1230 ctm = state->getCTM();
1232 ptm = tPat->getMatrix();
1233 // iCTM = invert CTM
1234 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1235 ictm[0] = ctm[3] * det;
1236 ictm[1] = -ctm[1] * det;
1237 ictm[2] = -ctm[2] * det;
1238 ictm[3] = ctm[0] * det;
1239 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1240 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1241 // m1 = PTM * BTM = PTM * base transform matrix
1242 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1243 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1244 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1245 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1246 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1247 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1248 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1249 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1250 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1251 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1252 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1253 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1254 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1256 // construct a (base space) -> (pattern space) transform matrix
1257 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1258 imb[0] = m1[3] * det;
1259 imb[1] = -m1[1] * det;
1260 imb[2] = -m1[2] * det;
1261 imb[3] = m1[0] * det;
1262 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1263 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1265 // save current graphics state
1266 out->saveState(state);
1267 state = state->save();
1269 // set underlying color space (for uncolored tiling patterns)
1270 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1271 state->setFillColorSpace(cs->copy());
1273 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1275 state->setFillPattern(NULL);
1276 out->updateFillColor(state);
1278 // clip to current path
1287 // transform clip region bbox to pattern space
1288 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1289 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1290 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1291 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1292 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1295 } else if (x1 > xMax) {
1300 } else if (y1 > yMax) {
1303 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1304 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1307 } else if (x1 > xMax) {
1312 } else if (y1 > yMax) {
1315 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1316 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1319 } else if (x1 > xMax) {
1324 } else if (y1 > yMax) {
1329 //~ this should treat negative steps differently -- start at right/top
1330 //~ edge instead of left/bottom (?)
1331 xstep = fabs(tPat->getXStep());
1332 ystep = fabs(tPat->getYStep());
1333 xi0 = (int)floor(xMin / xstep);
1334 xi1 = (int)ceil(xMax / xstep);
1335 yi0 = (int)floor(yMin / ystep);
1336 yi1 = (int)ceil(yMax / ystep);
1337 for (i = 0; i < 4; ++i) {
1340 for (yi = yi0; yi < yi1; ++yi) {
1341 for (xi = xi0; xi < xi1; ++xi) {
1344 m1[4] = x * m[0] + y * m[2] + m[4];
1345 m1[5] = x * m[1] + y * m[3] + m[5];
1346 doForm1(tPat->getContentStream(), tPat->getResDict(),
1347 m1, tPat->getBBox());
1351 // restore graphics state
1352 state = state->restore();
1353 out->restoreState(state);
1356 void Gfx::opShFill(Object args[], int numArgs) {
1357 GfxShading *shading;
1358 double xMin, yMin, xMax, yMax;
1360 if (!(shading = res->lookupShading(args[0].getName()))) {
1364 // save current graphics state
1365 out->saveState(state);
1366 state = state->save();
1369 if (shading->getHasBBox()) {
1370 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1371 state->moveTo(xMin, yMin);
1372 state->lineTo(xMax, yMin);
1373 state->lineTo(xMax, yMax);
1374 state->lineTo(xMin, yMax);
1381 // set the color space
1382 state->setFillColorSpace(shading->getColorSpace()->copy());
1384 // do shading type-specific operations
1385 switch (shading->getType()) {
1387 doAxialShFill((GfxAxialShading *)shading);
1390 doRadialShFill((GfxRadialShading *)shading);
1394 // restore graphics state
1395 state = state->restore();
1396 out->restoreState(state);
1401 void Gfx::doAxialShFill(GfxAxialShading *shading) {
1402 double xMin, yMin, xMax, yMax;
1403 double x0, y0, x1, y1;
1405 double tMin, tMax, t, tx, ty;
1406 double s[4], sMin, sMax, tmp;
1407 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1409 double ta[axialMaxSplits + 1];
1410 int next[axialMaxSplits + 1];
1411 GfxColor color0, color1;
1415 // get the clip region bbox
1416 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1418 // compute min and max t values, based on the four corners of the
1420 shading->getCoords(&x0, &y0, &x1, &y1);
1423 mul = 1 / (dx * dx + dy * dy);
1424 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1425 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1428 } else if (t > tMax) {
1431 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1434 } else if (t > tMax) {
1437 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1440 } else if (t > tMax) {
1443 if (tMin < 0 && !shading->getExtend0()) {
1446 if (tMax > 1 && !shading->getExtend1()) {
1450 // get the function domain
1451 t0 = shading->getDomain0();
1452 t1 = shading->getDomain1();
1454 // Traverse the t axis and do the shading.
1456 // For each point (tx, ty) on the t axis, consider a line through
1457 // that point perpendicular to the t axis:
1459 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1460 // y(s) = ty + s * dx --> s = (y - ty) / dx
1462 // Then look at the intersection of this line with the bounding box
1463 // (xMin, yMin, xMax, yMax). In the general case, there are four
1464 // intersection points:
1466 // s0 = (xMin - tx) / -dy
1467 // s1 = (xMax - tx) / -dy
1468 // s2 = (yMin - ty) / dx
1469 // s3 = (yMax - ty) / dx
1471 // and we want the middle two s values.
1473 // In the case where dx = 0, take s0 and s1; in the case where dy =
1474 // 0, take s2 and s3.
1476 // Each filled polygon is bounded by two of these line segments
1477 // perpdendicular to the t axis.
1479 // The t axis is bisected into smaller regions until the color
1480 // difference across a region is small enough, and then the region
1481 // is painted with a single color.
1484 nComps = shading->getColorSpace()->getNComps();
1486 ta[axialMaxSplits] = tMax;
1487 next[0] = axialMaxSplits;
1489 // compute the color at t = tMin
1492 } else if (tMin > 1) {
1495 tt = t0 + (t1 - t0) * tMin;
1497 shading->getColor(tt, &color0);
1499 // compute the coordinates of the point on the t axis at t = tMin;
1500 // then compute the intersection of the perpendicular line with the
1502 tx = x0 + tMin * dx;
1503 ty = y0 + tMin * dy;
1504 if (dx == 0 && dy == 0) {
1507 sMin = (xMin - tx) / -dy;
1508 sMax = (xMax - tx) / -dy;
1509 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1510 } else if (dy == 0) {
1511 sMin = (yMin - ty) / dx;
1512 sMax = (yMax - ty) / dx;
1513 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1515 s[0] = (yMin - ty) / dx;
1516 s[1] = (yMax - ty) / dx;
1517 s[2] = (xMin - tx) / -dy;
1518 s[3] = (xMax - tx) / -dy;
1519 for (j = 0; j < 3; ++j) {
1521 for (k = j + 1; k < 4; ++k) {
1526 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1531 ux0 = tx - sMin * dy;
1532 uy0 = ty + sMin * dx;
1533 vx0 = tx - sMax * dy;
1534 vy0 = ty + sMax * dx;
1537 while (i < axialMaxSplits) {
1539 // bisect until color difference is small enough or we hit the
1545 } else if (ta[j] > 1) {
1548 tt = t0 + (t1 - t0) * ta[j];
1550 shading->getColor(tt, &color1);
1551 for (k = 0; k < nComps; ++k) {
1552 if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1560 ta[k] = 0.5 * (ta[i] + ta[j]);
1566 // use the average of the colors of the two sides of the region
1567 for (k = 0; k < nComps; ++k) {
1568 color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
1571 // compute the coordinates of the point on the t axis; then
1572 // compute the intersection of the perpendicular line with the
1574 tx = x0 + ta[j] * dx;
1575 ty = y0 + ta[j] * dy;
1576 if (dx == 0 && dy == 0) {
1579 sMin = (xMin - tx) / -dy;
1580 sMax = (xMax - tx) / -dy;
1581 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1582 } else if (dy == 0) {
1583 sMin = (yMin - ty) / dx;
1584 sMax = (yMax - ty) / dx;
1585 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1587 s[0] = (yMin - ty) / dx;
1588 s[1] = (yMax - ty) / dx;
1589 s[2] = (xMin - tx) / -dy;
1590 s[3] = (xMax - tx) / -dy;
1591 for (j = 0; j < 3; ++j) {
1593 for (k = j + 1; k < 4; ++k) {
1598 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1603 ux1 = tx - sMin * dy;
1604 uy1 = ty + sMin * dx;
1605 vx1 = tx - sMax * dy;
1606 vy1 = ty + sMax * dx;
1609 state->setFillColor(&color0);
1610 out->updateFillColor(state);
1613 state->moveTo(ux0, uy0);
1614 state->lineTo(vx0, vy0);
1615 state->lineTo(vx1, vy1);
1616 state->lineTo(ux1, uy1);
1621 // set up for next region
1631 void Gfx::doRadialShFill(GfxRadialShading *shading) {
1632 double sMin, sMax, xMin, yMin, xMax, yMax;
1633 double x0, y0, r0, x1, y1, r1, t0, t1;
1635 GfxColor colorA, colorB;
1636 double xa, ya, xb, yb, ra, rb;
1637 double ta, tb, sa, sb;
1642 // get the shading info
1643 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1644 t0 = shading->getDomain0();
1645 t1 = shading->getDomain1();
1646 nComps = shading->getColorSpace()->getNComps();
1648 // compute the (possibly extended) s range
1651 if (shading->getExtend0()) {
1653 // extend the smaller end
1654 sMin = -r0 / (r1 - r0);
1656 // extend the larger end
1657 //~ this computes the diagonal of the bounding box -- we should
1658 //~ really compute the intersection of the moving/expanding
1659 //~ circles with each of the four corners and look for the max
1661 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1662 sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
1663 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1666 } else if (sMin < -20) {
1672 if (shading->getExtend1()) {
1674 // extend the smaller end
1675 sMax = -r0 / (r1 - r0);
1676 } else if (r1 > r0) {
1677 // extend the larger end
1678 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1679 sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
1680 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1683 } else if (sMax > 20) {
1690 // compute the number of steps into which circles must be divided to
1691 // achieve a curve flatness of 0.1 pixel in device space for the
1692 // largest circle (note that "device space" is 72 dpi when generating
1693 // PostScript, hence the relatively small 0.1 pixel accuracy)
1694 ctm = state->getCTM();
1696 if (fabs(ctm[1]) > t) {
1699 if (fabs(ctm[2]) > t) {
1702 if (fabs(ctm[3]) > t) {
1713 n = (int)(M_PI / acos(1 - 0.1 / t));
1716 } else if (n > 200) {
1721 // Traverse the t axis and do the shading.
1723 // This generates and fills a series of rings. Each ring is defined
1725 // sa, ta, xa, ya, ra, colorA
1726 // sb, tb, xb, yb, rb, colorB
1728 // The s/t axis is divided into radialMaxSplits parts; these parts
1729 // are combined as much as possible while respecting the
1730 // radialColorDelta parameter.
1732 // setup for the start circle
1735 ta = t0 + sa * (t1 - t0);
1736 xa = x0 + sa * (x1 - x0);
1737 ya = y0 + sa * (y1 - y0);
1738 ra = r0 + sa * (r1 - r0);
1740 shading->getColor(t0, &colorA);
1741 } else if (ta > t1) {
1742 shading->getColor(t1, &colorA);
1744 shading->getColor(ta, &colorA);
1747 while (ia < radialMaxSplits) {
1749 // go as far along the t axis (toward t1) as we can, such that the
1750 // color difference is within the tolerance (radialColorDelta) --
1751 // this uses bisection (between the current value, t, and t1),
1752 // limited to radialMaxSplits points along the t axis
1753 ib = radialMaxSplits;
1754 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
1755 tb = t0 + sb * (t1 - t0);
1757 shading->getColor(t0, &colorB);
1758 } else if (tb > t1) {
1759 shading->getColor(t1, &colorB);
1761 shading->getColor(tb, &colorB);
1763 while (ib - ia > 1) {
1764 for (k = 0; k < nComps; ++k) {
1765 if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
1773 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
1774 tb = t0 + sb * (t1 - t0);
1776 shading->getColor(t0, &colorB);
1777 } else if (tb > t1) {
1778 shading->getColor(t1, &colorB);
1780 shading->getColor(tb, &colorB);
1784 // compute center and radius of the circle
1785 xb = x0 + sb * (x1 - x0);
1786 yb = y0 + sb * (y1 - y0);
1787 rb = r0 + sb * (r1 - r0);
1789 // use the average of the colors at the two circles
1790 for (k = 0; k < nComps; ++k) {
1791 colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
1793 state->setFillColor(&colorA);
1794 out->updateFillColor(state);
1796 // construct path for first circle
1797 state->moveTo(xa + ra, ya);
1798 for (k = 1; k < n; ++k) {
1799 angle = ((double)k / (double)n) * 2 * M_PI;
1800 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
1804 // construct and append path for second circle
1805 state->moveTo(xb + rb, yb);
1806 for (k = 1; k < n; ++k) {
1807 angle = ((double)k / (double)n) * 2 * M_PI;
1808 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
1816 // step to the next value of t
1827 void Gfx::doEndPath() {
1828 if (state->isCurPt() && clip != clipNone) {
1830 if (clip == clipNormal) {
1840 //------------------------------------------------------------------------
1841 // path clipping operators
1842 //------------------------------------------------------------------------
1844 void Gfx::opClip(Object args[], int numArgs) {
1848 void Gfx::opEOClip(Object args[], int numArgs) {
1852 //------------------------------------------------------------------------
1853 // text object operators
1854 //------------------------------------------------------------------------
1856 void Gfx::opBeginText(Object args[], int numArgs) {
1857 state->setTextMat(1, 0, 0, 1, 0, 0);
1858 state->textMoveTo(0, 0);
1859 out->updateTextMat(state);
1860 out->updateTextPos(state);
1861 fontChanged = gTrue;
1864 void Gfx::opEndText(Object args[], int numArgs) {
1867 //------------------------------------------------------------------------
1868 // text state operators
1869 //------------------------------------------------------------------------
1871 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
1872 state->setCharSpace(args[0].getNum());
1873 out->updateCharSpace(state);
1876 void Gfx::opSetFont(Object args[], int numArgs) {
1879 if (!(font = res->lookupFont(args[0].getName()))) {
1882 if (printCommands) {
1883 printf(" font: tag=%s name='%s' %g\n",
1884 font->getTag()->getCString(),
1885 font->getName() ? font->getName()->getCString() : "???",
1889 state->setFont(font, args[1].getNum());
1890 fontChanged = gTrue;
1893 void Gfx::opSetTextLeading(Object args[], int numArgs) {
1894 state->setLeading(args[0].getNum());
1897 void Gfx::opSetTextRender(Object args[], int numArgs) {
1898 state->setRender(args[0].getInt());
1899 out->updateRender(state);
1902 void Gfx::opSetTextRise(Object args[], int numArgs) {
1903 state->setRise(args[0].getNum());
1904 out->updateRise(state);
1907 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
1908 state->setWordSpace(args[0].getNum());
1909 out->updateWordSpace(state);
1912 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
1913 state->setHorizScaling(args[0].getNum());
1914 out->updateHorizScaling(state);
1915 fontChanged = gTrue;
1918 //------------------------------------------------------------------------
1919 // text positioning operators
1920 //------------------------------------------------------------------------
1922 void Gfx::opTextMove(Object args[], int numArgs) {
1925 tx = state->getLineX() + args[0].getNum();
1926 ty = state->getLineY() + args[1].getNum();
1927 state->textMoveTo(tx, ty);
1928 out->updateTextPos(state);
1931 void Gfx::opTextMoveSet(Object args[], int numArgs) {
1934 tx = state->getLineX() + args[0].getNum();
1935 ty = args[1].getNum();
1936 state->setLeading(-ty);
1937 ty += state->getLineY();
1938 state->textMoveTo(tx, ty);
1939 out->updateTextPos(state);
1942 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
1943 state->setTextMat(args[0].getNum(), args[1].getNum(),
1944 args[2].getNum(), args[3].getNum(),
1945 args[4].getNum(), args[5].getNum());
1946 state->textMoveTo(0, 0);
1947 out->updateTextMat(state);
1948 out->updateTextPos(state);
1949 fontChanged = gTrue;
1952 void Gfx::opTextNextLine(Object args[], int numArgs) {
1955 tx = state->getLineX();
1956 ty = state->getLineY() - state->getLeading();
1957 state->textMoveTo(tx, ty);
1958 out->updateTextPos(state);
1961 //------------------------------------------------------------------------
1962 // text string operators
1963 //------------------------------------------------------------------------
1965 void Gfx::opShowText(Object args[], int numArgs) {
1966 if (!state->getFont()) {
1967 error(getPos(), "No font in show");
1970 doShowText(args[0].getString());
1973 void Gfx::opMoveShowText(Object args[], int numArgs) {
1976 if (!state->getFont()) {
1977 error(getPos(), "No font in move/show");
1980 tx = state->getLineX();
1981 ty = state->getLineY() - state->getLeading();
1982 state->textMoveTo(tx, ty);
1983 out->updateTextPos(state);
1984 doShowText(args[0].getString());
1987 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1990 if (!state->getFont()) {
1991 error(getPos(), "No font in move/set/show");
1994 state->setWordSpace(args[0].getNum());
1995 state->setCharSpace(args[1].getNum());
1996 tx = state->getLineX();
1997 ty = state->getLineY() - state->getLeading();
1998 state->textMoveTo(tx, ty);
1999 out->updateWordSpace(state);
2000 out->updateCharSpace(state);
2001 out->updateTextPos(state);
2002 doShowText(args[2].getString());
2005 void Gfx::opShowSpaceText(Object args[], int numArgs) {
2011 if (!state->getFont()) {
2012 error(getPos(), "No font in show/space");
2015 wMode = state->getFont()->getWMode();
2016 a = args[0].getArray();
2017 for (i = 0; i < a->getLength(); ++i) {
2021 state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
2023 state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
2025 out->updateTextShift(state, obj.getNum());
2026 } else if (obj.isString()) {
2027 doShowText(obj.getString());
2029 error(getPos(), "Element of show/space array must be number or string");
2035 void Gfx::doShowText(GString *s) {
2038 double riseX, riseY;
2041 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2042 double originX, originY, tOriginX, tOriginY;
2043 double oldCTM[6], newCTM[6];
2049 int len, n, uLen, nChars, nSpaces, i;
2052 out->updateFont(state);
2053 fontChanged = gFalse;
2055 font = state->getFont();
2056 wMode = font->getWMode();
2058 if (out->useDrawChar()) {
2059 out->beginString(state, s);
2062 // handle a Type 3 char
2063 if (font->getType() == fontType3 && out->interpretType3Chars()) {
2064 mat = state->getCTM();
2065 for (i = 0; i < 6; ++i) {
2068 mat = state->getTextMat();
2069 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2070 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2071 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2072 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2073 mat = font->getFontMatrix();
2074 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2075 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2076 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2077 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2078 newCTM[0] *= state->getFontSize();
2079 newCTM[3] *= state->getFontSize();
2080 newCTM[0] *= state->getHorizScaling();
2081 newCTM[2] *= state->getHorizScaling();
2082 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2083 curX = state->getCurX();
2084 curY = state->getCurY();
2085 lineX = state->getLineX();
2086 lineY = state->getLineY();
2088 p = s->getCString();
2089 len = s->getLength();
2091 n = font->getNextChar(p, len, &code,
2092 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2093 &dx, &dy, &originX, &originY);
2094 dx = dx * state->getFontSize() + state->getCharSpace();
2095 if (n == 1 && *p == ' ') {
2096 dx += state->getWordSpace();
2098 dx *= state->getHorizScaling();
2099 dy *= state->getFontSize();
2100 state->textTransformDelta(dx, dy, &tdx, &tdy);
2101 state->transform(curX + riseX, curY + riseY, &x, &y);
2102 out->saveState(state);
2103 state = state->save();
2104 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2105 //~ out->updateCTM(???)
2106 if (!out->beginType3Char(state, code, u, uLen)) {
2107 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2108 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2109 pushResources(resDict);
2111 if (charProc.isStream()) {
2112 display(&charProc, gFalse);
2114 error(getPos(), "Missing or bad Type3 CharProc entry");
2116 out->endType3Char(state);
2122 state = state->restore();
2123 out->restoreState(state);
2124 // GfxState::restore() does *not* restore the current position,
2125 // so we deal with it here using (curX, curY) and (lineX, lineY)
2128 state->moveTo(curX, curY);
2129 state->textSetPos(lineX, lineY);
2135 } else if (out->useDrawChar()) {
2136 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2137 p = s->getCString();
2138 len = s->getLength();
2140 n = font->getNextChar(p, len, &code,
2141 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2142 &dx, &dy, &originX, &originY);
2144 dx *= state->getFontSize();
2145 dy = dy * state->getFontSize() + state->getCharSpace();
2146 if (n == 1 && *p == ' ') {
2147 dy += state->getWordSpace();
2150 dx = dx * state->getFontSize() + state->getCharSpace();
2151 if (n == 1 && *p == ' ') {
2152 dx += state->getWordSpace();
2154 dx *= state->getHorizScaling();
2155 dy *= state->getFontSize();
2157 state->textTransformDelta(dx, dy, &tdx, &tdy);
2158 originX *= state->getFontSize();
2159 originY *= state->getFontSize();
2160 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2161 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2162 tdx, tdy, tOriginX, tOriginY, code, u, uLen);
2163 state->shift(tdx, tdy);
2170 p = s->getCString();
2171 len = s->getLength();
2172 nChars = nSpaces = 0;
2174 n = font->getNextChar(p, len, &code,
2175 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2176 &dx2, &dy2, &originX, &originY);
2179 if (n == 1 && *p == ' ') {
2187 dx *= state->getFontSize();
2188 dy = dy * state->getFontSize()
2189 + nChars * state->getCharSpace()
2190 + nSpaces * state->getWordSpace();
2192 dx = dx * state->getFontSize()
2193 + nChars * state->getCharSpace()
2194 + nSpaces * state->getWordSpace();
2195 dx *= state->getHorizScaling();
2196 dy *= state->getFontSize();
2198 state->textTransformDelta(dx, dy, &tdx, &tdy);
2199 out->drawString(state, s);
2200 state->shift(tdx, tdy);
2203 if (out->useDrawChar()) {
2204 out->endString(state);
2207 updateLevel += 10 * s->getLength();
2210 //------------------------------------------------------------------------
2211 // XObject operators
2212 //------------------------------------------------------------------------
2214 void Gfx::opXObject(Object args[], int numArgs) {
2215 Object obj1, obj2, obj3, refObj;
2220 if (!res->lookupXObject(args[0].getName(), &obj1)) {
2223 if (!obj1.isStream()) {
2224 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2229 obj1.streamGetDict()->lookup("OPI", &opiDict);
2230 if (opiDict.isDict()) {
2231 out->opiBegin(state, opiDict.getDict());
2234 obj1.streamGetDict()->lookup("Subtype", &obj2);
2235 if (obj2.isName("Image")) {
2236 res->lookupXObjectNF(args[0].getName(), &refObj);
2237 doImage(&refObj, obj1.getStream(), gFalse);
2239 } else if (obj2.isName("Form")) {
2241 } else if (obj2.isName("PS")) {
2242 obj1.streamGetDict()->lookup("Level1", &obj3);
2243 out->psXObject(obj1.getStream(),
2244 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2245 } else if (obj2.isName()) {
2246 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2248 error(getPos(), "XObject subtype is missing or wrong type");
2252 if (opiDict.isDict()) {
2253 out->opiEnd(state, opiDict.getDict());
2260 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2266 GfxColorSpace *colorSpace;
2267 GfxImageColorMap *colorMap;
2270 int maskColors[2*gfxColorMaxComps];
2275 dict = str->getDict();
2278 dict->lookup("Width", &obj1);
2279 if (obj1.isNull()) {
2281 dict->lookup("W", &obj1);
2285 width = obj1.getInt();
2287 dict->lookup("Height", &obj1);
2288 if (obj1.isNull()) {
2290 dict->lookup("H", &obj1);
2294 height = obj1.getInt();
2298 dict->lookup("ImageMask", &obj1);
2299 if (obj1.isNull()) {
2301 dict->lookup("IM", &obj1);
2305 mask = obj1.getBool();
2306 else if (!obj1.isNull())
2311 dict->lookup("BitsPerComponent", &obj1);
2312 if (obj1.isNull()) {
2314 dict->lookup("BPC", &obj1);
2318 bits = obj1.getInt();
2324 // check for inverted mask
2328 dict->lookup("Decode", &obj1);
2329 if (obj1.isNull()) {
2331 dict->lookup("D", &obj1);
2333 if (obj1.isArray()) {
2334 obj1.arrayGet(0, &obj2);
2335 if (obj2.isInt() && obj2.getInt() == 1)
2338 } else if (!obj1.isNull()) {
2344 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2348 // get color space and color map
2349 dict->lookup("ColorSpace", &obj1);
2350 if (obj1.isNull()) {
2352 dict->lookup("CS", &obj1);
2354 if (obj1.isName()) {
2355 res->lookupColorSpace(obj1.getName(), &obj2);
2356 if (!obj2.isNull()) {
2363 colorSpace = GfxColorSpace::parse(&obj1);
2368 dict->lookup("Decode", &obj1);
2369 if (obj1.isNull()) {
2371 dict->lookup("D", &obj1);
2373 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2375 if (!colorMap->isOk()) {
2382 dict->lookup("Mask", &maskObj);
2383 if (maskObj.isArray()) {
2384 for (i = 0; i < maskObj.arrayGetLength(); ++i) {
2385 maskObj.arrayGet(i, &obj1);
2386 maskColors[i] = obj1.getInt();
2393 out->drawImage(state, ref, str, width, height, colorMap,
2394 haveMask ? maskColors : (int *)NULL, inlineImg);
2400 if ((i = width * height) > 1000) {
2410 error(getPos(), "Bad image parameters");
2413 void Gfx::doForm(Object *str) {
2415 Object matrixObj, bboxObj;
2416 double m[6], bbox[6];
2423 dict = str->streamGetDict();
2426 dict->lookup("FormType", &obj1);
2427 if (!(obj1.isInt() && obj1.getInt() == 1)) {
2428 error(getPos(), "Unknown form type");
2433 dict->lookup("BBox", &bboxObj);
2434 if (!bboxObj.isArray()) {
2437 error(getPos(), "Bad form bounding box");
2440 for (i = 0; i < 4; ++i) {
2441 bboxObj.arrayGet(i, &obj1);
2442 bbox[i] = obj1.getNum();
2448 dict->lookup("Matrix", &matrixObj);
2449 if (matrixObj.isArray()) {
2450 for (i = 0; i < 6; ++i) {
2451 matrixObj.arrayGet(i, &obj1);
2452 m[i] = obj1.getNum();
2463 dict->lookup("Resources", &resObj);
2464 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2467 doForm1(str, resDict, m, bbox);
2472 void Gfx::doAnnot(Object *str, double xMin, double yMin,
2473 double xMax, double yMax) {
2474 Dict *dict, *resDict;
2475 Object matrixObj, bboxObj, resObj;
2477 double m[6], bbox[6], ictm[6];
2479 double formX0, formY0, formX1, formY1;
2480 double annotX0, annotY0, annotX1, annotY1;
2481 double det, x, y, sx, sy;
2485 dict = str->streamGetDict();
2487 // get the form bounding box
2488 dict->lookup("BBox", &bboxObj);
2489 if (!bboxObj.isArray()) {
2491 error(getPos(), "Bad form bounding box");
2494 for (i = 0; i < 4; ++i) {
2495 bboxObj.arrayGet(i, &obj1);
2496 bbox[i] = obj1.getNum();
2501 // get the form matrix
2502 dict->lookup("Matrix", &matrixObj);
2503 if (matrixObj.isArray()) {
2504 for (i = 0; i < 6; ++i) {
2505 matrixObj.arrayGet(i, &obj1);
2506 m[i] = obj1.getNum();
2516 // transform the form bbox from form space to user space
2517 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
2518 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
2519 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
2520 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
2522 // transform the annotation bbox from default user space to user
2523 // space: (bbox * baseMatrix) * iCTM
2524 ctm = state->getCTM();
2525 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2526 ictm[0] = ctm[3] * det;
2527 ictm[1] = -ctm[1] * det;
2528 ictm[2] = -ctm[2] * det;
2529 ictm[3] = ctm[0] * det;
2530 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2531 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2532 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
2533 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
2534 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
2535 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
2536 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
2537 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
2538 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
2539 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
2541 // swap min/max coords
2542 if (formX0 > formX1) {
2543 x = formX0; formX0 = formX1; formX1 = x;
2545 if (formY0 > formY1) {
2546 y = formY0; formY0 = formY1; formY1 = y;
2548 if (annotX0 > annotX1) {
2549 x = annotX0; annotX0 = annotX1; annotX1 = x;
2551 if (annotY0 > annotY1) {
2552 y = annotY0; annotY0 = annotY1; annotY1 = y;
2555 // scale the form to fit the annotation bbox
2556 if (formX1 == formX0) {
2557 // this shouldn't happen
2560 sx = (annotX1 - annotX0) / (formX1 - formX0);
2562 if (formY1 == formY0) {
2563 // this shouldn't happen
2566 sy = (annotY1 - annotY0) / (formY1 - formY0);
2570 m[4] = (m[4] - formX0) * sx + annotX0;
2573 m[5] = (m[5] - formY0) * sy + annotY0;
2576 dict->lookup("Resources", &resObj);
2577 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2580 doForm1(str, resDict, m, bbox);
2586 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
2588 double oldBaseMatrix[6];
2591 // push new resources on stack
2592 pushResources(resDict);
2594 // save current graphics state
2595 out->saveState(state);
2596 state = state->save();
2598 // save current parser
2601 // set form transformation matrix
2602 state->concatCTM(matrix[0], matrix[1], matrix[2],
2603 matrix[3], matrix[4], matrix[5]);
2604 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2605 matrix[3], matrix[4], matrix[5]);
2607 // set new base matrix
2608 for (i = 0; i < 6; ++i) {
2609 oldBaseMatrix[i] = baseMatrix[i];
2610 baseMatrix[i] = state->getCTM()[i];
2613 // set form bounding box
2614 state->moveTo(bbox[0], bbox[1]);
2615 state->lineTo(bbox[2], bbox[1]);
2616 state->lineTo(bbox[2], bbox[3]);
2617 state->lineTo(bbox[0], bbox[3]);
2624 display(str, gFalse);
2626 // restore base matrix
2627 for (i = 0; i < 6; ++i) {
2628 baseMatrix[i] = oldBaseMatrix[i];
2634 // restore graphics state
2635 state = state->restore();
2636 out->restoreState(state);
2638 // pop resource stack
2644 void Gfx::pushResources(Dict *resDict) {
2645 res = new GfxResources(xref, resDict, res);
2648 void Gfx::popResources() {
2649 GfxResources *resPtr;
2651 resPtr = res->getNext();
2656 //------------------------------------------------------------------------
2657 // in-line image operators
2658 //------------------------------------------------------------------------
2660 void Gfx::opBeginImage(Object args[], int numArgs) {
2664 // build dict/stream
2665 str = buildImageStream();
2667 // display the image
2669 doImage(NULL, str, gTrue);
2672 c1 = str->getBaseStream()->getChar();
2673 c2 = str->getBaseStream()->getChar();
2674 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2676 c2 = str->getBaseStream()->getChar();
2682 Stream *Gfx::buildImageStream() {
2689 dict.initDict(xref);
2690 parser->getObj(&obj);
2691 while (!obj.isCmd("ID") && !obj.isEOF()) {
2692 if (!obj.isName()) {
2693 error(getPos(), "Inline image dictionary key must be a name object");
2696 key = copyString(obj.getName());
2698 parser->getObj(&obj);
2699 if (obj.isEOF() || obj.isError()) {
2703 dict.dictAdd(key, &obj);
2705 parser->getObj(&obj);
2708 error(getPos(), "End of file in inline image");
2716 str = new EmbedStream(parser->getStream(), &dict);
2717 str = str->addFilters(&dict);
2722 void Gfx::opImageData(Object args[], int numArgs) {
2723 error(getPos(), "Internal: got 'ID' operator");
2726 void Gfx::opEndImage(Object args[], int numArgs) {
2727 error(getPos(), "Internal: got 'EI' operator");
2730 //------------------------------------------------------------------------
2731 // type 3 font operators
2732 //------------------------------------------------------------------------
2734 void Gfx::opSetCharWidth(Object args[], int numArgs) {
2735 out->type3D0(state, args[0].getNum(), args[1].getNum());
2738 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
2739 out->type3D1(state, args[0].getNum(), args[1].getNum(),
2740 args[2].getNum(), args[3].getNum(),
2741 args[4].getNum(), args[5].getNum());
2744 //------------------------------------------------------------------------
2745 // compatibility operators
2746 //------------------------------------------------------------------------
2748 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
2752 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
2753 if (ignoreUndef > 0)
2757 //------------------------------------------------------------------------
2758 // marked content operators
2759 //------------------------------------------------------------------------
2761 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
2762 if (printCommands) {
2763 printf(" marked content: %s ", args[0].getName());
2765 args[2].print(stdout);
2771 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
2774 void Gfx::opMarkPoint(Object args[], int numArgs) {
2775 if (printCommands) {
2776 printf(" mark point: %s ", args[0].getName());
2778 args[2].print(stdout);