1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
20 #include "GlobalParams.h"
21 #include "CharTypes.h"
30 #include "OutputDev.h"
35 // the MSVC math.h doesn't define this
37 #define M_PI 3.14159265358979323846
40 //------------------------------------------------------------------------
42 //------------------------------------------------------------------------
44 // Max recursive depth for a function shading fill.
45 #define functionMaxDepth 6
47 // Max delta allowed in any color component for a function shading fill.
48 #define functionColorDelta (1 / 256.0)
50 // Max number of splits along the t axis for an axial shading fill.
51 #define axialMaxSplits 256
53 // Max delta allowed in any color component for an axial shading fill.
54 #define axialColorDelta (1 / 256.0)
56 // Max number of splits along the t axis for a radial shading fill.
57 #define radialMaxSplits 256
59 // Max delta allowed in any color component for a radial shading fill.
60 #define radialColorDelta (1 / 256.0)
62 //------------------------------------------------------------------------
64 //------------------------------------------------------------------------
66 #ifdef WIN32 // this works around a bug in the VC7 compiler
67 # pragma optimize("",off)
70 Operator
Gfx::opTab
[] = {
71 {"\"", 3, {tchkNum
, tchkNum
, tchkString
},
72 &Gfx::opMoveSetShowText
},
73 {"'", 1, {tchkString
},
74 &Gfx::opMoveShowText
},
78 &Gfx::opEOFillStroke
},
79 {"BDC", 2, {tchkName
, tchkProps
},
80 &Gfx::opBeginMarkedContent
},
83 {"BMC", 1, {tchkName
},
84 &Gfx::opBeginMarkedContent
},
88 &Gfx::opBeginIgnoreUndef
},
90 &Gfx::opSetStrokeColorSpace
},
91 {"DP", 2, {tchkName
, tchkProps
},
97 {"EMC", 0, {tchkNone
},
98 &Gfx::opEndMarkedContent
},
101 {"EX", 0, {tchkNone
},
102 &Gfx::opEndIgnoreUndef
},
106 &Gfx::opSetStrokeGray
},
107 {"ID", 0, {tchkNone
},
111 {"K", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
112 &Gfx::opSetStrokeCMYKColor
},
114 &Gfx::opSetMiterLimit
},
115 {"MP", 1, {tchkName
},
119 {"RG", 3, {tchkNum
, tchkNum
, tchkNum
},
120 &Gfx::opSetStrokeRGBColor
},
123 {"SC", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
124 &Gfx::opSetStrokeColor
},
125 {"SCN", -5, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
127 &Gfx::opSetStrokeColorN
},
128 {"T*", 0, {tchkNone
},
129 &Gfx::opTextNextLine
},
130 {"TD", 2, {tchkNum
, tchkNum
},
131 &Gfx::opTextMoveSet
},
132 {"TJ", 1, {tchkArray
},
133 &Gfx::opShowSpaceText
},
135 &Gfx::opSetTextLeading
},
137 &Gfx::opSetCharSpacing
},
138 {"Td", 2, {tchkNum
, tchkNum
},
140 {"Tf", 2, {tchkName
, tchkNum
},
142 {"Tj", 1, {tchkString
},
144 {"Tm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
146 &Gfx::opSetTextMatrix
},
148 &Gfx::opSetTextRender
},
150 &Gfx::opSetTextRise
},
152 &Gfx::opSetWordSpacing
},
154 &Gfx::opSetHorizScaling
},
157 {"W*", 0, {tchkNone
},
160 &Gfx::opCloseFillStroke
},
161 {"b*", 0, {tchkNone
},
162 &Gfx::opCloseEOFillStroke
},
163 {"c", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
166 {"cm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
169 {"cs", 1, {tchkName
},
170 &Gfx::opSetFillColorSpace
},
171 {"d", 2, {tchkArray
, tchkNum
},
173 {"d0", 2, {tchkNum
, tchkNum
},
174 &Gfx::opSetCharWidth
},
175 {"d1", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
177 &Gfx::opSetCacheDevice
},
180 {"f*", 0, {tchkNone
},
183 &Gfx::opSetFillGray
},
184 {"gs", 1, {tchkName
},
185 &Gfx::opSetExtGState
},
191 &Gfx::opSetLineJoin
},
192 {"k", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
193 &Gfx::opSetFillCMYKColor
},
194 {"l", 2, {tchkNum
, tchkNum
},
196 {"m", 2, {tchkNum
, tchkNum
},
202 {"re", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
204 {"rg", 3, {tchkNum
, tchkNum
, tchkNum
},
205 &Gfx::opSetFillRGBColor
},
206 {"ri", 1, {tchkName
},
207 &Gfx::opSetRenderingIntent
},
209 &Gfx::opCloseStroke
},
210 {"sc", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
211 &Gfx::opSetFillColor
},
212 {"scn", -5, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
214 &Gfx::opSetFillColorN
},
215 {"sh", 1, {tchkName
},
217 {"v", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
220 &Gfx::opSetLineWidth
},
221 {"y", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
225 #ifdef WIN32 // this works around a bug in the VC7 compiler
226 # pragma optimize("",on)
229 #define numOps (sizeof(opTab) / sizeof(Operator))
231 //------------------------------------------------------------------------
233 //------------------------------------------------------------------------
235 GfxResources::GfxResources(XRef
*xref
, Dict
*resDict
, GfxResources
*nextA
) {
241 // build font dictionary
243 resDict
->lookupNF("Font", &obj1
);
245 obj1
.fetch(xref
, &obj2
);
248 fonts
= new GfxFontDict(xref
, &r
, obj2
.getDict());
251 } else if (obj1
.isDict()) {
252 fonts
= new GfxFontDict(xref
, NULL
, obj1
.getDict());
256 // get XObject dictionary
257 resDict
->lookup("XObject", &xObjDict
);
259 // get color space dictionary
260 resDict
->lookup("ColorSpace", &colorSpaceDict
);
262 // get pattern dictionary
263 resDict
->lookup("Pattern", &patternDict
);
265 // get shading dictionary
266 resDict
->lookup("Shading", &shadingDict
);
268 // get graphics state parameter dictionary
269 resDict
->lookup("ExtGState", &gStateDict
);
274 colorSpaceDict
.initNull();
275 patternDict
.initNull();
276 shadingDict
.initNull();
277 gStateDict
.initNull();
283 GfxResources::~GfxResources() {
288 colorSpaceDict
.free();
294 GfxFont
*GfxResources::lookupFont(char *name
) {
296 GfxResources
*resPtr
;
298 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
300 if ((font
= resPtr
->fonts
->lookup(name
)))
304 error(-1, "Unknown font tag '%s'", name
);
308 GBool
GfxResources::lookupXObject(char *name
, Object
*obj
) {
309 GfxResources
*resPtr
;
311 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
312 if (resPtr
->xObjDict
.isDict()) {
313 if (!resPtr
->xObjDict
.dictLookup(name
, obj
)->isNull())
318 error(-1, "XObject '%s' is unknown", name
);
322 GBool
GfxResources::lookupXObjectNF(char *name
, Object
*obj
) {
323 GfxResources
*resPtr
;
325 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
326 if (resPtr
->xObjDict
.isDict()) {
327 if (!resPtr
->xObjDict
.dictLookupNF(name
, obj
)->isNull())
332 error(-1, "XObject '%s' is unknown", name
);
336 void GfxResources::lookupColorSpace(char *name
, Object
*obj
) {
337 GfxResources
*resPtr
;
339 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
340 if (resPtr
->colorSpaceDict
.isDict()) {
341 if (!resPtr
->colorSpaceDict
.dictLookup(name
, obj
)->isNull()) {
350 GfxPattern
*GfxResources::lookupPattern(char *name
) {
351 GfxResources
*resPtr
;
355 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
356 if (resPtr
->patternDict
.isDict()) {
357 if (!resPtr
->patternDict
.dictLookup(name
, &obj
)->isNull()) {
358 pattern
= GfxPattern::parse(&obj
);
365 error(-1, "Unknown pattern '%s'", name
);
369 GfxShading
*GfxResources::lookupShading(char *name
) {
370 GfxResources
*resPtr
;
374 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
375 if (resPtr
->shadingDict
.isDict()) {
376 if (!resPtr
->shadingDict
.dictLookup(name
, &obj
)->isNull()) {
377 shading
= GfxShading::parse(&obj
);
384 error(-1, "Unknown shading '%s'", name
);
388 GBool
GfxResources::lookupGState(char *name
, Object
*obj
) {
389 GfxResources
*resPtr
;
391 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
392 if (resPtr
->gStateDict
.isDict()) {
393 if (!resPtr
->gStateDict
.dictLookup(name
, obj
)->isNull()) {
399 error(-1, "ExtGState '%s' is unknown", name
);
403 //------------------------------------------------------------------------
405 //------------------------------------------------------------------------
407 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, int pageNum
, Dict
*resDict
,
408 double hDPI
, double vDPI
, PDFRectangle
*box
, GBool crop
,
409 PDFRectangle
*cropBox
, int rotate
,
410 GBool (*abortCheckCbkA
)(void *data
),
411 void *abortCheckCbkDataA
) {
416 printCommands
= globalParams
->getPrintCommands();
418 // start the resource stack
419 res
= new GfxResources(xref
, resDict
, NULL
);
423 state
= new GfxState(hDPI
, vDPI
, box
, rotate
, out
->upsideDown());
424 fontChanged
= gFalse
;
427 out
->startPage(pageNum
, state
);
428 out
->setDefaultCTM(state
->getCTM());
429 out
->updateAll(state
);
430 for (i
= 0; i
< 6; ++i
) {
431 baseMatrix
[i
] = state
->getCTM()[i
];
434 abortCheckCbk
= abortCheckCbkA
;
435 abortCheckCbkData
= abortCheckCbkDataA
;
439 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
440 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
441 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
442 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
450 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, Dict
*resDict
,
451 PDFRectangle
*box
, GBool crop
, PDFRectangle
*cropBox
,
452 GBool (*abortCheckCbkA
)(void *data
),
453 void *abortCheckCbkDataA
) {
458 printCommands
= globalParams
->getPrintCommands();
460 // start the resource stack
461 res
= new GfxResources(xref
, resDict
, NULL
);
465 state
= new GfxState(72, 72, box
, 0, gFalse
);
466 fontChanged
= gFalse
;
469 for (i
= 0; i
< 6; ++i
) {
470 baseMatrix
[i
] = state
->getCTM()[i
];
473 abortCheckCbk
= abortCheckCbkA
;
474 abortCheckCbkData
= abortCheckCbkDataA
;
478 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
479 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
480 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
481 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
490 while (state
->hasSaves()) {
504 void Gfx::display(Object
*obj
, GBool topLevel
) {
508 if (obj
->isArray()) {
509 for (i
= 0; i
< obj
->arrayGetLength(); ++i
) {
510 obj
->arrayGet(i
, &obj2
);
511 if (!obj2
.isStream()) {
512 error(-1, "Weird page contents");
518 } else if (!obj
->isStream()) {
519 error(-1, "Weird page contents");
522 parser
= new Parser(xref
, new Lexer(xref
, obj
));
528 void Gfx::go(GBool topLevel
) {
530 Object args
[maxArgs
];
534 // scan a sequence of objects
535 updateLevel
= lastAbortCheck
= 0;
537 parser
->getObj(&obj
);
538 while (!obj
.isEOF()) {
540 // got a command - execute it
544 for (i
= 0; i
< numArgs
; ++i
) {
546 args
[i
].print(stdout
);
551 execOp(&obj
, args
, numArgs
);
553 for (i
= 0; i
< numArgs
; ++i
)
557 // periodically update display
558 if (++updateLevel
>= 20000) {
563 // check for an abort
565 if (updateLevel
- lastAbortCheck
> 10) {
566 if ((*abortCheckCbk
)(abortCheckCbkData
)) {
569 lastAbortCheck
= updateLevel
;
573 // got an argument - save it
574 } else if (numArgs
< maxArgs
) {
575 args
[numArgs
++] = obj
;
577 // too many arguments - something is wrong
579 error(getPos(), "Too many args in content stream");
581 printf("throwing away arg: ");
589 // grab the next object
590 parser
->getObj(&obj
);
594 // args at end with no command
596 error(getPos(), "Leftover args in content stream");
598 printf("%d leftovers:", numArgs
);
599 for (i
= 0; i
< numArgs
; ++i
) {
601 args
[i
].print(stdout
);
606 for (i
= 0; i
< numArgs
; ++i
)
611 if (topLevel
&& updateLevel
> 0) {
616 void Gfx::execOp(Object
*cmd
, Object args
[], int numArgs
) {
623 name
= cmd
->getCmd();
624 if (!(op
= findOp(name
))) {
625 if (ignoreUndef
== 0)
626 error(getPos(), "Unknown operator '%s'", name
);
632 if (op
->numArgs
>= 0) {
633 if (numArgs
< op
->numArgs
) {
634 error(getPos(), "Too few (%d) args to '%s' operator", numArgs
, name
);
637 if (numArgs
> op
->numArgs
) {
639 error(getPos(), "Too many (%d) args to '%s' operator", numArgs
, name
);
641 argPtr
+= numArgs
- op
->numArgs
;
642 numArgs
= op
->numArgs
;
645 if (numArgs
> -op
->numArgs
) {
646 error(getPos(), "Too many (%d) args to '%s' operator",
651 for (i
= 0; i
< numArgs
; ++i
) {
652 if (!checkArg(&argPtr
[i
], op
->tchk
[i
])) {
653 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
654 i
, name
, argPtr
[i
].getTypeName());
660 (this->*op
->func
)(argPtr
, numArgs
);
663 Operator
*Gfx::findOp(char *name
) {
668 // invariant: opTab[a] < name < opTab[b]
671 cmp
= strcmp(opTab
[m
].name
, name
);
684 GBool
Gfx::checkArg(Object
*arg
, TchkType type
) {
686 case tchkBool
: return arg
->isBool();
687 case tchkInt
: return arg
->isInt();
688 case tchkNum
: return arg
->isNum();
689 case tchkString
: return arg
->isString();
690 case tchkName
: return arg
->isName();
691 case tchkArray
: return arg
->isArray();
692 case tchkProps
: return arg
->isDict() || arg
->isName();
693 case tchkSCN
: return arg
->isNum() || arg
->isName();
694 case tchkNone
: return gFalse
;
700 return parser
? parser
->getPos() : -1;
703 //------------------------------------------------------------------------
704 // graphics state operators
705 //------------------------------------------------------------------------
707 void Gfx::opSave(Object args
[], int numArgs
) {
711 void Gfx::opRestore(Object args
[], int numArgs
) {
715 void Gfx::opConcat(Object args
[], int numArgs
) {
716 state
->concatCTM(args
[0].getNum(), args
[1].getNum(),
717 args
[2].getNum(), args
[3].getNum(),
718 args
[4].getNum(), args
[5].getNum());
719 out
->updateCTM(state
, args
[0].getNum(), args
[1].getNum(),
720 args
[2].getNum(), args
[3].getNum(),
721 args
[4].getNum(), args
[5].getNum());
725 void Gfx::opSetDash(Object args
[], int numArgs
) {
732 a
= args
[0].getArray();
733 length
= a
->getLength();
737 dash
= (double *)gmalloc(length
* sizeof(double));
738 for (i
= 0; i
< length
; ++i
) {
739 dash
[i
] = a
->get(i
, &obj
)->getNum();
743 state
->setLineDash(dash
, length
, args
[1].getNum());
744 out
->updateLineDash(state
);
747 void Gfx::opSetFlat(Object args
[], int numArgs
) {
748 state
->setFlatness((int)args
[0].getNum());
749 out
->updateFlatness(state
);
752 void Gfx::opSetLineJoin(Object args
[], int numArgs
) {
753 state
->setLineJoin(args
[0].getInt());
754 out
->updateLineJoin(state
);
757 void Gfx::opSetLineCap(Object args
[], int numArgs
) {
758 state
->setLineCap(args
[0].getInt());
759 out
->updateLineCap(state
);
762 void Gfx::opSetMiterLimit(Object args
[], int numArgs
) {
763 state
->setMiterLimit(args
[0].getNum());
764 out
->updateMiterLimit(state
);
767 void Gfx::opSetLineWidth(Object args
[], int numArgs
) {
768 state
->setLineWidth(args
[0].getNum());
769 out
->updateLineWidth(state
);
772 void Gfx::opSetExtGState(Object args
[], int numArgs
) {
775 if (!res
->lookupGState(args
[0].getName(), &obj1
)) {
778 if (!obj1
.isDict()) {
779 error(getPos(), "ExtGState '%s' is wrong type", args
[0].getName());
783 if (obj1
.dictLookup("ca", &obj2
)->isNum()) {
784 state
->setFillOpacity(obj2
.getNum());
785 out
->updateFillOpacity(state
);
788 if (obj1
.dictLookup("CA", &obj2
)->isNum()) {
789 state
->setStrokeOpacity(obj2
.getNum());
790 out
->updateStrokeOpacity(state
);
796 void Gfx::opSetRenderingIntent(Object args
[], int numArgs
) {
799 //------------------------------------------------------------------------
801 //------------------------------------------------------------------------
803 void Gfx::opSetFillGray(Object args
[], int numArgs
) {
806 state
->setFillPattern(NULL
);
807 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
808 color
.c
[0] = args
[0].getNum();
809 state
->setFillColor(&color
);
810 out
->updateFillColor(state
);
813 void Gfx::opSetStrokeGray(Object args
[], int numArgs
) {
816 state
->setStrokePattern(NULL
);
817 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
818 color
.c
[0] = args
[0].getNum();
819 state
->setStrokeColor(&color
);
820 out
->updateStrokeColor(state
);
823 void Gfx::opSetFillCMYKColor(Object args
[], int numArgs
) {
827 state
->setFillPattern(NULL
);
828 state
->setFillColorSpace(new GfxDeviceCMYKColorSpace());
829 for (i
= 0; i
< 4; ++i
) {
830 color
.c
[i
] = args
[i
].getNum();
832 state
->setFillColor(&color
);
833 out
->updateFillColor(state
);
836 void Gfx::opSetStrokeCMYKColor(Object args
[], int numArgs
) {
840 state
->setStrokePattern(NULL
);
841 state
->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
842 for (i
= 0; i
< 4; ++i
) {
843 color
.c
[i
] = args
[i
].getNum();
845 state
->setStrokeColor(&color
);
846 out
->updateStrokeColor(state
);
849 void Gfx::opSetFillRGBColor(Object args
[], int numArgs
) {
853 state
->setFillPattern(NULL
);
854 state
->setFillColorSpace(new GfxDeviceRGBColorSpace());
855 for (i
= 0; i
< 3; ++i
) {
856 color
.c
[i
] = args
[i
].getNum();
858 state
->setFillColor(&color
);
859 out
->updateFillColor(state
);
862 void Gfx::opSetStrokeRGBColor(Object args
[], int numArgs
) {
866 state
->setStrokePattern(NULL
);
867 state
->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
868 for (i
= 0; i
< 3; ++i
) {
869 color
.c
[i
] = args
[i
].getNum();
871 state
->setStrokeColor(&color
);
872 out
->updateStrokeColor(state
);
875 void Gfx::opSetFillColorSpace(Object args
[], int numArgs
) {
877 GfxColorSpace
*colorSpace
;
881 state
->setFillPattern(NULL
);
882 res
->lookupColorSpace(args
[0].getName(), &obj
);
884 colorSpace
= GfxColorSpace::parse(&args
[0]);
886 colorSpace
= GfxColorSpace::parse(&obj
);
890 state
->setFillColorSpace(colorSpace
);
892 error(getPos(), "Bad color space (fill)");
894 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
897 state
->setFillColor(&color
);
898 out
->updateFillColor(state
);
901 void Gfx::opSetStrokeColorSpace(Object args
[], int numArgs
) {
903 GfxColorSpace
*colorSpace
;
907 state
->setStrokePattern(NULL
);
908 res
->lookupColorSpace(args
[0].getName(), &obj
);
910 colorSpace
= GfxColorSpace::parse(&args
[0]);
912 colorSpace
= GfxColorSpace::parse(&obj
);
916 state
->setStrokeColorSpace(colorSpace
);
918 error(getPos(), "Bad color space (stroke)");
920 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
923 state
->setStrokeColor(&color
);
924 out
->updateStrokeColor(state
);
927 void Gfx::opSetFillColor(Object args
[], int numArgs
) {
931 state
->setFillPattern(NULL
);
932 for (i
= 0; i
< numArgs
; ++i
) {
933 color
.c
[i
] = args
[i
].getNum();
935 state
->setFillColor(&color
);
936 out
->updateFillColor(state
);
939 void Gfx::opSetStrokeColor(Object args
[], int numArgs
) {
943 state
->setStrokePattern(NULL
);
944 for (i
= 0; i
< numArgs
; ++i
) {
945 color
.c
[i
] = args
[i
].getNum();
947 state
->setStrokeColor(&color
);
948 out
->updateStrokeColor(state
);
951 void Gfx::opSetFillColorN(Object args
[], int numArgs
) {
956 if (state
->getFillColorSpace()->getMode() == csPattern
) {
958 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
959 if (args
[i
].isNum()) {
960 color
.c
[i
] = args
[i
].getNum();
963 state
->setFillColor(&color
);
964 out
->updateFillColor(state
);
966 if (args
[numArgs
-1].isName() &&
967 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
968 state
->setFillPattern(pattern
);
972 state
->setFillPattern(NULL
);
973 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
974 if (args
[i
].isNum()) {
975 color
.c
[i
] = args
[i
].getNum();
978 state
->setFillColor(&color
);
979 out
->updateFillColor(state
);
983 void Gfx::opSetStrokeColorN(Object args
[], int numArgs
) {
988 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
990 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
991 if (args
[i
].isNum()) {
992 color
.c
[i
] = args
[i
].getNum();
995 state
->setStrokeColor(&color
);
996 out
->updateStrokeColor(state
);
998 if (args
[numArgs
-1].isName() &&
999 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
1000 state
->setStrokePattern(pattern
);
1004 state
->setStrokePattern(NULL
);
1005 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
1006 if (args
[i
].isNum()) {
1007 color
.c
[i
] = args
[i
].getNum();
1010 state
->setStrokeColor(&color
);
1011 out
->updateStrokeColor(state
);
1015 //------------------------------------------------------------------------
1016 // path segment operators
1017 //------------------------------------------------------------------------
1019 void Gfx::opMoveTo(Object args
[], int numArgs
) {
1020 state
->moveTo(args
[0].getNum(), args
[1].getNum());
1023 void Gfx::opLineTo(Object args
[], int numArgs
) {
1024 if (!state
->isCurPt()) {
1025 error(getPos(), "No current point in lineto");
1028 state
->lineTo(args
[0].getNum(), args
[1].getNum());
1031 void Gfx::opCurveTo(Object args
[], int numArgs
) {
1032 double x1
, y1
, x2
, y2
, x3
, y3
;
1034 if (!state
->isCurPt()) {
1035 error(getPos(), "No current point in curveto");
1038 x1
= args
[0].getNum();
1039 y1
= args
[1].getNum();
1040 x2
= args
[2].getNum();
1041 y2
= args
[3].getNum();
1042 x3
= args
[4].getNum();
1043 y3
= args
[5].getNum();
1044 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1047 void Gfx::opCurveTo1(Object args
[], int numArgs
) {
1048 double x1
, y1
, x2
, y2
, x3
, y3
;
1050 if (!state
->isCurPt()) {
1051 error(getPos(), "No current point in curveto1");
1054 x1
= state
->getCurX();
1055 y1
= state
->getCurY();
1056 x2
= args
[0].getNum();
1057 y2
= args
[1].getNum();
1058 x3
= args
[2].getNum();
1059 y3
= args
[3].getNum();
1060 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1063 void Gfx::opCurveTo2(Object args
[], int numArgs
) {
1064 double x1
, y1
, x2
, y2
, x3
, y3
;
1066 if (!state
->isCurPt()) {
1067 error(getPos(), "No current point in curveto2");
1070 x1
= args
[0].getNum();
1071 y1
= args
[1].getNum();
1072 x2
= args
[2].getNum();
1073 y2
= args
[3].getNum();
1076 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1079 void Gfx::opRectangle(Object args
[], int numArgs
) {
1082 x
= args
[0].getNum();
1083 y
= args
[1].getNum();
1084 w
= args
[2].getNum();
1085 h
= args
[3].getNum();
1086 state
->moveTo(x
, y
);
1087 state
->lineTo(x
+ w
, y
);
1088 state
->lineTo(x
+ w
, y
+ h
);
1089 state
->lineTo(x
, y
+ h
);
1093 void Gfx::opClosePath(Object args
[], int numArgs
) {
1094 if (!state
->isCurPt()) {
1095 error(getPos(), "No current point in closepath");
1101 //------------------------------------------------------------------------
1102 // path painting operators
1103 //------------------------------------------------------------------------
1105 void Gfx::opEndPath(Object args
[], int numArgs
) {
1109 void Gfx::opStroke(Object args
[], int numArgs
) {
1110 if (!state
->isCurPt()) {
1111 //error(getPos(), "No path in stroke");
1114 if (state
->isPath())
1119 void Gfx::opCloseStroke(Object args
[], int numArgs
) {
1120 if (!state
->isCurPt()) {
1121 //error(getPos(), "No path in closepath/stroke");
1124 if (state
->isPath()) {
1131 void Gfx::opFill(Object args
[], int numArgs
) {
1132 if (!state
->isCurPt()) {
1133 //error(getPos(), "No path in fill");
1136 if (state
->isPath()) {
1137 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1138 doPatternFill(gFalse
);
1146 void Gfx::opEOFill(Object args
[], int numArgs
) {
1147 if (!state
->isCurPt()) {
1148 //error(getPos(), "No path in eofill");
1151 if (state
->isPath()) {
1152 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1153 doPatternFill(gTrue
);
1161 void Gfx::opFillStroke(Object args
[], int numArgs
) {
1162 if (!state
->isCurPt()) {
1163 //error(getPos(), "No path in fill/stroke");
1166 if (state
->isPath()) {
1167 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1168 doPatternFill(gFalse
);
1177 void Gfx::opCloseFillStroke(Object args
[], int numArgs
) {
1178 if (!state
->isCurPt()) {
1179 //error(getPos(), "No path in closepath/fill/stroke");
1182 if (state
->isPath()) {
1184 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1185 doPatternFill(gFalse
);
1194 void Gfx::opEOFillStroke(Object args
[], int numArgs
) {
1195 if (!state
->isCurPt()) {
1196 //error(getPos(), "No path in eofill/stroke");
1199 if (state
->isPath()) {
1200 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1201 doPatternFill(gTrue
);
1210 void Gfx::opCloseEOFillStroke(Object args
[], int numArgs
) {
1211 if (!state
->isCurPt()) {
1212 //error(getPos(), "No path in closepath/eofill/stroke");
1215 if (state
->isPath()) {
1217 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1218 doPatternFill(gTrue
);
1227 void Gfx::doPatternFill(GBool eoFill
) {
1228 GfxPattern
*pattern
;
1230 // this is a bit of a kludge -- patterns can be really slow, so we
1231 // skip them if we're only doing text extraction, since they almost
1232 // certainly don't contain any text
1233 if (!out
->needNonText()) {
1237 if (!(pattern
= state
->getFillPattern())) {
1240 switch (pattern
->getType()) {
1242 doTilingPatternFill((GfxTilingPattern
*)pattern
, eoFill
);
1245 doShadingPatternFill((GfxShadingPattern
*)pattern
, eoFill
);
1248 error(getPos(), "Unimplemented pattern type (%d) in fill",
1249 pattern
->getType());
1254 void Gfx::doTilingPatternFill(GfxTilingPattern
*tPat
, GBool eoFill
) {
1255 GfxPatternColorSpace
*patCS
;
1258 double xMin
, yMin
, xMax
, yMax
, x
, y
, x1
, y1
;
1259 double cxMin
, cyMin
, cxMax
, cyMax
;
1260 int xi0
, yi0
, xi1
, yi1
, xi
, yi
;
1261 double *ctm
, *btm
, *ptm
;
1262 double m
[6], ictm
[6], m1
[6], imb
[6];
1264 double xstep
, ystep
;
1268 patCS
= (GfxPatternColorSpace
*)state
->getFillColorSpace();
1270 // construct a (pattern space) -> (current space) transform matrix
1271 ctm
= state
->getCTM();
1273 ptm
= tPat
->getMatrix();
1274 // iCTM = invert CTM
1275 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1276 ictm
[0] = ctm
[3] * det
;
1277 ictm
[1] = -ctm
[1] * det
;
1278 ictm
[2] = -ctm
[2] * det
;
1279 ictm
[3] = ctm
[0] * det
;
1280 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1281 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1282 // m1 = PTM * BTM = PTM * base transform matrix
1283 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1284 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1285 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1286 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1287 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1288 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1289 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1290 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1291 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1292 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1293 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1294 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1295 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1297 // construct a (base space) -> (pattern space) transform matrix
1298 det
= 1 / (m1
[0] * m1
[3] - m1
[1] * m1
[2]);
1299 imb
[0] = m1
[3] * det
;
1300 imb
[1] = -m1
[1] * det
;
1301 imb
[2] = -m1
[2] * det
;
1302 imb
[3] = m1
[0] * det
;
1303 imb
[4] = (m1
[2] * m1
[5] - m1
[3] * m1
[4]) * det
;
1304 imb
[5] = (m1
[1] * m1
[4] - m1
[0] * m1
[5]) * det
;
1306 // save current graphics state
1307 savedPath
= state
->getPath()->copy();
1310 // set underlying color space (for uncolored tiling patterns); set
1311 // various other parameters (stroke color, line width) to match
1313 if (tPat
->getPaintType() == 2 && (cs
= patCS
->getUnder())) {
1314 state
->setFillColorSpace(cs
->copy());
1315 state
->setStrokeColorSpace(cs
->copy());
1316 state
->setStrokeColor(state
->getFillColor());
1318 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
1319 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1321 state
->setFillPattern(NULL
);
1322 out
->updateFillColor(state
);
1323 state
->setStrokePattern(NULL
);
1324 out
->updateStrokeColor(state
);
1325 state
->setLineWidth(0);
1326 out
->updateLineWidth(state
);
1328 // clip to current path
1337 // transform clip region bbox to pattern space
1338 state
->getClipBBox(&cxMin
, &cyMin
, &cxMax
, &cyMax
);
1339 xMin
= xMax
= cxMin
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1340 yMin
= yMax
= cxMin
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1341 x1
= cxMin
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1342 y1
= cxMin
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1345 } else if (x1
> xMax
) {
1350 } else if (y1
> yMax
) {
1353 x1
= cxMax
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1354 y1
= cxMax
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1357 } else if (x1
> xMax
) {
1362 } else if (y1
> yMax
) {
1365 x1
= cxMax
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1366 y1
= cxMax
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1369 } else if (x1
> xMax
) {
1374 } else if (y1
> yMax
) {
1379 //~ this should treat negative steps differently -- start at right/top
1380 //~ edge instead of left/bottom (?)
1381 xstep
= fabs(tPat
->getXStep());
1382 ystep
= fabs(tPat
->getYStep());
1383 xi0
= (int)floor((xMin
- tPat
->getBBox()[0]) / xstep
);
1384 xi1
= (int)ceil((xMax
- tPat
->getBBox()[0]) / xstep
);
1385 yi0
= (int)floor((yMin
- tPat
->getBBox()[1]) / ystep
);
1386 yi1
= (int)ceil((yMax
- tPat
->getBBox()[1]) / ystep
);
1387 for (i
= 0; i
< 4; ++i
) {
1390 for (yi
= yi0
; yi
< yi1
; ++yi
) {
1391 for (xi
= xi0
; xi
< xi1
; ++xi
) {
1394 m1
[4] = x
* m
[0] + y
* m
[2] + m
[4];
1395 m1
[5] = x
* m
[1] + y
* m
[3] + m
[5];
1396 doForm1(tPat
->getContentStream(), tPat
->getResDict(),
1397 m1
, tPat
->getBBox());
1401 // restore graphics state
1403 state
->setPath(savedPath
);
1406 void Gfx::doShadingPatternFill(GfxShadingPattern
*sPat
, GBool eoFill
) {
1407 GfxShading
*shading
;
1409 double *ctm
, *btm
, *ptm
;
1410 double m
[6], ictm
[6], m1
[6];
1411 double xMin
, yMin
, xMax
, yMax
;
1414 shading
= sPat
->getShading();
1416 // save current graphics state
1417 savedPath
= state
->getPath()->copy();
1421 if (shading
->getHasBBox()) {
1422 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1423 state
->moveTo(xMin
, yMin
);
1424 state
->lineTo(xMax
, yMin
);
1425 state
->lineTo(xMax
, yMax
);
1426 state
->lineTo(xMin
, yMax
);
1433 // clip to current path
1442 // construct a (pattern space) -> (current space) transform matrix
1443 ctm
= state
->getCTM();
1445 ptm
= sPat
->getMatrix();
1446 // iCTM = invert CTM
1447 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1448 ictm
[0] = ctm
[3] * det
;
1449 ictm
[1] = -ctm
[1] * det
;
1450 ictm
[2] = -ctm
[2] * det
;
1451 ictm
[3] = ctm
[0] * det
;
1452 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1453 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1454 // m1 = PTM * BTM = PTM * base transform matrix
1455 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1456 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1457 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1458 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1459 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1460 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1461 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1462 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1463 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1464 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1465 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1466 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1467 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1469 // set the new matrix
1470 state
->concatCTM(m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1471 out
->updateCTM(state
, m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1473 // set the color space
1474 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1476 // do shading type-specific operations
1477 switch (shading
->getType()) {
1479 doFunctionShFill((GfxFunctionShading
*)shading
);
1482 doAxialShFill((GfxAxialShading
*)shading
);
1485 doRadialShFill((GfxRadialShading
*)shading
);
1489 // restore graphics state
1491 state
->setPath(savedPath
);
1494 void Gfx::opShFill(Object args
[], int numArgs
) {
1495 GfxShading
*shading
;
1497 double xMin
, yMin
, xMax
, yMax
;
1499 if (!(shading
= res
->lookupShading(args
[0].getName()))) {
1503 // save current graphics state
1504 savedPath
= state
->getPath()->copy();
1508 if (shading
->getHasBBox()) {
1509 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1510 state
->moveTo(xMin
, yMin
);
1511 state
->lineTo(xMax
, yMin
);
1512 state
->lineTo(xMax
, yMax
);
1513 state
->lineTo(xMin
, yMax
);
1520 // set the color space
1521 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1523 // do shading type-specific operations
1524 switch (shading
->getType()) {
1526 doFunctionShFill((GfxFunctionShading
*)shading
);
1529 doAxialShFill((GfxAxialShading
*)shading
);
1532 doRadialShFill((GfxRadialShading
*)shading
);
1536 // restore graphics state
1538 state
->setPath(savedPath
);
1543 void Gfx::doFunctionShFill(GfxFunctionShading
*shading
) {
1544 double x0
, y0
, x1
, y1
;
1547 shading
->getDomain(&x0
, &y0
, &x1
, &y1
);
1548 shading
->getColor(x0
, y0
, &colors
[0]);
1549 shading
->getColor(x0
, y1
, &colors
[1]);
1550 shading
->getColor(x1
, y0
, &colors
[2]);
1551 shading
->getColor(x1
, y1
, &colors
[3]);
1552 doFunctionShFill1(shading
, x0
, y0
, x1
, y1
, colors
, 0);
1555 void Gfx::doFunctionShFill1(GfxFunctionShading
*shading
,
1556 double x0
, double y0
,
1557 double x1
, double y1
,
1558 GfxColor
*colors
, int depth
) {
1560 GfxColor color0M
, color1M
, colorM0
, colorM1
, colorMM
;
1561 GfxColor colors2
[4];
1566 nComps
= shading
->getColorSpace()->getNComps();
1567 matrix
= shading
->getMatrix();
1569 // compare the four corner colors
1570 for (i
= 0; i
< 4; ++i
) {
1571 for (j
= 0; j
< nComps
; ++j
) {
1572 if (fabs(colors
[i
].c
[j
] - colors
[(i
+1)&3].c
[j
]) > functionColorDelta
) {
1581 // center of the rectangle
1582 xM
= 0.5 * (x0
+ x1
);
1583 yM
= 0.5 * (y0
+ y1
);
1585 // the four corner colors are close (or we hit the recursive limit)
1586 // -- fill the rectangle; but require at least one subdivision
1587 // (depth==0) to avoid problems when the four outer corners of the
1588 // shaded region are the same color
1589 if ((i
== 4 && depth
> 0) || depth
== functionMaxDepth
) {
1591 // use the center color
1592 shading
->getColor(xM
, yM
, &fillColor
);
1593 state
->setFillColor(&fillColor
);
1594 out
->updateFillColor(state
);
1596 // fill the rectangle
1597 state
->moveTo(x0
* matrix
[0] + y0
* matrix
[2] + matrix
[4],
1598 x0
* matrix
[1] + y0
* matrix
[3] + matrix
[5]);
1599 state
->lineTo(x1
* matrix
[0] + y0
* matrix
[2] + matrix
[4],
1600 x1
* matrix
[1] + y0
* matrix
[3] + matrix
[5]);
1601 state
->lineTo(x1
* matrix
[0] + y1
* matrix
[2] + matrix
[4],
1602 x1
* matrix
[1] + y1
* matrix
[3] + matrix
[5]);
1603 state
->lineTo(x0
* matrix
[0] + y1
* matrix
[2] + matrix
[4],
1604 x0
* matrix
[1] + y1
* matrix
[3] + matrix
[5]);
1609 // the four corner colors are not close enough -- subdivide the
1613 // colors[0] colorM0 colors[2]
1614 // (x0,y0) (xM,y0) (x1,y0)
1615 // +----------+----------+
1618 // color0M | colorMM | color1M
1619 // (x0,yM) +----------+----------+ (x1,yM)
1623 // +----------+----------+
1624 // colors[1] colorM1 colors[3]
1625 // (x0,y1) (xM,y1) (x1,y1)
1627 shading
->getColor(x0
, yM
, &color0M
);
1628 shading
->getColor(x1
, yM
, &color1M
);
1629 shading
->getColor(xM
, y0
, &colorM0
);
1630 shading
->getColor(xM
, y1
, &colorM1
);
1631 shading
->getColor(xM
, yM
, &colorMM
);
1633 // upper-left sub-rectangle
1634 colors2
[0] = colors
[0];
1635 colors2
[1] = color0M
;
1636 colors2
[2] = colorM0
;
1637 colors2
[3] = colorMM
;
1638 doFunctionShFill1(shading
, x0
, y0
, xM
, yM
, colors2
, depth
+ 1);
1640 // lower-left sub-rectangle
1641 colors2
[0] = color0M
;
1642 colors2
[1] = colors
[1];
1643 colors2
[2] = colorMM
;
1644 colors2
[3] = colorM1
;
1645 doFunctionShFill1(shading
, x0
, yM
, xM
, y1
, colors2
, depth
+ 1);
1647 // upper-right sub-rectangle
1648 colors2
[0] = colorM0
;
1649 colors2
[1] = colorMM
;
1650 colors2
[2] = colors
[2];
1651 colors2
[3] = color1M
;
1652 doFunctionShFill1(shading
, xM
, y0
, x1
, yM
, colors2
, depth
+ 1);
1654 // lower-right sub-rectangle
1655 colors2
[0] = colorMM
;
1656 colors2
[1] = colorM1
;
1657 colors2
[2] = color1M
;
1658 colors2
[3] = colors
[3];
1659 doFunctionShFill1(shading
, xM
, yM
, x1
, y1
, colors2
, depth
+ 1);
1663 void Gfx::doAxialShFill(GfxAxialShading
*shading
) {
1664 double xMin
, yMin
, xMax
, yMax
;
1665 double x0
, y0
, x1
, y1
;
1667 double tMin
, tMax
, t
, tx
, ty
;
1668 double s
[4], sMin
, sMax
, tmp
;
1669 double ux0
, uy0
, ux1
, uy1
, vx0
, vy0
, vx1
, vy1
;
1671 double ta
[axialMaxSplits
+ 1];
1672 int next
[axialMaxSplits
+ 1];
1673 GfxColor color0
, color1
;
1677 // get the clip region bbox
1678 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1680 // compute min and max t values, based on the four corners of the
1682 shading
->getCoords(&x0
, &y0
, &x1
, &y1
);
1685 mul
= 1 / (dx
* dx
+ dy
* dy
);
1686 tMin
= tMax
= ((xMin
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
1687 t
= ((xMin
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
1690 } else if (t
> tMax
) {
1693 t
= ((xMax
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
1696 } else if (t
> tMax
) {
1699 t
= ((xMax
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
1702 } else if (t
> tMax
) {
1705 if (tMin
< 0 && !shading
->getExtend0()) {
1708 if (tMax
> 1 && !shading
->getExtend1()) {
1712 // get the function domain
1713 t0
= shading
->getDomain0();
1714 t1
= shading
->getDomain1();
1716 // Traverse the t axis and do the shading.
1718 // For each point (tx, ty) on the t axis, consider a line through
1719 // that point perpendicular to the t axis:
1721 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1722 // y(s) = ty + s * dx --> s = (y - ty) / dx
1724 // Then look at the intersection of this line with the bounding box
1725 // (xMin, yMin, xMax, yMax). In the general case, there are four
1726 // intersection points:
1728 // s0 = (xMin - tx) / -dy
1729 // s1 = (xMax - tx) / -dy
1730 // s2 = (yMin - ty) / dx
1731 // s3 = (yMax - ty) / dx
1733 // and we want the middle two s values.
1735 // In the case where dx = 0, take s0 and s1; in the case where dy =
1736 // 0, take s2 and s3.
1738 // Each filled polygon is bounded by two of these line segments
1739 // perpdendicular to the t axis.
1741 // The t axis is bisected into smaller regions until the color
1742 // difference across a region is small enough, and then the region
1743 // is painted with a single color.
1745 // set up: require at least one split to avoid problems when the two
1746 // ends of the t axis have the same color
1747 nComps
= shading
->getColorSpace()->getNComps();
1749 next
[0] = axialMaxSplits
/ 2;
1750 ta
[axialMaxSplits
/ 2] = 0.5 * (tMin
+ tMax
);
1751 next
[axialMaxSplits
/ 2] = axialMaxSplits
;
1752 ta
[axialMaxSplits
] = tMax
;
1754 // compute the color at t = tMin
1757 } else if (tMin
> 1) {
1760 tt
= t0
+ (t1
- t0
) * tMin
;
1762 shading
->getColor(tt
, &color0
);
1764 // compute the coordinates of the point on the t axis at t = tMin;
1765 // then compute the intersection of the perpendicular line with the
1767 tx
= x0
+ tMin
* dx
;
1768 ty
= y0
+ tMin
* dy
;
1769 if (dx
== 0 && dy
== 0) {
1772 sMin
= (xMin
- tx
) / -dy
;
1773 sMax
= (xMax
- tx
) / -dy
;
1774 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1775 } else if (dy
== 0) {
1776 sMin
= (yMin
- ty
) / dx
;
1777 sMax
= (yMax
- ty
) / dx
;
1778 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1780 s
[0] = (yMin
- ty
) / dx
;
1781 s
[1] = (yMax
- ty
) / dx
;
1782 s
[2] = (xMin
- tx
) / -dy
;
1783 s
[3] = (xMax
- tx
) / -dy
;
1784 for (j
= 0; j
< 3; ++j
) {
1786 for (k
= j
+ 1; k
< 4; ++k
) {
1791 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
1796 ux0
= tx
- sMin
* dy
;
1797 uy0
= ty
+ sMin
* dx
;
1798 vx0
= tx
- sMax
* dy
;
1799 vy0
= ty
+ sMax
* dx
;
1802 while (i
< axialMaxSplits
) {
1804 // bisect until color difference is small enough or we hit the
1810 } else if (ta
[j
] > 1) {
1813 tt
= t0
+ (t1
- t0
) * ta
[j
];
1815 shading
->getColor(tt
, &color1
);
1816 for (k
= 0; k
< nComps
; ++k
) {
1817 if (fabs(color1
.c
[k
] - color0
.c
[k
]) > axialColorDelta
) {
1825 ta
[k
] = 0.5 * (ta
[i
] + ta
[j
]);
1831 // use the average of the colors of the two sides of the region
1832 for (k
= 0; k
< nComps
; ++k
) {
1833 color0
.c
[k
] = 0.5 * (color0
.c
[k
] + color1
.c
[k
]);
1836 // compute the coordinates of the point on the t axis; then
1837 // compute the intersection of the perpendicular line with the
1839 tx
= x0
+ ta
[j
] * dx
;
1840 ty
= y0
+ ta
[j
] * dy
;
1841 if (dx
== 0 && dy
== 0) {
1844 sMin
= (xMin
- tx
) / -dy
;
1845 sMax
= (xMax
- tx
) / -dy
;
1846 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1847 } else if (dy
== 0) {
1848 sMin
= (yMin
- ty
) / dx
;
1849 sMax
= (yMax
- ty
) / dx
;
1850 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1852 s
[0] = (yMin
- ty
) / dx
;
1853 s
[1] = (yMax
- ty
) / dx
;
1854 s
[2] = (xMin
- tx
) / -dy
;
1855 s
[3] = (xMax
- tx
) / -dy
;
1856 for (j
= 0; j
< 3; ++j
) {
1858 for (k
= j
+ 1; k
< 4; ++k
) {
1863 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
1868 ux1
= tx
- sMin
* dy
;
1869 uy1
= ty
+ sMin
* dx
;
1870 vx1
= tx
- sMax
* dy
;
1871 vy1
= ty
+ sMax
* dx
;
1874 state
->setFillColor(&color0
);
1875 out
->updateFillColor(state
);
1878 state
->moveTo(ux0
, uy0
);
1879 state
->lineTo(vx0
, vy0
);
1880 state
->lineTo(vx1
, vy1
);
1881 state
->lineTo(ux1
, uy1
);
1886 // set up for next region
1896 void Gfx::doRadialShFill(GfxRadialShading
*shading
) {
1897 double sMin
, sMax
, xMin
, yMin
, xMax
, yMax
;
1898 double x0
, y0
, r0
, x1
, y1
, r1
, t0
, t1
;
1900 GfxColor colorA
, colorB
;
1901 double xa
, ya
, xb
, yb
, ra
, rb
;
1902 double ta
, tb
, sa
, sb
;
1907 // get the shading info
1908 shading
->getCoords(&x0
, &y0
, &r0
, &x1
, &y1
, &r1
);
1909 t0
= shading
->getDomain0();
1910 t1
= shading
->getDomain1();
1911 nComps
= shading
->getColorSpace()->getNComps();
1913 // compute the (possibly extended) s range
1916 if (shading
->getExtend0()) {
1918 // extend the smaller end
1919 sMin
= -r0
/ (r1
- r0
);
1921 // extend the larger end
1922 //~ this computes the diagonal of the bounding box -- we should
1923 //~ really compute the intersection of the moving/expanding
1924 //~ circles with each of the four corners and look for the max
1926 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1927 sMin
= (sqrt((xMax
- xMin
) * (xMax
- xMin
) +
1928 (yMax
- yMin
) * (yMax
- yMin
)) - r0
) / (r1
- r0
);
1931 } else if (sMin
< -20) {
1937 if (shading
->getExtend1()) {
1939 // extend the smaller end
1940 sMax
= -r0
/ (r1
- r0
);
1941 } else if (r1
> r0
) {
1942 // extend the larger end
1943 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1944 sMax
= (sqrt((xMax
- xMin
) * (xMax
- xMin
) +
1945 (yMax
- yMin
) * (yMax
- yMin
)) - r0
) / (r1
- r0
);
1948 } else if (sMax
> 20) {
1955 // compute the number of steps into which circles must be divided to
1956 // achieve a curve flatness of 0.1 pixel in device space for the
1957 // largest circle (note that "device space" is 72 dpi when generating
1958 // PostScript, hence the relatively small 0.1 pixel accuracy)
1959 ctm
= state
->getCTM();
1961 if (fabs(ctm
[1]) > t
) {
1964 if (fabs(ctm
[2]) > t
) {
1967 if (fabs(ctm
[3]) > t
) {
1978 n
= (int)(M_PI
/ acos(1 - 0.1 / t
));
1981 } else if (n
> 200) {
1986 // Traverse the t axis and do the shading.
1988 // This generates and fills a series of rings. Each ring is defined
1990 // sa, ta, xa, ya, ra, colorA
1991 // sb, tb, xb, yb, rb, colorB
1993 // The s/t axis is divided into radialMaxSplits parts; these parts
1994 // are combined as much as possible while respecting the
1995 // radialColorDelta parameter.
1997 // setup for the start circle
2000 ta
= t0
+ sa
* (t1
- t0
);
2001 xa
= x0
+ sa
* (x1
- x0
);
2002 ya
= y0
+ sa
* (y1
- y0
);
2003 ra
= r0
+ sa
* (r1
- r0
);
2005 shading
->getColor(t0
, &colorA
);
2006 } else if (ta
> t1
) {
2007 shading
->getColor(t1
, &colorA
);
2009 shading
->getColor(ta
, &colorA
);
2012 while (ia
< radialMaxSplits
) {
2014 // go as far along the t axis (toward t1) as we can, such that the
2015 // color difference is within the tolerance (radialColorDelta) --
2016 // this uses bisection (between the current value, t, and t1),
2017 // limited to radialMaxSplits points along the t axis; require at
2018 // least one split to avoid problems when the innermost and
2019 // outermost colors are the same
2020 ib
= radialMaxSplits
;
2021 sb
= sMin
+ ((double)ib
/ (double)radialMaxSplits
) * (sMax
- sMin
);
2022 tb
= t0
+ sb
* (t1
- t0
);
2024 shading
->getColor(t0
, &colorB
);
2025 } else if (tb
> t1
) {
2026 shading
->getColor(t1
, &colorB
);
2028 shading
->getColor(tb
, &colorB
);
2030 while (ib
- ia
> 1) {
2031 for (k
= 0; k
< nComps
; ++k
) {
2032 if (fabs(colorB
.c
[k
] - colorA
.c
[k
]) > radialColorDelta
) {
2036 if (k
== nComps
&& ib
< radialMaxSplits
) {
2040 sb
= sMin
+ ((double)ib
/ (double)radialMaxSplits
) * (sMax
- sMin
);
2041 tb
= t0
+ sb
* (t1
- t0
);
2043 shading
->getColor(t0
, &colorB
);
2044 } else if (tb
> t1
) {
2045 shading
->getColor(t1
, &colorB
);
2047 shading
->getColor(tb
, &colorB
);
2051 // compute center and radius of the circle
2052 xb
= x0
+ sb
* (x1
- x0
);
2053 yb
= y0
+ sb
* (y1
- y0
);
2054 rb
= r0
+ sb
* (r1
- r0
);
2056 // use the average of the colors at the two circles
2057 for (k
= 0; k
< nComps
; ++k
) {
2058 colorA
.c
[k
] = 0.5 * (colorA
.c
[k
] + colorB
.c
[k
]);
2060 state
->setFillColor(&colorA
);
2061 out
->updateFillColor(state
);
2063 // construct path for first circle
2064 state
->moveTo(xa
+ ra
, ya
);
2065 for (k
= 1; k
< n
; ++k
) {
2066 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2067 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2071 // construct and append path for second circle
2072 state
->moveTo(xb
+ rb
, yb
);
2073 for (k
= 1; k
< n
; ++k
) {
2074 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2075 state
->lineTo(xb
+ rb
* cos(angle
), yb
+ rb
* sin(angle
));
2083 // step to the next value of t
2094 void Gfx::doEndPath() {
2095 if (state
->isCurPt() && clip
!= clipNone
) {
2097 if (clip
== clipNormal
) {
2107 //------------------------------------------------------------------------
2108 // path clipping operators
2109 //------------------------------------------------------------------------
2111 void Gfx::opClip(Object args
[], int numArgs
) {
2115 void Gfx::opEOClip(Object args
[], int numArgs
) {
2119 //------------------------------------------------------------------------
2120 // text object operators
2121 //------------------------------------------------------------------------
2123 void Gfx::opBeginText(Object args
[], int numArgs
) {
2124 state
->setTextMat(1, 0, 0, 1, 0, 0);
2125 state
->textMoveTo(0, 0);
2126 out
->updateTextMat(state
);
2127 out
->updateTextPos(state
);
2128 fontChanged
= gTrue
;
2131 void Gfx::opEndText(Object args
[], int numArgs
) {
2132 out
->endTextObject(state
);
2135 //------------------------------------------------------------------------
2136 // text state operators
2137 //------------------------------------------------------------------------
2139 void Gfx::opSetCharSpacing(Object args
[], int numArgs
) {
2140 state
->setCharSpace(args
[0].getNum());
2141 out
->updateCharSpace(state
);
2144 void Gfx::opSetFont(Object args
[], int numArgs
) {
2147 if (!(font
= res
->lookupFont(args
[0].getName()))) {
2150 if (printCommands
) {
2151 printf(" font: tag=%s name='%s' %g\n",
2152 font
->getTag()->getCString(),
2153 font
->getName() ? font
->getName()->getCString() : "???",
2157 state
->setFont(font
, args
[1].getNum());
2158 fontChanged
= gTrue
;
2161 void Gfx::opSetTextLeading(Object args
[], int numArgs
) {
2162 state
->setLeading(args
[0].getNum());
2165 void Gfx::opSetTextRender(Object args
[], int numArgs
) {
2166 state
->setRender(args
[0].getInt());
2167 out
->updateRender(state
);
2170 void Gfx::opSetTextRise(Object args
[], int numArgs
) {
2171 state
->setRise(args
[0].getNum());
2172 out
->updateRise(state
);
2175 void Gfx::opSetWordSpacing(Object args
[], int numArgs
) {
2176 state
->setWordSpace(args
[0].getNum());
2177 out
->updateWordSpace(state
);
2180 void Gfx::opSetHorizScaling(Object args
[], int numArgs
) {
2181 state
->setHorizScaling(args
[0].getNum());
2182 out
->updateHorizScaling(state
);
2183 fontChanged
= gTrue
;
2186 //------------------------------------------------------------------------
2187 // text positioning operators
2188 //------------------------------------------------------------------------
2190 void Gfx::opTextMove(Object args
[], int numArgs
) {
2193 tx
= state
->getLineX() + args
[0].getNum();
2194 ty
= state
->getLineY() + args
[1].getNum();
2195 state
->textMoveTo(tx
, ty
);
2196 out
->updateTextPos(state
);
2199 void Gfx::opTextMoveSet(Object args
[], int numArgs
) {
2202 tx
= state
->getLineX() + args
[0].getNum();
2203 ty
= args
[1].getNum();
2204 state
->setLeading(-ty
);
2205 ty
+= state
->getLineY();
2206 state
->textMoveTo(tx
, ty
);
2207 out
->updateTextPos(state
);
2210 void Gfx::opSetTextMatrix(Object args
[], int numArgs
) {
2211 state
->setTextMat(args
[0].getNum(), args
[1].getNum(),
2212 args
[2].getNum(), args
[3].getNum(),
2213 args
[4].getNum(), args
[5].getNum());
2214 state
->textMoveTo(0, 0);
2215 out
->updateTextMat(state
);
2216 out
->updateTextPos(state
);
2217 fontChanged
= gTrue
;
2220 void Gfx::opTextNextLine(Object args
[], int numArgs
) {
2223 tx
= state
->getLineX();
2224 ty
= state
->getLineY() - state
->getLeading();
2225 state
->textMoveTo(tx
, ty
);
2226 out
->updateTextPos(state
);
2229 //------------------------------------------------------------------------
2230 // text string operators
2231 //------------------------------------------------------------------------
2233 void Gfx::opShowText(Object args
[], int numArgs
) {
2234 if (!state
->getFont()) {
2235 error(getPos(), "No font in show");
2238 doShowText(args
[0].getString());
2241 void Gfx::opMoveShowText(Object args
[], int numArgs
) {
2244 if (!state
->getFont()) {
2245 error(getPos(), "No font in move/show");
2248 tx
= state
->getLineX();
2249 ty
= state
->getLineY() - state
->getLeading();
2250 state
->textMoveTo(tx
, ty
);
2251 out
->updateTextPos(state
);
2252 doShowText(args
[0].getString());
2255 void Gfx::opMoveSetShowText(Object args
[], int numArgs
) {
2258 if (!state
->getFont()) {
2259 error(getPos(), "No font in move/set/show");
2262 state
->setWordSpace(args
[0].getNum());
2263 state
->setCharSpace(args
[1].getNum());
2264 tx
= state
->getLineX();
2265 ty
= state
->getLineY() - state
->getLeading();
2266 state
->textMoveTo(tx
, ty
);
2267 out
->updateWordSpace(state
);
2268 out
->updateCharSpace(state
);
2269 out
->updateTextPos(state
);
2270 doShowText(args
[2].getString());
2273 void Gfx::opShowSpaceText(Object args
[], int numArgs
) {
2279 if (!state
->getFont()) {
2280 error(getPos(), "No font in show/space");
2283 wMode
= state
->getFont()->getWMode();
2284 a
= args
[0].getArray();
2285 for (i
= 0; i
< a
->getLength(); ++i
) {
2289 state
->textShift(0, -obj
.getNum() * 0.001 * state
->getFontSize());
2291 state
->textShift(-obj
.getNum() * 0.001 * state
->getFontSize(), 0);
2293 out
->updateTextShift(state
, obj
.getNum());
2294 } else if (obj
.isString()) {
2295 doShowText(obj
.getString());
2297 error(getPos(), "Element of show/space array must be number or string");
2303 void Gfx::doShowText(GString
*s
) {
2306 double riseX
, riseY
;
2309 double x
, y
, dx
, dy
, dx2
, dy2
, curX
, curY
, tdx
, tdy
, lineX
, lineY
;
2310 double originX
, originY
, tOriginX
, tOriginY
;
2311 double oldCTM
[6], newCTM
[6];
2317 int len
, n
, uLen
, nChars
, nSpaces
, i
;
2320 out
->updateFont(state
);
2321 fontChanged
= gFalse
;
2323 font
= state
->getFont();
2324 wMode
= font
->getWMode();
2326 if (out
->useDrawChar()) {
2327 out
->beginString(state
, s
);
2330 // handle a Type 3 char
2331 if (font
->getType() == fontType3
&& out
->interpretType3Chars()) {
2332 mat
= state
->getCTM();
2333 for (i
= 0; i
< 6; ++i
) {
2336 mat
= state
->getTextMat();
2337 newCTM
[0] = mat
[0] * oldCTM
[0] + mat
[1] * oldCTM
[2];
2338 newCTM
[1] = mat
[0] * oldCTM
[1] + mat
[1] * oldCTM
[3];
2339 newCTM
[2] = mat
[2] * oldCTM
[0] + mat
[3] * oldCTM
[2];
2340 newCTM
[3] = mat
[2] * oldCTM
[1] + mat
[3] * oldCTM
[3];
2341 mat
= font
->getFontMatrix();
2342 newCTM
[0] = mat
[0] * newCTM
[0] + mat
[1] * newCTM
[2];
2343 newCTM
[1] = mat
[0] * newCTM
[1] + mat
[1] * newCTM
[3];
2344 newCTM
[2] = mat
[2] * newCTM
[0] + mat
[3] * newCTM
[2];
2345 newCTM
[3] = mat
[2] * newCTM
[1] + mat
[3] * newCTM
[3];
2346 newCTM
[0] *= state
->getFontSize();
2347 newCTM
[1] *= state
->getFontSize();
2348 newCTM
[2] *= state
->getFontSize();
2349 newCTM
[3] *= state
->getFontSize();
2350 newCTM
[0] *= state
->getHorizScaling();
2351 newCTM
[2] *= state
->getHorizScaling();
2352 state
->textTransformDelta(0, state
->getRise(), &riseX
, &riseY
);
2353 curX
= state
->getCurX();
2354 curY
= state
->getCurY();
2355 lineX
= state
->getLineX();
2356 lineY
= state
->getLineY();
2358 p
= s
->getCString();
2359 len
= s
->getLength();
2361 n
= font
->getNextChar(p
, len
, &code
,
2362 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
2363 &dx
, &dy
, &originX
, &originY
);
2364 dx
= dx
* state
->getFontSize() + state
->getCharSpace();
2365 if (n
== 1 && *p
== ' ') {
2366 dx
+= state
->getWordSpace();
2368 dx
*= state
->getHorizScaling();
2369 dy
*= state
->getFontSize();
2370 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
2371 state
->transform(curX
+ riseX
, curY
+ riseY
, &x
, &y
);
2373 state
->setCTM(newCTM
[0], newCTM
[1], newCTM
[2], newCTM
[3], x
, y
);
2374 //~ out->updateCTM(???)
2375 if (!out
->beginType3Char(state
, curX
+ riseX
, curY
+ riseY
, tdx
, tdy
,
2377 ((Gfx8BitFont
*)font
)->getCharProc(code
, &charProc
);
2378 if ((resDict
= ((Gfx8BitFont
*)font
)->getResources())) {
2379 pushResources(resDict
);
2381 if (charProc
.isStream()) {
2382 display(&charProc
, gFalse
);
2384 error(getPos(), "Missing or bad Type3 CharProc entry");
2386 out
->endType3Char(state
);
2393 // GfxState::restore() does *not* restore the current position,
2394 // so we deal with it here using (curX, curY) and (lineX, lineY)
2397 state
->moveTo(curX
, curY
);
2398 state
->textSetPos(lineX
, lineY
);
2404 } else if (out
->useDrawChar()) {
2405 state
->textTransformDelta(0, state
->getRise(), &riseX
, &riseY
);
2406 p
= s
->getCString();
2407 len
= s
->getLength();
2409 n
= font
->getNextChar(p
, len
, &code
,
2410 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
2411 &dx
, &dy
, &originX
, &originY
);
2413 dx
*= state
->getFontSize();
2414 dy
= dy
* state
->getFontSize() + state
->getCharSpace();
2415 if (n
== 1 && *p
== ' ') {
2416 dy
+= state
->getWordSpace();
2419 dx
= dx
* state
->getFontSize() + state
->getCharSpace();
2420 if (n
== 1 && *p
== ' ') {
2421 dx
+= state
->getWordSpace();
2423 dx
*= state
->getHorizScaling();
2424 dy
*= state
->getFontSize();
2426 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
2427 originX
*= state
->getFontSize();
2428 originY
*= state
->getFontSize();
2429 state
->textTransformDelta(originX
, originY
, &tOriginX
, &tOriginY
);
2430 out
->drawChar(state
, state
->getCurX() + riseX
, state
->getCurY() + riseY
,
2431 tdx
, tdy
, tOriginX
, tOriginY
, code
, u
, uLen
);
2432 state
->shift(tdx
, tdy
);
2439 p
= s
->getCString();
2440 len
= s
->getLength();
2441 nChars
= nSpaces
= 0;
2443 n
= font
->getNextChar(p
, len
, &code
,
2444 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
2445 &dx2
, &dy2
, &originX
, &originY
);
2448 if (n
== 1 && *p
== ' ') {
2456 dx
*= state
->getFontSize();
2457 dy
= dy
* state
->getFontSize()
2458 + nChars
* state
->getCharSpace()
2459 + nSpaces
* state
->getWordSpace();
2461 dx
= dx
* state
->getFontSize()
2462 + nChars
* state
->getCharSpace()
2463 + nSpaces
* state
->getWordSpace();
2464 dx
*= state
->getHorizScaling();
2465 dy
*= state
->getFontSize();
2467 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
2468 out
->drawString(state
, s
);
2469 state
->shift(tdx
, tdy
);
2472 if (out
->useDrawChar()) {
2473 out
->endString(state
);
2476 updateLevel
+= 10 * s
->getLength();
2479 //------------------------------------------------------------------------
2480 // XObject operators
2481 //------------------------------------------------------------------------
2483 void Gfx::opXObject(Object args
[], int numArgs
) {
2484 Object obj1
, obj2
, obj3
, refObj
;
2489 if (!res
->lookupXObject(args
[0].getName(), &obj1
)) {
2492 if (!obj1
.isStream()) {
2493 error(getPos(), "XObject '%s' is wrong type", args
[0].getName());
2498 obj1
.streamGetDict()->lookup("OPI", &opiDict
);
2499 if (opiDict
.isDict()) {
2500 out
->opiBegin(state
, opiDict
.getDict());
2503 obj1
.streamGetDict()->lookup("Subtype", &obj2
);
2504 if (obj2
.isName("Image")) {
2505 res
->lookupXObjectNF(args
[0].getName(), &refObj
);
2506 doImage(&refObj
, obj1
.getStream(), gFalse
);
2508 } else if (obj2
.isName("Form")) {
2510 } else if (obj2
.isName("PS")) {
2511 obj1
.streamGetDict()->lookup("Level1", &obj3
);
2512 out
->psXObject(obj1
.getStream(),
2513 obj3
.isStream() ? obj3
.getStream() : (Stream
*)NULL
);
2514 } else if (obj2
.isName()) {
2515 error(getPos(), "Unknown XObject subtype '%s'", obj2
.getName());
2517 error(getPos(), "XObject subtype is missing or wrong type");
2521 if (opiDict
.isDict()) {
2522 out
->opiEnd(state
, opiDict
.getDict());
2529 void Gfx::doImage(Object
*ref
, Stream
*str
, GBool inlineImg
) {
2535 GfxColorSpace
*colorSpace
;
2536 GfxImageColorMap
*colorMap
;
2539 int maskColors
[2*gfxColorMaxComps
];
2544 dict
= str
->getDict();
2547 dict
->lookup("Width", &obj1
);
2548 if (obj1
.isNull()) {
2550 dict
->lookup("W", &obj1
);
2554 width
= obj1
.getInt();
2556 dict
->lookup("Height", &obj1
);
2557 if (obj1
.isNull()) {
2559 dict
->lookup("H", &obj1
);
2563 height
= obj1
.getInt();
2567 dict
->lookup("ImageMask", &obj1
);
2568 if (obj1
.isNull()) {
2570 dict
->lookup("IM", &obj1
);
2574 mask
= obj1
.getBool();
2575 else if (!obj1
.isNull())
2580 dict
->lookup("BitsPerComponent", &obj1
);
2581 if (obj1
.isNull()) {
2583 dict
->lookup("BPC", &obj1
);
2586 bits
= obj1
.getInt();
2597 // check for inverted mask
2601 dict
->lookup("Decode", &obj1
);
2602 if (obj1
.isNull()) {
2604 dict
->lookup("D", &obj1
);
2606 if (obj1
.isArray()) {
2607 obj1
.arrayGet(0, &obj2
);
2608 if (obj2
.isInt() && obj2
.getInt() == 1)
2611 } else if (!obj1
.isNull()) {
2617 out
->drawImageMask(state
, ref
, str
, width
, height
, invert
, inlineImg
);
2621 // get color space and color map
2622 dict
->lookup("ColorSpace", &obj1
);
2623 if (obj1
.isNull()) {
2625 dict
->lookup("CS", &obj1
);
2627 if (obj1
.isName()) {
2628 res
->lookupColorSpace(obj1
.getName(), &obj2
);
2629 if (!obj2
.isNull()) {
2636 colorSpace
= GfxColorSpace::parse(&obj1
);
2641 dict
->lookup("Decode", &obj1
);
2642 if (obj1
.isNull()) {
2644 dict
->lookup("D", &obj1
);
2646 colorMap
= new GfxImageColorMap(bits
, &obj1
, colorSpace
);
2648 if (!colorMap
->isOk()) {
2655 dict
->lookup("Mask", &maskObj
);
2656 if (maskObj
.isArray()) {
2657 for (i
= 0; i
< maskObj
.arrayGetLength(); ++i
) {
2658 maskObj
.arrayGet(i
, &obj1
);
2659 maskColors
[i
] = obj1
.getInt();
2666 out
->drawImage(state
, ref
, str
, width
, height
, colorMap
,
2667 haveMask
? maskColors
: (int *)NULL
, inlineImg
);
2673 if ((i
= width
* height
) > 1000) {
2683 error(getPos(), "Bad image parameters");
2686 void Gfx::doForm(Object
*str
) {
2688 Object matrixObj
, bboxObj
;
2689 double m
[6], bbox
[6];
2695 // check for excessive recursion
2696 if (formDepth
> 20) {
2701 dict
= str
->streamGetDict();
2704 dict
->lookup("FormType", &obj1
);
2705 if (!(obj1
.isInt() && obj1
.getInt() == 1)) {
2706 error(getPos(), "Unknown form type");
2711 dict
->lookup("BBox", &bboxObj
);
2712 if (!bboxObj
.isArray()) {
2715 error(getPos(), "Bad form bounding box");
2718 for (i
= 0; i
< 4; ++i
) {
2719 bboxObj
.arrayGet(i
, &obj1
);
2720 bbox
[i
] = obj1
.getNum();
2726 dict
->lookup("Matrix", &matrixObj
);
2727 if (matrixObj
.isArray()) {
2728 for (i
= 0; i
< 6; ++i
) {
2729 matrixObj
.arrayGet(i
, &obj1
);
2730 m
[i
] = obj1
.getNum();
2741 dict
->lookup("Resources", &resObj
);
2742 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
2746 doForm1(str
, resDict
, m
, bbox
);
2752 void Gfx::doAnnot(Object
*str
, double xMin
, double yMin
,
2753 double xMax
, double yMax
) {
2754 Dict
*dict
, *resDict
;
2755 Object matrixObj
, bboxObj
, resObj
;
2757 double m
[6], bbox
[6], ictm
[6];
2759 double formX0
, formY0
, formX1
, formY1
;
2760 double annotX0
, annotY0
, annotX1
, annotY1
;
2761 double det
, x
, y
, sx
, sy
;
2765 dict
= str
->streamGetDict();
2767 // get the form bounding box
2768 dict
->lookup("BBox", &bboxObj
);
2769 if (!bboxObj
.isArray()) {
2771 error(getPos(), "Bad form bounding box");
2774 for (i
= 0; i
< 4; ++i
) {
2775 bboxObj
.arrayGet(i
, &obj1
);
2776 bbox
[i
] = obj1
.getNum();
2781 // get the form matrix
2782 dict
->lookup("Matrix", &matrixObj
);
2783 if (matrixObj
.isArray()) {
2784 for (i
= 0; i
< 6; ++i
) {
2785 matrixObj
.arrayGet(i
, &obj1
);
2786 m
[i
] = obj1
.getNum();
2796 // transform the form bbox from form space to user space
2797 formX0
= bbox
[0] * m
[0] + bbox
[1] * m
[2] + m
[4];
2798 formY0
= bbox
[0] * m
[1] + bbox
[1] * m
[3] + m
[5];
2799 formX1
= bbox
[2] * m
[0] + bbox
[3] * m
[2] + m
[4];
2800 formY1
= bbox
[2] * m
[1] + bbox
[3] * m
[3] + m
[5];
2802 // transform the annotation bbox from default user space to user
2803 // space: (bbox * baseMatrix) * iCTM
2804 ctm
= state
->getCTM();
2805 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
2806 ictm
[0] = ctm
[3] * det
;
2807 ictm
[1] = -ctm
[1] * det
;
2808 ictm
[2] = -ctm
[2] * det
;
2809 ictm
[3] = ctm
[0] * det
;
2810 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
2811 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
2812 x
= baseMatrix
[0] * xMin
+ baseMatrix
[2] * yMin
+ baseMatrix
[4];
2813 y
= baseMatrix
[1] * xMin
+ baseMatrix
[3] * yMin
+ baseMatrix
[5];
2814 annotX0
= ictm
[0] * x
+ ictm
[2] * y
+ ictm
[4];
2815 annotY0
= ictm
[1] * x
+ ictm
[3] * y
+ ictm
[5];
2816 x
= baseMatrix
[0] * xMax
+ baseMatrix
[2] * yMax
+ baseMatrix
[4];
2817 y
= baseMatrix
[1] * xMax
+ baseMatrix
[3] * yMax
+ baseMatrix
[5];
2818 annotX1
= ictm
[0] * x
+ ictm
[2] * y
+ ictm
[4];
2819 annotY1
= ictm
[1] * x
+ ictm
[3] * y
+ ictm
[5];
2821 // swap min/max coords
2822 if (formX0
> formX1
) {
2823 x
= formX0
; formX0
= formX1
; formX1
= x
;
2825 if (formY0
> formY1
) {
2826 y
= formY0
; formY0
= formY1
; formY1
= y
;
2828 if (annotX0
> annotX1
) {
2829 x
= annotX0
; annotX0
= annotX1
; annotX1
= x
;
2831 if (annotY0
> annotY1
) {
2832 y
= annotY0
; annotY0
= annotY1
; annotY1
= y
;
2835 // scale the form to fit the annotation bbox
2836 if (formX1
== formX0
) {
2837 // this shouldn't happen
2840 sx
= (annotX1
- annotX0
) / (formX1
- formX0
);
2842 if (formY1
== formY0
) {
2843 // this shouldn't happen
2846 sy
= (annotY1
- annotY0
) / (formY1
- formY0
);
2850 m
[4] = (m
[4] - formX0
) * sx
+ annotX0
;
2853 m
[5] = (m
[5] - formY0
) * sy
+ annotY0
;
2856 dict
->lookup("Resources", &resObj
);
2857 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
2860 doForm1(str
, resDict
, m
, bbox
);
2866 void Gfx::doForm1(Object
*str
, Dict
*resDict
, double *matrix
, double *bbox
) {
2868 double oldBaseMatrix
[6];
2871 // push new resources on stack
2872 pushResources(resDict
);
2874 // save current graphics state
2877 // kill any pre-existing path
2880 // save current parser
2883 // set form transformation matrix
2884 state
->concatCTM(matrix
[0], matrix
[1], matrix
[2],
2885 matrix
[3], matrix
[4], matrix
[5]);
2886 out
->updateCTM(state
, matrix
[0], matrix
[1], matrix
[2],
2887 matrix
[3], matrix
[4], matrix
[5]);
2889 // set new base matrix
2890 for (i
= 0; i
< 6; ++i
) {
2891 oldBaseMatrix
[i
] = baseMatrix
[i
];
2892 baseMatrix
[i
] = state
->getCTM()[i
];
2895 // set form bounding box
2896 state
->moveTo(bbox
[0], bbox
[1]);
2897 state
->lineTo(bbox
[2], bbox
[1]);
2898 state
->lineTo(bbox
[2], bbox
[3]);
2899 state
->lineTo(bbox
[0], bbox
[3]);
2906 display(str
, gFalse
);
2908 // restore base matrix
2909 for (i
= 0; i
< 6; ++i
) {
2910 baseMatrix
[i
] = oldBaseMatrix
[i
];
2916 // restore graphics state
2919 // pop resource stack
2925 //------------------------------------------------------------------------
2926 // in-line image operators
2927 //------------------------------------------------------------------------
2929 void Gfx::opBeginImage(Object args
[], int numArgs
) {
2933 // build dict/stream
2934 str
= buildImageStream();
2936 // display the image
2938 doImage(NULL
, str
, gTrue
);
2941 c1
= str
->getBaseStream()->getChar();
2942 c2
= str
->getBaseStream()->getChar();
2943 while (!(c1
== 'E' && c2
== 'I') && c2
!= EOF
) {
2945 c2
= str
->getBaseStream()->getChar();
2951 Stream
*Gfx::buildImageStream() {
2958 dict
.initDict(xref
);
2959 parser
->getObj(&obj
);
2960 while (!obj
.isCmd("ID") && !obj
.isEOF()) {
2961 if (!obj
.isName()) {
2962 error(getPos(), "Inline image dictionary key must be a name object");
2965 key
= copyString(obj
.getName());
2967 parser
->getObj(&obj
);
2968 if (obj
.isEOF() || obj
.isError()) {
2972 dict
.dictAdd(key
, &obj
);
2974 parser
->getObj(&obj
);
2977 error(getPos(), "End of file in inline image");
2985 str
= new EmbedStream(parser
->getStream(), &dict
, gFalse
, 0);
2986 str
= str
->addFilters(&dict
);
2991 void Gfx::opImageData(Object args
[], int numArgs
) {
2992 error(getPos(), "Internal: got 'ID' operator");
2995 void Gfx::opEndImage(Object args
[], int numArgs
) {
2996 error(getPos(), "Internal: got 'EI' operator");
2999 //------------------------------------------------------------------------
3000 // type 3 font operators
3001 //------------------------------------------------------------------------
3003 void Gfx::opSetCharWidth(Object args
[], int numArgs
) {
3004 out
->type3D0(state
, args
[0].getNum(), args
[1].getNum());
3007 void Gfx::opSetCacheDevice(Object args
[], int numArgs
) {
3008 out
->type3D1(state
, args
[0].getNum(), args
[1].getNum(),
3009 args
[2].getNum(), args
[3].getNum(),
3010 args
[4].getNum(), args
[5].getNum());
3013 //------------------------------------------------------------------------
3014 // compatibility operators
3015 //------------------------------------------------------------------------
3017 void Gfx::opBeginIgnoreUndef(Object args
[], int numArgs
) {
3021 void Gfx::opEndIgnoreUndef(Object args
[], int numArgs
) {
3022 if (ignoreUndef
> 0)
3026 //------------------------------------------------------------------------
3027 // marked content operators
3028 //------------------------------------------------------------------------
3030 void Gfx::opBeginMarkedContent(Object args
[], int numArgs
) {
3031 if (printCommands
) {
3032 printf(" marked content: %s ", args
[0].getName());
3034 args
[2].print(stdout
);
3040 void Gfx::opEndMarkedContent(Object args
[], int numArgs
) {
3043 void Gfx::opMarkPoint(Object args
[], int numArgs
) {
3044 if (printCommands
) {
3045 printf(" mark point: %s ", args
[0].getName());
3047 args
[2].print(stdout
);
3053 //------------------------------------------------------------------------
3055 //------------------------------------------------------------------------
3057 void Gfx::saveState() {
3058 out
->saveState(state
);
3059 state
= state
->save();
3062 void Gfx::restoreState() {
3063 state
= state
->restore();
3064 out
->restoreState(state
);
3067 void Gfx::pushResources(Dict
*resDict
) {
3068 res
= new GfxResources(xref
, resDict
, res
);
3071 void Gfx::popResources() {
3072 GfxResources
*resPtr
;
3074 resPtr
= res
->getNext();