1 //========================================================================
5 // Copyright 1996-2002 Glyph & Cog, LLC
7 //========================================================================
10 #pragma implementation
19 #include "CharTypes.h"
28 #include "OutputDev.h"
33 // the MSVC math.h doesn't define this
35 #define M_PI 3.14159265358979323846
38 //------------------------------------------------------------------------
40 //------------------------------------------------------------------------
42 // Max number of splits along the t axis for an axial shading fill.
43 #define axialMaxSplits 256
45 // Max delta allowed in any color component for an axial shading fill.
46 #define axialColorDelta (1 / 256.0)
48 // Max number of splits along the t axis for a radial shading fill.
49 #define radialMaxSplits 256
51 // Max delta allowed in any color component for a radial shading fill.
52 #define radialColorDelta (1 / 256.0)
54 //------------------------------------------------------------------------
56 //------------------------------------------------------------------------
58 Operator Gfx::opTab[] = {
59 {"\"", 3, {tchkNum, tchkNum, tchkString},
60 &Gfx::opMoveSetShowText},
61 {"'", 1, {tchkString},
62 &Gfx::opMoveShowText},
66 &Gfx::opEOFillStroke},
67 {"BDC", 2, {tchkName, tchkProps},
68 &Gfx::opBeginMarkedContent},
71 {"BMC", 1, {tchkName},
72 &Gfx::opBeginMarkedContent},
76 &Gfx::opBeginIgnoreUndef},
78 &Gfx::opSetStrokeColorSpace},
79 {"DP", 2, {tchkName, tchkProps},
85 {"EMC", 0, {tchkNone},
86 &Gfx::opEndMarkedContent},
90 &Gfx::opEndIgnoreUndef},
94 &Gfx::opSetStrokeGray},
99 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
100 &Gfx::opSetStrokeCMYKColor},
102 &Gfx::opSetMiterLimit},
103 {"MP", 1, {tchkName},
107 {"RG", 3, {tchkNum, tchkNum, tchkNum},
108 &Gfx::opSetStrokeRGBColor},
111 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
112 &Gfx::opSetStrokeColor},
113 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
115 &Gfx::opSetStrokeColorN},
116 {"T*", 0, {tchkNone},
117 &Gfx::opTextNextLine},
118 {"TD", 2, {tchkNum, tchkNum},
119 &Gfx::opTextMoveSet},
120 {"TJ", 1, {tchkArray},
121 &Gfx::opShowSpaceText},
123 &Gfx::opSetTextLeading},
125 &Gfx::opSetCharSpacing},
126 {"Td", 2, {tchkNum, tchkNum},
128 {"Tf", 2, {tchkName, tchkNum},
130 {"Tj", 1, {tchkString},
132 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
134 &Gfx::opSetTextMatrix},
136 &Gfx::opSetTextRender},
138 &Gfx::opSetTextRise},
140 &Gfx::opSetWordSpacing},
142 &Gfx::opSetHorizScaling},
145 {"W*", 0, {tchkNone},
148 &Gfx::opCloseFillStroke},
149 {"b*", 0, {tchkNone},
150 &Gfx::opCloseEOFillStroke},
151 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
154 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
157 {"cs", 1, {tchkName},
158 &Gfx::opSetFillColorSpace},
159 {"d", 2, {tchkArray, tchkNum},
161 {"d0", 2, {tchkNum, tchkNum},
162 &Gfx::opSetCharWidth},
163 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
165 &Gfx::opSetCacheDevice},
168 {"f*", 0, {tchkNone},
171 &Gfx::opSetFillGray},
172 {"gs", 1, {tchkName},
173 &Gfx::opSetExtGState},
179 &Gfx::opSetLineJoin},
180 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
181 &Gfx::opSetFillCMYKColor},
182 {"l", 2, {tchkNum, tchkNum},
184 {"m", 2, {tchkNum, tchkNum},
190 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
192 {"rg", 3, {tchkNum, tchkNum, tchkNum},
193 &Gfx::opSetFillRGBColor},
194 {"ri", 1, {tchkName},
195 &Gfx::opSetRenderingIntent},
197 &Gfx::opCloseStroke},
198 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
199 &Gfx::opSetFillColor},
200 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
202 &Gfx::opSetFillColorN},
203 {"sh", 1, {tchkName},
205 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
208 &Gfx::opSetLineWidth},
209 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
213 #define numOps (sizeof(opTab) / sizeof(Operator))
215 //------------------------------------------------------------------------
217 //------------------------------------------------------------------------
219 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
224 // build font dictionary
226 resDict->lookup("Font", &obj1);
228 fonts = new GfxFontDict(xref, obj1.getDict());
232 // get XObject dictionary
233 resDict->lookup("XObject", &xObjDict);
235 // get color space dictionary
236 resDict->lookup("ColorSpace", &colorSpaceDict);
238 // get pattern dictionary
239 resDict->lookup("Pattern", &patternDict);
241 // get shading dictionary
242 resDict->lookup("Shading", &shadingDict);
244 // get graphics state parameter dictionary
245 resDict->lookup("ExtGState", &gStateDict);
250 colorSpaceDict.initNull();
251 patternDict.initNull();
252 gStateDict.initNull();
258 GfxResources::~GfxResources() {
263 colorSpaceDict.free();
269 GfxFont *GfxResources::lookupFont(char *name) {
271 GfxResources *resPtr;
273 for (resPtr = this; resPtr; resPtr = resPtr->next) {
275 if ((font = resPtr->fonts->lookup(name)))
279 error(-1, "Unknown font tag '%s'", name);
283 GBool GfxResources::lookupXObject(char *name, Object *obj) {
284 GfxResources *resPtr;
286 for (resPtr = this; resPtr; resPtr = resPtr->next) {
287 if (resPtr->xObjDict.isDict()) {
288 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
293 error(-1, "XObject '%s' is unknown", name);
297 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
298 GfxResources *resPtr;
300 for (resPtr = this; resPtr; resPtr = resPtr->next) {
301 if (resPtr->xObjDict.isDict()) {
302 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
307 error(-1, "XObject '%s' is unknown", name);
311 void GfxResources::lookupColorSpace(char *name, Object *obj) {
312 GfxResources *resPtr;
314 for (resPtr = this; resPtr; resPtr = resPtr->next) {
315 if (resPtr->colorSpaceDict.isDict()) {
316 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
325 GfxPattern *GfxResources::lookupPattern(char *name) {
326 GfxResources *resPtr;
330 for (resPtr = this; resPtr; resPtr = resPtr->next) {
331 if (resPtr->patternDict.isDict()) {
332 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
333 pattern = GfxPattern::parse(&obj);
340 error(-1, "Unknown pattern '%s'", name);
344 GfxShading *GfxResources::lookupShading(char *name) {
345 GfxResources *resPtr;
349 for (resPtr = this; resPtr; resPtr = resPtr->next) {
350 if (resPtr->shadingDict.isDict()) {
351 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
352 shading = GfxShading::parse(&obj);
359 error(-1, "Unknown shading '%s'", name);
363 GBool GfxResources::lookupGState(char *name, Object *obj) {
364 GfxResources *resPtr;
366 for (resPtr = this; resPtr; resPtr = resPtr->next) {
367 if (resPtr->gStateDict.isDict()) {
368 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
374 error(-1, "ExtGState '%s' is unknown", name);
378 //------------------------------------------------------------------------
380 //------------------------------------------------------------------------
382 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
383 PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
384 GBool printCommandsA) {
389 printCommands = printCommandsA;
391 // start the resource stack
392 res = new GfxResources(xref, resDict, NULL);
396 state = new GfxState(dpi, box, rotate, out->upsideDown());
397 fontChanged = gFalse;
400 out->startPage(pageNum, state);
401 out->setDefaultCTM(state->getCTM());
402 out->updateAll(state);
403 for (i = 0; i < 6; ++i) {
404 baseMatrix[i] = state->getCTM()[i];
409 state->moveTo(cropBox->x1, cropBox->y1);
410 state->lineTo(cropBox->x2, cropBox->y1);
411 state->lineTo(cropBox->x2, cropBox->y2);
412 state->lineTo(cropBox->x1, cropBox->y2);
420 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
421 PDFRectangle *box, GBool crop, PDFRectangle *cropBox) {
426 printCommands = gFalse;
428 // start the resource stack
429 res = new GfxResources(xref, resDict, NULL);
433 state = new GfxState(72, box, 0, gFalse);
434 fontChanged = gFalse;
437 for (i = 0; i < 6; ++i) {
438 baseMatrix[i] = state->getCTM()[i];
443 state->moveTo(cropBox->x1, cropBox->y1);
444 state->lineTo(cropBox->x2, cropBox->y1);
445 state->lineTo(cropBox->x2, cropBox->y2);
446 state->lineTo(cropBox->x1, cropBox->y2);
455 while (state->hasSaves()) {
456 state = state->restore();
457 out->restoreState(state);
470 void Gfx::display(Object *obj, GBool topLevel) {
474 if (obj->isArray()) {
475 for (i = 0; i < obj->arrayGetLength(); ++i) {
476 obj->arrayGet(i, &obj2);
477 if (!obj2.isStream()) {
478 error(-1, "Weird page contents");
484 } else if (!obj->isStream()) {
485 error(-1, "Weird page contents");
488 parser = new Parser(xref, new Lexer(xref, obj));
494 void Gfx::go(GBool topLevel) {
496 Object args[maxArgs];
500 // scan a sequence of objects
503 parser->getObj(&obj);
504 while (!obj.isEOF()) {
506 // got a command - execute it
510 for (i = 0; i < numArgs; ++i) {
512 args[i].print(stdout);
517 execOp(&obj, args, numArgs);
519 for (i = 0; i < numArgs; ++i)
523 // periodically update display
524 if (++updateLevel >= 20000) {
529 // got an argument - save it
530 } else if (numArgs < maxArgs) {
531 args[numArgs++] = obj;
533 // too many arguments - something is wrong
535 error(getPos(), "Too many args in content stream");
537 printf("throwing away arg: ");
545 // grab the next object
546 parser->getObj(&obj);
550 // args at end with no command
552 error(getPos(), "Leftover args in content stream");
554 printf("%d leftovers:", numArgs);
555 for (i = 0; i < numArgs; ++i) {
557 args[i].print(stdout);
562 for (i = 0; i < numArgs; ++i)
567 if (topLevel && updateLevel > 0) {
572 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
578 name = cmd->getName();
579 if (!(op = findOp(name))) {
580 if (ignoreUndef == 0)
581 error(getPos(), "Unknown operator '%s'", name);
586 if (op->numArgs >= 0) {
587 if (numArgs != op->numArgs) {
588 error(getPos(), "Wrong number (%d) of args to '%s' operator",
593 if (numArgs > -op->numArgs) {
594 error(getPos(), "Too many (%d) args to '%s' operator",
599 for (i = 0; i < numArgs; ++i) {
600 if (!checkArg(&args[i], op->tchk[i])) {
601 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
602 i, name, args[i].getTypeName());
608 (this->*op->func)(args, numArgs);
611 Operator *Gfx::findOp(char *name) {
616 // invariant: opTab[a] < name < opTab[b]
619 cmp = strcmp(opTab[m].name, name);
632 GBool Gfx::checkArg(Object *arg, TchkType type) {
634 case tchkBool: return arg->isBool();
635 case tchkInt: return arg->isInt();
636 case tchkNum: return arg->isNum();
637 case tchkString: return arg->isString();
638 case tchkName: return arg->isName();
639 case tchkArray: return arg->isArray();
640 case tchkProps: return arg->isDict() || arg->isName();
641 case tchkSCN: return arg->isNum() || arg->isName();
642 case tchkNone: return gFalse;
648 return parser ? parser->getPos() : -1;
651 //------------------------------------------------------------------------
652 // graphics state operators
653 //------------------------------------------------------------------------
655 void Gfx::opSave(Object args[], int numArgs) {
656 out->saveState(state);
657 state = state->save();
660 void Gfx::opRestore(Object args[], int numArgs) {
661 state = state->restore();
662 out->restoreState(state);
665 void Gfx::opConcat(Object args[], int numArgs) {
666 state->concatCTM(args[0].getNum(), args[1].getNum(),
667 args[2].getNum(), args[3].getNum(),
668 args[4].getNum(), args[5].getNum());
669 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
670 args[2].getNum(), args[3].getNum(),
671 args[4].getNum(), args[5].getNum());
675 void Gfx::opSetDash(Object args[], int numArgs) {
682 a = args[0].getArray();
683 length = a->getLength();
687 dash = (double *)gmalloc(length * sizeof(double));
688 for (i = 0; i < length; ++i) {
689 dash[i] = a->get(i, &obj)->getNum();
693 state->setLineDash(dash, length, args[1].getNum());
694 out->updateLineDash(state);
697 void Gfx::opSetFlat(Object args[], int numArgs) {
698 state->setFlatness((int)args[0].getNum());
699 out->updateFlatness(state);
702 void Gfx::opSetLineJoin(Object args[], int numArgs) {
703 state->setLineJoin(args[0].getInt());
704 out->updateLineJoin(state);
707 void Gfx::opSetLineCap(Object args[], int numArgs) {
708 state->setLineCap(args[0].getInt());
709 out->updateLineCap(state);
712 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
713 state->setMiterLimit(args[0].getNum());
714 out->updateMiterLimit(state);
717 void Gfx::opSetLineWidth(Object args[], int numArgs) {
718 state->setLineWidth(args[0].getNum());
719 out->updateLineWidth(state);
722 void Gfx::opSetExtGState(Object args[], int numArgs) {
725 if (!res->lookupGState(args[0].getName(), &obj1)) {
728 if (!obj1.isDict()) {
729 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
733 if (obj1.dictLookup("ca", &obj2)->isNum()) {
734 state->setFillOpacity(obj2.getNum());
735 out->updateFillOpacity(state);
738 if (obj1.dictLookup("CA", &obj2)->isNum()) {
739 state->setStrokeOpacity(obj2.getNum());
740 out->updateStrokeOpacity(state);
746 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
749 //------------------------------------------------------------------------
751 //------------------------------------------------------------------------
753 void Gfx::opSetFillGray(Object args[], int numArgs) {
756 state->setFillPattern(NULL);
757 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
758 color.c[0] = args[0].getNum();
759 state->setFillColor(&color);
760 out->updateFillColor(state);
763 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
766 state->setStrokePattern(NULL);
767 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
768 color.c[0] = args[0].getNum();
769 state->setStrokeColor(&color);
770 out->updateStrokeColor(state);
773 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
777 state->setFillPattern(NULL);
778 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
779 for (i = 0; i < 4; ++i) {
780 color.c[i] = args[i].getNum();
782 state->setFillColor(&color);
783 out->updateFillColor(state);
786 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
790 state->setStrokePattern(NULL);
791 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
792 for (i = 0; i < 4; ++i) {
793 color.c[i] = args[i].getNum();
795 state->setStrokeColor(&color);
796 out->updateStrokeColor(state);
799 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
803 state->setFillPattern(NULL);
804 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
805 for (i = 0; i < 3; ++i) {
806 color.c[i] = args[i].getNum();
808 state->setFillColor(&color);
809 out->updateFillColor(state);
812 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
816 state->setStrokePattern(NULL);
817 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
818 for (i = 0; i < 3; ++i) {
819 color.c[i] = args[i].getNum();
821 state->setStrokeColor(&color);
822 out->updateStrokeColor(state);
825 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
827 GfxColorSpace *colorSpace;
831 state->setFillPattern(NULL);
832 res->lookupColorSpace(args[0].getName(), &obj);
834 colorSpace = GfxColorSpace::parse(&args[0]);
836 colorSpace = GfxColorSpace::parse(&obj);
840 state->setFillColorSpace(colorSpace);
842 error(getPos(), "Bad color space (fill)");
844 for (i = 0; i < gfxColorMaxComps; ++i) {
847 state->setFillColor(&color);
848 out->updateFillColor(state);
851 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
853 GfxColorSpace *colorSpace;
857 state->setStrokePattern(NULL);
858 res->lookupColorSpace(args[0].getName(), &obj);
860 colorSpace = GfxColorSpace::parse(&args[0]);
862 colorSpace = GfxColorSpace::parse(&obj);
866 state->setStrokeColorSpace(colorSpace);
868 error(getPos(), "Bad color space (stroke)");
870 for (i = 0; i < gfxColorMaxComps; ++i) {
873 state->setStrokeColor(&color);
874 out->updateStrokeColor(state);
877 void Gfx::opSetFillColor(Object args[], int numArgs) {
881 state->setFillPattern(NULL);
882 for (i = 0; i < numArgs; ++i) {
883 color.c[i] = args[i].getNum();
885 state->setFillColor(&color);
886 out->updateFillColor(state);
889 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
893 state->setStrokePattern(NULL);
894 for (i = 0; i < numArgs; ++i) {
895 color.c[i] = args[i].getNum();
897 state->setStrokeColor(&color);
898 out->updateStrokeColor(state);
901 void Gfx::opSetFillColorN(Object args[], int numArgs) {
906 if (state->getFillColorSpace()->getMode() == csPattern) {
908 for (i = 0; i < numArgs && i < 4; ++i) {
909 if (args[i].isNum()) {
910 color.c[i] = args[i].getNum();
913 state->setFillColor(&color);
914 out->updateFillColor(state);
916 if (args[numArgs-1].isName() &&
917 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
918 state->setFillPattern(pattern);
922 state->setFillPattern(NULL);
923 for (i = 0; i < numArgs && i < 4; ++i) {
924 if (args[i].isNum()) {
925 color.c[i] = args[i].getNum();
928 state->setFillColor(&color);
929 out->updateFillColor(state);
933 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
938 if (state->getStrokeColorSpace()->getMode() == csPattern) {
940 for (i = 0; i < numArgs && i < 4; ++i) {
941 if (args[i].isNum()) {
942 color.c[i] = args[i].getNum();
945 state->setStrokeColor(&color);
946 out->updateStrokeColor(state);
948 if (args[numArgs-1].isName() &&
949 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
950 state->setStrokePattern(pattern);
954 state->setStrokePattern(NULL);
955 for (i = 0; i < numArgs && i < 4; ++i) {
956 if (args[i].isNum()) {
957 color.c[i] = args[i].getNum();
960 state->setStrokeColor(&color);
961 out->updateStrokeColor(state);
965 //------------------------------------------------------------------------
966 // path segment operators
967 //------------------------------------------------------------------------
969 void Gfx::opMoveTo(Object args[], int numArgs) {
970 state->moveTo(args[0].getNum(), args[1].getNum());
973 void Gfx::opLineTo(Object args[], int numArgs) {
974 if (!state->isCurPt()) {
975 error(getPos(), "No current point in lineto");
978 state->lineTo(args[0].getNum(), args[1].getNum());
981 void Gfx::opCurveTo(Object args[], int numArgs) {
982 double x1, y1, x2, y2, x3, y3;
984 if (!state->isCurPt()) {
985 error(getPos(), "No current point in curveto");
988 x1 = args[0].getNum();
989 y1 = args[1].getNum();
990 x2 = args[2].getNum();
991 y2 = args[3].getNum();
992 x3 = args[4].getNum();
993 y3 = args[5].getNum();
994 state->curveTo(x1, y1, x2, y2, x3, y3);
997 void Gfx::opCurveTo1(Object args[], int numArgs) {
998 double x1, y1, x2, y2, x3, y3;
1000 if (!state->isCurPt()) {
1001 error(getPos(), "No current point in curveto1");
1004 x1 = state->getCurX();
1005 y1 = state->getCurY();
1006 x2 = args[0].getNum();
1007 y2 = args[1].getNum();
1008 x3 = args[2].getNum();
1009 y3 = args[3].getNum();
1010 state->curveTo(x1, y1, x2, y2, x3, y3);
1013 void Gfx::opCurveTo2(Object args[], int numArgs) {
1014 double x1, y1, x2, y2, x3, y3;
1016 if (!state->isCurPt()) {
1017 error(getPos(), "No current point in curveto2");
1020 x1 = args[0].getNum();
1021 y1 = args[1].getNum();
1022 x2 = args[2].getNum();
1023 y2 = args[3].getNum();
1026 state->curveTo(x1, y1, x2, y2, x3, y3);
1029 void Gfx::opRectangle(Object args[], int numArgs) {
1032 x = args[0].getNum();
1033 y = args[1].getNum();
1034 w = args[2].getNum();
1035 h = args[3].getNum();
1036 state->moveTo(x, y);
1037 state->lineTo(x + w, y);
1038 state->lineTo(x + w, y + h);
1039 state->lineTo(x, y + h);
1043 void Gfx::opClosePath(Object args[], int numArgs) {
1044 if (!state->isCurPt()) {
1045 error(getPos(), "No current point in closepath");
1051 //------------------------------------------------------------------------
1052 // path painting operators
1053 //------------------------------------------------------------------------
1055 void Gfx::opEndPath(Object args[], int numArgs) {
1059 void Gfx::opStroke(Object args[], int numArgs) {
1060 if (!state->isCurPt()) {
1061 //error(getPos(), "No path in stroke");
1064 if (state->isPath())
1069 void Gfx::opCloseStroke(Object args[], int numArgs) {
1070 if (!state->isCurPt()) {
1071 //error(getPos(), "No path in closepath/stroke");
1074 if (state->isPath()) {
1081 void Gfx::opFill(Object args[], int numArgs) {
1082 if (!state->isCurPt()) {
1083 //error(getPos(), "No path in fill");
1086 if (state->isPath()) {
1087 if (state->getFillColorSpace()->getMode() == csPattern) {
1088 doPatternFill(gFalse);
1096 void Gfx::opEOFill(Object args[], int numArgs) {
1097 if (!state->isCurPt()) {
1098 //error(getPos(), "No path in eofill");
1101 if (state->isPath()) {
1102 if (state->getFillColorSpace()->getMode() == csPattern) {
1103 doPatternFill(gTrue);
1111 void Gfx::opFillStroke(Object args[], int numArgs) {
1112 if (!state->isCurPt()) {
1113 //error(getPos(), "No path in fill/stroke");
1116 if (state->isPath()) {
1117 if (state->getFillColorSpace()->getMode() == csPattern) {
1118 doPatternFill(gFalse);
1127 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1128 if (!state->isCurPt()) {
1129 //error(getPos(), "No path in closepath/fill/stroke");
1132 if (state->isPath()) {
1134 if (state->getFillColorSpace()->getMode() == csPattern) {
1135 doPatternFill(gFalse);
1144 void Gfx::opEOFillStroke(Object args[], int numArgs) {
1145 if (!state->isCurPt()) {
1146 //error(getPos(), "No path in eofill/stroke");
1149 if (state->isPath()) {
1150 if (state->getFillColorSpace()->getMode() == csPattern) {
1151 doPatternFill(gTrue);
1160 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1161 if (!state->isCurPt()) {
1162 //error(getPos(), "No path in closepath/eofill/stroke");
1165 if (state->isPath()) {
1167 if (state->getFillColorSpace()->getMode() == csPattern) {
1168 doPatternFill(gTrue);
1177 void Gfx::doPatternFill(GBool eoFill) {
1178 GfxPatternColorSpace *patCS;
1179 GfxPattern *pattern;
1180 GfxTilingPattern *tPat;
1182 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1183 double cxMin, cyMin, cxMax, cyMax;
1184 int xi0, yi0, xi1, yi1, xi, yi;
1185 double *ctm, *btm, *ptm;
1186 double m[6], ictm[6], m1[6], imb[6];
1188 double xstep, ystep;
1191 // this is a bit of a kludge -- patterns can be really slow, so we
1192 // skip them if we're only doing text extraction, since they almost
1193 // certainly don't contain any text
1194 if (!out->needNonText()) {
1199 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1202 if (!(pattern = state->getFillPattern())) {
1205 if (pattern->getType() != 1) {
1208 tPat = (GfxTilingPattern *)pattern;
1210 // construct a (pattern space) -> (current space) transform matrix
1211 ctm = state->getCTM();
1213 ptm = tPat->getMatrix();
1214 // iCTM = invert CTM
1215 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1216 ictm[0] = ctm[3] * det;
1217 ictm[1] = -ctm[1] * det;
1218 ictm[2] = -ctm[2] * det;
1219 ictm[3] = ctm[0] * det;
1220 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1221 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1222 // m1 = PTM * BTM = PTM * base transform matrix
1223 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1224 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1225 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1226 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1227 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1228 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1229 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1230 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1231 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1232 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1233 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1234 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1235 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1237 // construct a (base space) -> (pattern space) transform matrix
1238 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1239 imb[0] = m1[3] * det;
1240 imb[1] = -m1[1] * det;
1241 imb[2] = -m1[2] * det;
1242 imb[3] = m1[0] * det;
1243 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1244 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1246 // save current graphics state
1247 out->saveState(state);
1248 state = state->save();
1250 // set underlying color space (for uncolored tiling patterns)
1251 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1252 state->setFillColorSpace(cs->copy());
1254 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1256 state->setFillPattern(NULL);
1257 out->updateFillColor(state);
1259 // clip to current path
1268 // transform clip region bbox to pattern space
1269 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1270 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1271 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1272 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1273 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1276 } else if (x1 > xMax) {
1281 } else if (y1 > yMax) {
1284 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1285 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1288 } else if (x1 > xMax) {
1293 } else if (y1 > yMax) {
1296 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1297 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1300 } else if (x1 > xMax) {
1305 } else if (y1 > yMax) {
1310 //~ this should treat negative steps differently -- start at right/top
1311 //~ edge instead of left/bottom (?)
1312 xstep = fabs(tPat->getXStep());
1313 ystep = fabs(tPat->getYStep());
1314 xi0 = (int)floor(xMin / xstep);
1315 xi1 = (int)ceil(xMax / xstep);
1316 yi0 = (int)floor(yMin / ystep);
1317 yi1 = (int)ceil(yMax / ystep);
1318 for (i = 0; i < 4; ++i) {
1321 for (yi = yi0; yi < yi1; ++yi) {
1322 for (xi = xi0; xi < xi1; ++xi) {
1325 m1[4] = x * m[0] + y * m[2] + m[4];
1326 m1[5] = x * m[1] + y * m[3] + m[5];
1327 doForm1(tPat->getContentStream(), tPat->getResDict(),
1328 m1, tPat->getBBox());
1332 // restore graphics state
1333 state = state->restore();
1334 out->restoreState(state);
1337 void Gfx::opShFill(Object args[], int numArgs) {
1338 GfxShading *shading;
1339 double xMin, yMin, xMax, yMax;
1341 if (!(shading = res->lookupShading(args[0].getName()))) {
1345 // save current graphics state
1346 out->saveState(state);
1347 state = state->save();
1350 if (shading->getHasBBox()) {
1351 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1352 state->moveTo(xMin, yMin);
1353 state->lineTo(xMax, yMin);
1354 state->lineTo(xMax, yMax);
1355 state->lineTo(xMin, yMax);
1362 // set the color space
1363 state->setFillColorSpace(shading->getColorSpace()->copy());
1365 // do shading type-specific operations
1366 switch (shading->getType()) {
1368 doAxialShFill((GfxAxialShading *)shading);
1371 doRadialShFill((GfxRadialShading *)shading);
1375 // restore graphics state
1376 state = state->restore();
1377 out->restoreState(state);
1382 void Gfx::doAxialShFill(GfxAxialShading *shading) {
1383 double xMin, yMin, xMax, yMax;
1384 double x0, y0, x1, y1;
1386 double tMin, tMax, t, tx, ty;
1387 double s[4], sMin, sMax, tmp;
1388 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1390 double ta[axialMaxSplits + 1];
1391 int next[axialMaxSplits + 1];
1392 GfxColor color0, color1;
1396 // get the clip region bbox
1397 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1399 // compute min and max t values, based on the four corners of the
1401 shading->getCoords(&x0, &y0, &x1, &y1);
1404 mul = 1 / (dx * dx + dy * dy);
1405 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1406 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1409 } else if (t > tMax) {
1412 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1415 } else if (t > tMax) {
1418 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1421 } else if (t > tMax) {
1424 if (tMin < 0 && !shading->getExtend0()) {
1427 if (tMax > 1 && !shading->getExtend1()) {
1431 // get the function domain
1432 t0 = shading->getDomain0();
1433 t1 = shading->getDomain1();
1435 // Traverse the t axis and do the shading.
1437 // For each point (tx, ty) on the t axis, consider a line through
1438 // that point perpendicular to the t axis:
1440 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1441 // y(s) = ty + s * dx --> s = (y - ty) / dx
1443 // Then look at the intersection of this line with the bounding box
1444 // (xMin, yMin, xMax, yMax). In the general case, there are four
1445 // intersection points:
1447 // s0 = (xMin - tx) / -dy
1448 // s1 = (xMax - tx) / -dy
1449 // s2 = (yMin - ty) / dx
1450 // s3 = (yMax - ty) / dx
1452 // and we want the middle two s values.
1454 // In the case where dx = 0, take s0 and s1; in the case where dy =
1455 // 0, take s2 and s3.
1457 // Each filled polygon is bounded by two of these line segments
1458 // perpdendicular to the t axis.
1460 // The t axis is bisected into smaller regions until the color
1461 // difference across a region is small enough, and then the region
1462 // is painted with a single color.
1465 nComps = shading->getColorSpace()->getNComps();
1467 ta[axialMaxSplits] = tMax;
1468 next[0] = axialMaxSplits;
1470 // compute the color at t = tMin
1473 } else if (tMin > 1) {
1476 tt = t0 + (t1 - t0) * tMin;
1478 shading->getColor(tt, &color0);
1480 // compute the coordinates of the point on the t axis at t = tMin;
1481 // then compute the intersection of the perpendicular line with the
1483 tx = x0 + tMin * dx;
1484 ty = y0 + tMin * dy;
1485 if (dx == 0 && dy == 0) {
1488 sMin = (xMin - tx) / -dy;
1489 sMax = (xMax - tx) / -dy;
1490 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1491 } else if (dy == 0) {
1492 sMin = (yMin - ty) / dx;
1493 sMax = (yMax - ty) / dx;
1494 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1496 s[0] = (yMin - ty) / dx;
1497 s[1] = (yMax - ty) / dx;
1498 s[2] = (xMin - tx) / -dy;
1499 s[3] = (xMax - tx) / -dy;
1500 for (j = 0; j < 3; ++j) {
1502 for (k = j + 1; k < 4; ++k) {
1507 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1512 ux0 = tx - sMin * dy;
1513 uy0 = ty + sMin * dx;
1514 vx0 = tx - sMax * dy;
1515 vy0 = ty + sMax * dx;
1518 while (i < axialMaxSplits) {
1520 // bisect until color difference is small enough or we hit the
1526 } else if (ta[j] > 1) {
1529 tt = t0 + (t1 - t0) * ta[j];
1531 shading->getColor(tt, &color1);
1532 for (k = 0; k < nComps; ++k) {
1533 if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1541 ta[k] = 0.5 * (ta[i] + ta[j]);
1547 // use the average of the colors of the two sides of the region
1548 for (k = 0; k < nComps; ++k) {
1549 color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
1552 // compute the coordinates of the point on the t axis; then
1553 // compute the intersection of the perpendicular line with the
1555 tx = x0 + ta[j] * dx;
1556 ty = y0 + ta[j] * dy;
1557 if (dx == 0 && dy == 0) {
1560 sMin = (xMin - tx) / -dy;
1561 sMax = (xMax - tx) / -dy;
1562 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1563 } else if (dy == 0) {
1564 sMin = (yMin - ty) / dx;
1565 sMax = (yMax - ty) / dx;
1566 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1568 s[0] = (yMin - ty) / dx;
1569 s[1] = (yMax - ty) / dx;
1570 s[2] = (xMin - tx) / -dy;
1571 s[3] = (xMax - tx) / -dy;
1572 for (j = 0; j < 3; ++j) {
1574 for (k = j + 1; k < 4; ++k) {
1579 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1584 ux1 = tx - sMin * dy;
1585 uy1 = ty + sMin * dx;
1586 vx1 = tx - sMax * dy;
1587 vy1 = ty + sMax * dx;
1590 state->setFillColor(&color0);
1591 out->updateFillColor(state);
1594 state->moveTo(ux0, uy0);
1595 state->lineTo(vx0, vy0);
1596 state->lineTo(vx1, vy1);
1597 state->lineTo(ux1, uy1);
1602 // set up for next region
1612 void Gfx::doRadialShFill(GfxRadialShading *shading) {
1613 double sMin, sMax, xMin, yMin, xMax, yMax;
1614 double x0, y0, r0, x1, y1, r1, t0, t1;
1616 GfxColor colorA, colorB;
1617 double xa, ya, xb, yb, ra, rb;
1618 double ta, tb, sa, sb;
1623 // get the shading info
1624 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1625 t0 = shading->getDomain0();
1626 t1 = shading->getDomain1();
1627 nComps = shading->getColorSpace()->getNComps();
1629 // compute the (possibly extended) s range
1632 if (shading->getExtend0()) {
1634 // extend the smaller end
1635 sMin = -r0 / (r1 - r0);
1637 // extend the larger end
1638 //~ this computes the diagonal of the bounding box -- we should
1639 //~ really compute the intersection of the moving/expanding
1640 //~ circles with each of the four corners and look for the max
1642 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1643 sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
1644 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1647 } else if (sMin < -20) {
1653 if (shading->getExtend1()) {
1655 // extend the smaller end
1656 sMax = -r0 / (r1 - r0);
1657 } else if (r1 > r0) {
1658 // extend the larger end
1659 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1660 sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
1661 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1664 } else if (sMax > 20) {
1671 // compute the number of steps into which circles must be divided to
1672 // achieve a curve flatness of 0.1 pixel in device space for the
1673 // largest circle (note that "device space" is 72 dpi when generating
1674 // PostScript, hence the relatively small 0.1 pixel accuracy)
1675 ctm = state->getCTM();
1677 if (fabs(ctm[1]) > t) {
1680 if (fabs(ctm[2]) > t) {
1683 if (fabs(ctm[3]) > t) {
1694 n = (int)(M_PI / acos(1 - 0.1 / t));
1697 } else if (n > 200) {
1702 // Traverse the t axis and do the shading.
1704 // This generates and fills a series of rings. Each ring is defined
1706 // sa, ta, xa, ya, ra, colorA
1707 // sb, tb, xb, yb, rb, colorB
1709 // The s/t axis is divided into radialMaxSplits parts; these parts
1710 // are combined as much as possible while respecting the
1711 // radialColorDelta parameter.
1713 // setup for the start circle
1716 ta = t0 + sa * (t1 - t0);
1717 xa = x0 + sa * (x1 - x0);
1718 ya = y0 + sa * (y1 - y0);
1719 ra = r0 + sa * (r1 - r0);
1721 shading->getColor(t0, &colorA);
1722 } else if (ta > t1) {
1723 shading->getColor(t1, &colorA);
1725 shading->getColor(ta, &colorA);
1728 while (ia < radialMaxSplits) {
1730 // go as far along the t axis (toward t1) as we can, such that the
1731 // color difference is within the tolerance (radialColorDelta) --
1732 // this uses bisection (between the current value, t, and t1),
1733 // limited to radialMaxSplits points along the t axis
1734 ib = radialMaxSplits;
1735 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
1736 tb = t0 + sb * (t1 - t0);
1738 shading->getColor(t0, &colorB);
1739 } else if (tb > t1) {
1740 shading->getColor(t1, &colorB);
1742 shading->getColor(tb, &colorB);
1744 while (ib - ia > 1) {
1745 for (k = 0; k < nComps; ++k) {
1746 if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
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);
1765 // compute center and radius of the circle
1766 xb = x0 + sb * (x1 - x0);
1767 yb = y0 + sb * (y1 - y0);
1768 rb = r0 + sb * (r1 - r0);
1770 // use the average of the colors at the two circles
1771 for (k = 0; k < nComps; ++k) {
1772 colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
1774 state->setFillColor(&colorA);
1775 out->updateFillColor(state);
1777 // construct path for first circle
1778 state->moveTo(xa + ra, ya);
1779 for (k = 1; k < n; ++k) {
1780 angle = ((double)k / (double)n) * 2 * M_PI;
1781 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
1785 // construct and append path for second circle
1786 state->moveTo(xb + rb, yb);
1787 for (k = 1; k < n; ++k) {
1788 angle = ((double)k / (double)n) * 2 * M_PI;
1789 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
1797 // step to the next value of t
1808 void Gfx::doEndPath() {
1809 if (state->isPath() && clip != clipNone) {
1811 if (clip == clipNormal) {
1821 //------------------------------------------------------------------------
1822 // path clipping operators
1823 //------------------------------------------------------------------------
1825 void Gfx::opClip(Object args[], int numArgs) {
1829 void Gfx::opEOClip(Object args[], int numArgs) {
1833 //------------------------------------------------------------------------
1834 // text object operators
1835 //------------------------------------------------------------------------
1837 void Gfx::opBeginText(Object args[], int numArgs) {
1838 state->setTextMat(1, 0, 0, 1, 0, 0);
1839 state->textMoveTo(0, 0);
1840 out->updateTextMat(state);
1841 out->updateTextPos(state);
1842 fontChanged = gTrue;
1845 void Gfx::opEndText(Object args[], int numArgs) {
1848 //------------------------------------------------------------------------
1849 // text state operators
1850 //------------------------------------------------------------------------
1852 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
1853 state->setCharSpace(args[0].getNum());
1854 out->updateCharSpace(state);
1857 void Gfx::opSetFont(Object args[], int numArgs) {
1860 if (!(font = res->lookupFont(args[0].getName()))) {
1863 if (printCommands) {
1864 printf(" font: tag=%s name='%s' %g\n",
1865 font->getTag()->getCString(),
1866 font->getName() ? font->getName()->getCString() : "???",
1870 state->setFont(font, args[1].getNum());
1871 fontChanged = gTrue;
1874 void Gfx::opSetTextLeading(Object args[], int numArgs) {
1875 state->setLeading(args[0].getNum());
1878 void Gfx::opSetTextRender(Object args[], int numArgs) {
1879 state->setRender(args[0].getInt());
1880 out->updateRender(state);
1883 void Gfx::opSetTextRise(Object args[], int numArgs) {
1884 state->setRise(args[0].getNum());
1885 out->updateRise(state);
1888 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
1889 state->setWordSpace(args[0].getNum());
1890 out->updateWordSpace(state);
1893 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
1894 state->setHorizScaling(args[0].getNum());
1895 out->updateHorizScaling(state);
1896 fontChanged = gTrue;
1899 //------------------------------------------------------------------------
1900 // text positioning operators
1901 //------------------------------------------------------------------------
1903 void Gfx::opTextMove(Object args[], int numArgs) {
1906 tx = state->getLineX() + args[0].getNum();
1907 ty = state->getLineY() + args[1].getNum();
1908 state->textMoveTo(tx, ty);
1909 out->updateTextPos(state);
1912 void Gfx::opTextMoveSet(Object args[], int numArgs) {
1915 tx = state->getLineX() + args[0].getNum();
1916 ty = args[1].getNum();
1917 state->setLeading(-ty);
1918 ty += state->getLineY();
1919 state->textMoveTo(tx, ty);
1920 out->updateTextPos(state);
1923 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
1924 state->setTextMat(args[0].getNum(), args[1].getNum(),
1925 args[2].getNum(), args[3].getNum(),
1926 args[4].getNum(), args[5].getNum());
1927 state->textMoveTo(0, 0);
1928 out->updateTextMat(state);
1929 out->updateTextPos(state);
1930 fontChanged = gTrue;
1933 void Gfx::opTextNextLine(Object args[], int numArgs) {
1936 tx = state->getLineX();
1937 ty = state->getLineY() - state->getLeading();
1938 state->textMoveTo(tx, ty);
1939 out->updateTextPos(state);
1942 //------------------------------------------------------------------------
1943 // text string operators
1944 //------------------------------------------------------------------------
1946 void Gfx::opShowText(Object args[], int numArgs) {
1947 if (!state->getFont()) {
1948 error(getPos(), "No font in show");
1951 doShowText(args[0].getString());
1954 void Gfx::opMoveShowText(Object args[], int numArgs) {
1957 if (!state->getFont()) {
1958 error(getPos(), "No font in move/show");
1961 tx = state->getLineX();
1962 ty = state->getLineY() - state->getLeading();
1963 state->textMoveTo(tx, ty);
1964 out->updateTextPos(state);
1965 doShowText(args[0].getString());
1968 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1971 if (!state->getFont()) {
1972 error(getPos(), "No font in move/set/show");
1975 state->setWordSpace(args[0].getNum());
1976 state->setCharSpace(args[1].getNum());
1977 tx = state->getLineX();
1978 ty = state->getLineY() - state->getLeading();
1979 state->textMoveTo(tx, ty);
1980 out->updateWordSpace(state);
1981 out->updateCharSpace(state);
1982 out->updateTextPos(state);
1983 doShowText(args[2].getString());
1986 void Gfx::opShowSpaceText(Object args[], int numArgs) {
1992 if (!state->getFont()) {
1993 error(getPos(), "No font in show/space");
1996 wMode = state->getFont()->getWMode();
1997 a = args[0].getArray();
1998 for (i = 0; i < a->getLength(); ++i) {
2002 state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
2004 state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
2006 out->updateTextShift(state, obj.getNum());
2007 } else if (obj.isString()) {
2008 doShowText(obj.getString());
2010 error(getPos(), "Element of show/space array must be number or string");
2016 void Gfx::doShowText(GString *s) {
2019 double riseX, riseY;
2022 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy;
2023 double originX, originY, tOriginX, tOriginY;
2024 double oldCTM[6], newCTM[6];
2030 int len, n, uLen, nChars, nSpaces, i;
2033 out->updateFont(state);
2034 fontChanged = gFalse;
2036 font = state->getFont();
2037 wMode = font->getWMode();
2039 if (out->useDrawChar()) {
2040 out->beginString(state, s);
2043 // handle a Type 3 char
2044 if (font->getType() == fontType3 && out->interpretType3Chars()) {
2045 mat = state->getCTM();
2046 for (i = 0; i < 6; ++i) {
2049 mat = state->getTextMat();
2050 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2051 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2052 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2053 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2054 mat = font->getFontMatrix();
2055 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2056 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2057 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2058 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2059 newCTM[0] *= state->getFontSize();
2060 newCTM[3] *= state->getFontSize();
2061 newCTM[0] *= state->getHorizScaling();
2062 newCTM[2] *= state->getHorizScaling();
2063 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2064 curX = state->getCurX();
2065 curY = state->getCurY();
2067 p = s->getCString();
2068 len = s->getLength();
2070 n = font->getNextChar(p, len, &code,
2071 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2072 &dx, &dy, &originX, &originY);
2073 dx = dx * state->getFontSize() + state->getCharSpace();
2074 if (n == 1 && *p == ' ') {
2075 dx += state->getWordSpace();
2077 dx *= state->getHorizScaling();
2078 dy *= state->getFontSize();
2079 state->textTransformDelta(dx, dy, &tdx, &tdy);
2080 state->transform(curX + riseX, curY + riseY, &x, &y);
2081 out->saveState(state);
2082 state = state->save();
2083 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2084 //~ out->updateCTM(???)
2085 if (!out->beginType3Char(state, code, u, uLen)) {
2086 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2087 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2088 pushResources(resDict);
2090 if (charProc.isStream()) {
2091 display(&charProc, gFalse);
2093 error(getPos(), "Missing or bad Type3 CharProc entry");
2095 out->endType3Char(state);
2101 state = state->restore();
2102 out->restoreState(state);
2103 // GfxState::restore() does *not* restore the current position,
2104 // so we track it here with (curX, curY)
2107 state->moveTo(curX, curY);
2113 } else if (out->useDrawChar()) {
2114 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2115 p = s->getCString();
2116 len = s->getLength();
2118 n = font->getNextChar(p, len, &code,
2119 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2120 &dx, &dy, &originX, &originY);
2122 dx *= state->getFontSize();
2123 dy = dy * state->getFontSize() + state->getCharSpace();
2124 if (n == 1 && *p == ' ') {
2125 dy += state->getWordSpace();
2128 dx = dx * state->getFontSize() + state->getCharSpace();
2129 if (n == 1 && *p == ' ') {
2130 dx += state->getWordSpace();
2132 dx *= state->getHorizScaling();
2133 dy *= state->getFontSize();
2135 state->textTransformDelta(dx, dy, &tdx, &tdy);
2136 originX *= state->getFontSize();
2137 originY *= state->getFontSize();
2138 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2139 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2140 tdx, tdy, tOriginX, tOriginY, code, u, uLen);
2141 state->shift(tdx, tdy);
2148 p = s->getCString();
2149 len = s->getLength();
2150 nChars = nSpaces = 0;
2152 n = font->getNextChar(p, len, &code,
2153 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2154 &dx2, &dy2, &originX, &originY);
2157 if (n == 1 && *p == ' ') {
2165 dx *= state->getFontSize();
2166 dy = dy * state->getFontSize()
2167 + nChars * state->getCharSpace()
2168 + nSpaces * state->getWordSpace();
2170 dx = dx * state->getFontSize()
2171 + nChars * state->getCharSpace()
2172 + nSpaces * state->getWordSpace();
2173 dx *= state->getHorizScaling();
2174 dy *= state->getFontSize();
2176 state->textTransformDelta(dx, dy, &tdx, &tdy);
2177 out->drawString(state, s);
2178 state->shift(tdx, tdy);
2181 if (out->useDrawChar()) {
2182 out->endString(state);
2185 updateLevel += 10 * s->getLength();
2188 //------------------------------------------------------------------------
2189 // XObject operators
2190 //------------------------------------------------------------------------
2192 void Gfx::opXObject(Object args[], int numArgs) {
2193 Object obj1, obj2, obj3, refObj;
2198 if (!res->lookupXObject(args[0].getName(), &obj1)) {
2201 if (!obj1.isStream()) {
2202 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2207 obj1.streamGetDict()->lookup("OPI", &opiDict);
2208 if (opiDict.isDict()) {
2209 out->opiBegin(state, opiDict.getDict());
2212 obj1.streamGetDict()->lookup("Subtype", &obj2);
2213 if (obj2.isName("Image")) {
2214 res->lookupXObjectNF(args[0].getName(), &refObj);
2215 doImage(&refObj, obj1.getStream(), gFalse);
2217 } else if (obj2.isName("Form")) {
2219 } else if (obj2.isName("PS")) {
2220 obj1.streamGetDict()->lookup("Level1", &obj3);
2221 out->psXObject(obj1.getStream(),
2222 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2223 } else if (obj2.isName()) {
2224 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2226 error(getPos(), "XObject subtype is missing or wrong type");
2230 if (opiDict.isDict()) {
2231 out->opiEnd(state, opiDict.getDict());
2238 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2244 GfxColorSpace *colorSpace;
2245 GfxImageColorMap *colorMap;
2248 int maskColors[2*gfxColorMaxComps];
2253 dict = str->getDict();
2256 dict->lookup("Width", &obj1);
2257 if (obj1.isNull()) {
2259 dict->lookup("W", &obj1);
2263 width = obj1.getInt();
2265 dict->lookup("Height", &obj1);
2266 if (obj1.isNull()) {
2268 dict->lookup("H", &obj1);
2272 height = obj1.getInt();
2276 dict->lookup("ImageMask", &obj1);
2277 if (obj1.isNull()) {
2279 dict->lookup("IM", &obj1);
2283 mask = obj1.getBool();
2284 else if (!obj1.isNull())
2289 dict->lookup("BitsPerComponent", &obj1);
2290 if (obj1.isNull()) {
2292 dict->lookup("BPC", &obj1);
2296 bits = obj1.getInt();
2302 // check for inverted mask
2306 dict->lookup("Decode", &obj1);
2307 if (obj1.isNull()) {
2309 dict->lookup("D", &obj1);
2311 if (obj1.isArray()) {
2312 obj1.arrayGet(0, &obj2);
2313 if (obj2.isInt() && obj2.getInt() == 1)
2316 } else if (!obj1.isNull()) {
2322 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2326 // get color space and color map
2327 dict->lookup("ColorSpace", &obj1);
2328 if (obj1.isNull()) {
2330 dict->lookup("CS", &obj1);
2332 if (obj1.isName()) {
2333 res->lookupColorSpace(obj1.getName(), &obj2);
2334 if (!obj2.isNull()) {
2341 colorSpace = GfxColorSpace::parse(&obj1);
2346 dict->lookup("Decode", &obj1);
2347 if (obj1.isNull()) {
2349 dict->lookup("D", &obj1);
2351 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2353 if (!colorMap->isOk()) {
2360 dict->lookup("Mask", &maskObj);
2361 if (maskObj.isArray()) {
2362 for (i = 0; i < maskObj.arrayGetLength(); ++i) {
2363 maskObj.arrayGet(i, &obj1);
2364 maskColors[i] = obj1.getInt();
2371 out->drawImage(state, ref, str, width, height, colorMap,
2372 haveMask ? maskColors : (int *)NULL, inlineImg);
2378 if ((i = width * height) > 1000) {
2388 error(getPos(), "Bad image parameters");
2391 void Gfx::doForm(Object *str) {
2393 Object matrixObj, bboxObj;
2394 double m[6], bbox[6];
2401 dict = str->streamGetDict();
2404 dict->lookup("FormType", &obj1);
2405 if (!(obj1.isInt() && obj1.getInt() == 1)) {
2406 error(getPos(), "Unknown form type");
2411 dict->lookup("BBox", &bboxObj);
2412 if (!bboxObj.isArray()) {
2415 error(getPos(), "Bad form bounding box");
2418 for (i = 0; i < 4; ++i) {
2419 bboxObj.arrayGet(i, &obj1);
2420 bbox[i] = obj1.getNum();
2426 dict->lookup("Matrix", &matrixObj);
2427 if (matrixObj.isArray()) {
2428 for (i = 0; i < 6; ++i) {
2429 matrixObj.arrayGet(i, &obj1);
2430 m[i] = obj1.getNum();
2441 dict->lookup("Resources", &resObj);
2442 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2445 doForm1(str, resDict, m, bbox);
2450 void Gfx::doAnnot(Object *str, double xMin, double yMin,
2451 double xMax, double yMax) {
2452 Dict *dict, *resDict;
2453 Object matrixObj, bboxObj, resObj;
2455 double m[6], bbox[6], ictm[6];
2457 double formX0, formY0, formX1, formY1;
2458 double annotX0, annotY0, annotX1, annotY1;
2459 double det, x, y, sx, sy;
2463 dict = str->streamGetDict();
2465 // get the form bounding box
2466 dict->lookup("BBox", &bboxObj);
2467 if (!bboxObj.isArray()) {
2469 error(getPos(), "Bad form bounding box");
2472 for (i = 0; i < 4; ++i) {
2473 bboxObj.arrayGet(i, &obj1);
2474 bbox[i] = obj1.getNum();
2479 // get the form matrix
2480 dict->lookup("Matrix", &matrixObj);
2481 if (matrixObj.isArray()) {
2482 for (i = 0; i < 6; ++i) {
2483 matrixObj.arrayGet(i, &obj1);
2484 m[i] = obj1.getNum();
2494 // transform the form bbox from form space to user space
2495 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
2496 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
2497 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
2498 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
2500 // transform the annotation bbox from default user space to user
2501 // space: (bbox * baseMatrix) * iCTM
2502 ctm = state->getCTM();
2503 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2504 ictm[0] = ctm[3] * det;
2505 ictm[1] = -ctm[1] * det;
2506 ictm[2] = -ctm[2] * det;
2507 ictm[3] = ctm[0] * det;
2508 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2509 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2510 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
2511 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
2512 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
2513 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
2514 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
2515 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
2516 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
2517 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
2519 // swap min/max coords
2520 if (formX0 > formX1) {
2521 x = formX0; formX0 = formX1; formX1 = x;
2523 if (formY0 > formY1) {
2524 y = formY0; formY0 = formY1; formY1 = y;
2526 if (annotX0 > annotX1) {
2527 x = annotX0; annotX0 = annotX1; annotX1 = x;
2529 if (annotY0 > annotY1) {
2530 y = annotY0; annotY0 = annotY1; annotY1 = y;
2533 // scale the form to fit the annotation bbox
2534 if (formX1 == formX0) {
2535 // this shouldn't happen
2538 sx = (annotX1 - annotX0) / (formX1 - formX0);
2540 if (formY1 == formY0) {
2541 // this shouldn't happen
2544 sy = (annotY1 - annotY0) / (formY1 - formY0);
2548 m[4] = (m[4] - formX0) * sx + annotX0;
2551 m[5] = (m[5] - formY0) * sy + annotY0;
2554 dict->lookup("Resources", &resObj);
2555 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2558 doForm1(str, resDict, m, bbox);
2564 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
2566 double oldBaseMatrix[6];
2569 // push new resources on stack
2570 pushResources(resDict);
2572 // save current graphics state
2573 out->saveState(state);
2574 state = state->save();
2576 // save current parser
2579 // set form transformation matrix
2580 state->concatCTM(matrix[0], matrix[1], matrix[2],
2581 matrix[3], matrix[4], matrix[5]);
2582 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2583 matrix[3], matrix[4], matrix[5]);
2585 // set new base matrix
2586 for (i = 0; i < 6; ++i) {
2587 oldBaseMatrix[i] = baseMatrix[i];
2588 baseMatrix[i] = state->getCTM()[i];
2591 // set form bounding box
2592 state->moveTo(bbox[0], bbox[1]);
2593 state->lineTo(bbox[2], bbox[1]);
2594 state->lineTo(bbox[2], bbox[3]);
2595 state->lineTo(bbox[0], bbox[3]);
2602 display(str, gFalse);
2604 // restore base matrix
2605 for (i = 0; i < 6; ++i) {
2606 baseMatrix[i] = oldBaseMatrix[i];
2612 // restore graphics state
2613 state = state->restore();
2614 out->restoreState(state);
2616 // pop resource stack
2622 void Gfx::pushResources(Dict *resDict) {
2623 res = new GfxResources(xref, resDict, res);
2626 void Gfx::popResources() {
2627 GfxResources *resPtr;
2629 resPtr = res->getNext();
2634 //------------------------------------------------------------------------
2635 // in-line image operators
2636 //------------------------------------------------------------------------
2638 void Gfx::opBeginImage(Object args[], int numArgs) {
2642 // build dict/stream
2643 str = buildImageStream();
2645 // display the image
2647 doImage(NULL, str, gTrue);
2650 c1 = str->getBaseStream()->getChar();
2651 c2 = str->getBaseStream()->getChar();
2652 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2654 c2 = str->getBaseStream()->getChar();
2660 Stream *Gfx::buildImageStream() {
2667 dict.initDict(xref);
2668 parser->getObj(&obj);
2669 while (!obj.isCmd("ID") && !obj.isEOF()) {
2670 if (!obj.isName()) {
2671 error(getPos(), "Inline image dictionary key must be a name object");
2674 key = copyString(obj.getName());
2676 parser->getObj(&obj);
2677 if (obj.isEOF() || obj.isError()) {
2681 dict.dictAdd(key, &obj);
2683 parser->getObj(&obj);
2686 error(getPos(), "End of file in inline image");
2694 str = new EmbedStream(parser->getStream(), &dict);
2695 str = str->addFilters(&dict);
2700 void Gfx::opImageData(Object args[], int numArgs) {
2701 error(getPos(), "Internal: got 'ID' operator");
2704 void Gfx::opEndImage(Object args[], int numArgs) {
2705 error(getPos(), "Internal: got 'EI' operator");
2708 //------------------------------------------------------------------------
2709 // type 3 font operators
2710 //------------------------------------------------------------------------
2712 void Gfx::opSetCharWidth(Object args[], int numArgs) {
2713 out->type3D0(state, args[0].getNum(), args[1].getNum());
2716 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
2717 out->type3D1(state, args[0].getNum(), args[1].getNum(),
2718 args[2].getNum(), args[3].getNum(),
2719 args[4].getNum(), args[5].getNum());
2722 //------------------------------------------------------------------------
2723 // compatibility operators
2724 //------------------------------------------------------------------------
2726 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
2730 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
2731 if (ignoreUndef > 0)
2735 //------------------------------------------------------------------------
2736 // marked content operators
2737 //------------------------------------------------------------------------
2739 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
2740 if (printCommands) {
2741 printf(" marked content: %s ", args[0].getName());
2743 args[2].print(stdout);
2749 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
2752 void Gfx::opMarkPoint(Object args[], int numArgs) {
2753 if (printCommands) {
2754 printf(" mark point: %s ", args[0].getName());
2756 args[2].print(stdout);