1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
21 #include "GlobalParams.h"
22 #include "CharTypes.h"
31 #include "OutputDev.h"
36 // the MSVC math.h doesn't define this
38 #define M_PI 3.14159265358979323846
41 //------------------------------------------------------------------------
43 //------------------------------------------------------------------------
45 // Max recursive depth for a function shading fill.
46 #define functionMaxDepth 6
48 // Max delta allowed in any color component for a function shading fill.
49 #define functionColorDelta (dblToCol(1 / 256.0))
51 // Max number of splits along the t axis for an axial shading fill.
52 #define axialMaxSplits 256
54 // Max delta allowed in any color component for an axial shading fill.
55 #define axialColorDelta (dblToCol(1 / 256.0))
57 // Max number of splits along the t axis for a radial shading fill.
58 #define radialMaxSplits 256
60 // Max delta allowed in any color component for a radial shading fill.
61 #define radialColorDelta (dblToCol(1 / 256.0))
63 // Max recursive depth for a Gouraud triangle shading fill.
64 #define gouraudMaxDepth 4
66 // Max delta allowed in any color component for a Gouraud triangle
68 #define gouraudColorDelta (dblToCol(1 / 256.0))
70 // Max recursive depth for a patch mesh shading fill.
71 #define patchMaxDepth 6
73 // Max delta allowed in any color component for a patch mesh shading
75 #define patchColorDelta (dblToCol(1 / 256.0))
77 //------------------------------------------------------------------------
79 //------------------------------------------------------------------------
81 #ifdef WIN32 // this works around a bug in the VC7 compiler
82 # pragma optimize("",off)
85 Operator
Gfx::opTab
[] = {
86 {"\"", 3, {tchkNum
, tchkNum
, tchkString
},
87 &Gfx::opMoveSetShowText
},
88 {"'", 1, {tchkString
},
89 &Gfx::opMoveShowText
},
93 &Gfx::opEOFillStroke
},
94 {"BDC", 2, {tchkName
, tchkProps
},
95 &Gfx::opBeginMarkedContent
},
98 {"BMC", 1, {tchkName
},
99 &Gfx::opBeginMarkedContent
},
100 {"BT", 0, {tchkNone
},
102 {"BX", 0, {tchkNone
},
103 &Gfx::opBeginIgnoreUndef
},
104 {"CS", 1, {tchkName
},
105 &Gfx::opSetStrokeColorSpace
},
106 {"DP", 2, {tchkName
, tchkProps
},
108 {"Do", 1, {tchkName
},
110 {"EI", 0, {tchkNone
},
112 {"EMC", 0, {tchkNone
},
113 &Gfx::opEndMarkedContent
},
114 {"ET", 0, {tchkNone
},
116 {"EX", 0, {tchkNone
},
117 &Gfx::opEndIgnoreUndef
},
121 &Gfx::opSetStrokeGray
},
122 {"ID", 0, {tchkNone
},
126 {"K", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
127 &Gfx::opSetStrokeCMYKColor
},
129 &Gfx::opSetMiterLimit
},
130 {"MP", 1, {tchkName
},
134 {"RG", 3, {tchkNum
, tchkNum
, tchkNum
},
135 &Gfx::opSetStrokeRGBColor
},
138 {"SC", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
139 &Gfx::opSetStrokeColor
},
140 {"SCN", -5, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
142 &Gfx::opSetStrokeColorN
},
143 {"T*", 0, {tchkNone
},
144 &Gfx::opTextNextLine
},
145 {"TD", 2, {tchkNum
, tchkNum
},
146 &Gfx::opTextMoveSet
},
147 {"TJ", 1, {tchkArray
},
148 &Gfx::opShowSpaceText
},
150 &Gfx::opSetTextLeading
},
152 &Gfx::opSetCharSpacing
},
153 {"Td", 2, {tchkNum
, tchkNum
},
155 {"Tf", 2, {tchkName
, tchkNum
},
157 {"Tj", 1, {tchkString
},
159 {"Tm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
161 &Gfx::opSetTextMatrix
},
163 &Gfx::opSetTextRender
},
165 &Gfx::opSetTextRise
},
167 &Gfx::opSetWordSpacing
},
169 &Gfx::opSetHorizScaling
},
172 {"W*", 0, {tchkNone
},
175 &Gfx::opCloseFillStroke
},
176 {"b*", 0, {tchkNone
},
177 &Gfx::opCloseEOFillStroke
},
178 {"c", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
181 {"cm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
184 {"cs", 1, {tchkName
},
185 &Gfx::opSetFillColorSpace
},
186 {"d", 2, {tchkArray
, tchkNum
},
188 {"d0", 2, {tchkNum
, tchkNum
},
189 &Gfx::opSetCharWidth
},
190 {"d1", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
192 &Gfx::opSetCacheDevice
},
195 {"f*", 0, {tchkNone
},
198 &Gfx::opSetFillGray
},
199 {"gs", 1, {tchkName
},
200 &Gfx::opSetExtGState
},
206 &Gfx::opSetLineJoin
},
207 {"k", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
208 &Gfx::opSetFillCMYKColor
},
209 {"l", 2, {tchkNum
, tchkNum
},
211 {"m", 2, {tchkNum
, tchkNum
},
217 {"re", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
219 {"rg", 3, {tchkNum
, tchkNum
, tchkNum
},
220 &Gfx::opSetFillRGBColor
},
221 {"ri", 1, {tchkName
},
222 &Gfx::opSetRenderingIntent
},
224 &Gfx::opCloseStroke
},
225 {"sc", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
226 &Gfx::opSetFillColor
},
227 {"scn", -5, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
229 &Gfx::opSetFillColorN
},
230 {"sh", 1, {tchkName
},
232 {"v", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
235 &Gfx::opSetLineWidth
},
236 {"y", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
240 #ifdef WIN32 // this works around a bug in the VC7 compiler
241 # pragma optimize("",on)
244 #define numOps (sizeof(opTab) / sizeof(Operator))
246 //------------------------------------------------------------------------
248 //------------------------------------------------------------------------
250 GfxResources::GfxResources(XRef
*xref
, Dict
*resDict
, GfxResources
*nextA
) {
256 // build font dictionary
258 resDict
->lookupNF("Font", &obj1
);
260 obj1
.fetch(xref
, &obj2
);
263 fonts
= new GfxFontDict(xref
, &r
, obj2
.getDict());
266 } else if (obj1
.isDict()) {
267 fonts
= new GfxFontDict(xref
, NULL
, obj1
.getDict());
271 // get XObject dictionary
272 resDict
->lookup("XObject", &xObjDict
);
274 // get color space dictionary
275 resDict
->lookup("ColorSpace", &colorSpaceDict
);
277 // get pattern dictionary
278 resDict
->lookup("Pattern", &patternDict
);
280 // get shading dictionary
281 resDict
->lookup("Shading", &shadingDict
);
283 // get graphics state parameter dictionary
284 resDict
->lookup("ExtGState", &gStateDict
);
289 colorSpaceDict
.initNull();
290 patternDict
.initNull();
291 shadingDict
.initNull();
292 gStateDict
.initNull();
298 GfxResources::~GfxResources() {
303 colorSpaceDict
.free();
309 GfxFont
*GfxResources::lookupFont(char *name
) {
311 GfxResources
*resPtr
;
313 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
315 if ((font
= resPtr
->fonts
->lookup(name
)))
319 error(-1, "Unknown font tag '%s'", name
);
323 GBool
GfxResources::lookupXObject(char *name
, Object
*obj
) {
324 GfxResources
*resPtr
;
326 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
327 if (resPtr
->xObjDict
.isDict()) {
328 if (!resPtr
->xObjDict
.dictLookup(name
, obj
)->isNull())
333 error(-1, "XObject '%s' is unknown", name
);
337 GBool
GfxResources::lookupXObjectNF(char *name
, Object
*obj
) {
338 GfxResources
*resPtr
;
340 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
341 if (resPtr
->xObjDict
.isDict()) {
342 if (!resPtr
->xObjDict
.dictLookupNF(name
, obj
)->isNull())
347 error(-1, "XObject '%s' is unknown", name
);
351 void GfxResources::lookupColorSpace(char *name
, Object
*obj
) {
352 GfxResources
*resPtr
;
354 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
355 if (resPtr
->colorSpaceDict
.isDict()) {
356 if (!resPtr
->colorSpaceDict
.dictLookup(name
, obj
)->isNull()) {
365 GfxPattern
*GfxResources::lookupPattern(char *name
) {
366 GfxResources
*resPtr
;
370 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
371 if (resPtr
->patternDict
.isDict()) {
372 if (!resPtr
->patternDict
.dictLookup(name
, &obj
)->isNull()) {
373 pattern
= GfxPattern::parse(&obj
);
380 error(-1, "Unknown pattern '%s'", name
);
384 GfxShading
*GfxResources::lookupShading(char *name
) {
385 GfxResources
*resPtr
;
389 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
390 if (resPtr
->shadingDict
.isDict()) {
391 if (!resPtr
->shadingDict
.dictLookup(name
, &obj
)->isNull()) {
392 shading
= GfxShading::parse(&obj
);
399 error(-1, "Unknown shading '%s'", name
);
403 GBool
GfxResources::lookupGState(char *name
, Object
*obj
) {
404 GfxResources
*resPtr
;
406 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
407 if (resPtr
->gStateDict
.isDict()) {
408 if (!resPtr
->gStateDict
.dictLookup(name
, obj
)->isNull()) {
414 error(-1, "ExtGState '%s' is unknown", name
);
418 //------------------------------------------------------------------------
420 //------------------------------------------------------------------------
422 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, int pageNum
, Dict
*resDict
,
423 double hDPI
, double vDPI
, PDFRectangle
*box
,
424 PDFRectangle
*cropBox
, int rotate
,
425 GBool (*abortCheckCbkA
)(void *data
),
426 void *abortCheckCbkDataA
) {
431 printCommands
= globalParams
->getPrintCommands();
433 // start the resource stack
434 res
= new GfxResources(xref
, resDict
, NULL
);
438 state
= new GfxState(hDPI
, vDPI
, box
, rotate
, out
->upsideDown());
439 fontChanged
= gFalse
;
442 renderThisPage
= out
->startPage(pageNum
, state
);
446 out
->setDefaultCTM(state
->getCTM());
447 out
->updateAll(state
);
448 for (i
= 0; i
< 6; ++i
) {
449 baseMatrix
[i
] = state
->getCTM()[i
];
452 abortCheckCbk
= abortCheckCbkA
;
453 abortCheckCbkData
= abortCheckCbkDataA
;
457 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
458 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
459 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
460 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
468 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, Dict
*resDict
,
469 PDFRectangle
*box
, PDFRectangle
*cropBox
,
470 GBool (*abortCheckCbkA
)(void *data
),
471 void *abortCheckCbkDataA
) {
476 printCommands
= globalParams
->getPrintCommands();
477 renderThisPage
= gTrue
;
479 // start the resource stack
480 res
= new GfxResources(xref
, resDict
, NULL
);
484 state
= new GfxState(72, 72, box
, 0, gFalse
);
485 fontChanged
= gFalse
;
488 for (i
= 0; i
< 6; ++i
) {
489 baseMatrix
[i
] = state
->getCTM()[i
];
492 abortCheckCbk
= abortCheckCbkA
;
493 abortCheckCbkData
= abortCheckCbkDataA
;
497 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
498 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
499 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
500 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
509 while (state
->hasSaves()) {
523 void Gfx::display(Object
*obj
, GBool topLevel
) {
530 if (obj
->isArray()) {
531 for (i
= 0; i
< obj
->arrayGetLength(); ++i
) {
532 obj
->arrayGet(i
, &obj2
);
533 if (!obj2
.isStream()) {
534 error(-1, "Weird page contents");
540 } else if (!obj
->isStream()) {
541 error(-1, "Weird page contents");
544 parser
= new Parser(xref
, new Lexer(xref
, obj
));
550 void Gfx::go(GBool topLevel
) {
552 Object args
[maxArgs
];
556 // scan a sequence of objects
557 updateLevel
= lastAbortCheck
= 0;
559 parser
->getObj(&obj
);
560 while (!obj
.isEOF()) {
562 // got a command - execute it
566 for (i
= 0; i
< numArgs
; ++i
) {
568 args
[i
].print(stdout
);
573 execOp(&obj
, args
, numArgs
);
575 for (i
= 0; i
< numArgs
; ++i
)
579 // periodically update display
580 if (++updateLevel
>= 20000) {
585 // check for an abort
587 if (updateLevel
- lastAbortCheck
> 10) {
588 if ((*abortCheckCbk
)(abortCheckCbkData
)) {
591 lastAbortCheck
= updateLevel
;
595 // got an argument - save it
596 } else if (numArgs
< maxArgs
) {
597 args
[numArgs
++] = obj
;
599 // too many arguments - something is wrong
601 error(getPos(), "Too many args in content stream");
603 printf("throwing away arg: ");
611 // grab the next object
612 parser
->getObj(&obj
);
616 // args at end with no command
618 error(getPos(), "Leftover args in content stream");
620 printf("%d leftovers:", numArgs
);
621 for (i
= 0; i
< numArgs
; ++i
) {
623 args
[i
].print(stdout
);
628 for (i
= 0; i
< numArgs
; ++i
)
633 if (topLevel
&& updateLevel
> 0) {
638 void Gfx::execOp(Object
*cmd
, Object args
[], int numArgs
) {
645 name
= cmd
->getCmd();
646 if (!(op
= findOp(name
))) {
647 if (ignoreUndef
== 0)
648 error(getPos(), "Unknown operator '%s'", name
);
654 if (op
->numArgs
>= 0) {
655 if (numArgs
< op
->numArgs
) {
656 error(getPos(), "Too few (%d) args to '%s' operator", numArgs
, name
);
659 if (numArgs
> op
->numArgs
) {
661 error(getPos(), "Too many (%d) args to '%s' operator", numArgs
, name
);
663 argPtr
+= numArgs
- op
->numArgs
;
664 numArgs
= op
->numArgs
;
667 if (numArgs
> -op
->numArgs
) {
668 error(getPos(), "Too many (%d) args to '%s' operator",
673 for (i
= 0; i
< numArgs
; ++i
) {
674 if (!checkArg(&argPtr
[i
], op
->tchk
[i
])) {
675 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
676 i
, name
, argPtr
[i
].getTypeName());
682 (this->*op
->func
)(argPtr
, numArgs
);
685 Operator
*Gfx::findOp(char *name
) {
690 // invariant: opTab[a] < name < opTab[b]
694 cmp
= strcmp(opTab
[m
].name
, name
);
707 GBool
Gfx::checkArg(Object
*arg
, TchkType type
) {
709 case tchkBool
: return arg
->isBool();
710 case tchkInt
: return arg
->isInt();
711 case tchkNum
: return arg
->isNum();
712 case tchkString
: return arg
->isString();
713 case tchkName
: return arg
->isName();
714 case tchkArray
: return arg
->isArray();
715 case tchkProps
: return arg
->isDict() || arg
->isName();
716 case tchkSCN
: return arg
->isNum() || arg
->isName();
717 case tchkNone
: return gFalse
;
723 return parser
? parser
->getPos() : -1;
726 //------------------------------------------------------------------------
727 // graphics state operators
728 //------------------------------------------------------------------------
730 void Gfx::opSave(Object args
[], int numArgs
) {
734 void Gfx::opRestore(Object args
[], int numArgs
) {
738 void Gfx::opConcat(Object args
[], int numArgs
) {
739 state
->concatCTM(args
[0].getNum(), args
[1].getNum(),
740 args
[2].getNum(), args
[3].getNum(),
741 args
[4].getNum(), args
[5].getNum());
742 out
->updateCTM(state
, args
[0].getNum(), args
[1].getNum(),
743 args
[2].getNum(), args
[3].getNum(),
744 args
[4].getNum(), args
[5].getNum());
748 void Gfx::opSetDash(Object args
[], int numArgs
) {
755 a
= args
[0].getArray();
756 length
= a
->getLength();
760 dash
= (double *)gmallocn(length
, sizeof(double));
761 for (i
= 0; i
< length
; ++i
) {
762 dash
[i
] = a
->get(i
, &obj
)->getNum();
766 state
->setLineDash(dash
, length
, args
[1].getNum());
767 out
->updateLineDash(state
);
770 void Gfx::opSetFlat(Object args
[], int numArgs
) {
771 state
->setFlatness((int)args
[0].getNum());
772 out
->updateFlatness(state
);
775 void Gfx::opSetLineJoin(Object args
[], int numArgs
) {
776 state
->setLineJoin(args
[0].getInt());
777 out
->updateLineJoin(state
);
780 void Gfx::opSetLineCap(Object args
[], int numArgs
) {
781 state
->setLineCap(args
[0].getInt());
782 out
->updateLineCap(state
);
785 void Gfx::opSetMiterLimit(Object args
[], int numArgs
) {
786 state
->setMiterLimit(args
[0].getNum());
787 out
->updateMiterLimit(state
);
790 void Gfx::opSetLineWidth(Object args
[], int numArgs
) {
791 state
->setLineWidth(args
[0].getNum());
792 out
->updateLineWidth(state
);
795 void Gfx::opSetExtGState(Object args
[], int numArgs
) {
800 if (!res
->lookupGState(args
[0].getName(), &obj1
)) {
803 if (!obj1
.isDict()) {
804 error(getPos(), "ExtGState '%s' is wrong type", args
[0].getName());
809 // transparency support: blend mode, fill/stroke opacity
810 if (!obj1
.dictLookup("BM", &obj2
)->isNull()) {
811 if (state
->parseBlendMode(&obj2
, &mode
)) {
812 state
->setBlendMode(mode
);
813 out
->updateBlendMode(state
);
815 error(getPos(), "Invalid blend mode in ExtGState");
819 if (obj1
.dictLookup("ca", &obj2
)->isNum()) {
820 state
->setFillOpacity(obj2
.getNum());
821 out
->updateFillOpacity(state
);
824 if (obj1
.dictLookup("CA", &obj2
)->isNum()) {
825 state
->setStrokeOpacity(obj2
.getNum());
826 out
->updateStrokeOpacity(state
);
830 // fill/stroke overprint
831 if ((haveFillOP
= (obj1
.dictLookup("op", &obj2
)->isBool()))) {
832 state
->setFillOverprint(obj2
.getBool());
833 out
->updateFillOverprint(state
);
836 if (obj1
.dictLookup("OP", &obj2
)->isBool()) {
837 state
->setStrokeOverprint(obj2
.getBool());
838 out
->updateStrokeOverprint(state
);
840 state
->setFillOverprint(obj2
.getBool());
841 out
->updateFillOverprint(state
);
849 void Gfx::opSetRenderingIntent(Object args
[], int numArgs
) {
852 //------------------------------------------------------------------------
854 //------------------------------------------------------------------------
856 void Gfx::opSetFillGray(Object args
[], int numArgs
) {
859 state
->setFillPattern(NULL
);
860 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
861 out
->updateFillColorSpace(state
);
862 color
.c
[0] = dblToCol(args
[0].getNum());
863 state
->setFillColor(&color
);
864 out
->updateFillColor(state
);
867 void Gfx::opSetStrokeGray(Object args
[], int numArgs
) {
870 state
->setStrokePattern(NULL
);
871 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
872 out
->updateStrokeColorSpace(state
);
873 color
.c
[0] = dblToCol(args
[0].getNum());
874 state
->setStrokeColor(&color
);
875 out
->updateStrokeColor(state
);
878 void Gfx::opSetFillCMYKColor(Object args
[], int numArgs
) {
882 state
->setFillPattern(NULL
);
883 state
->setFillColorSpace(new GfxDeviceCMYKColorSpace());
884 out
->updateFillColorSpace(state
);
885 for (i
= 0; i
< 4; ++i
) {
886 color
.c
[i
] = dblToCol(args
[i
].getNum());
888 state
->setFillColor(&color
);
889 out
->updateFillColor(state
);
892 void Gfx::opSetStrokeCMYKColor(Object args
[], int numArgs
) {
896 state
->setStrokePattern(NULL
);
897 state
->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
898 out
->updateStrokeColorSpace(state
);
899 for (i
= 0; i
< 4; ++i
) {
900 color
.c
[i
] = dblToCol(args
[i
].getNum());
902 state
->setStrokeColor(&color
);
903 out
->updateStrokeColor(state
);
906 void Gfx::opSetFillRGBColor(Object args
[], int numArgs
) {
910 state
->setFillPattern(NULL
);
911 state
->setFillColorSpace(new GfxDeviceRGBColorSpace());
912 out
->updateFillColorSpace(state
);
913 for (i
= 0; i
< 3; ++i
) {
914 color
.c
[i
] = dblToCol(args
[i
].getNum());
916 state
->setFillColor(&color
);
917 out
->updateFillColor(state
);
920 void Gfx::opSetStrokeRGBColor(Object args
[], int numArgs
) {
924 state
->setStrokePattern(NULL
);
925 state
->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
926 out
->updateStrokeColorSpace(state
);
927 for (i
= 0; i
< 3; ++i
) {
928 color
.c
[i
] = dblToCol(args
[i
].getNum());
930 state
->setStrokeColor(&color
);
931 out
->updateStrokeColor(state
);
934 void Gfx::opSetFillColorSpace(Object args
[], int numArgs
) {
936 GfxColorSpace
*colorSpace
;
940 state
->setFillPattern(NULL
);
941 res
->lookupColorSpace(args
[0].getName(), &obj
);
943 colorSpace
= GfxColorSpace::parse(&args
[0]);
945 colorSpace
= GfxColorSpace::parse(&obj
);
949 state
->setFillColorSpace(colorSpace
);
950 out
->updateFillColorSpace(state
);
952 error(getPos(), "Bad color space (fill)");
954 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
957 state
->setFillColor(&color
);
958 out
->updateFillColor(state
);
961 void Gfx::opSetStrokeColorSpace(Object args
[], int numArgs
) {
963 GfxColorSpace
*colorSpace
;
967 state
->setStrokePattern(NULL
);
968 res
->lookupColorSpace(args
[0].getName(), &obj
);
970 colorSpace
= GfxColorSpace::parse(&args
[0]);
972 colorSpace
= GfxColorSpace::parse(&obj
);
976 state
->setStrokeColorSpace(colorSpace
);
977 out
->updateStrokeColorSpace(state
);
979 error(getPos(), "Bad color space (stroke)");
981 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
984 state
->setStrokeColor(&color
);
985 out
->updateStrokeColor(state
);
988 void Gfx::opSetFillColor(Object args
[], int numArgs
) {
992 state
->setFillPattern(NULL
);
993 for (i
= 0; i
< numArgs
; ++i
) {
994 color
.c
[i
] = dblToCol(args
[i
].getNum());
996 state
->setFillColor(&color
);
997 out
->updateFillColor(state
);
1000 void Gfx::opSetStrokeColor(Object args
[], int numArgs
) {
1004 state
->setStrokePattern(NULL
);
1005 for (i
= 0; i
< numArgs
; ++i
) {
1006 color
.c
[i
] = dblToCol(args
[i
].getNum());
1008 state
->setStrokeColor(&color
);
1009 out
->updateStrokeColor(state
);
1012 void Gfx::opSetFillColorN(Object args
[], int numArgs
) {
1014 GfxPattern
*pattern
;
1017 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1019 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
1020 if (args
[i
].isNum()) {
1021 color
.c
[i
] = dblToCol(args
[i
].getNum());
1024 state
->setFillColor(&color
);
1025 out
->updateFillColor(state
);
1027 if (args
[numArgs
-1].isName() &&
1028 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
1029 state
->setFillPattern(pattern
);
1033 state
->setFillPattern(NULL
);
1034 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
1035 if (args
[i
].isNum()) {
1036 color
.c
[i
] = dblToCol(args
[i
].getNum());
1039 state
->setFillColor(&color
);
1040 out
->updateFillColor(state
);
1044 void Gfx::opSetStrokeColorN(Object args
[], int numArgs
) {
1046 GfxPattern
*pattern
;
1049 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1051 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
1052 if (args
[i
].isNum()) {
1053 color
.c
[i
] = dblToCol(args
[i
].getNum());
1056 state
->setStrokeColor(&color
);
1057 out
->updateStrokeColor(state
);
1059 if (args
[numArgs
-1].isName() &&
1060 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
1061 state
->setStrokePattern(pattern
);
1065 state
->setStrokePattern(NULL
);
1066 for (i
= 0; i
< numArgs
&& i
< 4; ++i
) {
1067 if (args
[i
].isNum()) {
1068 color
.c
[i
] = dblToCol(args
[i
].getNum());
1071 state
->setStrokeColor(&color
);
1072 out
->updateStrokeColor(state
);
1076 //------------------------------------------------------------------------
1077 // path segment operators
1078 //------------------------------------------------------------------------
1080 void Gfx::opMoveTo(Object args
[], int numArgs
) {
1081 state
->moveTo(args
[0].getNum(), args
[1].getNum());
1084 void Gfx::opLineTo(Object args
[], int numArgs
) {
1085 if (!state
->isCurPt()) {
1086 error(getPos(), "No current point in lineto");
1089 state
->lineTo(args
[0].getNum(), args
[1].getNum());
1092 void Gfx::opCurveTo(Object args
[], int numArgs
) {
1093 double x1
, y1
, x2
, y2
, x3
, y3
;
1095 if (!state
->isCurPt()) {
1096 error(getPos(), "No current point in curveto");
1099 x1
= args
[0].getNum();
1100 y1
= args
[1].getNum();
1101 x2
= args
[2].getNum();
1102 y2
= args
[3].getNum();
1103 x3
= args
[4].getNum();
1104 y3
= args
[5].getNum();
1105 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1108 void Gfx::opCurveTo1(Object args
[], int numArgs
) {
1109 double x1
, y1
, x2
, y2
, x3
, y3
;
1111 if (!state
->isCurPt()) {
1112 error(getPos(), "No current point in curveto1");
1115 x1
= state
->getCurX();
1116 y1
= state
->getCurY();
1117 x2
= args
[0].getNum();
1118 y2
= args
[1].getNum();
1119 x3
= args
[2].getNum();
1120 y3
= args
[3].getNum();
1121 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1124 void Gfx::opCurveTo2(Object args
[], int numArgs
) {
1125 double x1
, y1
, x2
, y2
, x3
, y3
;
1127 if (!state
->isCurPt()) {
1128 error(getPos(), "No current point in curveto2");
1131 x1
= args
[0].getNum();
1132 y1
= args
[1].getNum();
1133 x2
= args
[2].getNum();
1134 y2
= args
[3].getNum();
1137 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1140 void Gfx::opRectangle(Object args
[], int numArgs
) {
1143 x
= args
[0].getNum();
1144 y
= args
[1].getNum();
1145 w
= args
[2].getNum();
1146 h
= args
[3].getNum();
1147 state
->moveTo(x
, y
);
1148 state
->lineTo(x
+ w
, y
);
1149 state
->lineTo(x
+ w
, y
+ h
);
1150 state
->lineTo(x
, y
+ h
);
1154 void Gfx::opClosePath(Object args
[], int numArgs
) {
1155 if (!state
->isCurPt()) {
1156 error(getPos(), "No current point in closepath");
1162 //------------------------------------------------------------------------
1163 // path painting operators
1164 //------------------------------------------------------------------------
1166 void Gfx::opEndPath(Object args
[], int numArgs
) {
1170 void Gfx::opStroke(Object args
[], int numArgs
) {
1171 if (!state
->isCurPt()) {
1172 //error(getPos(), "No path in stroke");
1175 if (state
->isPath())
1180 void Gfx::opCloseStroke(Object args
[], int numArgs
) {
1181 if (!state
->isCurPt()) {
1182 //error(getPos(), "No path in closepath/stroke");
1185 if (state
->isPath()) {
1192 void Gfx::opFill(Object args
[], int numArgs
) {
1193 if (!state
->isCurPt()) {
1194 //error(getPos(), "No path in fill");
1197 if (state
->isPath()) {
1198 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1199 doPatternFill(gFalse
);
1207 void Gfx::opEOFill(Object args
[], int numArgs
) {
1208 if (!state
->isCurPt()) {
1209 //error(getPos(), "No path in eofill");
1212 if (state
->isPath()) {
1213 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1214 doPatternFill(gTrue
);
1222 void Gfx::opFillStroke(Object args
[], int numArgs
) {
1223 if (!state
->isCurPt()) {
1224 //error(getPos(), "No path in fill/stroke");
1227 if (state
->isPath()) {
1228 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1229 doPatternFill(gFalse
);
1238 void Gfx::opCloseFillStroke(Object args
[], int numArgs
) {
1239 if (!state
->isCurPt()) {
1240 //error(getPos(), "No path in closepath/fill/stroke");
1243 if (state
->isPath()) {
1245 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1246 doPatternFill(gFalse
);
1255 void Gfx::opEOFillStroke(Object args
[], int numArgs
) {
1256 if (!state
->isCurPt()) {
1257 //error(getPos(), "No path in eofill/stroke");
1260 if (state
->isPath()) {
1261 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1262 doPatternFill(gTrue
);
1271 void Gfx::opCloseEOFillStroke(Object args
[], int numArgs
) {
1272 if (!state
->isCurPt()) {
1273 //error(getPos(), "No path in closepath/eofill/stroke");
1276 if (state
->isPath()) {
1278 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1279 doPatternFill(gTrue
);
1288 void Gfx::doPatternFill(GBool eoFill
) {
1289 GfxPattern
*pattern
;
1291 // this is a bit of a kludge -- patterns can be really slow, so we
1292 // skip them if we're only doing text extraction, since they almost
1293 // certainly don't contain any text
1294 if (!out
->needNonText()) {
1298 if (!(pattern
= state
->getFillPattern())) {
1301 switch (pattern
->getType()) {
1303 doTilingPatternFill((GfxTilingPattern
*)pattern
, eoFill
);
1306 doShadingPatternFill((GfxShadingPattern
*)pattern
, eoFill
);
1309 error(getPos(), "Unimplemented pattern type (%d) in fill",
1310 pattern
->getType());
1315 void Gfx::doTilingPatternFill(GfxTilingPattern
*tPat
, GBool eoFill
) {
1316 GfxPatternColorSpace
*patCS
;
1319 double xMin
, yMin
, xMax
, yMax
, x
, y
, x1
, y1
;
1320 double cxMin
, cyMin
, cxMax
, cyMax
;
1321 int xi0
, yi0
, xi1
, yi1
, xi
, yi
;
1322 double *ctm
, *btm
, *ptm
;
1323 double m
[6], ictm
[6], m1
[6], imb
[6];
1325 double xstep
, ystep
;
1329 patCS
= (GfxPatternColorSpace
*)state
->getFillColorSpace();
1331 // construct a (pattern space) -> (current space) transform matrix
1332 ctm
= state
->getCTM();
1334 ptm
= tPat
->getMatrix();
1335 // iCTM = invert CTM
1336 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1337 ictm
[0] = ctm
[3] * det
;
1338 ictm
[1] = -ctm
[1] * det
;
1339 ictm
[2] = -ctm
[2] * det
;
1340 ictm
[3] = ctm
[0] * det
;
1341 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1342 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1343 // m1 = PTM * BTM = PTM * base transform matrix
1344 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1345 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1346 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1347 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1348 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1349 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1350 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1351 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1352 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1353 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1354 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1355 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1356 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1358 // construct a (device space) -> (pattern space) transform matrix
1359 det
= 1 / (m1
[0] * m1
[3] - m1
[1] * m1
[2]);
1360 imb
[0] = m1
[3] * det
;
1361 imb
[1] = -m1
[1] * det
;
1362 imb
[2] = -m1
[2] * det
;
1363 imb
[3] = m1
[0] * det
;
1364 imb
[4] = (m1
[2] * m1
[5] - m1
[3] * m1
[4]) * det
;
1365 imb
[5] = (m1
[1] * m1
[4] - m1
[0] * m1
[5]) * det
;
1367 // save current graphics state
1368 savedPath
= state
->getPath()->copy();
1371 // set underlying color space (for uncolored tiling patterns); set
1372 // various other parameters (stroke color, line width) to match
1374 if (tPat
->getPaintType() == 2 && (cs
= patCS
->getUnder())) {
1375 state
->setFillColorSpace(cs
->copy());
1376 out
->updateFillColorSpace(state
);
1377 state
->setStrokeColorSpace(cs
->copy());
1378 out
->updateStrokeColorSpace(state
);
1379 state
->setStrokeColor(state
->getFillColor());
1381 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
1382 out
->updateFillColorSpace(state
);
1383 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1384 out
->updateStrokeColorSpace(state
);
1386 state
->setFillPattern(NULL
);
1387 out
->updateFillColor(state
);
1388 state
->setStrokePattern(NULL
);
1389 out
->updateStrokeColor(state
);
1390 state
->setLineWidth(0);
1391 out
->updateLineWidth(state
);
1393 // clip to current path
1402 // get the clip region, check for empty
1403 state
->getClipBBox(&cxMin
, &cyMin
, &cxMax
, &cyMax
);
1404 if (cxMin
> cxMax
|| cyMin
> cyMax
) {
1408 // transform clip region bbox to pattern space
1409 xMin
= xMax
= cxMin
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1410 yMin
= yMax
= cxMin
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1411 x1
= cxMin
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1412 y1
= cxMin
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1415 } else if (x1
> xMax
) {
1420 } else if (y1
> yMax
) {
1423 x1
= cxMax
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1424 y1
= cxMax
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1427 } else if (x1
> xMax
) {
1432 } else if (y1
> yMax
) {
1435 x1
= cxMax
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1436 y1
= cxMax
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1439 } else if (x1
> xMax
) {
1444 } else if (y1
> yMax
) {
1449 //~ this should treat negative steps differently -- start at right/top
1450 //~ edge instead of left/bottom (?)
1451 xstep
= fabs(tPat
->getXStep());
1452 ystep
= fabs(tPat
->getYStep());
1453 xi0
= (int)floor((xMin
- tPat
->getBBox()[0]) / xstep
);
1454 xi1
= (int)ceil((xMax
- tPat
->getBBox()[0]) / xstep
);
1455 yi0
= (int)floor((yMin
- tPat
->getBBox()[1]) / ystep
);
1456 yi1
= (int)ceil((yMax
- tPat
->getBBox()[1]) / ystep
);
1457 for (i
= 0; i
< 4; ++i
) {
1460 if (out
->useTilingPatternFill()) {
1463 out
->tilingPatternFill(state
, tPat
->getContentStream(),
1464 tPat
->getPaintType(), tPat
->getResDict(),
1465 m1
, tPat
->getBBox(),
1466 xi0
, yi0
, xi1
, yi1
, xstep
, ystep
);
1468 for (yi
= yi0
; yi
< yi1
; ++yi
) {
1469 for (xi
= xi0
; xi
< xi1
; ++xi
) {
1472 m1
[4] = x
* m
[0] + y
* m
[2] + m
[4];
1473 m1
[5] = x
* m
[1] + y
* m
[3] + m
[5];
1474 doForm1(tPat
->getContentStream(), tPat
->getResDict(),
1475 m1
, tPat
->getBBox());
1480 // restore graphics state
1483 state
->setPath(savedPath
);
1486 void Gfx::doShadingPatternFill(GfxShadingPattern
*sPat
, GBool eoFill
) {
1487 GfxShading
*shading
;
1489 double *ctm
, *btm
, *ptm
;
1490 double m
[6], ictm
[6], m1
[6];
1491 double xMin
, yMin
, xMax
, yMax
;
1494 shading
= sPat
->getShading();
1496 // save current graphics state
1497 savedPath
= state
->getPath()->copy();
1501 if (shading
->getHasBBox()) {
1502 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1503 state
->moveTo(xMin
, yMin
);
1504 state
->lineTo(xMax
, yMin
);
1505 state
->lineTo(xMax
, yMax
);
1506 state
->lineTo(xMin
, yMax
);
1510 state
->setPath(savedPath
->copy());
1513 // clip to current path
1521 // set the color space
1522 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1523 out
->updateFillColorSpace(state
);
1525 // background color fill
1526 if (shading
->getHasBackground()) {
1527 state
->setFillColor(shading
->getBackground());
1528 out
->updateFillColor(state
);
1533 // construct a (pattern space) -> (current space) transform matrix
1534 ctm
= state
->getCTM();
1536 ptm
= sPat
->getMatrix();
1537 // iCTM = invert CTM
1538 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1539 ictm
[0] = ctm
[3] * det
;
1540 ictm
[1] = -ctm
[1] * det
;
1541 ictm
[2] = -ctm
[2] * det
;
1542 ictm
[3] = ctm
[0] * det
;
1543 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1544 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1545 // m1 = PTM * BTM = PTM * base transform matrix
1546 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1547 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1548 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1549 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1550 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1551 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1552 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1553 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1554 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1555 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1556 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1557 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1558 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1560 // set the new matrix
1561 state
->concatCTM(m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1562 out
->updateCTM(state
, m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1564 // do shading type-specific operations
1565 switch (shading
->getType()) {
1567 doFunctionShFill((GfxFunctionShading
*)shading
);
1570 doAxialShFill((GfxAxialShading
*)shading
);
1573 doRadialShFill((GfxRadialShading
*)shading
);
1577 doGouraudTriangleShFill((GfxGouraudTriangleShading
*)shading
);
1581 doPatchMeshShFill((GfxPatchMeshShading
*)shading
);
1585 // restore graphics state
1587 state
->setPath(savedPath
);
1590 void Gfx::opShFill(Object args
[], int numArgs
) {
1591 GfxShading
*shading
;
1593 double xMin
, yMin
, xMax
, yMax
;
1595 if (!(shading
= res
->lookupShading(args
[0].getName()))) {
1599 // save current graphics state
1600 savedPath
= state
->getPath()->copy();
1604 if (shading
->getHasBBox()) {
1605 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1606 state
->moveTo(xMin
, yMin
);
1607 state
->lineTo(xMax
, yMin
);
1608 state
->lineTo(xMax
, yMax
);
1609 state
->lineTo(xMin
, yMax
);
1616 // set the color space
1617 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1618 out
->updateFillColorSpace(state
);
1620 // do shading type-specific operations
1621 switch (shading
->getType()) {
1623 doFunctionShFill((GfxFunctionShading
*)shading
);
1626 doAxialShFill((GfxAxialShading
*)shading
);
1629 doRadialShFill((GfxRadialShading
*)shading
);
1633 doGouraudTriangleShFill((GfxGouraudTriangleShading
*)shading
);
1637 doPatchMeshShFill((GfxPatchMeshShading
*)shading
);
1641 // restore graphics state
1643 state
->setPath(savedPath
);
1648 void Gfx::doFunctionShFill(GfxFunctionShading
*shading
) {
1649 double x0
, y0
, x1
, y1
;
1652 if (out
->useShadedFills()) {
1653 out
->functionShadedFill(state
, shading
);
1655 shading
->getDomain(&x0
, &y0
, &x1
, &y1
);
1656 shading
->getColor(x0
, y0
, &colors
[0]);
1657 shading
->getColor(x0
, y1
, &colors
[1]);
1658 shading
->getColor(x1
, y0
, &colors
[2]);
1659 shading
->getColor(x1
, y1
, &colors
[3]);
1660 doFunctionShFill1(shading
, x0
, y0
, x1
, y1
, colors
, 0);
1664 void Gfx::doFunctionShFill1(GfxFunctionShading
*shading
,
1665 double x0
, double y0
,
1666 double x1
, double y1
,
1667 GfxColor
*colors
, int depth
) {
1669 GfxColor color0M
, color1M
, colorM0
, colorM1
, colorMM
;
1670 GfxColor colors2
[4];
1675 nComps
= shading
->getColorSpace()->getNComps();
1676 matrix
= shading
->getMatrix();
1678 // compare the four corner colors
1679 for (i
= 0; i
< 4; ++i
) {
1680 for (j
= 0; j
< nComps
; ++j
) {
1681 if (abs(colors
[i
].c
[j
] - colors
[(i
+1)&3].c
[j
]) > functionColorDelta
) {
1690 // center of the rectangle
1691 xM
= 0.5 * (x0
+ x1
);
1692 yM
= 0.5 * (y0
+ y1
);
1694 // the four corner colors are close (or we hit the recursive limit)
1695 // -- fill the rectangle; but require at least one subdivision
1696 // (depth==0) to avoid problems when the four outer corners of the
1697 // shaded region are the same color
1698 if ((i
== 4 && depth
> 0) || depth
== functionMaxDepth
) {
1700 // use the center color
1701 shading
->getColor(xM
, yM
, &fillColor
);
1702 state
->setFillColor(&fillColor
);
1703 out
->updateFillColor(state
);
1705 // fill the rectangle
1706 state
->moveTo(x0
* matrix
[0] + y0
* matrix
[2] + matrix
[4],
1707 x0
* matrix
[1] + y0
* matrix
[3] + matrix
[5]);
1708 state
->lineTo(x1
* matrix
[0] + y0
* matrix
[2] + matrix
[4],
1709 x1
* matrix
[1] + y0
* matrix
[3] + matrix
[5]);
1710 state
->lineTo(x1
* matrix
[0] + y1
* matrix
[2] + matrix
[4],
1711 x1
* matrix
[1] + y1
* matrix
[3] + matrix
[5]);
1712 state
->lineTo(x0
* matrix
[0] + y1
* matrix
[2] + matrix
[4],
1713 x0
* matrix
[1] + y1
* matrix
[3] + matrix
[5]);
1718 // the four corner colors are not close enough -- subdivide the
1722 // colors[0] colorM0 colors[2]
1723 // (x0,y0) (xM,y0) (x1,y0)
1724 // +----------+----------+
1727 // color0M | colorMM | color1M
1728 // (x0,yM) +----------+----------+ (x1,yM)
1732 // +----------+----------+
1733 // colors[1] colorM1 colors[3]
1734 // (x0,y1) (xM,y1) (x1,y1)
1736 shading
->getColor(x0
, yM
, &color0M
);
1737 shading
->getColor(x1
, yM
, &color1M
);
1738 shading
->getColor(xM
, y0
, &colorM0
);
1739 shading
->getColor(xM
, y1
, &colorM1
);
1740 shading
->getColor(xM
, yM
, &colorMM
);
1742 // upper-left sub-rectangle
1743 colors2
[0] = colors
[0];
1744 colors2
[1] = color0M
;
1745 colors2
[2] = colorM0
;
1746 colors2
[3] = colorMM
;
1747 doFunctionShFill1(shading
, x0
, y0
, xM
, yM
, colors2
, depth
+ 1);
1749 // lower-left sub-rectangle
1750 colors2
[0] = color0M
;
1751 colors2
[1] = colors
[1];
1752 colors2
[2] = colorMM
;
1753 colors2
[3] = colorM1
;
1754 doFunctionShFill1(shading
, x0
, yM
, xM
, y1
, colors2
, depth
+ 1);
1756 // upper-right sub-rectangle
1757 colors2
[0] = colorM0
;
1758 colors2
[1] = colorMM
;
1759 colors2
[2] = colors
[2];
1760 colors2
[3] = color1M
;
1761 doFunctionShFill1(shading
, xM
, y0
, x1
, yM
, colors2
, depth
+ 1);
1763 // lower-right sub-rectangle
1764 colors2
[0] = colorMM
;
1765 colors2
[1] = colorM1
;
1766 colors2
[2] = color1M
;
1767 colors2
[3] = colors
[3];
1768 doFunctionShFill1(shading
, xM
, yM
, x1
, y1
, colors2
, depth
+ 1);
1772 void Gfx::doAxialShFill(GfxAxialShading
*shading
) {
1773 double xMin
, yMin
, xMax
, yMax
;
1774 double x0
, y0
, x1
, y1
;
1776 GBool dxZero
, dyZero
;
1777 double tMin
, tMax
, t
, tx
, ty
;
1778 double s
[4], sMin
, sMax
, tmp
;
1779 double ux0
, uy0
, ux1
, uy1
, vx0
, vy0
, vx1
, vy1
;
1781 double ta
[axialMaxSplits
+ 1];
1782 int next
[axialMaxSplits
+ 1];
1783 GfxColor color0
, color1
;
1787 if (out
->useShadedFills()) {
1789 out
->axialShadedFill(state
, shading
);
1793 // get the clip region bbox
1794 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1796 // compute min and max t values, based on the four corners of the
1798 shading
->getCoords(&x0
, &y0
, &x1
, &y1
);
1801 dxZero
= fabs(dx
) < 0.001;
1802 dyZero
= fabs(dy
) < 0.001;
1803 mul
= 1 / (dx
* dx
+ dy
* dy
);
1804 tMin
= tMax
= ((xMin
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
1805 t
= ((xMin
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
1808 } else if (t
> tMax
) {
1811 t
= ((xMax
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
1814 } else if (t
> tMax
) {
1817 t
= ((xMax
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
1820 } else if (t
> tMax
) {
1823 if (tMin
< 0 && !shading
->getExtend0()) {
1826 if (tMax
> 1 && !shading
->getExtend1()) {
1830 // get the function domain
1831 t0
= shading
->getDomain0();
1832 t1
= shading
->getDomain1();
1834 // Traverse the t axis and do the shading.
1836 // For each point (tx, ty) on the t axis, consider a line through
1837 // that point perpendicular to the t axis:
1839 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1840 // y(s) = ty + s * dx --> s = (y - ty) / dx
1842 // Then look at the intersection of this line with the bounding box
1843 // (xMin, yMin, xMax, yMax). In the general case, there are four
1844 // intersection points:
1846 // s0 = (xMin - tx) / -dy
1847 // s1 = (xMax - tx) / -dy
1848 // s2 = (yMin - ty) / dx
1849 // s3 = (yMax - ty) / dx
1851 // and we want the middle two s values.
1853 // In the case where dx = 0, take s0 and s1; in the case where dy =
1854 // 0, take s2 and s3.
1856 // Each filled polygon is bounded by two of these line segments
1857 // perpdendicular to the t axis.
1859 // The t axis is bisected into smaller regions until the color
1860 // difference across a region is small enough, and then the region
1861 // is painted with a single color.
1863 // set up: require at least one split to avoid problems when the two
1864 // ends of the t axis have the same color
1865 nComps
= shading
->getColorSpace()->getNComps();
1867 next
[0] = axialMaxSplits
/ 2;
1868 ta
[axialMaxSplits
/ 2] = 0.5 * (tMin
+ tMax
);
1869 next
[axialMaxSplits
/ 2] = axialMaxSplits
;
1870 ta
[axialMaxSplits
] = tMax
;
1872 // compute the color at t = tMin
1875 } else if (tMin
> 1) {
1878 tt
= t0
+ (t1
- t0
) * tMin
;
1880 shading
->getColor(tt
, &color0
);
1882 // compute the coordinates of the point on the t axis at t = tMin;
1883 // then compute the intersection of the perpendicular line with the
1885 tx
= x0
+ tMin
* dx
;
1886 ty
= y0
+ tMin
* dy
;
1887 if (dxZero
&& dyZero
) {
1890 sMin
= (xMin
- tx
) / -dy
;
1891 sMax
= (xMax
- tx
) / -dy
;
1892 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1893 } else if (dyZero
) {
1894 sMin
= (yMin
- ty
) / dx
;
1895 sMax
= (yMax
- ty
) / dx
;
1896 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1898 s
[0] = (yMin
- ty
) / dx
;
1899 s
[1] = (yMax
- ty
) / dx
;
1900 s
[2] = (xMin
- tx
) / -dy
;
1901 s
[3] = (xMax
- tx
) / -dy
;
1902 for (j
= 0; j
< 3; ++j
) {
1904 for (k
= j
+ 1; k
< 4; ++k
) {
1909 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
1914 ux0
= tx
- sMin
* dy
;
1915 uy0
= ty
+ sMin
* dx
;
1916 vx0
= tx
- sMax
* dy
;
1917 vy0
= ty
+ sMax
* dx
;
1920 while (i
< axialMaxSplits
) {
1922 // bisect until color difference is small enough or we hit the
1928 } else if (ta
[j
] > 1) {
1931 tt
= t0
+ (t1
- t0
) * ta
[j
];
1933 shading
->getColor(tt
, &color1
);
1934 for (k
= 0; k
< nComps
; ++k
) {
1935 if (abs(color1
.c
[k
] - color0
.c
[k
]) > axialColorDelta
) {
1943 ta
[k
] = 0.5 * (ta
[i
] + ta
[j
]);
1949 // use the average of the colors of the two sides of the region
1950 for (k
= 0; k
< nComps
; ++k
) {
1951 color0
.c
[k
] = (color0
.c
[k
] + color1
.c
[k
]) / 2;
1954 // compute the coordinates of the point on the t axis; then
1955 // compute the intersection of the perpendicular line with the
1957 tx
= x0
+ ta
[j
] * dx
;
1958 ty
= y0
+ ta
[j
] * dy
;
1959 if (dxZero
&& dyZero
) {
1962 sMin
= (xMin
- tx
) / -dy
;
1963 sMax
= (xMax
- tx
) / -dy
;
1964 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1965 } else if (dyZero
) {
1966 sMin
= (yMin
- ty
) / dx
;
1967 sMax
= (yMax
- ty
) / dx
;
1968 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
1970 s
[0] = (yMin
- ty
) / dx
;
1971 s
[1] = (yMax
- ty
) / dx
;
1972 s
[2] = (xMin
- tx
) / -dy
;
1973 s
[3] = (xMax
- tx
) / -dy
;
1974 for (j
= 0; j
< 3; ++j
) {
1976 for (k
= j
+ 1; k
< 4; ++k
) {
1981 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
1986 ux1
= tx
- sMin
* dy
;
1987 uy1
= ty
+ sMin
* dx
;
1988 vx1
= tx
- sMax
* dy
;
1989 vy1
= ty
+ sMax
* dx
;
1992 state
->setFillColor(&color0
);
1993 out
->updateFillColor(state
);
1996 state
->moveTo(ux0
, uy0
);
1997 state
->lineTo(vx0
, vy0
);
1998 state
->lineTo(vx1
, vy1
);
1999 state
->lineTo(ux1
, uy1
);
2004 // set up for next region
2015 void Gfx::doRadialShFill(GfxRadialShading
*shading
) {
2016 double sMin
, sMax
, xMin
, yMin
, xMax
, yMax
;
2017 double x0
, y0
, r0
, x1
, y1
, r1
, t0
, t1
;
2019 GfxColor colorA
, colorB
;
2020 double xa
, ya
, xb
, yb
, ra
, rb
;
2021 double ta
, tb
, sa
, sb
;
2024 double angle
, t
, d0
, d1
;
2026 if (out
->useShadedFills()) {
2028 out
->radialShadedFill(state
, shading
);
2032 // get the shading info
2033 shading
->getCoords(&x0
, &y0
, &r0
, &x1
, &y1
, &r1
);
2034 t0
= shading
->getDomain0();
2035 t1
= shading
->getDomain1();
2036 nComps
= shading
->getColorSpace()->getNComps();
2038 // compute the (possibly extended) s range
2041 if (shading
->getExtend0()) {
2043 // extend the smaller end
2044 sMin
= -r0
/ (r1
- r0
);
2046 // extend the larger end
2047 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
2048 d0
= (x0
- xMin
) * (x0
- xMin
);
2049 d1
= (x0
- xMax
) * (x0
- xMax
);
2050 sMin
= d0
> d1
? d0
: d1
;
2051 d0
= (y0
- yMin
) * (y0
- yMin
);
2052 d1
= (y0
- yMax
) * (y0
- yMax
);
2053 sMin
+= d0
> d1
? d0
: d1
;
2054 sMin
= (sqrt(sMin
) - r0
) / (r1
- r0
);
2057 } else if (sMin
< -20) {
2063 if (shading
->getExtend1()) {
2065 // extend the smaller end
2066 sMax
= -r0
/ (r1
- r0
);
2067 } else if (r1
> r0
) {
2068 // extend the larger end
2069 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
2070 d0
= (x1
- xMin
) * (x1
- xMin
);
2071 d1
= (x1
- xMax
) * (x1
- xMax
);
2072 sMax
= d0
> d1
? d0
: d1
;
2073 d0
= (y1
- yMin
) * (y1
- yMin
);
2074 d1
= (y1
- yMax
) * (y1
- yMax
);
2075 sMax
+= d0
> d1
? d0
: d1
;
2076 sMax
= (sqrt(sMax
) - r0
) / (r1
- r0
);
2079 } else if (sMax
> 20) {
2086 // compute the number of steps into which circles must be divided to
2087 // achieve a curve flatness of 0.1 pixel in device space for the
2088 // largest circle (note that "device space" is 72 dpi when generating
2089 // PostScript, hence the relatively small 0.1 pixel accuracy)
2090 ctm
= state
->getCTM();
2092 if (fabs(ctm
[1]) > t
) {
2095 if (fabs(ctm
[2]) > t
) {
2098 if (fabs(ctm
[3]) > t
) {
2109 n
= (int)(M_PI
/ acos(1 - 0.1 / t
));
2112 } else if (n
> 200) {
2117 // Traverse the t axis and do the shading.
2119 // This generates and fills a series of rings. Each ring is defined
2121 // sa, ta, xa, ya, ra, colorA
2122 // sb, tb, xb, yb, rb, colorB
2124 // The s/t axis is divided into radialMaxSplits parts; these parts
2125 // are combined as much as possible while respecting the
2126 // radialColorDelta parameter.
2128 // setup for the start circle
2131 ta
= t0
+ sa
* (t1
- t0
);
2132 xa
= x0
+ sa
* (x1
- x0
);
2133 ya
= y0
+ sa
* (y1
- y0
);
2134 ra
= r0
+ sa
* (r1
- r0
);
2136 shading
->getColor(t0
, &colorA
);
2137 } else if (ta
> t1
) {
2138 shading
->getColor(t1
, &colorA
);
2140 shading
->getColor(ta
, &colorA
);
2143 while (ia
< radialMaxSplits
) {
2145 // go as far along the t axis (toward t1) as we can, such that the
2146 // color difference is within the tolerance (radialColorDelta) --
2147 // this uses bisection (between the current value, t, and t1),
2148 // limited to radialMaxSplits points along the t axis; require at
2149 // least one split to avoid problems when the innermost and
2150 // outermost colors are the same
2151 ib
= radialMaxSplits
;
2152 sb
= sMin
+ ((double)ib
/ (double)radialMaxSplits
) * (sMax
- sMin
);
2153 tb
= t0
+ sb
* (t1
- t0
);
2155 shading
->getColor(t0
, &colorB
);
2156 } else if (tb
> t1
) {
2157 shading
->getColor(t1
, &colorB
);
2159 shading
->getColor(tb
, &colorB
);
2161 while (ib
- ia
> 1) {
2162 for (k
= 0; k
< nComps
; ++k
) {
2163 if (abs(colorB
.c
[k
] - colorA
.c
[k
]) > radialColorDelta
) {
2167 if (k
== nComps
&& ib
< radialMaxSplits
) {
2171 sb
= sMin
+ ((double)ib
/ (double)radialMaxSplits
) * (sMax
- sMin
);
2172 tb
= t0
+ sb
* (t1
- t0
);
2174 shading
->getColor(t0
, &colorB
);
2175 } else if (tb
> t1
) {
2176 shading
->getColor(t1
, &colorB
);
2178 shading
->getColor(tb
, &colorB
);
2182 // compute center and radius of the circle
2183 xb
= x0
+ sb
* (x1
- x0
);
2184 yb
= y0
+ sb
* (y1
- y0
);
2185 rb
= r0
+ sb
* (r1
- r0
);
2187 // use the average of the colors at the two circles
2188 for (k
= 0; k
< nComps
; ++k
) {
2189 colorA
.c
[k
] = (colorA
.c
[k
] + colorB
.c
[k
]) / 2;
2191 state
->setFillColor(&colorA
);
2192 out
->updateFillColor(state
);
2194 // construct path for first circle
2195 state
->moveTo(xa
+ ra
, ya
);
2196 for (k
= 1; k
< n
; ++k
) {
2197 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2198 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2202 // construct and append path for second circle
2203 state
->moveTo(xb
+ rb
, yb
);
2204 for (k
= 1; k
< n
; ++k
) {
2205 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2206 state
->lineTo(xb
+ rb
* cos(angle
), yb
+ rb
* sin(angle
));
2214 // step to the next value of t
2226 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading
*shading
) {
2227 double x0
, y0
, x1
, y1
, x2
, y2
;
2228 GfxColor color0
, color1
, color2
;
2231 for (i
= 0; i
< shading
->getNTriangles(); ++i
) {
2232 shading
->getTriangle(i
, &x0
, &y0
, &color0
,
2235 gouraudFillTriangle(x0
, y0
, &color0
, x1
, y1
, &color1
, x2
, y2
, &color2
,
2236 shading
->getColorSpace()->getNComps(), 0);
2240 void Gfx::gouraudFillTriangle(double x0
, double y0
, GfxColor
*color0
,
2241 double x1
, double y1
, GfxColor
*color1
,
2242 double x2
, double y2
, GfxColor
*color2
,
2243 int nComps
, int depth
) {
2244 double x01
, y01
, x12
, y12
, x20
, y20
;
2245 GfxColor color01
, color12
, color20
;
2248 for (i
= 0; i
< nComps
; ++i
) {
2249 if (abs(color0
->c
[i
] - color1
->c
[i
]) > gouraudColorDelta
||
2250 abs(color1
->c
[i
] - color2
->c
[i
]) > gouraudColorDelta
) {
2254 if (i
== nComps
|| depth
== gouraudMaxDepth
) {
2255 state
->setFillColor(color0
);
2256 out
->updateFillColor(state
);
2257 state
->moveTo(x0
, y0
);
2258 state
->lineTo(x1
, y1
);
2259 state
->lineTo(x2
, y2
);
2264 x01
= 0.5 * (x0
+ x1
);
2265 y01
= 0.5 * (y0
+ y1
);
2266 x12
= 0.5 * (x1
+ x2
);
2267 y12
= 0.5 * (y1
+ y2
);
2268 x20
= 0.5 * (x2
+ x0
);
2269 y20
= 0.5 * (y2
+ y0
);
2270 //~ if the shading has a Function, this should interpolate on the
2271 //~ function parameter, not on the color components
2272 for (i
= 0; i
< nComps
; ++i
) {
2273 color01
.c
[i
] = (color0
->c
[i
] + color1
->c
[i
]) / 2;
2274 color12
.c
[i
] = (color1
->c
[i
] + color2
->c
[i
]) / 2;
2275 color20
.c
[i
] = (color2
->c
[i
] + color0
->c
[i
]) / 2;
2277 gouraudFillTriangle(x0
, y0
, color0
, x01
, y01
, &color01
,
2278 x20
, y20
, &color20
, nComps
, depth
+ 1);
2279 gouraudFillTriangle(x01
, y01
, &color01
, x1
, y1
, color1
,
2280 x12
, y12
, &color12
, nComps
, depth
+ 1);
2281 gouraudFillTriangle(x01
, y01
, &color01
, x12
, y12
, &color12
,
2282 x20
, y20
, &color20
, nComps
, depth
+ 1);
2283 gouraudFillTriangle(x20
, y20
, &color20
, x12
, y12
, &color12
,
2284 x2
, y2
, color2
, nComps
, depth
+ 1);
2288 void Gfx::doPatchMeshShFill(GfxPatchMeshShading
*shading
) {
2291 if (shading
->getNPatches() > 128) {
2293 } else if (shading
->getNPatches() > 64) {
2295 } else if (shading
->getNPatches() > 16) {
2300 for (i
= 0; i
< shading
->getNPatches(); ++i
) {
2301 fillPatch(shading
->getPatch(i
), shading
->getColorSpace()->getNComps(),
2306 void Gfx::fillPatch(GfxPatch
*patch
, int nComps
, int depth
) {
2307 GfxPatch patch00
, patch01
, patch10
, patch11
;
2308 double xx
[4][8], yy
[4][8];
2312 for (i
= 0; i
< nComps
; ++i
) {
2313 if (abs(patch
->color
[0][0].c
[i
] - patch
->color
[0][1].c
[i
])
2314 > patchColorDelta
||
2315 abs(patch
->color
[0][1].c
[i
] - patch
->color
[1][1].c
[i
])
2316 > patchColorDelta
||
2317 abs(patch
->color
[1][1].c
[i
] - patch
->color
[1][0].c
[i
])
2318 > patchColorDelta
||
2319 abs(patch
->color
[1][0].c
[i
] - patch
->color
[0][0].c
[i
])
2320 > patchColorDelta
) {
2324 if (i
== nComps
|| depth
== patchMaxDepth
) {
2325 state
->setFillColor(&patch
->color
[0][0]);
2326 out
->updateFillColor(state
);
2327 state
->moveTo(patch
->x
[0][0], patch
->y
[0][0]);
2328 state
->curveTo(patch
->x
[0][1], patch
->y
[0][1],
2329 patch
->x
[0][2], patch
->y
[0][2],
2330 patch
->x
[0][3], patch
->y
[0][3]);
2331 state
->curveTo(patch
->x
[1][3], patch
->y
[1][3],
2332 patch
->x
[2][3], patch
->y
[2][3],
2333 patch
->x
[3][3], patch
->y
[3][3]);
2334 state
->curveTo(patch
->x
[3][2], patch
->y
[3][2],
2335 patch
->x
[3][1], patch
->y
[3][1],
2336 patch
->x
[3][0], patch
->y
[3][0]);
2337 state
->curveTo(patch
->x
[2][0], patch
->y
[2][0],
2338 patch
->x
[1][0], patch
->y
[1][0],
2339 patch
->x
[0][0], patch
->y
[0][0]);
2344 for (i
= 0; i
< 4; ++i
) {
2345 xx
[i
][0] = patch
->x
[i
][0];
2346 yy
[i
][0] = patch
->y
[i
][0];
2347 xx
[i
][1] = 0.5 * (patch
->x
[i
][0] + patch
->x
[i
][1]);
2348 yy
[i
][1] = 0.5 * (patch
->y
[i
][0] + patch
->y
[i
][1]);
2349 xxm
= 0.5 * (patch
->x
[i
][1] + patch
->x
[i
][2]);
2350 yym
= 0.5 * (patch
->y
[i
][1] + patch
->y
[i
][2]);
2351 xx
[i
][6] = 0.5 * (patch
->x
[i
][2] + patch
->x
[i
][3]);
2352 yy
[i
][6] = 0.5 * (patch
->y
[i
][2] + patch
->y
[i
][3]);
2353 xx
[i
][2] = 0.5 * (xx
[i
][1] + xxm
);
2354 yy
[i
][2] = 0.5 * (yy
[i
][1] + yym
);
2355 xx
[i
][5] = 0.5 * (xxm
+ xx
[i
][6]);
2356 yy
[i
][5] = 0.5 * (yym
+ yy
[i
][6]);
2357 xx
[i
][3] = xx
[i
][4] = 0.5 * (xx
[i
][2] + xx
[i
][5]);
2358 yy
[i
][3] = yy
[i
][4] = 0.5 * (yy
[i
][2] + yy
[i
][5]);
2359 xx
[i
][7] = patch
->x
[i
][3];
2360 yy
[i
][7] = patch
->y
[i
][3];
2362 for (i
= 0; i
< 4; ++i
) {
2363 patch00
.x
[0][i
] = xx
[0][i
];
2364 patch00
.y
[0][i
] = yy
[0][i
];
2365 patch00
.x
[1][i
] = 0.5 * (xx
[0][i
] + xx
[1][i
]);
2366 patch00
.y
[1][i
] = 0.5 * (yy
[0][i
] + yy
[1][i
]);
2367 xxm
= 0.5 * (xx
[1][i
] + xx
[2][i
]);
2368 yym
= 0.5 * (yy
[1][i
] + yy
[2][i
]);
2369 patch10
.x
[2][i
] = 0.5 * (xx
[2][i
] + xx
[3][i
]);
2370 patch10
.y
[2][i
] = 0.5 * (yy
[2][i
] + yy
[3][i
]);
2371 patch00
.x
[2][i
] = 0.5 * (patch00
.x
[1][i
] + xxm
);
2372 patch00
.y
[2][i
] = 0.5 * (patch00
.y
[1][i
] + yym
);
2373 patch10
.x
[1][i
] = 0.5 * (xxm
+ patch10
.x
[2][i
]);
2374 patch10
.y
[1][i
] = 0.5 * (yym
+ patch10
.y
[2][i
]);
2375 patch00
.x
[3][i
] = 0.5 * (patch00
.x
[2][i
] + patch10
.x
[1][i
]);
2376 patch00
.y
[3][i
] = 0.5 * (patch00
.y
[2][i
] + patch10
.y
[1][i
]);
2377 patch10
.x
[0][i
] = patch00
.x
[3][i
];
2378 patch10
.y
[0][i
] = patch00
.y
[3][i
];
2379 patch10
.x
[3][i
] = xx
[3][i
];
2380 patch10
.y
[3][i
] = yy
[3][i
];
2382 for (i
= 4; i
< 8; ++i
) {
2383 patch01
.x
[0][i
-4] = xx
[0][i
];
2384 patch01
.y
[0][i
-4] = yy
[0][i
];
2385 patch01
.x
[1][i
-4] = 0.5 * (xx
[0][i
] + xx
[1][i
]);
2386 patch01
.y
[1][i
-4] = 0.5 * (yy
[0][i
] + yy
[1][i
]);
2387 xxm
= 0.5 * (xx
[1][i
] + xx
[2][i
]);
2388 yym
= 0.5 * (yy
[1][i
] + yy
[2][i
]);
2389 patch11
.x
[2][i
-4] = 0.5 * (xx
[2][i
] + xx
[3][i
]);
2390 patch11
.y
[2][i
-4] = 0.5 * (yy
[2][i
] + yy
[3][i
]);
2391 patch01
.x
[2][i
-4] = 0.5 * (patch01
.x
[1][i
-4] + xxm
);
2392 patch01
.y
[2][i
-4] = 0.5 * (patch01
.y
[1][i
-4] + yym
);
2393 patch11
.x
[1][i
-4] = 0.5 * (xxm
+ patch11
.x
[2][i
-4]);
2394 patch11
.y
[1][i
-4] = 0.5 * (yym
+ patch11
.y
[2][i
-4]);
2395 patch01
.x
[3][i
-4] = 0.5 * (patch01
.x
[2][i
-4] + patch11
.x
[1][i
-4]);
2396 patch01
.y
[3][i
-4] = 0.5 * (patch01
.y
[2][i
-4] + patch11
.y
[1][i
-4]);
2397 patch11
.x
[0][i
-4] = patch01
.x
[3][i
-4];
2398 patch11
.y
[0][i
-4] = patch01
.y
[3][i
-4];
2399 patch11
.x
[3][i
-4] = xx
[3][i
];
2400 patch11
.y
[3][i
-4] = yy
[3][i
];
2402 //~ if the shading has a Function, this should interpolate on the
2403 //~ function parameter, not on the color components
2404 for (i
= 0; i
< nComps
; ++i
) {
2405 patch00
.color
[0][0].c
[i
] = patch
->color
[0][0].c
[i
];
2406 patch00
.color
[0][1].c
[i
] = (patch
->color
[0][0].c
[i
] +
2407 patch
->color
[0][1].c
[i
]) / 2;
2408 patch01
.color
[0][0].c
[i
] = patch00
.color
[0][1].c
[i
];
2409 patch01
.color
[0][1].c
[i
] = patch
->color
[0][1].c
[i
];
2410 patch01
.color
[1][1].c
[i
] = (patch
->color
[0][1].c
[i
] +
2411 patch
->color
[1][1].c
[i
]) / 2;
2412 patch11
.color
[0][1].c
[i
] = patch01
.color
[1][1].c
[i
];
2413 patch11
.color
[1][1].c
[i
] = patch
->color
[1][1].c
[i
];
2414 patch11
.color
[1][0].c
[i
] = (patch
->color
[1][1].c
[i
] +
2415 patch
->color
[1][0].c
[i
]) / 2;
2416 patch10
.color
[1][1].c
[i
] = patch11
.color
[1][0].c
[i
];
2417 patch10
.color
[1][0].c
[i
] = patch
->color
[1][0].c
[i
];
2418 patch10
.color
[0][0].c
[i
] = (patch
->color
[1][0].c
[i
] +
2419 patch
->color
[0][0].c
[i
]) / 2;
2420 patch00
.color
[1][0].c
[i
] = patch10
.color
[0][0].c
[i
];
2421 patch00
.color
[1][1].c
[i
] = (patch00
.color
[1][0].c
[i
] +
2422 patch01
.color
[1][1].c
[i
]) / 2;
2423 patch01
.color
[1][0].c
[i
] = patch00
.color
[1][1].c
[i
];
2424 patch11
.color
[0][0].c
[i
] = patch00
.color
[1][1].c
[i
];
2425 patch10
.color
[0][1].c
[i
] = patch00
.color
[1][1].c
[i
];
2427 fillPatch(&patch00
, nComps
, depth
+ 1);
2428 fillPatch(&patch10
, nComps
, depth
+ 1);
2429 fillPatch(&patch01
, nComps
, depth
+ 1);
2430 fillPatch(&patch11
, nComps
, depth
+ 1);
2434 void Gfx::doEndPath() {
2435 if (state
->isCurPt() && clip
!= clipNone
) {
2437 if (clip
== clipNormal
) {
2447 //------------------------------------------------------------------------
2448 // path clipping operators
2449 //------------------------------------------------------------------------
2451 void Gfx::opClip(Object args
[], int numArgs
) {
2455 void Gfx::opEOClip(Object args
[], int numArgs
) {
2459 //------------------------------------------------------------------------
2460 // text object operators
2461 //------------------------------------------------------------------------
2463 void Gfx::opBeginText(Object args
[], int numArgs
) {
2464 state
->setTextMat(1, 0, 0, 1, 0, 0);
2465 state
->textMoveTo(0, 0);
2466 out
->updateTextMat(state
);
2467 out
->updateTextPos(state
);
2468 fontChanged
= gTrue
;
2471 void Gfx::opEndText(Object args
[], int numArgs
) {
2472 out
->endTextObject(state
);
2475 //------------------------------------------------------------------------
2476 // text state operators
2477 //------------------------------------------------------------------------
2479 void Gfx::opSetCharSpacing(Object args
[], int numArgs
) {
2480 state
->setCharSpace(args
[0].getNum());
2481 out
->updateCharSpace(state
);
2484 void Gfx::opSetFont(Object args
[], int numArgs
) {
2487 if (!(font
= res
->lookupFont(args
[0].getName()))) {
2490 if (printCommands
) {
2491 printf(" font: tag=%s name='%s' %g\n",
2492 font
->getTag()->getCString(),
2493 font
->getName() ? font
->getName()->getCString() : "???",
2497 state
->setFont(font
, args
[1].getNum());
2498 fontChanged
= gTrue
;
2501 void Gfx::opSetTextLeading(Object args
[], int numArgs
) {
2502 state
->setLeading(args
[0].getNum());
2505 void Gfx::opSetTextRender(Object args
[], int numArgs
) {
2506 state
->setRender(args
[0].getInt());
2507 out
->updateRender(state
);
2510 void Gfx::opSetTextRise(Object args
[], int numArgs
) {
2511 state
->setRise(args
[0].getNum());
2512 out
->updateRise(state
);
2515 void Gfx::opSetWordSpacing(Object args
[], int numArgs
) {
2516 state
->setWordSpace(args
[0].getNum());
2517 out
->updateWordSpace(state
);
2520 void Gfx::opSetHorizScaling(Object args
[], int numArgs
) {
2521 state
->setHorizScaling(args
[0].getNum());
2522 out
->updateHorizScaling(state
);
2523 fontChanged
= gTrue
;
2526 //------------------------------------------------------------------------
2527 // text positioning operators
2528 //------------------------------------------------------------------------
2530 void Gfx::opTextMove(Object args
[], int numArgs
) {
2533 tx
= state
->getLineX() + args
[0].getNum();
2534 ty
= state
->getLineY() + args
[1].getNum();
2535 state
->textMoveTo(tx
, ty
);
2536 out
->updateTextPos(state
);
2539 void Gfx::opTextMoveSet(Object args
[], int numArgs
) {
2542 tx
= state
->getLineX() + args
[0].getNum();
2543 ty
= args
[1].getNum();
2544 state
->setLeading(-ty
);
2545 ty
+= state
->getLineY();
2546 state
->textMoveTo(tx
, ty
);
2547 out
->updateTextPos(state
);
2550 void Gfx::opSetTextMatrix(Object args
[], int numArgs
) {
2551 state
->setTextMat(args
[0].getNum(), args
[1].getNum(),
2552 args
[2].getNum(), args
[3].getNum(),
2553 args
[4].getNum(), args
[5].getNum());
2554 state
->textMoveTo(0, 0);
2555 out
->updateTextMat(state
);
2556 out
->updateTextPos(state
);
2557 fontChanged
= gTrue
;
2560 void Gfx::opTextNextLine(Object args
[], int numArgs
) {
2563 tx
= state
->getLineX();
2564 ty
= state
->getLineY() - state
->getLeading();
2565 state
->textMoveTo(tx
, ty
);
2566 out
->updateTextPos(state
);
2569 //------------------------------------------------------------------------
2570 // text string operators
2571 //------------------------------------------------------------------------
2573 void Gfx::opShowText(Object args
[], int numArgs
) {
2574 if (!state
->getFont()) {
2575 error(getPos(), "No font in show");
2579 out
->updateFont(state
);
2580 fontChanged
= gFalse
;
2582 out
->beginStringOp(state
);
2583 doShowText(args
[0].getString());
2584 out
->endStringOp(state
);
2587 void Gfx::opMoveShowText(Object args
[], int numArgs
) {
2590 if (!state
->getFont()) {
2591 error(getPos(), "No font in move/show");
2595 out
->updateFont(state
);
2596 fontChanged
= gFalse
;
2598 tx
= state
->getLineX();
2599 ty
= state
->getLineY() - state
->getLeading();
2600 state
->textMoveTo(tx
, ty
);
2601 out
->updateTextPos(state
);
2602 out
->beginStringOp(state
);
2603 doShowText(args
[0].getString());
2604 out
->endStringOp(state
);
2607 void Gfx::opMoveSetShowText(Object args
[], int numArgs
) {
2610 if (!state
->getFont()) {
2611 error(getPos(), "No font in move/set/show");
2615 out
->updateFont(state
);
2616 fontChanged
= gFalse
;
2618 state
->setWordSpace(args
[0].getNum());
2619 state
->setCharSpace(args
[1].getNum());
2620 tx
= state
->getLineX();
2621 ty
= state
->getLineY() - state
->getLeading();
2622 state
->textMoveTo(tx
, ty
);
2623 out
->updateWordSpace(state
);
2624 out
->updateCharSpace(state
);
2625 out
->updateTextPos(state
);
2626 out
->beginStringOp(state
);
2627 doShowText(args
[2].getString());
2628 out
->endStringOp(state
);
2631 void Gfx::opShowSpaceText(Object args
[], int numArgs
) {
2637 if (!state
->getFont()) {
2638 error(getPos(), "No font in show/space");
2642 out
->updateFont(state
);
2643 fontChanged
= gFalse
;
2645 out
->beginStringOp(state
);
2646 wMode
= state
->getFont()->getWMode();
2647 a
= args
[0].getArray();
2648 for (i
= 0; i
< a
->getLength(); ++i
) {
2651 // this uses the absolute value of the font size to match
2652 // Acrobat's behavior
2654 state
->textShift(0, -obj
.getNum() * 0.001 *
2655 fabs(state
->getFontSize()));
2657 state
->textShift(-obj
.getNum() * 0.001 *
2658 fabs(state
->getFontSize()), 0);
2660 out
->updateTextShift(state
, obj
.getNum());
2661 } else if (obj
.isString()) {
2662 doShowText(obj
.getString());
2664 error(getPos(), "Element of show/space array must be number or string");
2668 out
->endStringOp(state
);
2671 void Gfx::doShowText(GString
*s
) {
2674 double riseX
, riseY
;
2677 double x
, y
, dx
, dy
, dx2
, dy2
, curX
, curY
, tdx
, tdy
, lineX
, lineY
;
2678 double originX
, originY
, tOriginX
, tOriginY
;
2679 double oldCTM
[6], newCTM
[6];
2685 int len
, n
, uLen
, nChars
, nSpaces
, i
;
2687 font
= state
->getFont();
2688 wMode
= font
->getWMode();
2690 if (out
->useDrawChar()) {
2691 out
->beginString(state
, s
);
2694 // handle a Type 3 char
2695 if (font
->getType() == fontType3
&& out
->interpretType3Chars()) {
2696 mat
= state
->getCTM();
2697 for (i
= 0; i
< 6; ++i
) {
2700 mat
= state
->getTextMat();
2701 newCTM
[0] = mat
[0] * oldCTM
[0] + mat
[1] * oldCTM
[2];
2702 newCTM
[1] = mat
[0] * oldCTM
[1] + mat
[1] * oldCTM
[3];
2703 newCTM
[2] = mat
[2] * oldCTM
[0] + mat
[3] * oldCTM
[2];
2704 newCTM
[3] = mat
[2] * oldCTM
[1] + mat
[3] * oldCTM
[3];
2705 mat
= font
->getFontMatrix();
2706 newCTM
[0] = mat
[0] * newCTM
[0] + mat
[1] * newCTM
[2];
2707 newCTM
[1] = mat
[0] * newCTM
[1] + mat
[1] * newCTM
[3];
2708 newCTM
[2] = mat
[2] * newCTM
[0] + mat
[3] * newCTM
[2];
2709 newCTM
[3] = mat
[2] * newCTM
[1] + mat
[3] * newCTM
[3];
2710 newCTM
[0] *= state
->getFontSize();
2711 newCTM
[1] *= state
->getFontSize();
2712 newCTM
[2] *= state
->getFontSize();
2713 newCTM
[3] *= state
->getFontSize();
2714 newCTM
[0] *= state
->getHorizScaling();
2715 newCTM
[2] *= state
->getHorizScaling();
2716 state
->textTransformDelta(0, state
->getRise(), &riseX
, &riseY
);
2717 curX
= state
->getCurX();
2718 curY
= state
->getCurY();
2719 lineX
= state
->getLineX();
2720 lineY
= state
->getLineY();
2722 p
= s
->getCString();
2723 len
= s
->getLength();
2725 n
= font
->getNextChar(p
, len
, &code
,
2726 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
2727 &dx
, &dy
, &originX
, &originY
);
2728 dx
= dx
* state
->getFontSize() + state
->getCharSpace();
2729 if (n
== 1 && *p
== ' ') {
2730 dx
+= state
->getWordSpace();
2732 dx
*= state
->getHorizScaling();
2733 dy
*= state
->getFontSize();
2734 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
2735 state
->transform(curX
+ riseX
, curY
+ riseY
, &x
, &y
);
2737 state
->setCTM(newCTM
[0], newCTM
[1], newCTM
[2], newCTM
[3], x
, y
);
2738 //~ out->updateCTM(???)
2739 if (!out
->beginType3Char(state
, curX
+ riseX
, curY
+ riseY
, tdx
, tdy
,
2741 ((Gfx8BitFont
*)font
)->getCharProc(code
, &charProc
);
2742 if ((resDict
= ((Gfx8BitFont
*)font
)->getResources())) {
2743 pushResources(resDict
);
2745 if (charProc
.isStream()) {
2746 display(&charProc
, gFalse
);
2748 error(getPos(), "Missing or bad Type3 CharProc entry");
2750 out
->endType3Char(state
);
2757 // GfxState::restore() does *not* restore the current position,
2758 // so we deal with it here using (curX, curY) and (lineX, lineY)
2761 state
->moveTo(curX
, curY
);
2762 state
->textSetPos(lineX
, lineY
);
2768 } else if (out
->useDrawChar()) {
2769 state
->textTransformDelta(0, state
->getRise(), &riseX
, &riseY
);
2770 p
= s
->getCString();
2771 len
= s
->getLength();
2773 n
= font
->getNextChar(p
, len
, &code
,
2774 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
2775 &dx
, &dy
, &originX
, &originY
);
2777 dx
*= state
->getFontSize();
2778 dy
= dy
* state
->getFontSize() + state
->getCharSpace();
2779 if (n
== 1 && *p
== ' ') {
2780 dy
+= state
->getWordSpace();
2783 dx
= dx
* state
->getFontSize() + state
->getCharSpace();
2784 if (n
== 1 && *p
== ' ') {
2785 dx
+= state
->getWordSpace();
2787 dx
*= state
->getHorizScaling();
2788 dy
*= state
->getFontSize();
2790 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
2791 originX
*= state
->getFontSize();
2792 originY
*= state
->getFontSize();
2793 state
->textTransformDelta(originX
, originY
, &tOriginX
, &tOriginY
);
2794 out
->drawChar(state
, state
->getCurX() + riseX
, state
->getCurY() + riseY
,
2795 tdx
, tdy
, tOriginX
, tOriginY
, code
, n
, u
, uLen
);
2796 state
->shift(tdx
, tdy
);
2803 p
= s
->getCString();
2804 len
= s
->getLength();
2805 nChars
= nSpaces
= 0;
2807 n
= font
->getNextChar(p
, len
, &code
,
2808 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
2809 &dx2
, &dy2
, &originX
, &originY
);
2812 if (n
== 1 && *p
== ' ') {
2820 dx
*= state
->getFontSize();
2821 dy
= dy
* state
->getFontSize()
2822 + nChars
* state
->getCharSpace()
2823 + nSpaces
* state
->getWordSpace();
2825 dx
= dx
* state
->getFontSize()
2826 + nChars
* state
->getCharSpace()
2827 + nSpaces
* state
->getWordSpace();
2828 dx
*= state
->getHorizScaling();
2829 dy
*= state
->getFontSize();
2831 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
2832 out
->drawString(state
, s
);
2833 state
->shift(tdx
, tdy
);
2836 if (out
->useDrawChar()) {
2837 out
->endString(state
);
2840 updateLevel
+= 10 * s
->getLength();
2843 //------------------------------------------------------------------------
2844 // XObject operators
2845 //------------------------------------------------------------------------
2847 void Gfx::opXObject(Object args
[], int numArgs
) {
2848 Object obj1
, obj2
, obj3
, refObj
;
2853 if (!res
->lookupXObject(args
[0].getName(), &obj1
)) {
2856 if (!obj1
.isStream()) {
2857 error(getPos(), "XObject '%s' is wrong type", args
[0].getName());
2862 obj1
.streamGetDict()->lookup("OPI", &opiDict
);
2863 if (opiDict
.isDict()) {
2864 out
->opiBegin(state
, opiDict
.getDict());
2867 obj1
.streamGetDict()->lookup("Subtype", &obj2
);
2868 if (obj2
.isName("Image")) {
2869 if (out
->needNonText()) {
2870 res
->lookupXObjectNF(args
[0].getName(), &refObj
);
2871 doImage(&refObj
, obj1
.getStream(), gFalse
);
2874 } else if (obj2
.isName("Form")) {
2876 } else if (obj2
.isName("PS")) {
2877 obj1
.streamGetDict()->lookup("Level1", &obj3
);
2878 out
->psXObject(obj1
.getStream(),
2879 obj3
.isStream() ? obj3
.getStream() : (Stream
*)NULL
);
2880 } else if (obj2
.isName()) {
2881 error(getPos(), "Unknown XObject subtype '%s'", obj2
.getName());
2883 error(getPos(), "XObject subtype is missing or wrong type");
2887 if (opiDict
.isDict()) {
2888 out
->opiEnd(state
, opiDict
.getDict());
2895 void Gfx::doImage(Object
*ref
, Stream
*str
, GBool inlineImg
) {
2896 Dict
*dict
, *maskDict
;
2899 StreamColorSpaceMode csMode
;
2902 GfxColorSpace
*colorSpace
, *maskColorSpace
;
2903 GfxImageColorMap
*colorMap
, *maskColorMap
;
2904 Object maskObj
, smaskObj
;
2905 GBool haveColorKeyMask
, haveExplicitMask
, haveSoftMask
;
2906 int maskColors
[2*gfxColorMaxComps
];
2907 int maskWidth
, maskHeight
;
2913 // get info from the stream
2915 csMode
= streamCSNone
;
2916 str
->getImageParams(&bits
, &csMode
);
2919 dict
= str
->getDict();
2922 dict
->lookup("Width", &obj1
);
2923 if (obj1
.isNull()) {
2925 dict
->lookup("W", &obj1
);
2929 width
= obj1
.getInt();
2931 dict
->lookup("Height", &obj1
);
2932 if (obj1
.isNull()) {
2934 dict
->lookup("H", &obj1
);
2938 height
= obj1
.getInt();
2942 dict
->lookup("ImageMask", &obj1
);
2943 if (obj1
.isNull()) {
2945 dict
->lookup("IM", &obj1
);
2949 mask
= obj1
.getBool();
2950 else if (!obj1
.isNull())
2956 dict
->lookup("BitsPerComponent", &obj1
);
2957 if (obj1
.isNull()) {
2959 dict
->lookup("BPC", &obj1
);
2962 bits
= obj1
.getInt();
2974 // check for inverted mask
2978 dict
->lookup("Decode", &obj1
);
2979 if (obj1
.isNull()) {
2981 dict
->lookup("D", &obj1
);
2983 if (obj1
.isArray()) {
2984 obj1
.arrayGet(0, &obj2
);
2985 if (obj2
.isInt() && obj2
.getInt() == 1)
2988 } else if (!obj1
.isNull()) {
2994 out
->drawImageMask(state
, ref
, str
, width
, height
, invert
, inlineImg
);
2998 // get color space and color map
2999 dict
->lookup("ColorSpace", &obj1
);
3000 if (obj1
.isNull()) {
3002 dict
->lookup("CS", &obj1
);
3004 if (obj1
.isName()) {
3005 res
->lookupColorSpace(obj1
.getName(), &obj2
);
3006 if (!obj2
.isNull()) {
3013 if (!obj1
.isNull()) {
3014 colorSpace
= GfxColorSpace::parse(&obj1
);
3015 } else if (csMode
== streamCSDeviceGray
) {
3016 colorSpace
= new GfxDeviceGrayColorSpace();
3017 } else if (csMode
== streamCSDeviceRGB
) {
3018 colorSpace
= new GfxDeviceRGBColorSpace();
3019 } else if (csMode
== streamCSDeviceCMYK
) {
3020 colorSpace
= new GfxDeviceCMYKColorSpace();
3028 dict
->lookup("Decode", &obj1
);
3029 if (obj1
.isNull()) {
3031 dict
->lookup("D", &obj1
);
3033 colorMap
= new GfxImageColorMap(bits
, &obj1
, colorSpace
);
3035 if (!colorMap
->isOk()) {
3041 haveColorKeyMask
= haveExplicitMask
= haveSoftMask
= gFalse
;
3042 maskStr
= NULL
; // make gcc happy
3043 maskWidth
= maskHeight
= 0; // make gcc happy
3044 maskInvert
= gFalse
; // make gcc happy
3045 maskColorMap
= NULL
; // make gcc happy
3046 dict
->lookup("Mask", &maskObj
);
3047 dict
->lookup("SMask", &smaskObj
);
3048 if (smaskObj
.isStream()) {
3053 maskStr
= smaskObj
.getStream();
3054 maskDict
= smaskObj
.streamGetDict();
3055 maskDict
->lookup("Width", &obj1
);
3056 if (obj1
.isNull()) {
3058 maskDict
->lookup("W", &obj1
);
3060 if (!obj1
.isInt()) {
3063 maskWidth
= obj1
.getInt();
3065 maskDict
->lookup("Height", &obj1
);
3066 if (obj1
.isNull()) {
3068 maskDict
->lookup("H", &obj1
);
3070 if (!obj1
.isInt()) {
3073 maskHeight
= obj1
.getInt();
3075 maskDict
->lookup("BitsPerComponent", &obj1
);
3076 if (obj1
.isNull()) {
3078 maskDict
->lookup("BPC", &obj1
);
3080 if (!obj1
.isInt()) {
3083 maskBits
= obj1
.getInt();
3085 maskDict
->lookup("ColorSpace", &obj1
);
3086 if (obj1
.isNull()) {
3088 maskDict
->lookup("CS", &obj1
);
3090 if (obj1
.isName()) {
3091 res
->lookupColorSpace(obj1
.getName(), &obj2
);
3092 if (!obj2
.isNull()) {
3099 maskColorSpace
= GfxColorSpace::parse(&obj1
);
3101 if (!maskColorSpace
|| maskColorSpace
->getMode() != csDeviceGray
) {
3104 maskDict
->lookup("Decode", &obj1
);
3105 if (obj1
.isNull()) {
3107 maskDict
->lookup("D", &obj1
);
3109 maskColorMap
= new GfxImageColorMap(maskBits
, &obj1
, maskColorSpace
);
3111 if (!maskColorMap
->isOk()) {
3112 delete maskColorMap
;
3115 //~ handle the Matte entry
3116 haveSoftMask
= gTrue
;
3117 } else if (maskObj
.isArray()) {
3120 i
< maskObj
.arrayGetLength() && i
< 2*gfxColorMaxComps
;
3122 maskObj
.arrayGet(i
, &obj1
);
3123 maskColors
[i
] = obj1
.getInt();
3126 haveColorKeyMask
= gTrue
;
3127 } else if (maskObj
.isStream()) {
3132 maskStr
= maskObj
.getStream();
3133 maskDict
= maskObj
.streamGetDict();
3134 maskDict
->lookup("Width", &obj1
);
3135 if (obj1
.isNull()) {
3137 maskDict
->lookup("W", &obj1
);
3139 if (!obj1
.isInt()) {
3142 maskWidth
= obj1
.getInt();
3144 maskDict
->lookup("Height", &obj1
);
3145 if (obj1
.isNull()) {
3147 maskDict
->lookup("H", &obj1
);
3149 if (!obj1
.isInt()) {
3152 maskHeight
= obj1
.getInt();
3154 maskDict
->lookup("ImageMask", &obj1
);
3155 if (obj1
.isNull()) {
3157 maskDict
->lookup("IM", &obj1
);
3159 if (!obj1
.isBool() || !obj1
.getBool()) {
3163 maskInvert
= gFalse
;
3164 maskDict
->lookup("Decode", &obj1
);
3165 if (obj1
.isNull()) {
3167 maskDict
->lookup("D", &obj1
);
3169 if (obj1
.isArray()) {
3170 obj1
.arrayGet(0, &obj2
);
3171 if (obj2
.isInt() && obj2
.getInt() == 1) {
3175 } else if (!obj1
.isNull()) {
3179 haveExplicitMask
= gTrue
;
3184 out
->drawSoftMaskedImage(state
, ref
, str
, width
, height
, colorMap
,
3185 maskStr
, maskWidth
, maskHeight
, maskColorMap
);
3186 delete maskColorMap
;
3187 } else if (haveExplicitMask
) {
3188 out
->drawMaskedImage(state
, ref
, str
, width
, height
, colorMap
,
3189 maskStr
, maskWidth
, maskHeight
, maskInvert
);
3191 out
->drawImage(state
, ref
, str
, width
, height
, colorMap
,
3192 haveColorKeyMask
? maskColors
: (int *)NULL
, inlineImg
);
3200 if ((i
= width
* height
) > 1000) {
3210 error(getPos(), "Bad image parameters");
3213 void Gfx::doForm(Object
*str
) {
3215 Object matrixObj
, bboxObj
;
3216 double m
[6], bbox
[6];
3222 // check for excessive recursion
3223 if (formDepth
> 20) {
3228 dict
= str
->streamGetDict();
3231 dict
->lookup("FormType", &obj1
);
3232 if (!(obj1
.isNull() || (obj1
.isInt() && obj1
.getInt() == 1))) {
3233 error(getPos(), "Unknown form type");
3238 dict
->lookup("BBox", &bboxObj
);
3239 if (!bboxObj
.isArray()) {
3242 error(getPos(), "Bad form bounding box");
3245 for (i
= 0; i
< 4; ++i
) {
3246 bboxObj
.arrayGet(i
, &obj1
);
3247 bbox
[i
] = obj1
.getNum();
3253 dict
->lookup("Matrix", &matrixObj
);
3254 if (matrixObj
.isArray()) {
3255 for (i
= 0; i
< 6; ++i
) {
3256 matrixObj
.arrayGet(i
, &obj1
);
3257 m
[i
] = obj1
.getNum();
3268 dict
->lookup("Resources", &resObj
);
3269 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
3273 doForm1(str
, resDict
, m
, bbox
);
3279 void Gfx::doAnnot(Object
*str
, double xMin
, double yMin
,
3280 double xMax
, double yMax
) {
3281 Dict
*dict
, *resDict
;
3282 Object matrixObj
, bboxObj
, resObj
, flagsObj
;
3284 double m
[6], bbox
[6], ictm
[6];
3286 double formX0
, formY0
, formX1
, formY1
;
3287 double annotX0
, annotY0
, annotX1
, annotY1
;
3288 double det
, x
, y
, sx
, sy
;
3292 dict
= str
->streamGetDict();
3294 // get annotation flags and only print annotations that are hidden or
3295 // don't have the print bit set.
3296 dict
->lookup("F", &flagsObj
);
3297 if (flagsObj
.isInt()) {
3298 flags
= flagsObj
.getInt();
3300 // Print anything that doesn't have any flags set...
3305 fprintf(stderr
, "DEBUG: pdftops: doAnnot found annotation with flags = %x\n",
3308 if ((flags
& 2) == 2 || (flags
& 4) == 0) {
3309 // Don't print hidden or no-print annotations...
3313 // get the form bounding box
3314 dict
->lookup("BBox", &bboxObj
);
3315 if (!bboxObj
.isArray()) {
3317 error(getPos(), "Bad form bounding box");
3320 for (i
= 0; i
< 4; ++i
) {
3321 bboxObj
.arrayGet(i
, &obj1
);
3322 bbox
[i
] = obj1
.getNum();
3327 // get the form matrix
3328 dict
->lookup("Matrix", &matrixObj
);
3329 if (matrixObj
.isArray()) {
3330 for (i
= 0; i
< 6; ++i
) {
3331 matrixObj
.arrayGet(i
, &obj1
);
3332 m
[i
] = obj1
.getNum();
3342 // transform the form bbox from form space to user space
3343 formX0
= bbox
[0] * m
[0] + bbox
[1] * m
[2] + m
[4];
3344 formY0
= bbox
[0] * m
[1] + bbox
[1] * m
[3] + m
[5];
3345 formX1
= bbox
[2] * m
[0] + bbox
[3] * m
[2] + m
[4];
3346 formY1
= bbox
[2] * m
[1] + bbox
[3] * m
[3] + m
[5];
3348 // transform the annotation bbox from default user space to user
3349 // space: (bbox * baseMatrix) * iCTM
3350 ctm
= state
->getCTM();
3351 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
3352 ictm
[0] = ctm
[3] * det
;
3353 ictm
[1] = -ctm
[1] * det
;
3354 ictm
[2] = -ctm
[2] * det
;
3355 ictm
[3] = ctm
[0] * det
;
3356 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
3357 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
3358 x
= baseMatrix
[0] * xMin
+ baseMatrix
[2] * yMin
+ baseMatrix
[4];
3359 y
= baseMatrix
[1] * xMin
+ baseMatrix
[3] * yMin
+ baseMatrix
[5];
3360 annotX0
= ictm
[0] * x
+ ictm
[2] * y
+ ictm
[4];
3361 annotY0
= ictm
[1] * x
+ ictm
[3] * y
+ ictm
[5];
3362 x
= baseMatrix
[0] * xMax
+ baseMatrix
[2] * yMax
+ baseMatrix
[4];
3363 y
= baseMatrix
[1] * xMax
+ baseMatrix
[3] * yMax
+ baseMatrix
[5];
3364 annotX1
= ictm
[0] * x
+ ictm
[2] * y
+ ictm
[4];
3365 annotY1
= ictm
[1] * x
+ ictm
[3] * y
+ ictm
[5];
3367 // swap min/max coords
3368 if (formX0
> formX1
) {
3369 x
= formX0
; formX0
= formX1
; formX1
= x
;
3371 if (formY0
> formY1
) {
3372 y
= formY0
; formY0
= formY1
; formY1
= y
;
3374 if (annotX0
> annotX1
) {
3375 x
= annotX0
; annotX0
= annotX1
; annotX1
= x
;
3377 if (annotY0
> annotY1
) {
3378 y
= annotY0
; annotY0
= annotY1
; annotY1
= y
;
3381 // scale the form to fit the annotation bbox
3382 if (formX1
== formX0
) {
3383 // this shouldn't happen
3386 sx
= (annotX1
- annotX0
) / (formX1
- formX0
);
3388 if (formY1
== formY0
) {
3389 // this shouldn't happen
3392 sy
= (annotY1
- annotY0
) / (formY1
- formY0
);
3396 m
[4] = (m
[4] - formX0
) * sx
+ annotX0
;
3399 m
[5] = (m
[5] - formY0
) * sy
+ annotY0
;
3402 dict
->lookup("Resources", &resObj
);
3403 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
3406 doForm1(str
, resDict
, m
, bbox
);
3412 void Gfx::doForm1(Object
*str
, Dict
*resDict
, double *matrix
, double *bbox
) {
3414 double oldBaseMatrix
[6];
3417 // push new resources on stack
3418 pushResources(resDict
);
3420 // save current graphics state
3423 // kill any pre-existing path
3426 // save current parser
3429 // set form transformation matrix
3430 state
->concatCTM(matrix
[0], matrix
[1], matrix
[2],
3431 matrix
[3], matrix
[4], matrix
[5]);
3432 out
->updateCTM(state
, matrix
[0], matrix
[1], matrix
[2],
3433 matrix
[3], matrix
[4], matrix
[5]);
3435 // set new base matrix
3436 for (i
= 0; i
< 6; ++i
) {
3437 oldBaseMatrix
[i
] = baseMatrix
[i
];
3438 baseMatrix
[i
] = state
->getCTM()[i
];
3441 // set form bounding box
3442 state
->moveTo(bbox
[0], bbox
[1]);
3443 state
->lineTo(bbox
[2], bbox
[1]);
3444 state
->lineTo(bbox
[2], bbox
[3]);
3445 state
->lineTo(bbox
[0], bbox
[3]);
3452 display(str
, gFalse
);
3454 // restore base matrix
3455 for (i
= 0; i
< 6; ++i
) {
3456 baseMatrix
[i
] = oldBaseMatrix
[i
];
3462 // restore graphics state
3465 // pop resource stack
3471 //------------------------------------------------------------------------
3472 // in-line image operators
3473 //------------------------------------------------------------------------
3475 void Gfx::opBeginImage(Object args
[], int numArgs
) {
3479 // build dict/stream
3480 str
= buildImageStream();
3482 // display the image
3484 doImage(NULL
, str
, gTrue
);
3487 c1
= str
->getBaseStream()->getChar();
3488 c2
= str
->getBaseStream()->getChar();
3489 while (!(c1
== 'E' && c2
== 'I') && c2
!= EOF
) {
3491 c2
= str
->getBaseStream()->getChar();
3497 Stream
*Gfx::buildImageStream() {
3504 dict
.initDict(xref
);
3505 parser
->getObj(&obj
);
3506 while (!obj
.isCmd("ID") && !obj
.isEOF()) {
3507 if (!obj
.isName()) {
3508 error(getPos(), "Inline image dictionary key must be a name object");
3511 key
= copyString(obj
.getName());
3513 parser
->getObj(&obj
);
3514 if (obj
.isEOF() || obj
.isError()) {
3518 dict
.dictAdd(key
, &obj
);
3520 parser
->getObj(&obj
);
3523 error(getPos(), "End of file in inline image");
3531 str
= new EmbedStream(parser
->getStream(), &dict
, gFalse
, 0);
3532 str
= str
->addFilters(&dict
);
3537 void Gfx::opImageData(Object args
[], int numArgs
) {
3538 error(getPos(), "Internal: got 'ID' operator");
3541 void Gfx::opEndImage(Object args
[], int numArgs
) {
3542 error(getPos(), "Internal: got 'EI' operator");
3545 //------------------------------------------------------------------------
3546 // type 3 font operators
3547 //------------------------------------------------------------------------
3549 void Gfx::opSetCharWidth(Object args
[], int numArgs
) {
3550 out
->type3D0(state
, args
[0].getNum(), args
[1].getNum());
3553 void Gfx::opSetCacheDevice(Object args
[], int numArgs
) {
3554 out
->type3D1(state
, args
[0].getNum(), args
[1].getNum(),
3555 args
[2].getNum(), args
[3].getNum(),
3556 args
[4].getNum(), args
[5].getNum());
3559 //------------------------------------------------------------------------
3560 // compatibility operators
3561 //------------------------------------------------------------------------
3563 void Gfx::opBeginIgnoreUndef(Object args
[], int numArgs
) {
3567 void Gfx::opEndIgnoreUndef(Object args
[], int numArgs
) {
3568 if (ignoreUndef
> 0)
3572 //------------------------------------------------------------------------
3573 // marked content operators
3574 //------------------------------------------------------------------------
3576 void Gfx::opBeginMarkedContent(Object args
[], int numArgs
) {
3577 if (printCommands
) {
3578 printf(" marked content: %s ", args
[0].getName());
3580 args
[2].print(stdout
);
3586 void Gfx::opEndMarkedContent(Object args
[], int numArgs
) {
3589 void Gfx::opMarkPoint(Object args
[], int numArgs
) {
3590 if (printCommands
) {
3591 printf(" mark point: %s ", args
[0].getName());
3593 args
[2].print(stdout
);
3599 //------------------------------------------------------------------------
3601 //------------------------------------------------------------------------
3603 void Gfx::saveState() {
3604 out
->saveState(state
);
3605 state
= state
->save();
3608 void Gfx::restoreState() {
3609 state
= state
->restore();
3610 out
->restoreState(state
);
3613 void Gfx::pushResources(Dict
*resDict
) {
3614 res
= new GfxResources(xref
, resDict
, res
);
3617 void Gfx::popResources() {
3618 GfxResources
*resPtr
;
3620 resPtr
= res
->getNext();