1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
25 #include "OutputDev.h"
30 //------------------------------------------------------------------------
32 //------------------------------------------------------------------------
34 Operator Gfx::opTab[] = {
35 {"\"", 3, {tchkNum, tchkNum, tchkString},
36 &Gfx::opMoveSetShowText},
37 {"'", 1, {tchkString},
38 &Gfx::opMoveShowText},
42 &Gfx::opEOFillStroke},
43 {"BDC", 2, {tchkName, tchkProps},
44 &Gfx::opBeginMarkedContent},
47 {"BMC", 1, {tchkName},
48 &Gfx::opBeginMarkedContent},
52 &Gfx::opBeginIgnoreUndef},
54 &Gfx::opSetStrokeColorSpace},
55 {"DP", 2, {tchkName, tchkProps},
61 {"EMC", 0, {tchkNone},
62 &Gfx::opEndMarkedContent},
66 &Gfx::opEndIgnoreUndef},
70 &Gfx::opSetStrokeGray},
75 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
76 &Gfx::opSetStrokeCMYKColor},
78 &Gfx::opSetMiterLimit},
83 {"RG", 3, {tchkNum, tchkNum, tchkNum},
84 &Gfx::opSetStrokeRGBColor},
87 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
88 &Gfx::opSetStrokeColor},
89 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
91 &Gfx::opSetStrokeColorN},
93 &Gfx::opTextNextLine},
94 {"TD", 2, {tchkNum, tchkNum},
96 {"TJ", 1, {tchkArray},
97 &Gfx::opShowSpaceText},
99 &Gfx::opSetTextLeading},
101 &Gfx::opSetCharSpacing},
102 {"Td", 2, {tchkNum, tchkNum},
104 {"Tf", 2, {tchkName, tchkNum},
106 {"Tj", 1, {tchkString},
108 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
110 &Gfx::opSetTextMatrix},
112 &Gfx::opSetTextRender},
114 &Gfx::opSetTextRise},
116 &Gfx::opSetWordSpacing},
118 &Gfx::opSetHorizScaling},
121 {"W*", 0, {tchkNone},
124 &Gfx::opCloseFillStroke},
125 {"b*", 0, {tchkNone},
126 &Gfx::opCloseEOFillStroke},
127 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
130 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
133 {"cs", 1, {tchkName},
134 &Gfx::opSetFillColorSpace},
135 {"d", 2, {tchkArray, tchkNum},
137 {"d0", 2, {tchkNum, tchkNum},
138 &Gfx::opSetCharWidth},
139 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
141 &Gfx::opSetCacheDevice},
144 {"f*", 0, {tchkNone},
147 &Gfx::opSetFillGray},
148 {"gs", 1, {tchkName},
149 &Gfx::opSetExtGState},
155 &Gfx::opSetLineJoin},
156 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
157 &Gfx::opSetFillCMYKColor},
158 {"l", 2, {tchkNum, tchkNum},
160 {"m", 2, {tchkNum, tchkNum},
166 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
168 {"rg", 3, {tchkNum, tchkNum, tchkNum},
169 &Gfx::opSetFillRGBColor},
170 {"ri", 1, {tchkName},
171 &Gfx::opSetRenderingIntent},
173 &Gfx::opCloseStroke},
174 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
175 &Gfx::opSetFillColor},
176 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
178 &Gfx::opSetFillColorN},
179 {"sh", 1, {tchkName},
181 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
184 &Gfx::opSetLineWidth},
185 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
189 #define numOps (sizeof(opTab) / sizeof(Operator))
191 //------------------------------------------------------------------------
193 //------------------------------------------------------------------------
195 Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
196 int dpi, double x1, double y1, double x2, double y2, GBool crop,
197 double cropX1, double cropY1, double cropX2, double cropY2,
201 // start the resource stack
202 res = new GfxResources(NULL);
204 // build font dictionary
207 resDict->lookup("Font", &obj1);
209 res->fonts = new GfxFontDict(obj1.getDict());
213 // get XObject dictionary
215 resDict->lookup("XObject", &res->xObjDict);
217 res->xObjDict.initNull();
219 // get colorspace dictionary
221 resDict->lookup("ColorSpace", &res->colorSpaceDict);
223 res->colorSpaceDict.initNull();
227 state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
228 fontChanged = gFalse;
231 out->startPage(pageNum, state);
232 out->setDefaultCTM(state->getCTM());
233 out->updateAll(state);
237 state->moveTo(cropX1, cropY1);
238 state->lineTo(cropX2, cropY1);
239 state->lineTo(cropX2, cropY2);
240 state->lineTo(cropX1, cropY2);
248 GfxResources *resPtr;
250 while (state->hasSaves()) {
251 state = state->restore();
252 out->restoreState(state);
264 GfxResources::~GfxResources() {
268 colorSpaceDict.free();
271 void Gfx::display(Object *obj) {
275 if (obj->isArray()) {
276 for (i = 0; i < obj->arrayGetLength(); ++i) {
277 obj->arrayGet(i, &obj2);
278 if (!obj2.isStream()) {
279 error(-1, "Weird page contents");
285 } else if (!obj->isStream()) {
286 error(-1, "Weird page contents");
289 parser = new Parser(new Lexer(obj));
295 Object args[maxArgs];
296 int numCmds, numArgs;
299 // scan a sequence of objects
302 parser->getObj(&obj);
303 while (!obj.isEOF()) {
305 // got a command - execute it
309 for (i = 0; i < numArgs; ++i) {
311 args[i].print(stdout);
315 execOp(&obj, args, numArgs);
317 for (i = 0; i < numArgs; ++i)
321 // periodically update display
322 if (++numCmds == 200) {
327 // got an argument - save it
328 } else if (numArgs < maxArgs) {
329 args[numArgs++] = obj;
331 // too many arguments - something is wrong
333 error(getPos(), "Too many args in content stream");
335 printf("throwing away arg: ");
342 // grab the next object
343 parser->getObj(&obj);
347 // args at end with no command
349 error(getPos(), "Leftover args in content stream");
351 printf("%d leftovers:", numArgs);
352 for (i = 0; i < numArgs; ++i) {
354 args[i].print(stdout);
358 for (i = 0; i < numArgs; ++i)
373 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
379 name = cmd->getName();
380 if (!(op = findOp(name))) {
381 if (ignoreUndef == 0)
382 error(getPos(), "Unknown operator '%s'", name);
387 if (op->numArgs >= 0) {
388 if (numArgs != op->numArgs) {
389 error(getPos(), "Wrong number (%d) of args to '%s' operator",
394 if (numArgs > -op->numArgs) {
395 error(getPos(), "Too many (%d) args to '%s' operator",
400 for (i = 0; i < numArgs; ++i) {
401 if (!checkArg(&args[i], op->tchk[i])) {
402 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
403 i, name, args[i].getTypeName());
409 (this->*op->func)(args, numArgs);
412 Operator *Gfx::findOp(char *name) {
417 // invariant: opTab[a] < name < opTab[b]
420 cmp = strcmp(opTab[m].name, name);
433 GBool Gfx::checkArg(Object *arg, TchkType type) {
435 case tchkBool: return arg->isBool();
436 case tchkInt: return arg->isInt();
437 case tchkNum: return arg->isNum();
438 case tchkString: return arg->isString();
439 case tchkName: return arg->isName();
440 case tchkArray: return arg->isArray();
441 case tchkProps: return arg->isDict() || arg->isName();
442 case tchkSCN: return arg->isNum() || arg->isName();
443 case tchkNone: return gFalse;
449 return parser->getPos();
452 GfxFont *Gfx::lookupFont(char *name) {
454 GfxResources *resPtr;
456 for (resPtr = res; resPtr; resPtr = resPtr->next) {
458 if ((font = resPtr->fonts->lookup(name)))
462 error(getPos(), "unknown font tag '%s'", name);
466 GBool Gfx::lookupXObject(char *name, Object *obj) {
467 GfxResources *resPtr;
469 for (resPtr = res; resPtr; resPtr = resPtr->next) {
470 if (resPtr->xObjDict.isDict()) {
471 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
476 error(getPos(), "XObject '%s' is unknown", name);
480 void Gfx::lookupColorSpace(char *name, Object *obj) {
481 GfxResources *resPtr;
483 for (resPtr = res; resPtr; resPtr = resPtr->next) {
484 if (resPtr->colorSpaceDict.isDict()) {
485 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull())
493 //------------------------------------------------------------------------
494 // graphics state operators
495 //------------------------------------------------------------------------
497 void Gfx::opSave(Object args[], int numArgs) {
498 out->saveState(state);
499 state = state->save();
502 void Gfx::opRestore(Object args[], int numArgs) {
503 state = state->restore();
504 out->restoreState(state);
507 void Gfx::opConcat(Object args[], int numArgs) {
508 state->concatCTM(args[0].getNum(), args[1].getNum(),
509 args[2].getNum(), args[3].getNum(),
510 args[4].getNum(), args[5].getNum());
511 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
512 args[2].getNum(), args[3].getNum(),
513 args[4].getNum(), args[5].getNum());
517 void Gfx::opSetDash(Object args[], int numArgs) {
524 a = args[0].getArray();
525 length = a->getLength();
529 dash = (double *)gmalloc(length * sizeof(double));
530 for (i = 0; i < length; ++i) {
531 dash[i] = a->get(i, &obj)->getNum();
535 state->setLineDash(dash, length, args[1].getNum());
536 out->updateLineDash(state);
539 void Gfx::opSetFlat(Object args[], int numArgs) {
540 state->setFlatness((int)args[0].getNum());
541 out->updateFlatness(state);
544 void Gfx::opSetLineJoin(Object args[], int numArgs) {
545 state->setLineJoin(args[0].getInt());
546 out->updateLineJoin(state);
549 void Gfx::opSetLineCap(Object args[], int numArgs) {
550 state->setLineCap(args[0].getInt());
551 out->updateLineCap(state);
554 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
555 state->setMiterLimit(args[0].getNum());
556 out->updateMiterLimit(state);
559 void Gfx::opSetLineWidth(Object args[], int numArgs) {
560 state->setLineWidth(args[0].getNum());
561 out->updateLineWidth(state);
564 void Gfx::opSetExtGState(Object args[], int numArgs) {
567 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
570 //------------------------------------------------------------------------
572 //------------------------------------------------------------------------
574 void Gfx::opSetFillGray(Object args[], int numArgs) {
575 state->setFillColorSpace(new GfxColorSpace(colorGray));
576 state->setFillGray(args[0].getNum());
577 out->updateFillColor(state);
580 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
581 state->setStrokeColorSpace(new GfxColorSpace(colorGray));
582 state->setStrokeGray(args[0].getNum());
583 out->updateStrokeColor(state);
586 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
587 state->setFillColorSpace(new GfxColorSpace(colorCMYK));
588 state->setFillCMYK(args[0].getNum(), args[1].getNum(),
589 args[2].getNum(), args[3].getNum());
590 out->updateFillColor(state);
593 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
594 state->setStrokeColorSpace(new GfxColorSpace(colorCMYK));
595 state->setStrokeCMYK(args[0].getNum(), args[1].getNum(),
596 args[2].getNum(), args[3].getNum());
597 out->updateStrokeColor(state);
600 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
601 state->setFillColorSpace(new GfxColorSpace(colorRGB));
602 state->setFillRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
603 out->updateFillColor(state);
606 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
607 state->setStrokeColorSpace(new GfxColorSpace(colorRGB));
608 state->setStrokeRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
609 out->updateStrokeColor(state);
612 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
614 GfxColorSpace *colorSpace;
617 lookupColorSpace(args[0].getName(), &obj);
619 colorSpace = new GfxColorSpace(&args[0]);
621 colorSpace = new GfxColorSpace(&obj);
623 if (colorSpace->isOk()) {
624 state->setFillColorSpace(colorSpace);
627 error(getPos(), "Bad colorspace");
629 x[0] = x[1] = x[2] = x[3] = 0;
630 state->setFillColor(x);
631 out->updateFillColor(state);
634 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
636 GfxColorSpace *colorSpace;
639 lookupColorSpace(args[0].getName(), &obj);
641 colorSpace = new GfxColorSpace(&args[0]);
643 colorSpace = new GfxColorSpace(&obj);
645 if (colorSpace->isOk()) {
646 state->setStrokeColorSpace(colorSpace);
649 error(getPos(), "Bad colorspace");
651 x[0] = x[1] = x[2] = x[3] = 0;
652 state->setStrokeColor(x);
653 out->updateStrokeColor(state);
656 void Gfx::opSetFillColor(Object args[], int numArgs) {
660 x[0] = x[1] = x[2] = x[3] = 0;
661 for (i = 0; i < numArgs; ++i)
662 x[i] = args[i].getNum();
663 state->setFillColor(x);
664 out->updateFillColor(state);
667 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
671 x[0] = x[1] = x[2] = x[3] = 0;
672 for (i = 0; i < numArgs; ++i)
673 x[i] = args[i].getNum();
674 state->setStrokeColor(x);
675 out->updateStrokeColor(state);
678 void Gfx::opSetFillColorN(Object args[], int numArgs) {
682 x[0] = x[1] = x[2] = x[3] = 0;
683 for (i = 0; i < numArgs && i < 4; ++i) {
685 x[i] = args[i].getNum();
689 state->setFillColor(x);
690 out->updateFillColor(state);
693 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
697 x[0] = x[1] = x[2] = x[3] = 0;
698 for (i = 0; i < numArgs && i < 4; ++i) {
700 x[i] = args[i].getNum();
704 state->setStrokeColor(x);
705 out->updateStrokeColor(state);
708 //------------------------------------------------------------------------
709 // path segment operators
710 //------------------------------------------------------------------------
712 void Gfx::opMoveTo(Object args[], int numArgs) {
713 state->moveTo(args[0].getNum(), args[1].getNum());
716 void Gfx::opLineTo(Object args[], int numArgs) {
717 if (!state->isCurPt()) {
718 error(getPos(), "No current point in lineto");
721 state->lineTo(args[0].getNum(), args[1].getNum());
724 void Gfx::opCurveTo(Object args[], int numArgs) {
725 double x1, y1, x2, y2, x3, y3;
727 if (!state->isCurPt()) {
728 error(getPos(), "No current point in curveto");
731 x1 = args[0].getNum();
732 y1 = args[1].getNum();
733 x2 = args[2].getNum();
734 y2 = args[3].getNum();
735 x3 = args[4].getNum();
736 y3 = args[5].getNum();
737 state->curveTo(x1, y1, x2, y2, x3, y3);
740 void Gfx::opCurveTo1(Object args[], int numArgs) {
741 double x1, y1, x2, y2, x3, y3;
743 if (!state->isCurPt()) {
744 error(getPos(), "No current point in curveto1");
747 x1 = state->getCurX();
748 y1 = state->getCurY();
749 x2 = args[0].getNum();
750 y2 = args[1].getNum();
751 x3 = args[2].getNum();
752 y3 = args[3].getNum();
753 state->curveTo(x1, y1, x2, y2, x3, y3);
756 void Gfx::opCurveTo2(Object args[], int numArgs) {
757 double x1, y1, x2, y2, x3, y3;
759 if (!state->isCurPt()) {
760 error(getPos(), "No current point in curveto2");
763 x1 = args[0].getNum();
764 y1 = args[1].getNum();
765 x2 = args[2].getNum();
766 y2 = args[3].getNum();
769 state->curveTo(x1, y1, x2, y2, x3, y3);
772 void Gfx::opRectangle(Object args[], int numArgs) {
775 x = args[0].getNum();
776 y = args[1].getNum();
777 w = args[2].getNum();
778 h = args[3].getNum();
780 state->lineTo(x + w, y);
781 state->lineTo(x + w, y + h);
782 state->lineTo(x, y + h);
786 void Gfx::opClosePath(Object args[], int numArgs) {
787 if (!state->isPath()) {
788 error(getPos(), "No current point in closepath");
794 //------------------------------------------------------------------------
795 // path painting operators
796 //------------------------------------------------------------------------
798 void Gfx::opEndPath(Object args[], int numArgs) {
802 void Gfx::opStroke(Object args[], int numArgs) {
803 if (!state->isCurPt()) {
804 //error(getPos(), "No path in stroke");
812 void Gfx::opCloseStroke(Object args[], int numArgs) {
813 if (!state->isCurPt()) {
814 //error(getPos(), "No path in closepath/stroke");
817 if (state->isPath()) {
824 void Gfx::opFill(Object args[], int numArgs) {
825 if (!state->isCurPt()) {
826 //error(getPos(), "No path in fill");
834 void Gfx::opEOFill(Object args[], int numArgs) {
835 if (!state->isCurPt()) {
836 //error(getPos(), "No path in eofill");
844 void Gfx::opFillStroke(Object args[], int numArgs) {
845 if (!state->isCurPt()) {
846 //error(getPos(), "No path in fill/stroke");
849 if (state->isPath()) {
856 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
857 if (!state->isCurPt()) {
858 //error(getPos(), "No path in closepath/fill/stroke");
861 if (state->isPath()) {
869 void Gfx::opEOFillStroke(Object args[], int numArgs) {
870 if (!state->isCurPt()) {
871 //error(getPos(), "No path in eofill/stroke");
874 if (state->isPath()) {
881 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
882 if (!state->isCurPt()) {
883 //error(getPos(), "No path in closepath/eofill/stroke");
886 if (state->isPath()) {
894 void Gfx::opShFill(Object args[], int numArgs) {
897 void Gfx::doEndPath() {
898 if (state->isPath()) {
899 if (clip == clipNormal)
901 else if (clip == clipEO)
908 //------------------------------------------------------------------------
909 // path clipping operators
910 //------------------------------------------------------------------------
912 void Gfx::opClip(Object args[], int numArgs) {
916 void Gfx::opEOClip(Object args[], int numArgs) {
920 //------------------------------------------------------------------------
921 // text object operators
922 //------------------------------------------------------------------------
924 void Gfx::opBeginText(Object args[], int numArgs) {
925 state->setTextMat(1, 0, 0, 1, 0, 0);
926 state->textMoveTo(0, 0);
927 out->updateTextMat(state);
928 out->updateTextPos(state);
932 void Gfx::opEndText(Object args[], int numArgs) {
935 //------------------------------------------------------------------------
936 // text state operators
937 //------------------------------------------------------------------------
939 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
940 state->setCharSpace(args[0].getNum());
941 out->updateCharSpace(state);
944 void Gfx::opSetFont(Object args[], int numArgs) {
947 if (!(font = lookupFont(args[0].getName())))
950 printf(" font: '%s' %g\n",
951 font->getName() ? font->getName()->getCString() : "???",
954 state->setFont(font, args[1].getNum());
958 void Gfx::opSetTextLeading(Object args[], int numArgs) {
959 state->setLeading(args[0].getNum());
962 void Gfx::opSetTextRender(Object args[], int numArgs) {
963 state->setRender(args[0].getInt());
964 out->updateRender(state);
967 void Gfx::opSetTextRise(Object args[], int numArgs) {
968 state->setRise(args[0].getNum());
969 out->updateRise(state);
972 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
973 state->setWordSpace(args[0].getNum());
974 out->updateWordSpace(state);
977 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
978 state->setHorizScaling(args[0].getNum());
979 out->updateHorizScaling(state);
982 //------------------------------------------------------------------------
983 // text positioning operators
984 //------------------------------------------------------------------------
986 void Gfx::opTextMove(Object args[], int numArgs) {
989 tx = state->getLineX() + args[0].getNum();
990 ty = state->getLineY() + args[1].getNum();
991 state->textMoveTo(tx, ty);
992 out->updateTextPos(state);
995 void Gfx::opTextMoveSet(Object args[], int numArgs) {
998 tx = state->getLineX() + args[0].getNum();
999 ty = args[1].getNum();
1000 state->setLeading(-ty);
1001 ty += state->getLineY();
1002 state->textMoveTo(tx, ty);
1003 out->updateTextPos(state);
1006 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
1007 state->setTextMat(args[0].getNum(), args[1].getNum(),
1008 args[2].getNum(), args[3].getNum(),
1009 args[4].getNum(), args[5].getNum());
1010 state->textMoveTo(0, 0);
1011 out->updateTextMat(state);
1012 out->updateTextPos(state);
1013 fontChanged = gTrue;
1016 void Gfx::opTextNextLine(Object args[], int numArgs) {
1019 tx = state->getLineX();
1020 ty = state->getLineY() - state->getLeading();
1021 state->textMoveTo(tx, ty);
1022 out->updateTextPos(state);
1025 //------------------------------------------------------------------------
1026 // text string operators
1027 //------------------------------------------------------------------------
1029 void Gfx::opShowText(Object args[], int numArgs) {
1030 if (!state->getFont()) {
1031 error(getPos(), "No font in show");
1034 doShowText(args[0].getString());
1037 void Gfx::opMoveShowText(Object args[], int numArgs) {
1040 if (!state->getFont()) {
1041 error(getPos(), "No font in move/show");
1044 tx = state->getLineX();
1045 ty = state->getLineY() - state->getLeading();
1046 state->textMoveTo(tx, ty);
1047 out->updateTextPos(state);
1048 doShowText(args[0].getString());
1051 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1054 if (!state->getFont()) {
1055 error(getPos(), "No font in move/set/show");
1058 state->setWordSpace(args[0].getNum());
1059 state->setCharSpace(args[1].getNum());
1060 tx = state->getLineX();
1061 ty = state->getLineY() - state->getLeading();
1062 state->textMoveTo(tx, ty);
1063 out->updateWordSpace(state);
1064 out->updateCharSpace(state);
1065 out->updateTextPos(state);
1066 doShowText(args[2].getString());
1069 void Gfx::opShowSpaceText(Object args[], int numArgs) {
1074 if (!state->getFont()) {
1075 error(getPos(), "No font in show/space");
1078 a = args[0].getArray();
1079 for (i = 0; i < a->getLength(); ++i) {
1082 state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
1083 out->updateTextShift(state, obj.getNum());
1084 } else if (obj.isString()) {
1085 doShowText(obj.getString());
1087 error(getPos(), "Element of show/space array must be number or string");
1093 void Gfx::doShowText(GString *s) {
1095 GfxFontEncoding16 *enc;
1101 double dx, dy, width, height, w, h;
1104 out->updateFont(state);
1105 fontChanged = gFalse;
1107 font = state->getFont();
1110 if (font->is16Bit()) {
1111 enc = font->getEncoding16();
1112 if (out->useDrawChar()) {
1113 out->beginString(state, s);
1116 s16 = new GString(" ");
1118 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1119 p = (Guchar *)s->getCString();
1122 m = getNextChar16(enc, p, &c16);
1123 if (enc->wMode == 0) {
1124 width = state->getFontSize() * state->getHorizScaling() *
1125 font->getWidth16(c16) +
1126 state->getCharSpace();
1128 width += state->getWordSpace();
1133 height = state->getFontSize() * font->getHeight16(c16);
1135 state->textTransformDelta(width, height, &w, &h);
1136 if (out->useDrawChar()) {
1137 out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
1140 s16->setChar(0, (char)(c16 >> 8));
1141 s16->setChar(1, (char)c16);
1142 out->drawString16(state, s16);
1144 state->textShift(width, height);
1148 if (out->useDrawChar())
1149 out->endString(state);
1155 if (out->useDrawChar()) {
1156 out->beginString(state, s);
1157 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1158 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1160 width = state->getFontSize() * state->getHorizScaling() *
1161 font->getWidth(c8) +
1162 state->getCharSpace();
1164 width += state->getWordSpace();
1165 state->textTransformDelta(width, 0, &w, &h);
1166 out->drawChar(state, state->getCurX() + dx, state->getCurY() + dy,
1168 state->textShift(width);
1170 out->endString(state);
1172 out->drawString(state, s);
1173 width = state->getFontSize() * state->getHorizScaling() *
1175 s->getLength() * state->getCharSpace();
1176 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1178 width += state->getWordSpace();
1180 state->textShift(width);
1185 int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
1190 n = enc->codeLen[*p];
1192 *c16 = enc->map1[*p];
1194 code = (p[0] << 8) + p[1];
1197 // invariant: map2[2*a] <= code < map2[2*b]
1200 if (enc->map2[2*m] <= code)
1202 else if (enc->map2[2*m] > code)
1207 *c16 = enc->map2[2*a+1] + (code - enc->map2[2*a]);
1212 //------------------------------------------------------------------------
1213 // XObject operators
1214 //------------------------------------------------------------------------
1216 void Gfx::opXObject(Object args[], int numArgs) {
1219 if (!lookupXObject(args[0].getName(), &obj1))
1221 if (!obj1.isStream("XObject")) {
1222 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
1226 obj1.streamGetDict()->lookup("Subtype", &obj2);
1227 if (obj2.isName("Image"))
1228 doImage(obj1.getStream(), gFalse);
1229 else if (obj2.isName("Form"))
1231 else if (obj2.isName())
1232 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
1234 error(getPos(), "XObject subtype is missing or wrong type");
1239 void Gfx::doImage(Stream *str, GBool inlineImg) {
1245 GfxColorSpace *colorSpace;
1246 GfxImageColorMap *colorMap;
1250 dict = str->getDict();
1253 dict->lookup("Width", &obj1);
1254 if (obj1.isNull()) {
1256 dict->lookup("W", &obj1);
1260 width = obj1.getInt();
1262 dict->lookup("Height", &obj1);
1263 if (obj1.isNull()) {
1265 dict->lookup("H", &obj1);
1269 height = obj1.getInt();
1273 dict->lookup("ImageMask", &obj1);
1274 if (obj1.isNull()) {
1276 dict->lookup("IM", &obj1);
1280 mask = obj1.getBool();
1281 else if (!obj1.isNull())
1286 dict->lookup("BitsPerComponent", &obj1);
1287 if (obj1.isNull()) {
1289 dict->lookup("BPC", &obj1);
1293 bits = obj1.getInt();
1299 // check for inverted mask
1303 dict->lookup("Decode", &obj1);
1304 if (obj1.isNull()) {
1306 dict->lookup("D", &obj1);
1308 if (obj1.isArray()) {
1309 obj1.arrayGet(0, &obj2);
1310 if (obj2.isInt() && obj2.getInt() == 1)
1313 } else if (!obj1.isNull()) {
1319 out->drawImageMask(state, str, width, height, invert, inlineImg);
1323 // get color space and color map
1324 dict->lookup("ColorSpace", &obj1);
1325 if (obj1.isNull()) {
1327 dict->lookup("CS", &obj1);
1329 if (obj1.isName()) {
1330 lookupColorSpace(obj1.getName(), &obj2);
1331 if (!obj2.isNull()) {
1338 colorSpace = new GfxColorSpace(&obj1);
1340 if (!colorSpace->isOk()) {
1344 dict->lookup("Decode", &obj1);
1345 if (obj1.isNull()) {
1347 dict->lookup("D", &obj1);
1349 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
1351 if (!colorMap->isOk()) {
1357 out->drawImage(state, str, width, height, colorMap, inlineImg);
1366 error(getPos(), "Bad image parameters");
1369 void Gfx::doForm(Object *str) {
1371 GfxResources *resPtr;
1374 Object matrixObj, bboxObj;
1380 dict = str->streamGetDict();
1383 dict->lookup("FormType", &obj1);
1384 if (!(obj1.isInt() && obj1.getInt() == 1)) {
1385 error(getPos(), "Unknown form type");
1389 // get matrix and bounding box
1390 dict->lookup("Matrix", &matrixObj);
1391 if (!matrixObj.isArray()) {
1393 error(getPos(), "Bad form matrix");
1396 dict->lookup("BBox", &bboxObj);
1397 if (!bboxObj.isArray()) {
1400 error(getPos(), "Bad form bounding box");
1404 // push new resources on stack
1405 res = new GfxResources(res);
1406 dict->lookup("Resources", &obj1);
1407 if (obj1.isDict()) {
1408 resDict = obj1.getDict();
1410 resDict->lookup("Font", &obj2);
1412 res->fonts = new GfxFontDict(obj2.getDict());
1414 resDict->lookup("XObject", &res->xObjDict);
1415 resDict->lookup("ColorSpace", &res->colorSpaceDict);
1419 // save current graphics state
1420 out->saveState(state);
1421 state = state->save();
1423 // save current parser
1426 // set form transformation matrix
1427 for (i = 0; i < 6; ++i) {
1428 matrixObj.arrayGet(i, &obj1);
1429 m[i] = obj1.getNum();
1432 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1433 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1435 // set form bounding box
1436 for (i = 0; i < 4; ++i) {
1437 bboxObj.arrayGet(i, &obj1);
1438 m[i] = obj1.getNum();
1441 state->moveTo(m[0], m[1]);
1442 state->lineTo(m[0]+m[2], m[1]);
1443 state->lineTo(m[0]+m[2], m[1]+m[3]);
1444 state->lineTo(m[0], m[1]+m[3]);
1452 // free matrix and bounding box
1459 // restore graphics state
1460 state = state->restore();
1461 out->restoreState(state);
1463 // pop resource stack
1471 //------------------------------------------------------------------------
1472 // in-line image operators
1473 //------------------------------------------------------------------------
1475 void Gfx::opBeginImage(Object args[], int numArgs) {
1479 // build dict/stream
1480 str = buildImageStream();
1482 // display the image
1484 doImage(str, gTrue);
1487 c1 = str->getBaseStream()->getChar();
1488 c2 = str->getBaseStream()->getChar();
1489 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
1491 c2 = str->getBaseStream()->getChar();
1497 Stream *Gfx::buildImageStream() {
1505 parser->getObj(&obj);
1506 while (!obj.isCmd("ID") && !obj.isEOF()) {
1507 if (!obj.isName()) {
1508 error(getPos(), "Inline image dictionary key must be a name object");
1510 parser->getObj(&obj);
1512 key = copyString(obj.getName());
1514 parser->getObj(&obj);
1515 if (obj.isEOF() || obj.isError())
1517 dict.dictAdd(key, &obj);
1519 parser->getObj(&obj);
1522 error(getPos(), "End of file in inline image");
1526 str = new EmbedStream(parser->getStream(), &dict);
1527 str = str->addFilters(&dict);
1532 void Gfx::opImageData(Object args[], int numArgs) {
1533 error(getPos(), "Internal: got 'ID' operator");
1536 void Gfx::opEndImage(Object args[], int numArgs) {
1537 error(getPos(), "Internal: got 'EI' operator");
1540 //------------------------------------------------------------------------
1541 // type 3 font operators
1542 //------------------------------------------------------------------------
1544 void Gfx::opSetCharWidth(Object args[], int numArgs) {
1545 error(getPos(), "Encountered 'd0' operator in content stream");
1548 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
1549 error(getPos(), "Encountered 'd1' operator in content stream");
1552 //------------------------------------------------------------------------
1553 // compatibility operators
1554 //------------------------------------------------------------------------
1556 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
1560 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
1561 if (ignoreUndef > 0)
1565 //------------------------------------------------------------------------
1566 // marked content operators
1567 //------------------------------------------------------------------------
1569 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
1570 if (printCommands) {
1571 printf(" marked content: %s ", args[0].getName());
1573 args[2].print(stdout);
1578 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
1581 void Gfx::opMarkPoint(Object args[], int numArgs) {
1582 if (printCommands) {
1583 printf(" mark point: %s ", args[0].getName());
1585 args[2].print(stdout);