]>
git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/Gfx.cxx
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 GBool printCommands
= gFalse
;
195 //------------------------------------------------------------------------
197 //------------------------------------------------------------------------
199 Gfx::Gfx(OutputDev
*out1
, int pageNum
, Dict
*resDict
,
200 int dpi
, double x1
, double y1
, double x2
, double y2
, GBool crop
,
201 double cropX1
, double cropY1
, double cropX2
, double cropY2
,
205 // start the resource stack
206 res
= new GfxResources(NULL
);
208 // build font dictionary
211 resDict
->lookup("Font", &obj1
);
213 res
->fonts
= new GfxFontDict(obj1
.getDict());
217 // get XObject dictionary
219 resDict
->lookup("XObject", &res
->xObjDict
);
221 res
->xObjDict
.initNull();
223 // get colorspace dictionary
225 resDict
->lookup("ColorSpace", &res
->colorSpaceDict
);
227 res
->colorSpaceDict
.initNull();
231 state
= new GfxState(dpi
, x1
, y1
, x2
, y2
, rotate
, out
->upsideDown());
232 fontChanged
= gFalse
;
235 out
->startPage(pageNum
, state
);
236 out
->setDefaultCTM(state
->getCTM());
237 out
->updateAll(state
);
241 state
->moveTo(cropX1
, cropY1
);
242 state
->lineTo(cropX2
, cropY1
);
243 state
->lineTo(cropX2
, cropY2
);
244 state
->lineTo(cropX1
, cropY2
);
252 GfxResources
*resPtr
;
254 while (state
->hasSaves()) {
255 state
= state
->restore();
256 out
->restoreState(state
);
268 GfxResources::~GfxResources() {
272 colorSpaceDict
.free();
275 void Gfx::display(Object
*obj
) {
279 if (obj
->isArray()) {
280 for (i
= 0; i
< obj
->arrayGetLength(); ++i
) {
281 obj
->arrayGet(i
, &obj2
);
282 if (!obj2
.isStream()) {
283 error(-1, "Weird page contents");
289 } else if (!obj
->isStream()) {
290 error(-1, "Weird page contents");
293 parser
= new Parser(new Lexer(obj
));
301 Object args
[maxArgs
];
302 int numCmds
, numArgs
;
305 // scan a sequence of objects
308 parser
->getObj(&obj
);
309 while (!obj
.isEOF()) {
311 // got a command - execute it
315 for (i
= 0; i
< numArgs
; ++i
) {
317 args
[i
].print(stdout
);
321 execOp(&obj
, args
, numArgs
);
323 for (i
= 0; i
< numArgs
; ++i
)
327 // periodically update display
328 if (++numCmds
== 200) {
333 // got an argument - save it
334 } else if (numArgs
< maxArgs
) {
335 args
[numArgs
++] = obj
;
337 // too many arguments - something is wrong
339 error(getPos(), "Too many args in content stream");
341 printf("throwing away arg: ");
348 // grab the next object
349 parser
->getObj(&obj
);
353 // args at end with no command
355 error(getPos(), "Leftover args in content stream");
357 printf("%d leftovers:", numArgs
);
358 for (i
= 0; i
< numArgs
; ++i
) {
360 args
[i
].print(stdout
);
364 for (i
= 0; i
< numArgs
; ++i
)
377 void Gfx::execOp(Object
*cmd
, Object args
[], int numArgs
) {
383 name
= cmd
->getName();
384 if (!(op
= findOp(name
))) {
385 if (ignoreUndef
== 0)
386 error(getPos(), "Unknown operator '%s'", name
);
391 if (op
->numArgs
>= 0) {
392 if (numArgs
!= op
->numArgs
) {
393 error(getPos(), "Wrong number (%d) of args to '%s' operator",
398 if (numArgs
> -op
->numArgs
) {
399 error(getPos(), "Too many (%d) args to '%s' operator",
404 for (i
= 0; i
< numArgs
; ++i
) {
405 if (!checkArg(&args
[i
], op
->tchk
[i
])) {
406 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
407 i
, name
, args
[i
].getTypeName());
413 (this->*op
->func
)(args
, numArgs
);
416 Operator
*Gfx::findOp(const char *name
) {
421 // invariant: opTab[a] < name < opTab[b]
424 cmp
= strcmp(opTab
[m
].name
, name
);
437 GBool
Gfx::checkArg(Object
*arg
, TchkType type
) {
439 case tchkBool
: return arg
->isBool();
440 case tchkInt
: return arg
->isInt();
441 case tchkNum
: return arg
->isNum();
442 case tchkString
: return arg
->isString();
443 case tchkName
: return arg
->isName();
444 case tchkArray
: return arg
->isArray();
445 case tchkProps
: return arg
->isDict() || arg
->isName();
446 case tchkSCN
: return arg
->isNum() || arg
->isName();
447 case tchkNone
: return gFalse
;
453 return parser
? parser
->getPos() : -1;
456 GfxFont
*Gfx::lookupFont(const char *name
) {
458 GfxResources
*resPtr
;
460 for (resPtr
= res
; resPtr
; resPtr
= resPtr
->next
) {
462 if ((font
= resPtr
->fonts
->lookup(name
)))
466 error(getPos(), "unknown font tag '%s'", name
);
470 GBool
Gfx::lookupXObject(const char *name
, Object
*obj
) {
471 GfxResources
*resPtr
;
473 for (resPtr
= res
; resPtr
; resPtr
= resPtr
->next
) {
474 if (resPtr
->xObjDict
.isDict()) {
475 if (!resPtr
->xObjDict
.dictLookup(name
, obj
)->isNull())
480 error(getPos(), "XObject '%s' is unknown", name
);
484 void Gfx::lookupColorSpace(const char *name
, Object
*obj
) {
485 GfxResources
*resPtr
;
487 for (resPtr
= res
; resPtr
; resPtr
= resPtr
->next
) {
488 if (resPtr
->colorSpaceDict
.isDict()) {
489 if (!resPtr
->colorSpaceDict
.dictLookup(name
, obj
)->isNull())
497 //------------------------------------------------------------------------
498 // graphics state operators
499 //------------------------------------------------------------------------
501 void Gfx::opSave(Object args
[], int numArgs
) {
505 out
->saveState(state
);
506 state
= state
->save();
509 void Gfx::opRestore(Object args
[], int numArgs
) {
513 state
= state
->restore();
514 out
->restoreState(state
);
516 // Some PDF producers (Macromedia FreeHand) generate a save (q) and
517 // restore (Q) inside a path sequence. The PDF spec seems to imply
518 // that this is illegal. Calling clearPath() here implements the
519 // behavior apparently expected by this software.
523 void Gfx::opConcat(Object args
[], int numArgs
) {
526 state
->concatCTM(args
[0].getNum(), args
[1].getNum(),
527 args
[2].getNum(), args
[3].getNum(),
528 args
[4].getNum(), args
[5].getNum());
529 out
->updateCTM(state
, args
[0].getNum(), args
[1].getNum(),
530 args
[2].getNum(), args
[3].getNum(),
531 args
[4].getNum(), args
[5].getNum());
535 void Gfx::opSetDash(Object args
[], int numArgs
) {
544 a
= args
[0].getArray();
545 length
= a
->getLength();
549 dash
= (double *)gmalloc(length
* sizeof(double));
550 for (i
= 0; i
< length
; ++i
) {
551 dash
[i
] = a
->get(i
, &obj
)->getNum();
555 state
->setLineDash(dash
, length
, args
[1].getNum());
556 out
->updateLineDash(state
);
559 void Gfx::opSetFlat(Object args
[], int numArgs
) {
562 state
->setFlatness((int)args
[0].getNum());
563 out
->updateFlatness(state
);
566 void Gfx::opSetLineJoin(Object args
[], int numArgs
) {
569 state
->setLineJoin(args
[0].getInt());
570 out
->updateLineJoin(state
);
573 void Gfx::opSetLineCap(Object args
[], int numArgs
) {
576 state
->setLineCap(args
[0].getInt());
577 out
->updateLineCap(state
);
580 void Gfx::opSetMiterLimit(Object args
[], int numArgs
) {
583 state
->setMiterLimit(args
[0].getNum());
584 out
->updateMiterLimit(state
);
587 void Gfx::opSetLineWidth(Object args
[], int numArgs
) {
590 state
->setLineWidth(args
[0].getNum());
591 out
->updateLineWidth(state
);
594 void Gfx::opSetExtGState(Object args
[], int numArgs
) {
599 void Gfx::opSetRenderingIntent(Object args
[], int numArgs
) {
604 //------------------------------------------------------------------------
606 //------------------------------------------------------------------------
608 void Gfx::opSetFillGray(Object args
[], int numArgs
) {
611 state
->setFillColorSpace(new GfxColorSpace(colorGray
));
612 state
->setFillGray(args
[0].getNum());
613 out
->updateFillColor(state
);
616 void Gfx::opSetStrokeGray(Object args
[], int numArgs
) {
619 state
->setStrokeColorSpace(new GfxColorSpace(colorGray
));
620 state
->setStrokeGray(args
[0].getNum());
621 out
->updateStrokeColor(state
);
624 void Gfx::opSetFillCMYKColor(Object args
[], int numArgs
) {
627 state
->setFillColorSpace(new GfxColorSpace(colorCMYK
));
628 state
->setFillCMYK(args
[0].getNum(), args
[1].getNum(),
629 args
[2].getNum(), args
[3].getNum());
630 out
->updateFillColor(state
);
633 void Gfx::opSetStrokeCMYKColor(Object args
[], int numArgs
) {
636 state
->setStrokeColorSpace(new GfxColorSpace(colorCMYK
));
637 state
->setStrokeCMYK(args
[0].getNum(), args
[1].getNum(),
638 args
[2].getNum(), args
[3].getNum());
639 out
->updateStrokeColor(state
);
642 void Gfx::opSetFillRGBColor(Object args
[], int numArgs
) {
645 state
->setFillColorSpace(new GfxColorSpace(colorRGB
));
646 state
->setFillRGB(args
[0].getNum(), args
[1].getNum(), args
[2].getNum());
647 out
->updateFillColor(state
);
650 void Gfx::opSetStrokeRGBColor(Object args
[], int numArgs
) {
653 state
->setStrokeColorSpace(new GfxColorSpace(colorRGB
));
654 state
->setStrokeRGB(args
[0].getNum(), args
[1].getNum(), args
[2].getNum());
655 out
->updateStrokeColor(state
);
658 void Gfx::opSetFillColorSpace(Object args
[], int numArgs
) {
660 GfxColorSpace
*colorSpace
;
665 lookupColorSpace(args
[0].getName(), &obj
);
667 colorSpace
= new GfxColorSpace(&args
[0]);
669 colorSpace
= new GfxColorSpace(&obj
);
671 if (colorSpace
->isOk()) {
672 state
->setFillColorSpace(colorSpace
);
675 error(getPos(), "Bad colorspace");
677 x
[0] = x
[1] = x
[2] = x
[3] = 0;
678 state
->setFillColor(x
);
679 out
->updateFillColor(state
);
682 void Gfx::opSetStrokeColorSpace(Object args
[], int numArgs
) {
684 GfxColorSpace
*colorSpace
;
689 lookupColorSpace(args
[0].getName(), &obj
);
691 colorSpace
= new GfxColorSpace(&args
[0]);
693 colorSpace
= new GfxColorSpace(&obj
);
695 if (colorSpace
->isOk()) {
696 state
->setStrokeColorSpace(colorSpace
);
699 error(getPos(), "Bad colorspace");
701 x
[0] = x
[1] = x
[2] = x
[3] = 0;
702 state
->setStrokeColor(x
);
703 out
->updateStrokeColor(state
);
706 void Gfx::opSetFillColor(Object args
[], int numArgs
) {
710 x
[0] = x
[1] = x
[2] = x
[3] = 0;
711 for (i
= 0; i
< numArgs
; ++i
)
712 x
[i
] = args
[i
].getNum();
713 state
->setFillColor(x
);
714 out
->updateFillColor(state
);
717 void Gfx::opSetStrokeColor(Object args
[], int numArgs
) {
721 x
[0] = x
[1] = x
[2] = x
[3] = 0;
722 for (i
= 0; i
< numArgs
; ++i
)
723 x
[i
] = args
[i
].getNum();
724 state
->setStrokeColor(x
);
725 out
->updateStrokeColor(state
);
728 void Gfx::opSetFillColorN(Object args
[], int numArgs
) {
732 x
[0] = x
[1] = x
[2] = x
[3] = 0;
733 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
735 x
[i
] = args
[i
].getNum();
739 state
->setFillColor(x
);
740 out
->updateFillColor(state
);
743 void Gfx::opSetStrokeColorN(Object args
[], int numArgs
) {
747 x
[0] = x
[1] = x
[2] = x
[3] = 0;
748 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
750 x
[i
] = args
[i
].getNum();
754 state
->setStrokeColor(x
);
755 out
->updateStrokeColor(state
);
758 //------------------------------------------------------------------------
759 // path segment operators
760 //------------------------------------------------------------------------
762 void Gfx::opMoveTo(Object args
[], int numArgs
) {
765 state
->moveTo(args
[0].getNum(), args
[1].getNum());
768 void Gfx::opLineTo(Object args
[], int numArgs
) {
771 if (!state
->isCurPt()) {
772 error(getPos(), "No current point in lineto");
775 state
->lineTo(args
[0].getNum(), args
[1].getNum());
778 void Gfx::opCurveTo(Object args
[], int numArgs
) {
779 double x1
, y1
, x2
, y2
, x3
, y3
;
783 if (!state
->isCurPt()) {
784 error(getPos(), "No current point in curveto");
787 x1
= args
[0].getNum();
788 y1
= args
[1].getNum();
789 x2
= args
[2].getNum();
790 y2
= args
[3].getNum();
791 x3
= args
[4].getNum();
792 y3
= args
[5].getNum();
793 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
796 void Gfx::opCurveTo1(Object args
[], int numArgs
) {
797 double x1
, y1
, x2
, y2
, x3
, y3
;
801 if (!state
->isCurPt()) {
802 error(getPos(), "No current point in curveto1");
805 x1
= state
->getCurX();
806 y1
= state
->getCurY();
807 x2
= args
[0].getNum();
808 y2
= args
[1].getNum();
809 x3
= args
[2].getNum();
810 y3
= args
[3].getNum();
811 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
814 void Gfx::opCurveTo2(Object args
[], int numArgs
) {
815 double x1
, y1
, x2
, y2
, x3
, y3
;
819 if (!state
->isCurPt()) {
820 error(getPos(), "No current point in curveto2");
823 x1
= args
[0].getNum();
824 y1
= args
[1].getNum();
825 x2
= args
[2].getNum();
826 y2
= args
[3].getNum();
829 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
832 void Gfx::opRectangle(Object args
[], int numArgs
) {
837 x
= args
[0].getNum();
838 y
= args
[1].getNum();
839 w
= args
[2].getNum();
840 h
= args
[3].getNum();
842 state
->lineTo(x
+ w
, y
);
843 state
->lineTo(x
+ w
, y
+ h
);
844 state
->lineTo(x
, y
+ h
);
848 void Gfx::opClosePath(Object args
[], int numArgs
) {
852 if (!state
->isPath()) {
853 error(getPos(), "No current point in closepath");
859 //------------------------------------------------------------------------
860 // path painting operators
861 //------------------------------------------------------------------------
863 void Gfx::opEndPath(Object args
[], int numArgs
) {
870 void Gfx::opStroke(Object args
[], int numArgs
) {
874 if (!state
->isCurPt()) {
875 //error(getPos(), "No path in stroke");
883 void Gfx::opCloseStroke(Object args
[], int numArgs
) {
887 if (!state
->isCurPt()) {
888 //error(getPos(), "No path in closepath/stroke");
891 if (state
->isPath()) {
898 void Gfx::opFill(Object args
[], int numArgs
) {
902 if (!state
->isCurPt()) {
903 //error(getPos(), "No path in fill");
911 void Gfx::opEOFill(Object args
[], int numArgs
) {
915 if (!state
->isCurPt()) {
916 //error(getPos(), "No path in eofill");
924 void Gfx::opFillStroke(Object args
[], int numArgs
) {
928 if (!state
->isCurPt()) {
929 //error(getPos(), "No path in fill/stroke");
932 if (state
->isPath()) {
939 void Gfx::opCloseFillStroke(Object args
[], int numArgs
) {
943 if (!state
->isCurPt()) {
944 //error(getPos(), "No path in closepath/fill/stroke");
947 if (state
->isPath()) {
955 void Gfx::opEOFillStroke(Object args
[], int numArgs
) {
959 if (!state
->isCurPt()) {
960 //error(getPos(), "No path in eofill/stroke");
963 if (state
->isPath()) {
970 void Gfx::opCloseEOFillStroke(Object args
[], int numArgs
) {
974 if (!state
->isCurPt()) {
975 //error(getPos(), "No path in closepath/eofill/stroke");
978 if (state
->isPath()) {
986 void Gfx::opShFill(Object args
[], int numArgs
) {
991 void Gfx::doEndPath() {
992 if (state
->isPath()) {
993 if (clip
== clipNormal
)
995 else if (clip
== clipEO
)
1002 //------------------------------------------------------------------------
1003 // path clipping operators
1004 //------------------------------------------------------------------------
1006 void Gfx::opClip(Object args
[], int numArgs
) {
1013 void Gfx::opEOClip(Object args
[], int numArgs
) {
1020 //------------------------------------------------------------------------
1021 // text object operators
1022 //------------------------------------------------------------------------
1024 void Gfx::opBeginText(Object args
[], int numArgs
) {
1028 state
->setTextMat(1, 0, 0, 1, 0, 0);
1029 state
->textMoveTo(0, 0);
1030 out
->updateTextMat(state
);
1031 out
->updateTextPos(state
);
1032 fontChanged
= gTrue
;
1035 void Gfx::opEndText(Object args
[], int numArgs
) {
1040 //------------------------------------------------------------------------
1041 // text state operators
1042 //------------------------------------------------------------------------
1044 void Gfx::opSetCharSpacing(Object args
[], int numArgs
) {
1048 state
->setCharSpace(args
[0].getNum());
1049 out
->updateCharSpace(state
);
1052 void Gfx::opSetFont(Object args
[], int numArgs
) {
1057 if (!(font
= lookupFont(args
[0].getName())))
1059 if (printCommands
) {
1060 printf(" font: '%s' %g\n",
1061 font
->getName() ? font
->getName()->getCString() : "???",
1064 state
->setFont(font
, args
[1].getNum());
1065 fontChanged
= gTrue
;
1068 void Gfx::opSetTextLeading(Object args
[], int numArgs
) {
1071 state
->setLeading(args
[0].getNum());
1074 void Gfx::opSetTextRender(Object args
[], int numArgs
) {
1077 state
->setRender(args
[0].getInt());
1078 out
->updateRender(state
);
1081 void Gfx::opSetTextRise(Object args
[], int numArgs
) {
1084 state
->setRise(args
[0].getNum());
1085 out
->updateRise(state
);
1088 void Gfx::opSetWordSpacing(Object args
[], int numArgs
) {
1091 state
->setWordSpace(args
[0].getNum());
1092 out
->updateWordSpace(state
);
1095 void Gfx::opSetHorizScaling(Object args
[], int numArgs
) {
1098 state
->setHorizScaling(args
[0].getNum());
1099 out
->updateHorizScaling(state
);
1102 //------------------------------------------------------------------------
1103 // text positioning operators
1104 //------------------------------------------------------------------------
1106 void Gfx::opTextMove(Object args
[], int numArgs
) {
1111 tx
= state
->getLineX() + args
[0].getNum();
1112 ty
= state
->getLineY() + args
[1].getNum();
1113 state
->textMoveTo(tx
, ty
);
1114 out
->updateTextPos(state
);
1117 void Gfx::opTextMoveSet(Object args
[], int numArgs
) {
1122 tx
= state
->getLineX() + args
[0].getNum();
1123 ty
= args
[1].getNum();
1124 state
->setLeading(-ty
);
1125 ty
+= state
->getLineY();
1126 state
->textMoveTo(tx
, ty
);
1127 out
->updateTextPos(state
);
1130 void Gfx::opSetTextMatrix(Object args
[], int numArgs
) {
1133 state
->setTextMat(args
[0].getNum(), args
[1].getNum(),
1134 args
[2].getNum(), args
[3].getNum(),
1135 args
[4].getNum(), args
[5].getNum());
1136 state
->textMoveTo(0, 0);
1137 out
->updateTextMat(state
);
1138 out
->updateTextPos(state
);
1139 fontChanged
= gTrue
;
1142 void Gfx::opTextNextLine(Object args
[], int numArgs
) {
1148 tx
= state
->getLineX();
1149 ty
= state
->getLineY() - state
->getLeading();
1150 state
->textMoveTo(tx
, ty
);
1151 out
->updateTextPos(state
);
1154 //------------------------------------------------------------------------
1155 // text string operators
1156 //------------------------------------------------------------------------
1158 void Gfx::opShowText(Object args
[], int numArgs
) {
1161 if (!state
->getFont()) {
1162 error(getPos(), "No font in show");
1165 doShowText(args
[0].getString());
1168 void Gfx::opMoveShowText(Object args
[], int numArgs
) {
1173 if (!state
->getFont()) {
1174 error(getPos(), "No font in move/show");
1177 tx
= state
->getLineX();
1178 ty
= state
->getLineY() - state
->getLeading();
1179 state
->textMoveTo(tx
, ty
);
1180 out
->updateTextPos(state
);
1181 doShowText(args
[0].getString());
1184 void Gfx::opMoveSetShowText(Object args
[], int numArgs
) {
1189 if (!state
->getFont()) {
1190 error(getPos(), "No font in move/set/show");
1193 state
->setWordSpace(args
[0].getNum());
1194 state
->setCharSpace(args
[1].getNum());
1195 tx
= state
->getLineX();
1196 ty
= state
->getLineY() - state
->getLeading();
1197 state
->textMoveTo(tx
, ty
);
1198 out
->updateWordSpace(state
);
1199 out
->updateCharSpace(state
);
1200 out
->updateTextPos(state
);
1201 doShowText(args
[2].getString());
1204 void Gfx::opShowSpaceText(Object args
[], int numArgs
) {
1211 if (!state
->getFont()) {
1212 error(getPos(), "No font in show/space");
1215 a
= args
[0].getArray();
1216 for (i
= 0; i
< a
->getLength(); ++i
) {
1219 state
->textShift(-obj
.getNum() * 0.001 * state
->getFontSize());
1220 out
->updateTextShift(state
, obj
.getNum());
1221 } else if (obj
.isString()) {
1222 doShowText(obj
.getString());
1224 error(getPos(), "Element of show/space array must be number or string");
1230 void Gfx::doShowText(GString
*s
) {
1232 GfxFontEncoding16
*enc
;
1240 double dx
, dy
, width
, height
, w
, h
, x
, y
;
1241 double oldCTM
[6], newCTM
[6];
1247 double dx
, dy
, width
, height
, w
, h
, sWidth
, sHeight
;
1251 out
->updateFont(state
);
1252 fontChanged
= gFalse
;
1254 font
= state
->getFont();
1257 if (font
->is16Bit()) {
1258 enc
= font
->getEncoding16();
1259 if (out
->useDrawChar()) {
1260 out
->beginString(state
, s
);
1263 s16
= new GString();
1265 sWidth
= sHeight
= 0;
1266 state
->textTransformDelta(0, state
->getRise(), &dx
, &dy
);
1267 p
= (Guchar
*)s
->getCString();
1270 m
= getNextChar16(enc
, p
, &c16
);
1271 if (enc
->wMode
== 0) {
1272 width
= state
->getFontSize() * state
->getHorizScaling() *
1273 font
->getWidth16(c16
) +
1274 state
->getCharSpace();
1276 width
+= state
->getWordSpace();
1281 height
= state
->getFontSize() * font
->getHeight16(c16
);
1283 state
->textTransformDelta(width
, height
, &w
, &h
);
1284 if (out
->useDrawChar()) {
1285 out
->drawChar16(state
, state
->getCurX() + dx
, state
->getCurY() + dy
,
1287 state
->textShift(width
, height
);
1289 s16a
[0] = (char)(c16
>> 8);
1290 s16a
[1] = (char)c16
;
1291 s16
->append(s16a
, 2);
1298 if (out
->useDrawChar()) {
1299 out
->endString(state
);
1301 out
->drawString16(state
, s16
);
1303 state
->textShift(sWidth
, sHeight
);
1309 //~ also check out->renderType3()
1310 if (font
->getType() == fontType3
) {
1311 out
->beginString(state
, s
);
1312 mat
= state
->getCTM();
1313 for (i
= 0; i
< 6; ++i
) {
1316 mat
= state
->getTextMat();
1317 newCTM
[0] = mat
[0] * oldCTM
[0] + mat
[1] * oldCTM
[2];
1318 newCTM
[1] = mat
[0] * oldCTM
[1] + mat
[1] * oldCTM
[3];
1319 newCTM
[2] = mat
[2] * oldCTM
[0] + mat
[3] * oldCTM
[2];
1320 newCTM
[3] = mat
[2] * oldCTM
[1] + mat
[3] * oldCTM
[3];
1321 mat
= font
->getFontMatrix();
1322 newCTM
[0] = mat
[0] * newCTM
[0] + mat
[1] * newCTM
[2];
1323 newCTM
[1] = mat
[0] * newCTM
[1] + mat
[1] * newCTM
[3];
1324 newCTM
[2] = mat
[2] * newCTM
[0] + mat
[3] * newCTM
[2];
1325 newCTM
[3] = mat
[2] * newCTM
[1] + mat
[3] * newCTM
[3];
1326 newCTM
[0] *= state
->getFontSize();
1327 newCTM
[3] *= state
->getFontSize();
1328 newCTM
[0] *= state
->getHorizScaling();
1329 newCTM
[2] *= state
->getHorizScaling();
1330 state
->textTransformDelta(0, state
->getRise(), &dx
, &dy
);
1332 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
1334 font
->getCharProc(c8
, &charProc
);
1335 state
->transform(state
->getCurX() + dx
, state
->getCurY() + dy
, &x
, &y
);
1336 state
->setCTM(newCTM
[0], newCTM
[1], newCTM
[2], newCTM
[3], x
, y
);
1337 //~ out->updateCTM(???)
1338 if (charProc
.isStream()) {
1341 error(getPos(), "Missing or bad Type3 CharProc entry");
1343 state
->setCTM(oldCTM
[0], oldCTM
[1], oldCTM
[2],
1344 oldCTM
[3], oldCTM
[4], oldCTM
[5]);
1345 //~ out->updateCTM(???) - use gsave/grestore instead?
1347 width
= state
->getFontSize() * state
->getHorizScaling() *
1348 font
->getWidth(c8
) +
1349 state
->getCharSpace();
1351 width
+= state
->getWordSpace();
1353 state
->textShift(width
);
1356 out
->endString(state
);
1359 if (out
->useDrawChar()) {
1360 out
->beginString(state
, s
);
1361 state
->textTransformDelta(0, state
->getRise(), &dx
, &dy
);
1362 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
1364 width
= state
->getFontSize() * state
->getHorizScaling() *
1365 font
->getWidth(c8
) +
1366 state
->getCharSpace();
1368 width
+= state
->getWordSpace();
1369 state
->textTransformDelta(width
, 0, &w
, &h
);
1370 out
->drawChar(state
, state
->getCurX() + dx
, state
->getCurY() + dy
,
1372 state
->textShift(width
);
1374 out
->endString(state
);
1376 out
->drawString(state
, s
);
1377 width
= state
->getFontSize() * state
->getHorizScaling() *
1379 s
->getLength() * state
->getCharSpace();
1380 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
1382 width
+= state
->getWordSpace();
1384 state
->textShift(width
);
1389 int Gfx::getNextChar16(GfxFontEncoding16
*enc
, Guchar
*p
, int *c16
) {
1394 n
= enc
->codeLen
[*p
];
1396 *c16
= enc
->map1
[*p
];
1398 code
= (p
[0] << 8) + p
[1];
1401 // invariant: map2[2*a] <= code < map2[2*b]
1404 if (enc
->map2
[2*m
] <= code
)
1406 else if (enc
->map2
[2*m
] > code
)
1411 *c16
= enc
->map2
[2*a
+1] + (code
- enc
->map2
[2*a
]);
1416 //------------------------------------------------------------------------
1417 // XObject operators
1418 //------------------------------------------------------------------------
1420 void Gfx::opXObject(Object args
[], int numArgs
) {
1428 if (!lookupXObject(args
[0].getName(), &obj1
))
1430 if (!obj1
.isStream()) {
1431 error(getPos(), "XObject '%s' is wrong type", args
[0].getName());
1436 obj1
.streamGetDict()->lookup("OPI", &opiDict
);
1437 if (opiDict
.isDict()) {
1438 out
->opiBegin(state
, opiDict
.getDict());
1441 obj1
.streamGetDict()->lookup("Subtype", &obj2
);
1442 if (obj2
.isName("Image"))
1443 doImage(obj1
.getStream(), gFalse
);
1444 else if (obj2
.isName("Form"))
1446 else if (obj2
.isName())
1447 error(getPos(), "Unknown XObject subtype '%s'", obj2
.getName());
1449 error(getPos(), "XObject subtype is missing or wrong type");
1452 if (opiDict
.isDict()) {
1453 out
->opiEnd(state
, opiDict
.getDict());
1460 void Gfx::doImage(Stream
*str
, GBool inlineImg
) {
1466 GfxColorSpace
*colorSpace
;
1467 GfxImageColorMap
*colorMap
;
1471 dict
= str
->getDict();
1474 dict
->lookup("Width", &obj1
);
1475 if (obj1
.isNull()) {
1477 dict
->lookup("W", &obj1
);
1481 width
= obj1
.getInt();
1483 dict
->lookup("Height", &obj1
);
1484 if (obj1
.isNull()) {
1486 dict
->lookup("H", &obj1
);
1490 height
= obj1
.getInt();
1494 dict
->lookup("ImageMask", &obj1
);
1495 if (obj1
.isNull()) {
1497 dict
->lookup("IM", &obj1
);
1501 mask
= obj1
.getBool();
1502 else if (!obj1
.isNull())
1507 dict
->lookup("BitsPerComponent", &obj1
);
1508 if (obj1
.isNull()) {
1510 dict
->lookup("BPC", &obj1
);
1514 bits
= obj1
.getInt();
1520 // check for inverted mask
1524 dict
->lookup("Decode", &obj1
);
1525 if (obj1
.isNull()) {
1527 dict
->lookup("D", &obj1
);
1529 if (obj1
.isArray()) {
1530 obj1
.arrayGet(0, &obj2
);
1531 if (obj2
.isInt() && obj2
.getInt() == 1)
1534 } else if (!obj1
.isNull()) {
1540 out
->drawImageMask(state
, str
, width
, height
, invert
, inlineImg
);
1544 // get color space and color map
1545 dict
->lookup("ColorSpace", &obj1
);
1546 if (obj1
.isNull()) {
1548 dict
->lookup("CS", &obj1
);
1550 if (obj1
.isName()) {
1551 lookupColorSpace(obj1
.getName(), &obj2
);
1552 if (!obj2
.isNull()) {
1559 colorSpace
= new GfxColorSpace(&obj1
);
1561 if (!colorSpace
->isOk()) {
1565 dict
->lookup("Decode", &obj1
);
1566 if (obj1
.isNull()) {
1568 dict
->lookup("D", &obj1
);
1570 colorMap
= new GfxImageColorMap(bits
, &obj1
, colorSpace
);
1572 if (!colorMap
->isOk()) {
1578 out
->drawImage(state
, str
, width
, height
, colorMap
, inlineImg
);
1587 error(getPos(), "Bad image parameters");
1590 void Gfx::doForm(Object
*str
) {
1592 Object matrixObj
, bboxObj
;
1596 dict
= str
->streamGetDict();
1599 dict
->lookup("FormType", &obj1
);
1600 if (!(obj1
.isInt() && obj1
.getInt() == 1)) {
1601 error(getPos(), "Unknown form type");
1605 // get matrix and bounding box
1606 dict
->lookup("Matrix", &matrixObj
);
1607 if (!matrixObj
.isArray()) {
1609 error(getPos(), "Bad form matrix");
1612 dict
->lookup("BBox", &bboxObj
);
1613 if (!bboxObj
.isArray()) {
1616 error(getPos(), "Bad form bounding box");
1620 doForm1(str
, dict
, &matrixObj
, &bboxObj
);
1626 void Gfx::doWidgetForm(Object
*str
, double x
, double y
) {
1628 Object matrixObj
, bboxObj
;
1632 dict
= str
->streamGetDict();
1635 dict
->lookup("BBox", &bboxObj
);
1636 if (!bboxObj
.isArray()) {
1638 error(getPos(), "Bad form bounding box");
1643 matrixObj
.initArray();
1645 matrixObj
.arrayAdd(&obj1
);
1647 matrixObj
.arrayAdd(&obj1
);
1649 matrixObj
.arrayAdd(&obj1
);
1651 matrixObj
.arrayAdd(&obj1
);
1653 matrixObj
.arrayAdd(&obj1
);
1655 matrixObj
.arrayAdd(&obj1
);
1657 doForm1(str
, dict
, &matrixObj
, &bboxObj
);
1663 void Gfx::doForm1(Object
*str
, Dict
*dict
,
1664 Object
*matrixObj
, Object
*bboxObj
) {
1666 GfxResources
*resPtr
;
1672 // push new resources on stack
1673 res
= new GfxResources(res
);
1674 dict
->lookup("Resources", &obj1
);
1675 if (obj1
.isDict()) {
1676 resDict
= obj1
.getDict();
1678 resDict
->lookup("Font", &obj2
);
1680 res
->fonts
= new GfxFontDict(obj2
.getDict());
1682 resDict
->lookup("XObject", &res
->xObjDict
);
1683 resDict
->lookup("ColorSpace", &res
->colorSpaceDict
);
1687 // save current graphics state
1688 out
->saveState(state
);
1689 state
= state
->save();
1691 // save current parser
1694 // set form transformation matrix
1695 for (i
= 0; i
< 6; ++i
) {
1696 matrixObj
->arrayGet(i
, &obj1
);
1697 m
[i
] = obj1
.getNum();
1700 state
->concatCTM(m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1701 out
->updateCTM(state
, m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1703 // set form bounding box
1704 for (i
= 0; i
< 4; ++i
) {
1705 bboxObj
->arrayGet(i
, &obj1
);
1706 m
[i
] = obj1
.getNum();
1709 state
->moveTo(m
[0], m
[1]);
1710 state
->lineTo(m
[2], m
[1]);
1711 state
->lineTo(m
[2], m
[3]);
1712 state
->lineTo(m
[0], m
[3]);
1723 // restore graphics state
1724 state
= state
->restore();
1725 out
->restoreState(state
);
1727 // pop resource stack
1735 //------------------------------------------------------------------------
1736 // in-line image operators
1737 //------------------------------------------------------------------------
1739 void Gfx::opBeginImage(Object args
[], int numArgs
) {
1746 // build dict/stream
1747 str
= buildImageStream();
1749 // display the image
1751 doImage(str
, gTrue
);
1754 c1
= str
->getBaseStream()->getChar();
1755 c2
= str
->getBaseStream()->getChar();
1756 while (!(c1
== 'E' && c2
== 'I') && c2
!= EOF
) {
1758 c2
= str
->getBaseStream()->getChar();
1764 Stream
*Gfx::buildImageStream() {
1772 parser
->getObj(&obj
);
1773 while (!obj
.isCmd("ID") && !obj
.isEOF()) {
1774 if (!obj
.isName()) {
1775 error(getPos(), "Inline image dictionary key must be a name object");
1777 parser
->getObj(&obj
);
1779 key
= copyString(obj
.getName());
1781 parser
->getObj(&obj
);
1782 if (obj
.isEOF() || obj
.isError())
1784 dict
.dictAdd(key
, &obj
);
1786 parser
->getObj(&obj
);
1789 error(getPos(), "End of file in inline image");
1793 str
= new EmbedStream(parser
->getStream(), &dict
);
1794 str
= str
->addFilters(&dict
);
1799 void Gfx::opImageData(Object args
[], int numArgs
) {
1803 error(getPos(), "Internal: got 'ID' operator");
1806 void Gfx::opEndImage(Object args
[], int numArgs
) {
1810 error(getPos(), "Internal: got 'EI' operator");
1813 //------------------------------------------------------------------------
1814 // type 3 font operators
1815 //------------------------------------------------------------------------
1817 void Gfx::opSetCharWidth(Object args
[], int numArgs
) {
1821 error(getPos(), "Encountered 'd0' operator in content stream");
1824 void Gfx::opSetCacheDevice(Object args
[], int numArgs
) {
1828 error(getPos(), "Encountered 'd1' operator in content stream");
1831 //------------------------------------------------------------------------
1832 // compatibility operators
1833 //------------------------------------------------------------------------
1835 void Gfx::opBeginIgnoreUndef(Object args
[], int numArgs
) {
1842 void Gfx::opEndIgnoreUndef(Object args
[], int numArgs
) {
1846 if (ignoreUndef
> 0)
1850 //------------------------------------------------------------------------
1851 // marked content operators
1852 //------------------------------------------------------------------------
1854 void Gfx::opBeginMarkedContent(Object args
[], int numArgs
) {
1857 if (printCommands
) {
1858 printf(" marked content: %s ", args
[0].getName());
1860 args
[2].print(stdout
);
1865 void Gfx::opEndMarkedContent(Object args
[], int numArgs
) {
1870 void Gfx::opMarkPoint(Object args
[], int numArgs
) {
1873 if (printCommands
) {
1874 printf(" mark point: %s ", args
[0].getName());
1876 args
[2].print(stdout
);