]>
git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/Gfx.cxx
1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
26 #include "OutputDev.h"
32 //------------------------------------------------------------------------
34 //------------------------------------------------------------------------
36 // Max number of splits along the t axis for an axial shading fill.
37 #define axialMaxSplits 256
39 // Max delta allowed in any color component for an axial shading fill.
40 #define axialColorDelta (1 / 256.0)
42 //------------------------------------------------------------------------
44 //------------------------------------------------------------------------
46 Operator
Gfx::opTab
[] = {
47 {"\"", 3, {tchkNum
, tchkNum
, tchkString
},
48 &Gfx::opMoveSetShowText
},
49 {"'", 1, {tchkString
},
50 &Gfx::opMoveShowText
},
54 &Gfx::opEOFillStroke
},
55 {"BDC", 2, {tchkName
, tchkProps
},
56 &Gfx::opBeginMarkedContent
},
59 {"BMC", 1, {tchkName
},
60 &Gfx::opBeginMarkedContent
},
64 &Gfx::opBeginIgnoreUndef
},
66 &Gfx::opSetStrokeColorSpace
},
67 {"DP", 2, {tchkName
, tchkProps
},
73 {"EMC", 0, {tchkNone
},
74 &Gfx::opEndMarkedContent
},
78 &Gfx::opEndIgnoreUndef
},
82 &Gfx::opSetStrokeGray
},
87 {"K", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
88 &Gfx::opSetStrokeCMYKColor
},
90 &Gfx::opSetMiterLimit
},
95 {"RG", 3, {tchkNum
, tchkNum
, tchkNum
},
96 &Gfx::opSetStrokeRGBColor
},
99 {"SC", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
100 &Gfx::opSetStrokeColor
},
101 {"SCN", -5, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
103 &Gfx::opSetStrokeColorN
},
104 {"T*", 0, {tchkNone
},
105 &Gfx::opTextNextLine
},
106 {"TD", 2, {tchkNum
, tchkNum
},
107 &Gfx::opTextMoveSet
},
108 {"TJ", 1, {tchkArray
},
109 &Gfx::opShowSpaceText
},
111 &Gfx::opSetTextLeading
},
113 &Gfx::opSetCharSpacing
},
114 {"Td", 2, {tchkNum
, tchkNum
},
116 {"Tf", 2, {tchkName
, tchkNum
},
118 {"Tj", 1, {tchkString
},
120 {"Tm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
122 &Gfx::opSetTextMatrix
},
124 &Gfx::opSetTextRender
},
126 &Gfx::opSetTextRise
},
128 &Gfx::opSetWordSpacing
},
130 &Gfx::opSetHorizScaling
},
133 {"W*", 0, {tchkNone
},
136 &Gfx::opCloseFillStroke
},
137 {"b*", 0, {tchkNone
},
138 &Gfx::opCloseEOFillStroke
},
139 {"c", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
142 {"cm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
145 {"cs", 1, {tchkName
},
146 &Gfx::opSetFillColorSpace
},
147 {"d", 2, {tchkArray
, tchkNum
},
149 {"d0", 2, {tchkNum
, tchkNum
},
150 &Gfx::opSetCharWidth
},
151 {"d1", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
153 &Gfx::opSetCacheDevice
},
156 {"f*", 0, {tchkNone
},
159 &Gfx::opSetFillGray
},
160 {"gs", 1, {tchkName
},
161 &Gfx::opSetExtGState
},
167 &Gfx::opSetLineJoin
},
168 {"k", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
169 &Gfx::opSetFillCMYKColor
},
170 {"l", 2, {tchkNum
, tchkNum
},
172 {"m", 2, {tchkNum
, tchkNum
},
178 {"re", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
180 {"rg", 3, {tchkNum
, tchkNum
, tchkNum
},
181 &Gfx::opSetFillRGBColor
},
182 {"ri", 1, {tchkName
},
183 &Gfx::opSetRenderingIntent
},
185 &Gfx::opCloseStroke
},
186 {"sc", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
187 &Gfx::opSetFillColor
},
188 {"scn", -5, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
190 &Gfx::opSetFillColorN
},
191 {"sh", 1, {tchkName
},
193 {"v", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
196 &Gfx::opSetLineWidth
},
197 {"y", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
201 #define numOps (sizeof(opTab) / sizeof(Operator))
203 //------------------------------------------------------------------------
205 //------------------------------------------------------------------------
207 GfxResources::GfxResources(XRef
*xref
, Dict
*resDict
, GfxResources
*nextA
) {
212 // build font dictionary
214 resDict
->lookup("Font", &obj1
);
216 fonts
= new GfxFontDict(xref
, obj1
.getDict());
220 // get XObject dictionary
221 resDict
->lookup("XObject", &xObjDict
);
223 // get color space dictionary
224 resDict
->lookup("ColorSpace", &colorSpaceDict
);
226 // get pattern dictionary
227 resDict
->lookup("Pattern", &patternDict
);
229 // get shading dictionary
230 resDict
->lookup("Shading", &shadingDict
);
232 // get graphics state parameter dictionary
233 resDict
->lookup("ExtGState", &gStateDict
);
238 colorSpaceDict
.initNull();
239 patternDict
.initNull();
240 gStateDict
.initNull();
246 GfxResources::~GfxResources() {
251 colorSpaceDict
.free();
257 GfxFont
*GfxResources::lookupFont(char *name
) {
259 GfxResources
*resPtr
;
261 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
263 if ((font
= resPtr
->fonts
->lookup(name
)))
267 error(-1, "Unknown font tag '%s'", name
);
271 GBool
GfxResources::lookupXObject(char *name
, Object
*obj
) {
272 GfxResources
*resPtr
;
274 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
275 if (resPtr
->xObjDict
.isDict()) {
276 if (!resPtr
->xObjDict
.dictLookup(name
, obj
)->isNull())
281 error(-1, "XObject '%s' is unknown", name
);
285 GBool
GfxResources::lookupXObjectNF(char *name
, Object
*obj
) {
286 GfxResources
*resPtr
;
288 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
289 if (resPtr
->xObjDict
.isDict()) {
290 if (!resPtr
->xObjDict
.dictLookupNF(name
, obj
)->isNull())
295 error(-1, "XObject '%s' is unknown", name
);
299 void GfxResources::lookupColorSpace(char *name
, Object
*obj
) {
300 GfxResources
*resPtr
;
302 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
303 if (resPtr
->colorSpaceDict
.isDict()) {
304 if (!resPtr
->colorSpaceDict
.dictLookup(name
, obj
)->isNull()) {
313 GfxPattern
*GfxResources::lookupPattern(char *name
) {
314 GfxResources
*resPtr
;
318 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
319 if (resPtr
->patternDict
.isDict()) {
320 if (!resPtr
->patternDict
.dictLookup(name
, &obj
)->isNull()) {
321 pattern
= GfxPattern::parse(&obj
);
328 error(-1, "Unknown pattern '%s'", name
);
332 GfxShading
*GfxResources::lookupShading(char *name
) {
333 GfxResources
*resPtr
;
337 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
338 if (resPtr
->shadingDict
.isDict()) {
339 if (!resPtr
->shadingDict
.dictLookup(name
, &obj
)->isNull()) {
340 shading
= GfxShading::parse(&obj
);
347 error(-1, "Unknown shading '%s'", name
);
351 GBool
GfxResources::lookupGState(char *name
, Object
*obj
) {
352 GfxResources
*resPtr
;
354 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
355 if (resPtr
->gStateDict
.isDict()) {
356 if (!resPtr
->gStateDict
.dictLookup(name
, obj
)->isNull()) {
362 error(-1, "ExtGState '%s' is unknown", name
);
366 //------------------------------------------------------------------------
368 //------------------------------------------------------------------------
370 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, int pageNum
, Dict
*resDict
, double dpi
,
371 PDFRectangle
*box
, GBool crop
, PDFRectangle
*cropBox
, int rotate
,
372 GBool printCommandsA
) {
376 printCommands
= printCommandsA
;
378 // start the resource stack
379 res
= new GfxResources(xref
, resDict
, NULL
);
383 state
= new GfxState(dpi
, box
, rotate
, out
->upsideDown());
384 fontChanged
= gFalse
;
387 out
->startPage(pageNum
, state
);
388 out
->setDefaultCTM(state
->getCTM());
389 out
->updateAll(state
);
390 for (i
= 0; i
< 6; ++i
) {
391 baseMatrix
[i
] = state
->getCTM()[i
];
396 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
397 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
398 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
399 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
408 GfxResources
*resPtr
;
410 while (state
->hasSaves()) {
411 state
= state
->restore();
412 out
->restoreState(state
);
416 resPtr
= res
->getNext();
424 void Gfx::display(Object
*obj
, GBool topLevel
) {
428 if (obj
->isArray()) {
429 for (i
= 0; i
< obj
->arrayGetLength(); ++i
) {
430 obj
->arrayGet(i
, &obj2
);
431 if (!obj2
.isStream()) {
432 error(-1, "Weird page contents");
438 } else if (!obj
->isStream()) {
439 error(-1, "Weird page contents");
442 parser
= new Parser(xref
, new Lexer(xref
, obj
));
448 void Gfx::go(GBool topLevel
) {
450 Object args
[maxArgs
];
451 int numCmds
, numArgs
;
454 // scan a sequence of objects
457 parser
->getObj(&obj
);
458 while (!obj
.isEOF()) {
460 // got a command - execute it
464 for (i
= 0; i
< numArgs
; ++i
) {
466 args
[i
].print(stdout
);
471 execOp(&obj
, args
, numArgs
);
473 for (i
= 0; i
< numArgs
; ++i
)
477 // periodically update display
478 if (++numCmds
== 200) {
483 // got an argument - save it
484 } else if (numArgs
< maxArgs
) {
485 args
[numArgs
++] = obj
;
487 // too many arguments - something is wrong
489 error(getPos(), "Too many args in content stream");
491 printf("throwing away arg: ");
499 // grab the next object
500 parser
->getObj(&obj
);
504 // args at end with no command
506 error(getPos(), "Leftover args in content stream");
508 printf("%d leftovers:", numArgs
);
509 for (i
= 0; i
< numArgs
; ++i
) {
511 args
[i
].print(stdout
);
516 for (i
= 0; i
< numArgs
; ++i
)
521 if (topLevel
&& numCmds
> 0) {
526 void Gfx::execOp(Object
*cmd
, Object args
[], int numArgs
) {
532 name
= cmd
->getName();
533 if (!(op
= findOp(name
))) {
534 if (ignoreUndef
== 0)
535 error(getPos(), "Unknown operator '%s'", name
);
540 if (op
->numArgs
>= 0) {
541 if (numArgs
!= op
->numArgs
) {
542 error(getPos(), "Wrong number (%d) of args to '%s' operator",
547 if (numArgs
> -op
->numArgs
) {
548 error(getPos(), "Too many (%d) args to '%s' operator",
553 for (i
= 0; i
< numArgs
; ++i
) {
554 if (!checkArg(&args
[i
], op
->tchk
[i
])) {
555 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
556 i
, name
, args
[i
].getTypeName());
562 (this->*op
->func
)(args
, numArgs
);
565 Operator
*Gfx::findOp(char *name
) {
570 // invariant: opTab[a] < name < opTab[b]
573 cmp
= strcmp(opTab
[m
].name
, name
);
586 GBool
Gfx::checkArg(Object
*arg
, TchkType type
) {
588 case tchkBool
: return arg
->isBool();
589 case tchkInt
: return arg
->isInt();
590 case tchkNum
: return arg
->isNum();
591 case tchkString
: return arg
->isString();
592 case tchkName
: return arg
->isName();
593 case tchkArray
: return arg
->isArray();
594 case tchkProps
: return arg
->isDict() || arg
->isName();
595 case tchkSCN
: return arg
->isNum() || arg
->isName();
596 case tchkNone
: return gFalse
;
602 return parser
? parser
->getPos() : -1;
605 //------------------------------------------------------------------------
606 // graphics state operators
607 //------------------------------------------------------------------------
609 void Gfx::opSave(Object args
[], int numArgs
) {
610 out
->saveState(state
);
611 state
= state
->save();
614 void Gfx::opRestore(Object args
[], int numArgs
) {
615 state
= state
->restore();
616 out
->restoreState(state
);
618 // Some PDF producers (Macromedia FreeHand) generate a save (q) and
619 // restore (Q) inside a path sequence. The PDF spec seems to imply
620 // that this is illegal. Calling clearPath() here implements the
621 // behavior apparently expected by this software.
625 void Gfx::opConcat(Object args
[], int numArgs
) {
626 state
->concatCTM(args
[0].getNum(), args
[1].getNum(),
627 args
[2].getNum(), args
[3].getNum(),
628 args
[4].getNum(), args
[5].getNum());
629 out
->updateCTM(state
, args
[0].getNum(), args
[1].getNum(),
630 args
[2].getNum(), args
[3].getNum(),
631 args
[4].getNum(), args
[5].getNum());
635 void Gfx::opSetDash(Object args
[], int numArgs
) {
642 a
= args
[0].getArray();
643 length
= a
->getLength();
647 dash
= (double *)gmalloc(length
* sizeof(double));
648 for (i
= 0; i
< length
; ++i
) {
649 dash
[i
] = a
->get(i
, &obj
)->getNum();
653 state
->setLineDash(dash
, length
, args
[1].getNum());
654 out
->updateLineDash(state
);
657 void Gfx::opSetFlat(Object args
[], int numArgs
) {
658 state
->setFlatness((int)args
[0].getNum());
659 out
->updateFlatness(state
);
662 void Gfx::opSetLineJoin(Object args
[], int numArgs
) {
663 state
->setLineJoin(args
[0].getInt());
664 out
->updateLineJoin(state
);
667 void Gfx::opSetLineCap(Object args
[], int numArgs
) {
668 state
->setLineCap(args
[0].getInt());
669 out
->updateLineCap(state
);
672 void Gfx::opSetMiterLimit(Object args
[], int numArgs
) {
673 state
->setMiterLimit(args
[0].getNum());
674 out
->updateMiterLimit(state
);
677 void Gfx::opSetLineWidth(Object args
[], int numArgs
) {
678 state
->setLineWidth(args
[0].getNum());
679 out
->updateLineWidth(state
);
682 void Gfx::opSetExtGState(Object args
[], int numArgs
) {
685 if (!res
->lookupGState(args
[0].getName(), &obj1
)) {
688 if (!obj1
.isDict()) {
689 error(getPos(), "ExtGState '%s' is wrong type", args
[0].getName());
693 if (obj1
.dictLookup("ca", &obj2
)->isNum()) {
694 state
->setFillOpacity(obj2
.getNum());
695 out
->updateFillOpacity(state
);
698 if (obj1
.dictLookup("CA", &obj2
)->isNum()) {
699 state
->setStrokeOpacity(obj2
.getNum());
700 out
->updateStrokeOpacity(state
);
706 void Gfx::opSetRenderingIntent(Object args
[], int numArgs
) {
709 //------------------------------------------------------------------------
711 //------------------------------------------------------------------------
713 void Gfx::opSetFillGray(Object args
[], int numArgs
) {
716 state
->setFillPattern(NULL
);
717 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
718 color
.c
[0] = args
[0].getNum();
719 state
->setFillColor(&color
);
720 out
->updateFillColor(state
);
723 void Gfx::opSetStrokeGray(Object args
[], int numArgs
) {
726 state
->setStrokePattern(NULL
);
727 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
728 color
.c
[0] = args
[0].getNum();
729 state
->setStrokeColor(&color
);
730 out
->updateStrokeColor(state
);
733 void Gfx::opSetFillCMYKColor(Object args
[], int numArgs
) {
737 state
->setFillPattern(NULL
);
738 state
->setFillColorSpace(new GfxDeviceCMYKColorSpace());
739 for (i
= 0; i
< 4; ++i
) {
740 color
.c
[i
] = args
[i
].getNum();
742 state
->setFillColor(&color
);
743 out
->updateFillColor(state
);
746 void Gfx::opSetStrokeCMYKColor(Object args
[], int numArgs
) {
750 state
->setStrokePattern(NULL
);
751 state
->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
752 for (i
= 0; i
< 4; ++i
) {
753 color
.c
[i
] = args
[i
].getNum();
755 state
->setStrokeColor(&color
);
756 out
->updateStrokeColor(state
);
759 void Gfx::opSetFillRGBColor(Object args
[], int numArgs
) {
763 state
->setFillPattern(NULL
);
764 state
->setFillColorSpace(new GfxDeviceRGBColorSpace());
765 for (i
= 0; i
< 3; ++i
) {
766 color
.c
[i
] = args
[i
].getNum();
768 state
->setFillColor(&color
);
769 out
->updateFillColor(state
);
772 void Gfx::opSetStrokeRGBColor(Object args
[], int numArgs
) {
776 state
->setStrokePattern(NULL
);
777 state
->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
778 for (i
= 0; i
< 3; ++i
) {
779 color
.c
[i
] = args
[i
].getNum();
781 state
->setStrokeColor(&color
);
782 out
->updateStrokeColor(state
);
785 void Gfx::opSetFillColorSpace(Object args
[], int numArgs
) {
787 GfxColorSpace
*colorSpace
;
791 state
->setFillPattern(NULL
);
792 res
->lookupColorSpace(args
[0].getName(), &obj
);
794 colorSpace
= GfxColorSpace::parse(&args
[0]);
796 colorSpace
= GfxColorSpace::parse(&obj
);
800 state
->setFillColorSpace(colorSpace
);
802 error(getPos(), "Bad color space (fill)");
804 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
807 state
->setFillColor(&color
);
808 out
->updateFillColor(state
);
811 void Gfx::opSetStrokeColorSpace(Object args
[], int numArgs
) {
813 GfxColorSpace
*colorSpace
;
817 state
->setStrokePattern(NULL
);
818 res
->lookupColorSpace(args
[0].getName(), &obj
);
820 colorSpace
= GfxColorSpace::parse(&args
[0]);
822 colorSpace
= GfxColorSpace::parse(&obj
);
826 state
->setStrokeColorSpace(colorSpace
);
828 error(getPos(), "Bad color space (stroke)");
830 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
833 state
->setStrokeColor(&color
);
834 out
->updateStrokeColor(state
);
837 void Gfx::opSetFillColor(Object args
[], int numArgs
) {
841 state
->setFillPattern(NULL
);
842 for (i
= 0; i
< numArgs
; ++i
) {
843 color
.c
[i
] = args
[i
].getNum();
845 state
->setFillColor(&color
);
846 out
->updateFillColor(state
);
849 void Gfx::opSetStrokeColor(Object args
[], int numArgs
) {
853 state
->setStrokePattern(NULL
);
854 for (i
= 0; i
< numArgs
; ++i
) {
855 color
.c
[i
] = args
[i
].getNum();
857 state
->setStrokeColor(&color
);
858 out
->updateStrokeColor(state
);
861 void Gfx::opSetFillColorN(Object args
[], int numArgs
) {
866 if (state
->getFillColorSpace()->getMode() == csPattern
) {
868 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
869 if (args
[i
].isNum()) {
870 color
.c
[i
] = args
[i
].getNum();
873 state
->setFillColor(&color
);
874 out
->updateFillColor(state
);
876 if (args
[numArgs
-1].isName() &&
877 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
878 state
->setFillPattern(pattern
);
882 state
->setFillPattern(NULL
);
883 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
884 if (args
[i
].isNum()) {
885 color
.c
[i
] = args
[i
].getNum();
888 state
->setFillColor(&color
);
889 out
->updateFillColor(state
);
893 void Gfx::opSetStrokeColorN(Object args
[], int numArgs
) {
898 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
900 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
901 if (args
[i
].isNum()) {
902 color
.c
[i
] = args
[i
].getNum();
905 state
->setStrokeColor(&color
);
906 out
->updateStrokeColor(state
);
908 if (args
[numArgs
-1].isName() &&
909 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
910 state
->setStrokePattern(pattern
);
914 state
->setStrokePattern(NULL
);
915 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
916 if (args
[i
].isNum()) {
917 color
.c
[i
] = args
[i
].getNum();
920 state
->setStrokeColor(&color
);
921 out
->updateStrokeColor(state
);
925 //------------------------------------------------------------------------
926 // path segment operators
927 //------------------------------------------------------------------------
929 void Gfx::opMoveTo(Object args
[], int numArgs
) {
930 state
->moveTo(args
[0].getNum(), args
[1].getNum());
933 void Gfx::opLineTo(Object args
[], int numArgs
) {
934 if (!state
->isCurPt()) {
935 error(getPos(), "No current point in lineto");
938 state
->lineTo(args
[0].getNum(), args
[1].getNum());
941 void Gfx::opCurveTo(Object args
[], int numArgs
) {
942 double x1
, y1
, x2
, y2
, x3
, y3
;
944 if (!state
->isCurPt()) {
945 error(getPos(), "No current point in curveto");
948 x1
= args
[0].getNum();
949 y1
= args
[1].getNum();
950 x2
= args
[2].getNum();
951 y2
= args
[3].getNum();
952 x3
= args
[4].getNum();
953 y3
= args
[5].getNum();
954 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
957 void Gfx::opCurveTo1(Object args
[], int numArgs
) {
958 double x1
, y1
, x2
, y2
, x3
, y3
;
960 if (!state
->isCurPt()) {
961 error(getPos(), "No current point in curveto1");
964 x1
= state
->getCurX();
965 y1
= state
->getCurY();
966 x2
= args
[0].getNum();
967 y2
= args
[1].getNum();
968 x3
= args
[2].getNum();
969 y3
= args
[3].getNum();
970 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
973 void Gfx::opCurveTo2(Object args
[], int numArgs
) {
974 double x1
, y1
, x2
, y2
, x3
, y3
;
976 if (!state
->isCurPt()) {
977 error(getPos(), "No current point in curveto2");
980 x1
= args
[0].getNum();
981 y1
= args
[1].getNum();
982 x2
= args
[2].getNum();
983 y2
= args
[3].getNum();
986 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
989 void Gfx::opRectangle(Object args
[], int numArgs
) {
992 x
= args
[0].getNum();
993 y
= args
[1].getNum();
994 w
= args
[2].getNum();
995 h
= args
[3].getNum();
997 state
->lineTo(x
+ w
, y
);
998 state
->lineTo(x
+ w
, y
+ h
);
999 state
->lineTo(x
, y
+ h
);
1003 void Gfx::opClosePath(Object args
[], int numArgs
) {
1004 if (!state
->isPath()) {
1005 error(getPos(), "No current point in closepath");
1011 //------------------------------------------------------------------------
1012 // path painting operators
1013 //------------------------------------------------------------------------
1015 void Gfx::opEndPath(Object args
[], int numArgs
) {
1019 void Gfx::opStroke(Object args
[], int numArgs
) {
1020 if (!state
->isCurPt()) {
1021 //error(getPos(), "No path in stroke");
1024 if (state
->isPath())
1029 void Gfx::opCloseStroke(Object args
[], int numArgs
) {
1030 if (!state
->isCurPt()) {
1031 //error(getPos(), "No path in closepath/stroke");
1034 if (state
->isPath()) {
1041 void Gfx::opFill(Object args
[], int numArgs
) {
1042 if (!state
->isCurPt()) {
1043 //error(getPos(), "No path in fill");
1046 if (state
->isPath()) {
1047 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1048 doPatternFill(gFalse
);
1056 void Gfx::opEOFill(Object args
[], int numArgs
) {
1057 if (!state
->isCurPt()) {
1058 //error(getPos(), "No path in eofill");
1061 if (state
->isPath()) {
1062 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1063 doPatternFill(gTrue
);
1071 void Gfx::opFillStroke(Object args
[], int numArgs
) {
1072 if (!state
->isCurPt()) {
1073 //error(getPos(), "No path in fill/stroke");
1076 if (state
->isPath()) {
1077 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1078 doPatternFill(gFalse
);
1087 void Gfx::opCloseFillStroke(Object args
[], int numArgs
) {
1088 if (!state
->isCurPt()) {
1089 //error(getPos(), "No path in closepath/fill/stroke");
1092 if (state
->isPath()) {
1094 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1095 doPatternFill(gFalse
);
1104 void Gfx::opEOFillStroke(Object args
[], int numArgs
) {
1105 if (!state
->isCurPt()) {
1106 //error(getPos(), "No path in eofill/stroke");
1109 if (state
->isPath()) {
1110 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1111 doPatternFill(gTrue
);
1120 void Gfx::opCloseEOFillStroke(Object args
[], int numArgs
) {
1121 if (!state
->isCurPt()) {
1122 //error(getPos(), "No path in closepath/eofill/stroke");
1125 if (state
->isPath()) {
1127 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1128 doPatternFill(gTrue
);
1137 void Gfx::doPatternFill(GBool eoFill
) {
1138 GfxPatternColorSpace
*patCS
;
1139 GfxPattern
*pattern
;
1140 GfxTilingPattern
*tPat
;
1142 double xMin
, yMin
, xMax
, yMax
, x
, y
, x1
, y1
;
1143 double cxMin
, cyMin
, cxMax
, cyMax
;
1144 int xi0
, yi0
, xi1
, yi1
, xi
, yi
;
1145 double *ctm
, *btm
, *ptm
;
1146 double m
[6], ictm
[6], m1
[6], im
[6], imb
[6];
1148 double xstep
, ystep
;
1152 patCS
= (GfxPatternColorSpace
*)state
->getFillColorSpace();
1155 if (!(pattern
= state
->getFillPattern())) {
1158 if (pattern
->getType() != 1) {
1161 tPat
= (GfxTilingPattern
*)pattern
;
1163 // construct a (pattern space) -> (current space) transform matrix
1164 ctm
= state
->getCTM();
1166 ptm
= tPat
->getMatrix();
1167 // iCTM = invert CTM
1168 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1169 ictm
[0] = ctm
[3] * det
;
1170 ictm
[1] = -ctm
[1] * det
;
1171 ictm
[2] = -ctm
[2] * det
;
1172 ictm
[3] = ctm
[0] * det
;
1173 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1174 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1175 // m1 = PTM * BTM = PTM * base transform matrix
1176 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1177 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1178 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1179 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1180 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1181 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1182 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1183 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1184 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1185 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1186 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1187 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1188 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1190 // construct a (current space) -> (pattern space) transform matrix
1191 det
= 1 / (m
[0] * m
[3] - m
[1] * m
[2]);
1193 im
[1] = -m
[1] * det
;
1194 im
[2] = -m
[2] * det
;
1196 im
[4] = (m
[2] * m
[5] - m
[3] * m
[4]) * det
;
1197 im
[5] = (m
[1] * m
[4] - m
[0] * m
[5]) * det
;
1199 // construct a (base space) -> (pattern space) transform matrix
1200 det
= 1 / (m1
[0] * m1
[3] - m1
[1] * m1
[2]);
1201 imb
[0] = m1
[3] * det
;
1202 imb
[1] = -m1
[1] * det
;
1203 imb
[2] = -m1
[2] * det
;
1204 imb
[3] = m1
[0] * det
;
1205 imb
[4] = (m1
[2] * m1
[5] - m1
[3] * m1
[4]) * det
;
1206 imb
[5] = (m1
[1] * m1
[4] - m1
[0] * m1
[5]) * det
;
1208 // save current graphics state
1209 out
->saveState(state
);
1210 state
= state
->save();
1212 // set underlying color space (for uncolored tiling patterns)
1213 if (tPat
->getPaintType() == 2 && (cs
= patCS
->getUnder())) {
1214 state
->setFillColorSpace(cs
->copy());
1216 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
1218 state
->setFillPattern(NULL
);
1219 out
->updateFillColor(state
);
1221 // clip to current path
1230 // transform clip region bbox to pattern space
1231 state
->getClipBBox(&cxMin
, &cyMin
, &cxMax
, &cyMax
);
1232 xMin
= xMax
= cxMin
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1233 yMin
= yMax
= cxMin
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1234 x1
= cxMin
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1235 y1
= cxMin
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1238 } else if (x1
> xMax
) {
1243 } else if (y1
> yMax
) {
1246 x1
= cxMax
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1247 y1
= cxMax
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1250 } else if (x1
> xMax
) {
1255 } else if (y1
> yMax
) {
1258 x1
= cxMax
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1259 y1
= cxMax
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1262 } else if (x1
> xMax
) {
1267 } else if (y1
> yMax
) {
1272 //~ this should treat negative steps differently -- start at right/top
1273 //~ edge instead of left/bottom (?)
1274 xstep
= fabs(tPat
->getXStep());
1275 ystep
= fabs(tPat
->getYStep());
1276 xi0
= (int)floor(xMin
/ xstep
);
1277 xi1
= (int)ceil(xMax
/ xstep
);
1278 yi0
= (int)floor(yMin
/ ystep
);
1279 yi1
= (int)ceil(yMax
/ ystep
);
1280 for (i
= 0; i
< 4; ++i
) {
1283 for (yi
= yi0
; yi
< yi1
; ++yi
) {
1284 for (xi
= xi0
; xi
< xi1
; ++xi
) {
1287 m1
[4] = x
* m
[0] + y
* m
[2] + m
[4];
1288 m1
[5] = x
* m
[1] + y
* m
[3] + m
[5];
1289 doForm1(tPat
->getContentStream(), tPat
->getResDict(),
1290 m1
, tPat
->getBBox());
1294 // restore graphics state
1295 state
= state
->restore();
1296 out
->restoreState(state
);
1299 void Gfx::opShFill(Object args
[], int numArgs
) {
1300 GfxShading
*shading
;
1301 double xMin
, yMin
, xMax
, yMax
;
1303 if (!(shading
= res
->lookupShading(args
[0].getName()))) {
1307 // save current graphics state
1308 out
->saveState(state
);
1309 state
= state
->save();
1312 if (shading
->getHasBBox()) {
1313 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1314 state
->moveTo(xMin
, yMin
);
1315 state
->lineTo(xMax
, yMin
);
1316 state
->lineTo(xMax
, yMax
);
1317 state
->lineTo(xMin
, yMax
);
1324 // set the color space
1325 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1327 // do shading type-specific operations
1328 switch (shading
->getType()) {
1330 doAxialShFill((GfxAxialShading
*)shading
);
1334 // restore graphics state
1335 state
= state
->restore();
1336 out
->restoreState(state
);
1341 void Gfx::doAxialShFill(GfxAxialShading
*shading
) {
1342 double xMin
, yMin
, xMax
, yMax
;
1343 double x0
, y0
, x1
, y1
;
1348 double tMin
, tMax
, t
, tx
, ty
;
1349 double s
[4], sMin
, sMax
, tmp
;
1350 double ux0
, uy0
, ux1
, uy1
, vx0
, vy0
, vx1
, vy1
;
1352 double ta
[axialMaxSplits
+ 1];
1353 int next
[axialMaxSplits
+ 1];
1354 GfxColor color0
, color1
;
1358 // get clip region bbox and transform to current user space
1359 state
->getClipBBox(&x0
, &y0
, &x1
, &y1
);
1360 ctm
= state
->getCTM();
1361 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1362 ictm
[0] = ctm
[3] * det
;
1363 ictm
[1] = -ctm
[1] * det
;
1364 ictm
[2] = -ctm
[2] * det
;
1365 ictm
[3] = ctm
[0] * det
;
1366 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1367 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1368 xMin
= xMax
= x0
* ictm
[0] + y0
* ictm
[2] + ictm
[4];
1369 yMin
= yMax
= x0
* ictm
[1] + y0
* ictm
[3] + ictm
[5];
1370 tx
= x0
* ictm
[0] + y1
* ictm
[2] + ictm
[4];
1371 ty
= x0
* ictm
[1] + y1
* ictm
[3] + ictm
[5];
1374 } else if (tx
> xMax
) {
1379 } else if (ty
> yMax
) {
1382 tx
= x1
* ictm
[0] + y0
* ictm
[2] + ictm
[4];
1383 ty
= x1
* ictm
[1] + y0
* ictm
[3] + ictm
[5];
1386 } else if (tx
> xMax
) {
1391 } else if (ty
> yMax
) {
1394 tx
= x1
* ictm
[0] + y1
* ictm
[2] + ictm
[4];
1395 ty
= x1
* ictm
[1] + y1
* ictm
[3] + ictm
[5];
1398 } else if (tx
> xMax
) {
1403 } else if (ty
> yMax
) {
1407 // compute min and max t values, based on the four corners of the
1409 shading
->getCoords(&x0
, &y0
, &x1
, &y1
);
1412 mul
= 1 / (dx
* dx
+ dy
* dy
);
1413 tMin
= tMax
= ((xMin
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
1414 t
= ((xMin
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
1417 } else if (t
> tMax
) {
1420 t
= ((xMax
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
1423 } else if (t
> tMax
) {
1426 t
= ((xMax
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
1429 } else if (t
> tMax
) {
1432 if (tMin
< 0 && !shading
->getExtend0()) {
1435 if (tMax
> 1 && !shading
->getExtend1()) {
1439 // get the function domain
1440 t0
= shading
->getDomain0();
1441 t1
= shading
->getDomain1();
1443 // Traverse the t axis and do the shading.
1445 // For each point (tx, ty) on the t axis, consider a line through
1446 // that point perpendicular to the t axis:
1448 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1449 // y(s) = ty + s * dx --> s = (y - ty) / dx
1451 // Then look at the intersection of this line with the bounding box
1452 // (xMin, yMin, xMax, yMax). In the general case, there are four
1453 // intersection points:
1455 // s0 = (xMin - tx) / -dy
1456 // s1 = (xMax - tx) / -dy
1457 // s2 = (yMin - ty) / dx
1458 // s3 = (yMax - ty) / dx
1460 // and we want the middle two s values.
1462 // In the case where dx = 0, take s0 and s1; in the case where dy =
1463 // 0, take s2 and s3.
1465 // Each filled polygon is bounded by two of these line segments
1466 // perpdendicular to the t axis.
1468 // The t axis is bisected into smaller regions until the color
1469 // difference across a region is small enough, and then the region
1470 // is painted with a single color.
1473 nComps
= shading
->getColorSpace()->getNComps();
1475 ta
[axialMaxSplits
] = tMax
;
1476 next
[0] = axialMaxSplits
;
1478 // compute the color at t = tMin
1481 } else if (tMin
> 1) {
1484 tt
= t0
+ (t1
- t0
) * tMin
;
1486 shading
->getColor(tt
, &color0
);
1488 // compute the coordinates of the point on the t axis at t = tMin;
1489 // then compute the intersection of the perpendicular line with the
1491 tx
= x0
+ tMin
* dx
;
1492 ty
= y0
+ tMin
* dy
;
1493 if (dx
== 0 && dy
== 0) {
1496 sMin
= (xMin
- tx
) / -dy
;
1497 sMax
= (xMax
- tx
) / -dy
;
1498 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1499 } else if (dy
== 0) {
1500 sMin
= (yMin
- ty
) / dx
;
1501 sMax
= (yMax
- ty
) / dx
;
1502 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1504 s
[0] = (yMin
- ty
) / dx
;
1505 s
[1] = (yMax
- ty
) / dx
;
1506 s
[2] = (xMin
- tx
) / -dy
;
1507 s
[3] = (xMax
- tx
) / -dy
;
1508 for (j
= 0; j
< 3; ++j
) {
1510 for (k
= j
+ 1; k
< 4; ++k
) {
1515 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
1520 ux0
= tx
- sMin
* dy
;
1521 uy0
= ty
+ sMin
* dx
;
1522 vx0
= tx
- sMax
* dy
;
1523 vy0
= ty
+ sMax
* dx
;
1526 while (i
< axialMaxSplits
) {
1528 // bisect until color difference is small enough or we hit the
1534 } else if (ta
[j
] > 1) {
1537 tt
= t0
+ (t1
- t0
) * ta
[j
];
1539 shading
->getColor(tt
, &color1
);
1540 for (k
= 0; k
< nComps
; ++k
) {
1541 if (fabs(color1
.c
[k
] - color0
.c
[k
]) > axialColorDelta
) {
1549 ta
[k
] = 0.5 * (ta
[i
] + ta
[j
]);
1555 // use the average of the colors of the two sides of the region
1556 for (k
= 0; k
< nComps
; ++k
) {
1557 color0
.c
[k
] = 0.5 * (color0
.c
[k
] + color1
.c
[k
]);
1560 // compute the coordinates of the point on the t axis; then
1561 // compute the intersection of the perpendicular line with the
1563 tx
= x0
+ ta
[j
] * dx
;
1564 ty
= y0
+ ta
[j
] * dy
;
1565 if (dx
== 0 && dy
== 0) {
1568 sMin
= (xMin
- tx
) / -dy
;
1569 sMax
= (xMax
- tx
) / -dy
;
1570 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1571 } else if (dy
== 0) {
1572 sMin
= (yMin
- ty
) / dx
;
1573 sMax
= (yMax
- ty
) / dx
;
1574 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1576 s
[0] = (yMin
- ty
) / dx
;
1577 s
[1] = (yMax
- ty
) / dx
;
1578 s
[2] = (xMin
- tx
) / -dy
;
1579 s
[3] = (xMax
- tx
) / -dy
;
1580 for (j
= 0; j
< 3; ++j
) {
1582 for (k
= j
+ 1; k
< 4; ++k
) {
1587 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
1592 ux1
= tx
- sMin
* dy
;
1593 uy1
= ty
+ sMin
* dx
;
1594 vx1
= tx
- sMax
* dy
;
1595 vy1
= ty
+ sMax
* dx
;
1598 state
->setFillColor(&color0
);
1599 out
->updateFillColor(state
);
1602 state
->moveTo(ux0
, uy0
);
1603 state
->lineTo(vx0
, vy0
);
1604 state
->lineTo(vx1
, vy1
);
1605 state
->lineTo(ux1
, uy1
);
1610 // set up for next region
1620 void Gfx::doEndPath() {
1621 if (state
->isPath() && clip
!= clipNone
) {
1623 if (clip
== clipNormal
) {
1633 //------------------------------------------------------------------------
1634 // path clipping operators
1635 //------------------------------------------------------------------------
1637 void Gfx::opClip(Object args
[], int numArgs
) {
1641 void Gfx::opEOClip(Object args
[], int numArgs
) {
1645 //------------------------------------------------------------------------
1646 // text object operators
1647 //------------------------------------------------------------------------
1649 void Gfx::opBeginText(Object args
[], int numArgs
) {
1650 state
->setTextMat(1, 0, 0, 1, 0, 0);
1651 state
->textMoveTo(0, 0);
1652 out
->updateTextMat(state
);
1653 out
->updateTextPos(state
);
1654 fontChanged
= gTrue
;
1657 void Gfx::opEndText(Object args
[], int numArgs
) {
1660 //------------------------------------------------------------------------
1661 // text state operators
1662 //------------------------------------------------------------------------
1664 void Gfx::opSetCharSpacing(Object args
[], int numArgs
) {
1665 state
->setCharSpace(args
[0].getNum());
1666 out
->updateCharSpace(state
);
1669 void Gfx::opSetFont(Object args
[], int numArgs
) {
1672 if (!(font
= res
->lookupFont(args
[0].getName()))) {
1675 if (printCommands
) {
1676 printf(" font: '%s' %g\n",
1677 font
->getName() ? font
->getName()->getCString() : "???",
1681 state
->setFont(font
, args
[1].getNum());
1682 fontChanged
= gTrue
;
1685 void Gfx::opSetTextLeading(Object args
[], int numArgs
) {
1686 state
->setLeading(args
[0].getNum());
1689 void Gfx::opSetTextRender(Object args
[], int numArgs
) {
1690 state
->setRender(args
[0].getInt());
1691 out
->updateRender(state
);
1694 void Gfx::opSetTextRise(Object args
[], int numArgs
) {
1695 state
->setRise(args
[0].getNum());
1696 out
->updateRise(state
);
1699 void Gfx::opSetWordSpacing(Object args
[], int numArgs
) {
1700 state
->setWordSpace(args
[0].getNum());
1701 out
->updateWordSpace(state
);
1704 void Gfx::opSetHorizScaling(Object args
[], int numArgs
) {
1705 state
->setHorizScaling(args
[0].getNum());
1706 out
->updateHorizScaling(state
);
1707 fontChanged
= gTrue
;
1710 //------------------------------------------------------------------------
1711 // text positioning operators
1712 //------------------------------------------------------------------------
1714 void Gfx::opTextMove(Object args
[], int numArgs
) {
1717 tx
= state
->getLineX() + args
[0].getNum();
1718 ty
= state
->getLineY() + args
[1].getNum();
1719 state
->textMoveTo(tx
, ty
);
1720 out
->updateTextPos(state
);
1723 void Gfx::opTextMoveSet(Object args
[], int numArgs
) {
1726 tx
= state
->getLineX() + args
[0].getNum();
1727 ty
= args
[1].getNum();
1728 state
->setLeading(-ty
);
1729 ty
+= state
->getLineY();
1730 state
->textMoveTo(tx
, ty
);
1731 out
->updateTextPos(state
);
1734 void Gfx::opSetTextMatrix(Object args
[], int numArgs
) {
1735 state
->setTextMat(args
[0].getNum(), args
[1].getNum(),
1736 args
[2].getNum(), args
[3].getNum(),
1737 args
[4].getNum(), args
[5].getNum());
1738 state
->textMoveTo(0, 0);
1739 out
->updateTextMat(state
);
1740 out
->updateTextPos(state
);
1741 fontChanged
= gTrue
;
1744 void Gfx::opTextNextLine(Object args
[], int numArgs
) {
1747 tx
= state
->getLineX();
1748 ty
= state
->getLineY() - state
->getLeading();
1749 state
->textMoveTo(tx
, ty
);
1750 out
->updateTextPos(state
);
1753 //------------------------------------------------------------------------
1754 // text string operators
1755 //------------------------------------------------------------------------
1757 void Gfx::opShowText(Object args
[], int numArgs
) {
1758 if (!state
->getFont()) {
1759 error(getPos(), "No font in show");
1762 doShowText(args
[0].getString());
1765 void Gfx::opMoveShowText(Object args
[], int numArgs
) {
1768 if (!state
->getFont()) {
1769 error(getPos(), "No font in move/show");
1772 tx
= state
->getLineX();
1773 ty
= state
->getLineY() - state
->getLeading();
1774 state
->textMoveTo(tx
, ty
);
1775 out
->updateTextPos(state
);
1776 doShowText(args
[0].getString());
1779 void Gfx::opMoveSetShowText(Object args
[], int numArgs
) {
1782 if (!state
->getFont()) {
1783 error(getPos(), "No font in move/set/show");
1786 state
->setWordSpace(args
[0].getNum());
1787 state
->setCharSpace(args
[1].getNum());
1788 tx
= state
->getLineX();
1789 ty
= state
->getLineY() - state
->getLeading();
1790 state
->textMoveTo(tx
, ty
);
1791 out
->updateWordSpace(state
);
1792 out
->updateCharSpace(state
);
1793 out
->updateTextPos(state
);
1794 doShowText(args
[2].getString());
1797 void Gfx::opShowSpaceText(Object args
[], int numArgs
) {
1802 if (!state
->getFont()) {
1803 error(getPos(), "No font in show/space");
1806 a
= args
[0].getArray();
1807 for (i
= 0; i
< a
->getLength(); ++i
) {
1810 state
->textShift(-obj
.getNum() * 0.001 * state
->getFontSize());
1811 out
->updateTextShift(state
, obj
.getNum());
1812 } else if (obj
.isString()) {
1813 doShowText(obj
.getString());
1815 error(getPos(), "Element of show/space array must be number or string");
1821 void Gfx::doShowText(GString
*s
) {
1823 GfxFontEncoding16
*enc
;
1831 double dx
, dy
, width
, height
, w
, h
, x
, y
;
1832 double oldCTM
[6], newCTM
[6];
1838 double dx
, dy
, width
, height
, w
, h
;
1840 double sWidth
, sHeight
;
1843 out
->updateFont(state
);
1844 fontChanged
= gFalse
;
1846 font
= state
->getFont();
1849 if (font
->is16Bit()) {
1850 enc
= font
->getEncoding16();
1851 if (out
->useDrawChar()) {
1852 out
->beginString(state
, s
);
1855 s16
= new GString();
1857 sWidth
= sHeight
= 0;
1858 state
->textTransformDelta(0, state
->getRise(), &dx
, &dy
);
1859 p
= (Guchar
*)s
->getCString();
1862 m
= getNextChar16(enc
, p
, &c16
);
1863 if (enc
->wMode
== 0) {
1864 width
= state
->getFontSize() * font
->getWidth16(c16
) +
1865 state
->getCharSpace();
1866 if (m
== 1 && c16
== ' ') {
1867 width
+= state
->getWordSpace();
1869 width
*= state
->getHorizScaling();
1873 height
= state
->getFontSize() * font
->getHeight16(c16
);
1875 state
->textTransformDelta(width
, height
, &w
, &h
);
1876 if (out
->useDrawChar()) {
1877 out
->drawChar16(state
, state
->getCurX() + dx
, state
->getCurY() + dy
,
1879 state
->textShift(width
, height
);
1881 s16a
[0] = (char)(c16
>> 8);
1882 s16a
[1] = (char)c16
;
1883 s16
->append(s16a
, 2);
1890 if (out
->useDrawChar()) {
1891 out
->endString(state
);
1893 out
->drawString16(state
, s16
);
1895 state
->textShift(sWidth
, sHeight
);
1901 //~ also check out->renderType3()
1902 if (font
->getType() == fontType3
) {
1903 out
->beginString(state
, s
);
1904 mat
= state
->getCTM();
1905 for (i
= 0; i
< 6; ++i
) {
1908 mat
= state
->getTextMat();
1909 newCTM
[0] = mat
[0] * oldCTM
[0] + mat
[1] * oldCTM
[2];
1910 newCTM
[1] = mat
[0] * oldCTM
[1] + mat
[1] * oldCTM
[3];
1911 newCTM
[2] = mat
[2] * oldCTM
[0] + mat
[3] * oldCTM
[2];
1912 newCTM
[3] = mat
[2] * oldCTM
[1] + mat
[3] * oldCTM
[3];
1913 mat
= font
->getFontMatrix();
1914 newCTM
[0] = mat
[0] * newCTM
[0] + mat
[1] * newCTM
[2];
1915 newCTM
[1] = mat
[0] * newCTM
[1] + mat
[1] * newCTM
[3];
1916 newCTM
[2] = mat
[2] * newCTM
[0] + mat
[3] * newCTM
[2];
1917 newCTM
[3] = mat
[2] * newCTM
[1] + mat
[3] * newCTM
[3];
1918 newCTM
[0] *= state
->getFontSize();
1919 newCTM
[3] *= state
->getFontSize();
1920 newCTM
[0] *= state
->getHorizScaling();
1921 newCTM
[2] *= state
->getHorizScaling();
1922 state
->textTransformDelta(0, state
->getRise(), &dx
, &dy
);
1924 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
1926 font
->getCharProc(c8
, &charProc
);
1927 state
->transform(state
->getCurX() + dx
, state
->getCurY() + dy
, &x
, &y
);
1928 out
->saveState(state
);
1929 state
= state
->save();
1930 state
->setCTM(newCTM
[0], newCTM
[1], newCTM
[2], newCTM
[3], x
, y
);
1931 //~ out->updateCTM(???)
1932 if (charProc
.isStream()) {
1933 display(&charProc
, gFalse
);
1935 error(getPos(), "Missing or bad Type3 CharProc entry");
1937 state
= state
->restore();
1938 out
->restoreState(state
);
1940 width
= state
->getFontSize() * font
->getWidth(c8
) +
1941 state
->getCharSpace();
1943 width
+= state
->getWordSpace();
1945 width
*= state
->getHorizScaling();
1946 state
->textShift(width
);
1949 out
->endString(state
);
1952 if (out
->useDrawChar()) {
1953 out
->beginString(state
, s
);
1954 state
->textTransformDelta(0, state
->getRise(), &dx
, &dy
);
1955 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
1957 width
= state
->getFontSize() * font
->getWidth(c8
) +
1958 state
->getCharSpace();
1960 width
+= state
->getWordSpace();
1962 width
*= state
->getHorizScaling();
1963 state
->textTransformDelta(width
, 0, &w
, &h
);
1964 out
->drawChar(state
, state
->getCurX() + dx
, state
->getCurY() + dy
,
1966 state
->textShift(width
);
1968 out
->endString(state
);
1970 out
->drawString(state
, s
);
1971 width
= state
->getFontSize() * font
->getWidth(s
) +
1972 s
->getLength() * state
->getCharSpace();
1973 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
1975 width
+= state
->getWordSpace();
1978 width
*= state
->getHorizScaling();
1979 state
->textShift(width
);
1984 int Gfx::getNextChar16(GfxFontEncoding16
*enc
, Guchar
*p
, int *c16
) {
1989 n
= enc
->codeLen
[*p
];
1991 *c16
= enc
->map1
[*p
];
1993 code
= (p
[0] << 8) + p
[1];
1996 // invariant: map2[2*a] <= code < map2[2*b]
1999 if (enc
->map2
[2*m
] <= code
)
2001 else if (enc
->map2
[2*m
] > code
)
2006 *c16
= enc
->map2
[2*a
+1] + (code
- enc
->map2
[2*a
]);
2011 //------------------------------------------------------------------------
2012 // XObject operators
2013 //------------------------------------------------------------------------
2015 void Gfx::opXObject(Object args
[], int numArgs
) {
2016 Object obj1
, obj2
, refObj
;
2021 if (!res
->lookupXObject(args
[0].getName(), &obj1
)) {
2024 if (!obj1
.isStream()) {
2025 error(getPos(), "XObject '%s' is wrong type", args
[0].getName());
2030 obj1
.streamGetDict()->lookup("OPI", &opiDict
);
2031 if (opiDict
.isDict()) {
2032 out
->opiBegin(state
, opiDict
.getDict());
2035 obj1
.streamGetDict()->lookup("Subtype", &obj2
);
2036 if (obj2
.isName("Image")) {
2037 res
->lookupXObjectNF(args
[0].getName(), &refObj
);
2038 doImage(&refObj
, obj1
.getStream(), gFalse
);
2040 } else if (obj2
.isName("Form")) {
2042 } else if (obj2
.isName()) {
2043 error(getPos(), "Unknown XObject subtype '%s'", obj2
.getName());
2045 error(getPos(), "XObject subtype is missing or wrong type");
2049 if (opiDict
.isDict()) {
2050 out
->opiEnd(state
, opiDict
.getDict());
2057 void Gfx::doImage(Object
*ref
, Stream
*str
, GBool inlineImg
) {
2063 GfxColorSpace
*colorSpace
;
2064 GfxImageColorMap
*colorMap
;
2067 int maskColors
[2*gfxColorMaxComps
];
2072 dict
= str
->getDict();
2075 dict
->lookup("Width", &obj1
);
2076 if (obj1
.isNull()) {
2078 dict
->lookup("W", &obj1
);
2082 width
= obj1
.getInt();
2084 dict
->lookup("Height", &obj1
);
2085 if (obj1
.isNull()) {
2087 dict
->lookup("H", &obj1
);
2091 height
= obj1
.getInt();
2095 dict
->lookup("ImageMask", &obj1
);
2096 if (obj1
.isNull()) {
2098 dict
->lookup("IM", &obj1
);
2102 mask
= obj1
.getBool();
2103 else if (!obj1
.isNull())
2108 dict
->lookup("BitsPerComponent", &obj1
);
2109 if (obj1
.isNull()) {
2111 dict
->lookup("BPC", &obj1
);
2115 bits
= obj1
.getInt();
2121 // check for inverted mask
2125 dict
->lookup("Decode", &obj1
);
2126 if (obj1
.isNull()) {
2128 dict
->lookup("D", &obj1
);
2130 if (obj1
.isArray()) {
2131 obj1
.arrayGet(0, &obj2
);
2132 if (obj2
.isInt() && obj2
.getInt() == 1)
2135 } else if (!obj1
.isNull()) {
2141 out
->drawImageMask(state
, ref
, str
, width
, height
, invert
, inlineImg
);
2145 // get color space and color map
2146 dict
->lookup("ColorSpace", &obj1
);
2147 if (obj1
.isNull()) {
2149 dict
->lookup("CS", &obj1
);
2151 if (obj1
.isName()) {
2152 res
->lookupColorSpace(obj1
.getName(), &obj2
);
2153 if (!obj2
.isNull()) {
2160 colorSpace
= GfxColorSpace::parse(&obj1
);
2165 dict
->lookup("Decode", &obj1
);
2166 if (obj1
.isNull()) {
2168 dict
->lookup("D", &obj1
);
2170 colorMap
= new GfxImageColorMap(bits
, &obj1
, colorSpace
);
2172 if (!colorMap
->isOk()) {
2179 dict
->lookup("Mask", &maskObj
);
2180 if (maskObj
.isArray()) {
2181 for (i
= 0; i
< maskObj
.arrayGetLength(); ++i
) {
2182 maskObj
.arrayGet(i
, &obj1
);
2183 maskColors
[i
] = obj1
.getInt();
2190 out
->drawImage(state
, ref
, str
, width
, height
, colorMap
,
2191 haveMask
? maskColors
: (int *)NULL
, inlineImg
);
2203 error(getPos(), "Bad image parameters");
2206 void Gfx::doForm(Object
*str
) {
2208 Object matrixObj
, bboxObj
;
2209 double m
[6], bbox
[6];
2216 dict
= str
->streamGetDict();
2219 dict
->lookup("FormType", &obj1
);
2220 if (!(obj1
.isInt() && obj1
.getInt() == 1)) {
2221 error(getPos(), "Unknown form type");
2226 dict
->lookup("BBox", &bboxObj
);
2227 if (!bboxObj
.isArray()) {
2230 error(getPos(), "Bad form bounding box");
2233 for (i
= 0; i
< 4; ++i
) {
2234 bboxObj
.arrayGet(i
, &obj1
);
2235 bbox
[i
] = obj1
.getNum();
2241 dict
->lookup("Matrix", &matrixObj
);
2242 if (matrixObj
.isArray()) {
2243 for (i
= 0; i
< 6; ++i
) {
2244 matrixObj
.arrayGet(i
, &obj1
);
2245 m
[i
] = obj1
.getNum();
2256 dict
->lookup("Resources", &resObj
);
2257 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
2260 doForm1(str
, resDict
, m
, bbox
);
2265 void Gfx::doWidgetForm(Object
*str
, double xMin
, double yMin
,
2266 double xMax
, double yMax
) {
2267 Dict
*dict
, *resDict
;
2268 Object matrixObj
, bboxObj
, resObj
;
2270 double m
[6], bbox
[6];
2275 dict
= str
->streamGetDict();
2278 dict
->lookup("BBox", &bboxObj
);
2279 if (!bboxObj
.isArray()) {
2281 error(getPos(), "Bad form bounding box");
2284 for (i
= 0; i
< 4; ++i
) {
2285 bboxObj
.arrayGet(i
, &obj1
);
2286 bbox
[i
] = obj1
.getNum();
2292 dict
->lookup("Matrix", &matrixObj
);
2293 if (matrixObj
.isArray()) {
2294 for (i
= 0; i
< 6; ++i
) {
2295 matrixObj
.arrayGet(i
, &obj1
);
2296 m
[i
] = obj1
.getNum();
2306 // scale form bbox to widget rectangle
2307 sx
= fabs((xMax
- xMin
) / (bbox
[2] - bbox
[0]));
2308 sy
= fabs((yMax
- yMin
) / (bbox
[3] - bbox
[1]));
2309 m
[0] *= sx
; m
[1] *= sy
;
2310 m
[2] *= sx
; m
[3] *= sy
;
2311 m
[4] *= sx
; m
[5] *= sy
;
2313 // translate to widget rectangle
2318 dict
->lookup("Resources", &resObj
);
2319 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
2322 doForm1(str
, resDict
, m
, bbox
);
2328 void Gfx::doForm1(Object
*str
, Dict
*resDict
, double *matrix
, double *bbox
) {
2330 double oldBaseMatrix
[6];
2331 GfxResources
*resPtr
;
2334 // push new resources on stack
2335 res
= new GfxResources(xref
, resDict
, res
);
2337 // save current graphics state
2338 out
->saveState(state
);
2339 state
= state
->save();
2341 // save current parser
2344 // set form transformation matrix
2345 state
->concatCTM(matrix
[0], matrix
[1], matrix
[2],
2346 matrix
[3], matrix
[4], matrix
[5]);
2347 out
->updateCTM(state
, matrix
[0], matrix
[1], matrix
[2],
2348 matrix
[3], matrix
[4], matrix
[5]);
2350 // set new base matrix
2351 for (i
= 0; i
< 6; ++i
) {
2352 oldBaseMatrix
[i
] = baseMatrix
[i
];
2353 baseMatrix
[i
] = state
->getCTM()[i
];
2356 // set form bounding box
2357 state
->moveTo(bbox
[0], bbox
[1]);
2358 state
->lineTo(bbox
[2], bbox
[1]);
2359 state
->lineTo(bbox
[2], bbox
[3]);
2360 state
->lineTo(bbox
[0], bbox
[3]);
2367 display(str
, gFalse
);
2369 // restore base matrix
2370 for (i
= 0; i
< 6; ++i
) {
2371 baseMatrix
[i
] = oldBaseMatrix
[i
];
2377 // restore graphics state
2378 state
= state
->restore();
2379 out
->restoreState(state
);
2381 // pop resource stack
2382 resPtr
= res
->getNext();
2389 //------------------------------------------------------------------------
2390 // in-line image operators
2391 //------------------------------------------------------------------------
2393 void Gfx::opBeginImage(Object args
[], int numArgs
) {
2397 // build dict/stream
2398 str
= buildImageStream();
2400 // display the image
2402 doImage(NULL
, str
, gTrue
);
2405 c1
= str
->getBaseStream()->getChar();
2406 c2
= str
->getBaseStream()->getChar();
2407 while (!(c1
== 'E' && c2
== 'I') && c2
!= EOF
) {
2409 c2
= str
->getBaseStream()->getChar();
2415 Stream
*Gfx::buildImageStream() {
2422 dict
.initDict(xref
);
2423 parser
->getObj(&obj
);
2424 while (!obj
.isCmd("ID") && !obj
.isEOF()) {
2425 if (!obj
.isName()) {
2426 error(getPos(), "Inline image dictionary key must be a name object");
2428 parser
->getObj(&obj
);
2430 key
= copyString(obj
.getName());
2432 parser
->getObj(&obj
);
2433 if (obj
.isEOF() || obj
.isError())
2435 dict
.dictAdd(key
, &obj
);
2437 parser
->getObj(&obj
);
2440 error(getPos(), "End of file in inline image");
2444 str
= new EmbedStream(parser
->getStream(), &dict
);
2445 str
= str
->addFilters(&dict
);
2450 void Gfx::opImageData(Object args
[], int numArgs
) {
2451 error(getPos(), "Internal: got 'ID' operator");
2454 void Gfx::opEndImage(Object args
[], int numArgs
) {
2455 error(getPos(), "Internal: got 'EI' operator");
2458 //------------------------------------------------------------------------
2459 // type 3 font operators
2460 //------------------------------------------------------------------------
2462 void Gfx::opSetCharWidth(Object args
[], int numArgs
) {
2463 // error(getPos(), "Encountered 'd0' operator in content stream");
2466 void Gfx::opSetCacheDevice(Object args
[], int numArgs
) {
2467 // error(getPos(), "Encountered 'd1' operator in content stream");
2470 //------------------------------------------------------------------------
2471 // compatibility operators
2472 //------------------------------------------------------------------------
2474 void Gfx::opBeginIgnoreUndef(Object args
[], int numArgs
) {
2478 void Gfx::opEndIgnoreUndef(Object args
[], int numArgs
) {
2479 if (ignoreUndef
> 0)
2483 //------------------------------------------------------------------------
2484 // marked content operators
2485 //------------------------------------------------------------------------
2487 void Gfx::opBeginMarkedContent(Object args
[], int numArgs
) {
2488 if (printCommands
) {
2489 printf(" marked content: %s ", args
[0].getName());
2491 args
[2].print(stdout
);
2497 void Gfx::opEndMarkedContent(Object args
[], int numArgs
) {
2500 void Gfx::opMarkPoint(Object args
[], int numArgs
) {
2501 if (printCommands
) {
2502 printf(" mark point: %s ", args
[0].getName());
2504 args
[2].print(stdout
);