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},
171 &Gfx::opCloseStroke},
172 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
173 &Gfx::opSetFillColor},
174 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
176 &Gfx::opSetFillColorN},
177 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
180 &Gfx::opSetLineWidth},
181 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
185 #define numOps (sizeof(opTab) / sizeof(Operator))
187 //------------------------------------------------------------------------
189 //------------------------------------------------------------------------
191 Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
192 int dpi, double x1, double y1, double x2, double y2, GBool crop,
193 double cropX1, double cropY1, double cropX2, double cropY2,
197 // start the resource stack
198 res = new GfxResources(NULL);
200 // build font dictionary
203 resDict->lookup("Font", &obj1);
205 res->fonts = new GfxFontDict(obj1.getDict());
209 // get XObject dictionary
211 resDict->lookup("XObject", &res->xObjDict);
213 res->xObjDict.initNull();
215 // get colorspace dictionary
217 resDict->lookup("ColorSpace", &res->colorSpaceDict);
219 res->colorSpaceDict.initNull();
223 state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
224 fontChanged = gFalse;
227 out->startPage(pageNum, state);
228 out->setDefaultCTM(state->getCTM());
229 out->updateAll(state);
233 state->moveTo(cropX1, cropY1);
234 state->lineTo(cropX2, cropY1);
235 state->lineTo(cropX2, cropY2);
236 state->lineTo(cropX1, cropY2);
244 GfxResources *resPtr;
246 while (state->hasSaves()) {
247 state = state->restore();
248 out->restoreState(state);
260 GfxResources::~GfxResources() {
264 colorSpaceDict.free();
267 void Gfx::display(Object *obj) {
271 if (obj->isArray()) {
272 for (i = 0; i < obj->arrayGetLength(); ++i) {
273 obj->arrayGet(i, &obj2);
274 if (!obj2.isStream()) {
275 error(-1, "Weird page contents");
281 } else if (!obj->isStream()) {
282 error(-1, "Weird page contents");
285 parser = new Parser(new Lexer(obj));
291 Object args[maxArgs];
292 int numCmds, numArgs;
295 // scan a sequence of objects
298 parser->getObj(&obj);
299 while (!obj.isEOF()) {
301 // got a command - execute it
305 for (i = 0; i < numArgs; ++i) {
307 args[i].print(stdout);
311 execOp(&obj, args, numArgs);
313 for (i = 0; i < numArgs; ++i)
317 // periodically update display
318 if (++numCmds == 200) {
323 // got an argument - save it
324 } else if (numArgs < maxArgs) {
325 args[numArgs++] = obj;
327 // too many arguments - something is wrong
329 error(getPos(), "Too many args in content stream");
331 printf("throwing away arg: ");
338 // grab the next object
339 parser->getObj(&obj);
343 // args at end with no command
345 error(getPos(), "Leftover args in content stream");
347 printf("%d leftovers:", numArgs);
348 for (i = 0; i < numArgs; ++i) {
350 args[i].print(stdout);
354 for (i = 0; i < numArgs; ++i)
369 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
375 name = cmd->getName();
376 if (!(op = findOp(name))) {
377 if (ignoreUndef == 0)
378 error(getPos(), "Unknown operator '%s'", name);
383 if (op->numArgs >= 0) {
384 if (numArgs != op->numArgs) {
385 error(getPos(), "Wrong number (%d) of args to '%s' operator",
390 if (numArgs > -op->numArgs) {
391 error(getPos(), "Too many (%d) args to '%s' operator",
396 for (i = 0; i < numArgs; ++i) {
397 if (!checkArg(&args[i], op->tchk[i])) {
398 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
399 i, name, args[i].getTypeName());
405 (this->*op->func)(args, numArgs);
408 Operator *Gfx::findOp(char *name) {
413 // invariant: opTab[a] < name < opTab[b]
416 cmp = strcmp(opTab[m].name, name);
429 GBool Gfx::checkArg(Object *arg, TchkType type) {
431 case tchkBool: return arg->isBool();
432 case tchkInt: return arg->isInt();
433 case tchkNum: return arg->isNum();
434 case tchkString: return arg->isString();
435 case tchkName: return arg->isName();
436 case tchkArray: return arg->isArray();
437 case tchkProps: return arg->isDict() || arg->isName();
438 case tchkSCN: return arg->isNum() || arg->isName();
439 case tchkNone: return gFalse;
445 return parser->getPos();
448 GfxFont *Gfx::lookupFont(char *name) {
450 GfxResources *resPtr;
452 for (resPtr = res; resPtr; resPtr = resPtr->next) {
454 if ((font = resPtr->fonts->lookup(name)))
458 error(getPos(), "unknown font tag '%s'", name);
462 GBool Gfx::lookupXObject(char *name, Object *obj) {
463 GfxResources *resPtr;
465 for (resPtr = res; resPtr; resPtr = resPtr->next) {
466 if (resPtr->xObjDict.isDict()) {
467 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
472 error(getPos(), "XObject '%s' is unknown", name);
476 void Gfx::lookupColorSpace(char *name, Object *obj) {
477 GfxResources *resPtr;
479 for (resPtr = res; resPtr; resPtr = resPtr->next) {
480 if (resPtr->colorSpaceDict.isDict()) {
481 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull())
489 //------------------------------------------------------------------------
490 // graphics state operators
491 //------------------------------------------------------------------------
493 void Gfx::opSave(Object args[], int numArgs) {
494 out->saveState(state);
495 state = state->save();
498 void Gfx::opRestore(Object args[], int numArgs) {
499 state = state->restore();
500 out->restoreState(state);
503 void Gfx::opConcat(Object args[], int numArgs) {
504 state->concatCTM(args[0].getNum(), args[1].getNum(),
505 args[2].getNum(), args[3].getNum(),
506 args[4].getNum(), args[5].getNum());
507 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
508 args[2].getNum(), args[3].getNum(),
509 args[4].getNum(), args[5].getNum());
513 void Gfx::opSetDash(Object args[], int numArgs) {
520 a = args[0].getArray();
521 length = a->getLength();
525 dash = (double *)gmalloc(length * sizeof(double));
526 for (i = 0; i < length; ++i) {
527 dash[i] = a->get(i, &obj)->getNum();
531 state->setLineDash(dash, length, args[1].getNum());
532 out->updateLineDash(state);
535 void Gfx::opSetFlat(Object args[], int numArgs) {
536 state->setFlatness((int)args[0].getNum());
537 out->updateFlatness(state);
540 void Gfx::opSetLineJoin(Object args[], int numArgs) {
541 state->setLineJoin(args[0].getInt());
542 out->updateLineJoin(state);
545 void Gfx::opSetLineCap(Object args[], int numArgs) {
546 state->setLineCap(args[0].getInt());
547 out->updateLineCap(state);
550 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
551 state->setMiterLimit(args[0].getNum());
552 out->updateMiterLimit(state);
555 void Gfx::opSetLineWidth(Object args[], int numArgs) {
556 state->setLineWidth(args[0].getNum());
557 out->updateLineWidth(state);
560 void Gfx::opSetExtGState(Object args[], int numArgs) {
563 //------------------------------------------------------------------------
565 //------------------------------------------------------------------------
567 void Gfx::opSetFillGray(Object args[], int numArgs) {
568 state->setFillColorSpace(new GfxColorSpace(colorGray));
569 state->setFillGray(args[0].getNum());
570 out->updateFillColor(state);
573 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
574 state->setStrokeColorSpace(new GfxColorSpace(colorGray));
575 state->setStrokeGray(args[0].getNum());
576 out->updateStrokeColor(state);
579 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
580 state->setFillColorSpace(new GfxColorSpace(colorCMYK));
581 state->setFillCMYK(args[0].getNum(), args[1].getNum(),
582 args[2].getNum(), args[3].getNum());
583 out->updateFillColor(state);
586 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
587 state->setStrokeColorSpace(new GfxColorSpace(colorCMYK));
588 state->setStrokeCMYK(args[0].getNum(), args[1].getNum(),
589 args[2].getNum(), args[3].getNum());
590 out->updateStrokeColor(state);
593 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
594 state->setFillColorSpace(new GfxColorSpace(colorRGB));
595 state->setFillRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
596 out->updateFillColor(state);
599 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
600 state->setStrokeColorSpace(new GfxColorSpace(colorRGB));
601 state->setStrokeRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
602 out->updateStrokeColor(state);
605 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
607 GfxColorSpace *colorSpace;
610 lookupColorSpace(args[0].getName(), &obj);
612 colorSpace = new GfxColorSpace(&args[0]);
614 colorSpace = new GfxColorSpace(&obj);
616 if (colorSpace->isOk()) {
617 state->setFillColorSpace(colorSpace);
620 error(getPos(), "Bad colorspace");
622 x[0] = x[1] = x[2] = x[3] = 0;
623 state->setFillColor(x);
624 out->updateFillColor(state);
627 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
629 GfxColorSpace *colorSpace;
632 lookupColorSpace(args[0].getName(), &obj);
634 colorSpace = new GfxColorSpace(&args[0]);
636 colorSpace = new GfxColorSpace(&obj);
638 if (colorSpace->isOk()) {
639 state->setStrokeColorSpace(colorSpace);
642 error(getPos(), "Bad colorspace");
644 x[0] = x[1] = x[2] = x[3] = 0;
645 state->setStrokeColor(x);
646 out->updateStrokeColor(state);
649 void Gfx::opSetFillColor(Object args[], int numArgs) {
653 x[0] = x[1] = x[2] = x[3] = 0;
654 for (i = 0; i < numArgs; ++i)
655 x[i] = args[i].getNum();
656 state->setFillColor(x);
657 out->updateFillColor(state);
660 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
664 x[0] = x[1] = x[2] = x[3] = 0;
665 for (i = 0; i < numArgs; ++i)
666 x[i] = args[i].getNum();
667 state->setStrokeColor(x);
668 out->updateStrokeColor(state);
671 void Gfx::opSetFillColorN(Object args[], int numArgs) {
675 x[0] = x[1] = x[2] = x[3] = 0;
676 for (i = 0; i < numArgs && i < 4; ++i) {
678 x[i] = args[i].getNum();
682 state->setFillColor(x);
683 out->updateFillColor(state);
686 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
690 x[0] = x[1] = x[2] = x[3] = 0;
691 for (i = 0; i < numArgs && i < 4; ++i) {
693 x[i] = args[i].getNum();
697 state->setStrokeColor(x);
698 out->updateStrokeColor(state);
701 //------------------------------------------------------------------------
702 // path segment operators
703 //------------------------------------------------------------------------
705 void Gfx::opMoveTo(Object args[], int numArgs) {
706 state->moveTo(args[0].getNum(), args[1].getNum());
709 void Gfx::opLineTo(Object args[], int numArgs) {
710 if (!state->isCurPt()) {
711 error(getPos(), "No current point in lineto");
714 state->lineTo(args[0].getNum(), args[1].getNum());
717 void Gfx::opCurveTo(Object args[], int numArgs) {
718 double x1, y1, x2, y2, x3, y3;
720 if (!state->isCurPt()) {
721 error(getPos(), "No current point in curveto");
724 x1 = args[0].getNum();
725 y1 = args[1].getNum();
726 x2 = args[2].getNum();
727 y2 = args[3].getNum();
728 x3 = args[4].getNum();
729 y3 = args[5].getNum();
730 state->curveTo(x1, y1, x2, y2, x3, y3);
733 void Gfx::opCurveTo1(Object args[], int numArgs) {
734 double x1, y1, x2, y2, x3, y3;
736 if (!state->isCurPt()) {
737 error(getPos(), "No current point in curveto1");
740 x1 = state->getCurX();
741 y1 = state->getCurY();
742 x2 = args[0].getNum();
743 y2 = args[1].getNum();
744 x3 = args[2].getNum();
745 y3 = args[3].getNum();
746 state->curveTo(x1, y1, x2, y2, x3, y3);
749 void Gfx::opCurveTo2(Object args[], int numArgs) {
750 double x1, y1, x2, y2, x3, y3;
752 if (!state->isCurPt()) {
753 error(getPos(), "No current point in curveto2");
756 x1 = args[0].getNum();
757 y1 = args[1].getNum();
758 x2 = args[2].getNum();
759 y2 = args[3].getNum();
762 state->curveTo(x1, y1, x2, y2, x3, y3);
765 void Gfx::opRectangle(Object args[], int numArgs) {
768 x = args[0].getNum();
769 y = args[1].getNum();
770 w = args[2].getNum();
771 h = args[3].getNum();
773 state->lineTo(x + w, y);
774 state->lineTo(x + w, y + h);
775 state->lineTo(x, y + h);
779 void Gfx::opClosePath(Object args[], int numArgs) {
780 if (!state->isPath()) {
781 error(getPos(), "No current point in closepath");
787 //------------------------------------------------------------------------
788 // path painting operators
789 //------------------------------------------------------------------------
791 void Gfx::opEndPath(Object args[], int numArgs) {
795 void Gfx::opStroke(Object args[], int numArgs) {
796 if (!state->isCurPt()) {
797 //error(getPos(), "No path in stroke");
805 void Gfx::opCloseStroke(Object args[], int numArgs) {
806 if (!state->isCurPt()) {
807 //error(getPos(), "No path in closepath/stroke");
810 if (state->isPath()) {
817 void Gfx::opFill(Object args[], int numArgs) {
818 if (!state->isCurPt()) {
819 //error(getPos(), "No path in fill");
827 void Gfx::opEOFill(Object args[], int numArgs) {
828 if (!state->isCurPt()) {
829 //error(getPos(), "No path in eofill");
837 void Gfx::opFillStroke(Object args[], int numArgs) {
838 if (!state->isCurPt()) {
839 //error(getPos(), "No path in fill/stroke");
842 if (state->isPath()) {
849 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
850 if (!state->isCurPt()) {
851 //error(getPos(), "No path in closepath/fill/stroke");
854 if (state->isPath()) {
862 void Gfx::opEOFillStroke(Object args[], int numArgs) {
863 if (!state->isCurPt()) {
864 //error(getPos(), "No path in eofill/stroke");
867 if (state->isPath()) {
874 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
875 if (!state->isCurPt()) {
876 //error(getPos(), "No path in closepath/eofill/stroke");
879 if (state->isPath()) {
887 void Gfx::doEndPath() {
888 if (state->isPath()) {
889 if (clip == clipNormal)
891 else if (clip == clipEO)
898 //------------------------------------------------------------------------
899 // path clipping operators
900 //------------------------------------------------------------------------
902 void Gfx::opClip(Object args[], int numArgs) {
906 void Gfx::opEOClip(Object args[], int numArgs) {
910 //------------------------------------------------------------------------
911 // text object operators
912 //------------------------------------------------------------------------
914 void Gfx::opBeginText(Object args[], int numArgs) {
915 state->setTextMat(1, 0, 0, 1, 0, 0);
916 state->textMoveTo(0, 0);
917 out->updateTextMat(state);
918 out->updateTextPos(state);
922 void Gfx::opEndText(Object args[], int numArgs) {
925 //------------------------------------------------------------------------
926 // text state operators
927 //------------------------------------------------------------------------
929 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
930 state->setCharSpace(args[0].getNum());
931 out->updateCharSpace(state);
934 void Gfx::opSetFont(Object args[], int numArgs) {
937 if (!(font = lookupFont(args[0].getName())))
940 printf(" font: '%s' %g\n",
941 font->getName() ? font->getName()->getCString() : "???",
944 state->setFont(font, args[1].getNum());
948 void Gfx::opSetTextLeading(Object args[], int numArgs) {
949 state->setLeading(args[0].getNum());
952 void Gfx::opSetTextRender(Object args[], int numArgs) {
953 state->setRender(args[0].getInt());
954 out->updateRender(state);
957 void Gfx::opSetTextRise(Object args[], int numArgs) {
958 state->setRise(args[0].getNum());
959 out->updateRise(state);
962 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
963 state->setWordSpace(args[0].getNum());
964 out->updateWordSpace(state);
967 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
968 state->setHorizScaling(args[0].getNum());
969 out->updateHorizScaling(state);
972 //------------------------------------------------------------------------
973 // text positioning operators
974 //------------------------------------------------------------------------
976 void Gfx::opTextMove(Object args[], int numArgs) {
979 tx = state->getLineX() + args[0].getNum();
980 ty = state->getLineY() + args[1].getNum();
981 state->textMoveTo(tx, ty);
982 out->updateTextPos(state);
985 void Gfx::opTextMoveSet(Object args[], int numArgs) {
988 tx = state->getLineX() + args[0].getNum();
989 ty = args[1].getNum();
990 state->setLeading(-ty);
991 ty += state->getLineY();
992 state->textMoveTo(tx, ty);
993 out->updateTextPos(state);
996 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
997 state->setTextMat(args[0].getNum(), args[1].getNum(),
998 args[2].getNum(), args[3].getNum(),
999 args[4].getNum(), args[5].getNum());
1000 state->textMoveTo(0, 0);
1001 out->updateTextMat(state);
1002 out->updateTextPos(state);
1003 fontChanged = gTrue;
1006 void Gfx::opTextNextLine(Object args[], int numArgs) {
1009 tx = state->getLineX();
1010 ty = state->getLineY() - state->getLeading();
1011 state->textMoveTo(tx, ty);
1012 out->updateTextPos(state);
1015 //------------------------------------------------------------------------
1016 // text string operators
1017 //------------------------------------------------------------------------
1019 void Gfx::opShowText(Object args[], int numArgs) {
1020 if (!state->getFont()) {
1021 error(getPos(), "No font in show");
1024 doShowText(args[0].getString());
1027 void Gfx::opMoveShowText(Object args[], int numArgs) {
1030 if (!state->getFont()) {
1031 error(getPos(), "No font in move/show");
1034 tx = state->getLineX();
1035 ty = state->getLineY() - state->getLeading();
1036 state->textMoveTo(tx, ty);
1037 out->updateTextPos(state);
1038 doShowText(args[0].getString());
1041 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1044 if (!state->getFont()) {
1045 error(getPos(), "No font in move/set/show");
1048 state->setWordSpace(args[0].getNum());
1049 state->setCharSpace(args[1].getNum());
1050 tx = state->getLineX();
1051 ty = state->getLineY() - state->getLeading();
1052 state->textMoveTo(tx, ty);
1053 out->updateWordSpace(state);
1054 out->updateCharSpace(state);
1055 out->updateTextPos(state);
1056 doShowText(args[2].getString());
1059 void Gfx::opShowSpaceText(Object args[], int numArgs) {
1064 if (!state->getFont()) {
1065 error(getPos(), "No font in show/space");
1068 a = args[0].getArray();
1069 for (i = 0; i < a->getLength(); ++i) {
1072 state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
1073 out->updateTextShift(state, obj.getNum());
1074 } else if (obj.isString()) {
1075 doShowText(obj.getString());
1077 error(getPos(), "Element of show/space array must be number or string");
1083 void Gfx::doShowText(GString *s) {
1085 GfxFontEncoding16 *enc;
1091 double dx, dy, width, w, h;
1094 out->updateFont(state);
1095 fontChanged = gFalse;
1097 font = state->getFont();
1100 if (font->is16Bit()) {
1101 enc = font->getEncoding16();
1102 if (out->useDrawChar()) {
1103 out->beginString(state, s);
1106 s16 = new GString(" ");
1108 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1109 p = (Guchar *)s->getCString();
1112 m = getNextChar16(enc, p, &c16);
1113 width = state->getFontSize() * state->getHorizScaling() *
1114 font->getWidth16(c16) +
1115 state->getCharSpace();
1117 width += state->getWordSpace();
1118 state->textTransformDelta(width, 0, &w, &h);
1119 if (out->useDrawChar()) {
1120 out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
1123 s16->setChar(0, (char)(c16 >> 8));
1124 s16->setChar(1, (char)c16);
1125 out->drawString16(state, s16);
1127 state->textShift(width);
1131 if (out->useDrawChar())
1132 out->endString(state);
1138 if (out->useDrawChar()) {
1139 out->beginString(state, s);
1140 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1141 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1143 width = state->getFontSize() * state->getHorizScaling() *
1144 font->getWidth(c8) +
1145 state->getCharSpace();
1147 width += state->getWordSpace();
1148 state->textTransformDelta(width, 0, &w, &h);
1149 out->drawChar(state, state->getCurX() + dx, state->getCurY() + dy,
1151 state->textShift(width);
1153 out->endString(state);
1155 out->drawString(state, s);
1156 width = state->getFontSize() * state->getHorizScaling() *
1158 s->getLength() * state->getCharSpace();
1159 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1161 width += state->getWordSpace();
1163 state->textShift(width);
1168 int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
1173 n = enc->codeLen[*p];
1175 *c16 = enc->map1[*p];
1177 code = (p[0] << 8) + p[1];
1180 // invariant: map2[2*a] <= code < map2[2*b]
1183 if (enc->map2[2*m] <= code)
1185 else if (enc->map2[2*m] > code)
1190 *c16 = enc->map2[2*a+1] + (code - enc->map2[2*a]);
1195 //------------------------------------------------------------------------
1196 // XObject operators
1197 //------------------------------------------------------------------------
1199 void Gfx::opXObject(Object args[], int numArgs) {
1202 if (!lookupXObject(args[0].getName(), &obj1))
1204 if (!obj1.isStream("XObject")) {
1205 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
1209 obj1.streamGetDict()->lookup("Subtype", &obj2);
1210 if (obj2.isName("Image"))
1211 doImage(obj1.getStream(), gFalse);
1212 else if (obj2.isName("Form"))
1214 else if (obj2.isName())
1215 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
1217 error(getPos(), "XObject subtype is missing or wrong type");
1222 void Gfx::doImage(Stream *str, GBool inlineImg) {
1228 GfxColorSpace *colorSpace;
1229 GfxImageColorMap *colorMap;
1233 dict = str->getDict();
1236 dict->lookup("Width", &obj1);
1237 if (obj1.isNull()) {
1239 dict->lookup("W", &obj1);
1243 width = obj1.getInt();
1245 dict->lookup("Height", &obj1);
1246 if (obj1.isNull()) {
1248 dict->lookup("H", &obj1);
1252 height = obj1.getInt();
1256 dict->lookup("ImageMask", &obj1);
1257 if (obj1.isNull()) {
1259 dict->lookup("IM", &obj1);
1263 mask = obj1.getBool();
1264 else if (!obj1.isNull())
1269 dict->lookup("BitsPerComponent", &obj1);
1270 if (obj1.isNull()) {
1272 dict->lookup("BPC", &obj1);
1276 bits = obj1.getInt();
1282 // check for inverted mask
1286 dict->lookup("Decode", &obj1);
1287 if (obj1.isNull()) {
1289 dict->lookup("D", &obj1);
1291 if (obj1.isArray()) {
1292 obj1.arrayGet(0, &obj2);
1293 if (obj2.isInt() && obj2.getInt() == 1)
1296 } else if (!obj1.isNull()) {
1302 out->drawImageMask(state, str, width, height, invert, inlineImg);
1306 // get color space and color map
1307 dict->lookup("ColorSpace", &obj1);
1308 if (obj1.isNull()) {
1310 dict->lookup("CS", &obj1);
1312 if (obj1.isName()) {
1313 lookupColorSpace(obj1.getName(), &obj2);
1314 if (!obj2.isNull()) {
1321 colorSpace = new GfxColorSpace(&obj1);
1323 if (!colorSpace->isOk()) {
1327 dict->lookup("Decode", &obj1);
1328 if (obj1.isNull()) {
1330 dict->lookup("D", &obj1);
1332 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
1334 if (!colorMap->isOk()) {
1340 out->drawImage(state, str, width, height, colorMap, inlineImg);
1349 error(getPos(), "Bad image parameters");
1352 void Gfx::doForm(Object *str) {
1354 GfxResources *resPtr;
1357 Object matrixObj, bboxObj;
1363 dict = str->streamGetDict();
1366 dict->lookup("FormType", &obj1);
1367 if (!(obj1.isInt() && obj1.getInt() == 1)) {
1369 error(getPos(), "Unknown form type");
1374 // get matrix and bounding box
1375 dict->lookup("Matrix", &matrixObj);
1376 if (!matrixObj.isArray()) {
1378 error(getPos(), "Bad form matrix");
1381 dict->lookup("BBox", &bboxObj);
1382 if (!bboxObj.isArray()) {
1385 error(getPos(), "Bad form bounding box");
1389 // push new resources on stack
1390 dict->lookup("Resources", &obj1);
1391 if (obj1.isDict()) {
1392 resDict = obj1.getDict();
1393 res = new GfxResources(res);
1395 resDict->lookup("Font", &obj2);
1397 res->fonts = new GfxFontDict(obj2.getDict());
1399 resDict->lookup("XObject", &res->xObjDict);
1400 resDict->lookup("ColorSpace", &res->colorSpaceDict);
1404 // save current graphics state
1405 out->saveState(state);
1406 state = state->save();
1408 // save current parser
1411 // set form transformation matrix
1412 for (i = 0; i < 6; ++i) {
1413 matrixObj.arrayGet(i, &obj1);
1414 m[i] = obj1.getNum();
1417 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1418 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1420 // set form bounding box
1421 for (i = 0; i < 4; ++i) {
1422 bboxObj.arrayGet(i, &obj1);
1423 m[i] = obj1.getNum();
1426 state->moveTo(m[0], m[1]);
1427 state->lineTo(m[0]+m[2], m[1]);
1428 state->lineTo(m[0]+m[2], m[1]+m[3]);
1429 state->lineTo(m[0], m[1]+m[3]);
1437 // free matrix and bounding box
1444 // restore graphics state
1445 state = state->restore();
1446 out->restoreState(state);
1448 // pop resource stack
1456 //------------------------------------------------------------------------
1457 // in-line image operators
1458 //------------------------------------------------------------------------
1460 void Gfx::opBeginImage(Object args[], int numArgs) {
1464 // build dict/stream
1465 str = buildImageStream();
1467 // display the image
1469 doImage(str, gTrue);
1472 c1 = str->getBaseStream()->getChar();
1473 c2 = str->getBaseStream()->getChar();
1474 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
1476 c2 = str->getBaseStream()->getChar();
1482 Stream *Gfx::buildImageStream() {
1490 parser->getObj(&obj);
1491 while (!obj.isCmd("ID") && !obj.isEOF()) {
1492 if (!obj.isName()) {
1493 error(getPos(), "Inline image dictionary key must be a name object");
1495 parser->getObj(&obj);
1497 key = copyString(obj.getName());
1499 parser->getObj(&obj);
1500 if (obj.isEOF() || obj.isError())
1502 dict.dictAdd(key, &obj);
1504 parser->getObj(&obj);
1507 error(getPos(), "End of file in inline image");
1511 str = new SubStream(parser->getStream(), &dict);
1512 str = str->addFilters(&dict);
1517 void Gfx::opImageData(Object args[], int numArgs) {
1518 error(getPos(), "Internal: got 'ID' operator");
1521 void Gfx::opEndImage(Object args[], int numArgs) {
1522 error(getPos(), "Internal: got 'EI' operator");
1525 //------------------------------------------------------------------------
1526 // type 3 font operators
1527 //------------------------------------------------------------------------
1529 void Gfx::opSetCharWidth(Object args[], int numArgs) {
1530 error(getPos(), "Encountered 'd0' operator in content stream");
1533 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
1534 error(getPos(), "Encountered 'd1' operator in content stream");
1537 //------------------------------------------------------------------------
1538 // compatibility operators
1539 //------------------------------------------------------------------------
1541 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
1545 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
1546 if (ignoreUndef > 0)
1550 //------------------------------------------------------------------------
1551 // marked content operators
1552 //------------------------------------------------------------------------
1554 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
1555 if (printCommands) {
1556 printf(" marked content: %s ", args[0].getName());
1558 args[2].print(stdout);
1563 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
1566 void Gfx::opMarkPoint(Object args[], int numArgs) {
1567 if (printCommands) {
1568 printf(" mark point: %s ", args[0].getName());
1570 args[2].print(stdout);