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"
37 // the MSVC math.h doesn't define this
39 #define M_PI 3.14159265358979323846
42 //------------------------------------------------------------------------
44 //------------------------------------------------------------------------
46 // Max recursive depth for a function shading fill.
47 #define functionMaxDepth 6
49 // Max delta allowed in any color component for a function shading fill.
50 #define functionColorDelta (dblToCol(1 / 256.0))
52 // Max number of splits along the t axis for an axial shading fill.
53 #define axialMaxSplits 256
55 // Max delta allowed in any color component for an axial shading fill.
56 #define axialColorDelta (dblToCol(1 / 256.0))
58 // Max number of splits along the t axis for a radial shading fill.
59 #define radialMaxSplits 256
61 // Max delta allowed in any color component for a radial shading fill.
62 #define radialColorDelta (dblToCol(1 / 256.0))
64 // Max recursive depth for a Gouraud triangle shading fill.
65 #define gouraudMaxDepth 6
67 // Max delta allowed in any color component for a Gouraud triangle
69 #define gouraudColorDelta (dblToCol(1 / 256.0))
71 // Max recursive depth for a patch mesh shading fill.
72 #define patchMaxDepth 6
74 // Max delta allowed in any color component for a patch mesh shading
76 #define patchColorDelta (dblToCol(1 / 256.0))
78 //------------------------------------------------------------------------
80 //------------------------------------------------------------------------
82 #ifdef WIN32 // this works around a bug in the VC7 compiler
83 # pragma optimize("",off)
86 Operator
Gfx::opTab
[] = {
87 {"\"", 3, {tchkNum
, tchkNum
, tchkString
},
88 &Gfx::opMoveSetShowText
},
89 {"'", 1, {tchkString
},
90 &Gfx::opMoveShowText
},
94 &Gfx::opEOFillStroke
},
95 {"BDC", 2, {tchkName
, tchkProps
},
96 &Gfx::opBeginMarkedContent
},
99 {"BMC", 1, {tchkName
},
100 &Gfx::opBeginMarkedContent
},
101 {"BT", 0, {tchkNone
},
103 {"BX", 0, {tchkNone
},
104 &Gfx::opBeginIgnoreUndef
},
105 {"CS", 1, {tchkName
},
106 &Gfx::opSetStrokeColorSpace
},
107 {"DP", 2, {tchkName
, tchkProps
},
109 {"Do", 1, {tchkName
},
111 {"EI", 0, {tchkNone
},
113 {"EMC", 0, {tchkNone
},
114 &Gfx::opEndMarkedContent
},
115 {"ET", 0, {tchkNone
},
117 {"EX", 0, {tchkNone
},
118 &Gfx::opEndIgnoreUndef
},
122 &Gfx::opSetStrokeGray
},
123 {"ID", 0, {tchkNone
},
127 {"K", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
128 &Gfx::opSetStrokeCMYKColor
},
130 &Gfx::opSetMiterLimit
},
131 {"MP", 1, {tchkName
},
135 {"RG", 3, {tchkNum
, tchkNum
, tchkNum
},
136 &Gfx::opSetStrokeRGBColor
},
139 {"SC", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
140 &Gfx::opSetStrokeColor
},
141 {"SCN", -33, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
142 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
143 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
144 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
145 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
146 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
147 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
148 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
150 &Gfx::opSetStrokeColorN
},
151 {"T*", 0, {tchkNone
},
152 &Gfx::opTextNextLine
},
153 {"TD", 2, {tchkNum
, tchkNum
},
154 &Gfx::opTextMoveSet
},
155 {"TJ", 1, {tchkArray
},
156 &Gfx::opShowSpaceText
},
158 &Gfx::opSetTextLeading
},
160 &Gfx::opSetCharSpacing
},
161 {"Td", 2, {tchkNum
, tchkNum
},
163 {"Tf", 2, {tchkName
, tchkNum
},
165 {"Tj", 1, {tchkString
},
167 {"Tm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
169 &Gfx::opSetTextMatrix
},
171 &Gfx::opSetTextRender
},
173 &Gfx::opSetTextRise
},
175 &Gfx::opSetWordSpacing
},
177 &Gfx::opSetHorizScaling
},
180 {"W*", 0, {tchkNone
},
183 &Gfx::opCloseFillStroke
},
184 {"b*", 0, {tchkNone
},
185 &Gfx::opCloseEOFillStroke
},
186 {"c", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
189 {"cm", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
192 {"cs", 1, {tchkName
},
193 &Gfx::opSetFillColorSpace
},
194 {"d", 2, {tchkArray
, tchkNum
},
196 {"d0", 2, {tchkNum
, tchkNum
},
197 &Gfx::opSetCharWidth
},
198 {"d1", 6, {tchkNum
, tchkNum
, tchkNum
, tchkNum
,
200 &Gfx::opSetCacheDevice
},
203 {"f*", 0, {tchkNone
},
206 &Gfx::opSetFillGray
},
207 {"gs", 1, {tchkName
},
208 &Gfx::opSetExtGState
},
214 &Gfx::opSetLineJoin
},
215 {"k", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
216 &Gfx::opSetFillCMYKColor
},
217 {"l", 2, {tchkNum
, tchkNum
},
219 {"m", 2, {tchkNum
, tchkNum
},
225 {"re", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
227 {"rg", 3, {tchkNum
, tchkNum
, tchkNum
},
228 &Gfx::opSetFillRGBColor
},
229 {"ri", 1, {tchkName
},
230 &Gfx::opSetRenderingIntent
},
232 &Gfx::opCloseStroke
},
233 {"sc", -4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
234 &Gfx::opSetFillColor
},
235 {"scn", -33, {tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
236 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
237 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
238 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
239 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
240 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
241 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
242 tchkSCN
, tchkSCN
, tchkSCN
, tchkSCN
,
244 &Gfx::opSetFillColorN
},
245 {"sh", 1, {tchkName
},
247 {"v", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
250 &Gfx::opSetLineWidth
},
251 {"y", 4, {tchkNum
, tchkNum
, tchkNum
, tchkNum
},
255 #ifdef WIN32 // this works around a bug in the VC7 compiler
256 # pragma optimize("",on)
259 #define numOps (sizeof(opTab) / sizeof(Operator))
261 //------------------------------------------------------------------------
263 //------------------------------------------------------------------------
265 GfxResources::GfxResources(XRef
*xref
, Dict
*resDict
, GfxResources
*nextA
) {
271 // build font dictionary
273 resDict
->lookupNF("Font", &obj1
);
275 obj1
.fetch(xref
, &obj2
);
278 fonts
= new GfxFontDict(xref
, &r
, obj2
.getDict());
281 } else if (obj1
.isDict()) {
282 fonts
= new GfxFontDict(xref
, NULL
, obj1
.getDict());
286 // get XObject dictionary
287 resDict
->lookup("XObject", &xObjDict
);
289 // get color space dictionary
290 resDict
->lookup("ColorSpace", &colorSpaceDict
);
292 // get pattern dictionary
293 resDict
->lookup("Pattern", &patternDict
);
295 // get shading dictionary
296 resDict
->lookup("Shading", &shadingDict
);
298 // get graphics state parameter dictionary
299 resDict
->lookup("ExtGState", &gStateDict
);
304 colorSpaceDict
.initNull();
305 patternDict
.initNull();
306 shadingDict
.initNull();
307 gStateDict
.initNull();
313 GfxResources::~GfxResources() {
318 colorSpaceDict
.free();
324 GfxFont
*GfxResources::lookupFont(char *name
) {
326 GfxResources
*resPtr
;
328 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
330 if ((font
= resPtr
->fonts
->lookup(name
)))
334 error(-1, "Unknown font tag '%s'", name
);
338 GBool
GfxResources::lookupXObject(char *name
, xObject
*obj
) {
339 GfxResources
*resPtr
;
341 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
342 if (resPtr
->xObjDict
.isDict()) {
343 if (!resPtr
->xObjDict
.dictLookup(name
, obj
)->isNull())
348 error(-1, "XObject '%s' is unknown", name
);
352 GBool
GfxResources::lookupXObjectNF(char *name
, xObject
*obj
) {
353 GfxResources
*resPtr
;
355 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
356 if (resPtr
->xObjDict
.isDict()) {
357 if (!resPtr
->xObjDict
.dictLookupNF(name
, obj
)->isNull())
362 error(-1, "XObject '%s' is unknown", name
);
366 void GfxResources::lookupColorSpace(char *name
, xObject
*obj
) {
367 GfxResources
*resPtr
;
369 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
370 if (resPtr
->colorSpaceDict
.isDict()) {
371 if (!resPtr
->colorSpaceDict
.dictLookup(name
, obj
)->isNull()) {
380 GfxPattern
*GfxResources::lookupPattern(char *name
) {
381 GfxResources
*resPtr
;
385 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
386 if (resPtr
->patternDict
.isDict()) {
387 if (!resPtr
->patternDict
.dictLookup(name
, &obj
)->isNull()) {
388 pattern
= GfxPattern::parse(&obj
);
395 error(-1, "Unknown pattern '%s'", name
);
399 GfxShading
*GfxResources::lookupShading(char *name
) {
400 GfxResources
*resPtr
;
404 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
405 if (resPtr
->shadingDict
.isDict()) {
406 if (!resPtr
->shadingDict
.dictLookup(name
, &obj
)->isNull()) {
407 shading
= GfxShading::parse(&obj
);
414 error(-1, "Unknown shading '%s'", name
);
418 GBool
GfxResources::lookupGState(char *name
, xObject
*obj
) {
419 GfxResources
*resPtr
;
421 for (resPtr
= this; resPtr
; resPtr
= resPtr
->next
) {
422 if (resPtr
->gStateDict
.isDict()) {
423 if (!resPtr
->gStateDict
.dictLookup(name
, obj
)->isNull()) {
429 error(-1, "ExtGState '%s' is unknown", name
);
433 //------------------------------------------------------------------------
435 //------------------------------------------------------------------------
437 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, int pageNum
, Dict
*resDict
,
438 double hDPI
, double vDPI
, PDFRectangle
*box
,
439 PDFRectangle
*cropBox
, int rotate
,
440 GBool (*abortCheckCbkA
)(void *data
),
441 void *abortCheckCbkDataA
) {
446 printCommands
= globalParams
->getPrintCommands();
448 // start the resource stack
449 res
= new GfxResources(xref
, resDict
, NULL
);
453 state
= new GfxState(hDPI
, vDPI
, box
, rotate
, out
->upsideDown());
454 fontChanged
= gFalse
;
457 out
->startPage(pageNum
, state
);
458 out
->setDefaultCTM(state
->getCTM());
459 out
->updateAll(state
);
460 for (i
= 0; i
< 6; ++i
) {
461 baseMatrix
[i
] = state
->getCTM()[i
];
464 abortCheckCbk
= abortCheckCbkA
;
465 abortCheckCbkData
= abortCheckCbkDataA
;
469 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
470 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
471 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
472 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
480 Gfx::Gfx(XRef
*xrefA
, OutputDev
*outA
, Dict
*resDict
,
481 PDFRectangle
*box
, PDFRectangle
*cropBox
,
482 GBool (*abortCheckCbkA
)(void *data
),
483 void *abortCheckCbkDataA
) {
488 printCommands
= globalParams
->getPrintCommands();
490 // start the resource stack
491 res
= new GfxResources(xref
, resDict
, NULL
);
495 state
= new GfxState(72, 72, box
, 0, gFalse
);
496 fontChanged
= gFalse
;
499 for (i
= 0; i
< 6; ++i
) {
500 baseMatrix
[i
] = state
->getCTM()[i
];
503 abortCheckCbk
= abortCheckCbkA
;
504 abortCheckCbkData
= abortCheckCbkDataA
;
508 state
->moveTo(cropBox
->x1
, cropBox
->y1
);
509 state
->lineTo(cropBox
->x2
, cropBox
->y1
);
510 state
->lineTo(cropBox
->x2
, cropBox
->y2
);
511 state
->lineTo(cropBox
->x1
, cropBox
->y2
);
520 while (state
->hasSaves()) {
534 void Gfx::display(xObject
*obj
, GBool topLevel
) {
538 if (obj
->isArray()) {
539 for (i
= 0; i
< obj
->arrayGetLength(); ++i
) {
540 obj
->arrayGet(i
, &obj2
);
541 if (!obj2
.isStream()) {
542 error(-1, "Weird page contents");
548 } else if (!obj
->isStream()) {
549 error(-1, "Weird page contents");
552 parser
= new Parser(xref
, new Lexer(xref
, obj
), gFalse
);
558 void Gfx::go(GBool topLevel
) {
560 xObject args
[maxArgs
];
564 // scan a sequence of xObjects
565 updateLevel
= lastAbortCheck
= 0;
567 parser
->getObj(&obj
);
568 while (!obj
.isEOF()) {
570 // got a command - execute it
574 for (i
= 0; i
< numArgs
; ++i
) {
576 args
[i
].print(stdout
);
581 execOp(&obj
, args
, numArgs
);
583 for (i
= 0; i
< numArgs
; ++i
)
587 // periodically update display
588 if (++updateLevel
>= 20000) {
593 // check for an abort
595 if (updateLevel
- lastAbortCheck
> 10) {
596 if ((*abortCheckCbk
)(abortCheckCbkData
)) {
599 lastAbortCheck
= updateLevel
;
603 // got an argument - save it
604 } else if (numArgs
< maxArgs
) {
605 args
[numArgs
++] = obj
;
607 // too many arguments - something is wrong
609 error(getPos(), "Too many args in content stream");
611 printf("throwing away arg: ");
619 // grab the next xObject
620 parser
->getObj(&obj
);
624 // args at end with no command
626 error(getPos(), "Leftover args in content stream");
628 printf("%d leftovers:", numArgs
);
629 for (i
= 0; i
< numArgs
; ++i
) {
631 args
[i
].print(stdout
);
636 for (i
= 0; i
< numArgs
; ++i
)
641 if (topLevel
&& updateLevel
> 0) {
646 void Gfx::execOp(xObject
*cmd
, xObject args
[], int numArgs
) {
653 name
= cmd
->getCmd();
654 if (!(op
= findOp(name
))) {
655 if (ignoreUndef
== 0)
656 error(getPos(), "Unknown operator '%s'", name
);
662 if (op
->numArgs
>= 0) {
663 if (numArgs
< op
->numArgs
) {
664 error(getPos(), "Too few (%d) args to '%s' operator", numArgs
, name
);
667 if (numArgs
> op
->numArgs
) {
669 error(getPos(), "Too many (%d) args to '%s' operator", numArgs
, name
);
671 argPtr
+= numArgs
- op
->numArgs
;
672 numArgs
= op
->numArgs
;
675 if (numArgs
> -op
->numArgs
) {
676 error(getPos(), "Too many (%d) args to '%s' operator",
681 for (i
= 0; i
< numArgs
; ++i
) {
682 if (!checkArg(&argPtr
[i
], op
->tchk
[i
])) {
683 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
684 i
, name
, argPtr
[i
].getTypeName());
690 (this->*op
->func
)(argPtr
, numArgs
);
693 Operator
*Gfx::findOp(char *name
) {
698 // invariant: opTab[a] < name < opTab[b]
701 cmp
= strcmp(opTab
[m
].name
, name
);
714 GBool
Gfx::checkArg(xObject
*arg
, TchkType type
) {
716 case tchkBool
: return arg
->isBool();
717 case tchkInt
: return arg
->isInt();
718 case tchkNum
: return arg
->isNum();
719 case tchkString
: return arg
->isString();
720 case tchkName
: return arg
->isName();
721 case tchkArray
: return arg
->isArray();
722 case tchkProps
: return arg
->isDict() || arg
->isName();
723 case tchkSCN
: return arg
->isNum() || arg
->isName();
724 case tchkNone
: return gFalse
;
730 return parser
? parser
->getPos() : -1;
733 //------------------------------------------------------------------------
734 // graphics state operators
735 //------------------------------------------------------------------------
737 void Gfx::opSave(xObject args
[], int numArgs
) {
741 void Gfx::opRestore(xObject args
[], int numArgs
) {
745 void Gfx::opConcat(xObject args
[], int numArgs
) {
746 state
->concatCTM(args
[0].getNum(), args
[1].getNum(),
747 args
[2].getNum(), args
[3].getNum(),
748 args
[4].getNum(), args
[5].getNum());
749 out
->updateCTM(state
, args
[0].getNum(), args
[1].getNum(),
750 args
[2].getNum(), args
[3].getNum(),
751 args
[4].getNum(), args
[5].getNum());
755 void Gfx::opSetDash(xObject args
[], int numArgs
) {
762 a
= args
[0].getArray();
763 length
= a
->getLength();
767 dash
= (double *)gmallocn(length
, sizeof(double));
768 for (i
= 0; i
< length
; ++i
) {
769 dash
[i
] = a
->get(i
, &obj
)->getNum();
773 state
->setLineDash(dash
, length
, args
[1].getNum());
774 out
->updateLineDash(state
);
777 void Gfx::opSetFlat(xObject args
[], int numArgs
) {
778 state
->setFlatness((int)args
[0].getNum());
779 out
->updateFlatness(state
);
782 void Gfx::opSetLineJoin(xObject args
[], int numArgs
) {
783 state
->setLineJoin(args
[0].getInt());
784 out
->updateLineJoin(state
);
787 void Gfx::opSetLineCap(xObject args
[], int numArgs
) {
788 state
->setLineCap(args
[0].getInt());
789 out
->updateLineCap(state
);
792 void Gfx::opSetMiterLimit(xObject args
[], int numArgs
) {
793 state
->setMiterLimit(args
[0].getNum());
794 out
->updateMiterLimit(state
);
797 void Gfx::opSetLineWidth(xObject args
[], int numArgs
) {
798 state
->setLineWidth(args
[0].getNum());
799 out
->updateLineWidth(state
);
802 void Gfx::opSetExtGState(xObject args
[], int numArgs
) {
803 xObject obj1
, obj2
, obj3
, obj4
, obj5
;
807 GfxColor backdropColor
;
808 GBool haveBackdropColor
;
809 GfxColorSpace
*blendingColorSpace
;
810 GBool alpha
, isolated
, knockout
;
813 if (!res
->lookupGState(args
[0].getName(), &obj1
)) {
816 if (!obj1
.isDict()) {
817 error(getPos(), "ExtGState '%s' is wrong type", args
[0].getName());
822 printf(" gfx state dict: ");
827 // transparency support: blend mode, fill/stroke opacity
828 if (!obj1
.dictLookup("BM", &obj2
)->isNull()) {
829 if (state
->parseBlendMode(&obj2
, &mode
)) {
830 state
->setBlendMode(mode
);
831 out
->updateBlendMode(state
);
833 error(getPos(), "Invalid blend mode in ExtGState");
837 if (obj1
.dictLookup("ca", &obj2
)->isNum()) {
838 state
->setFillOpacity(obj2
.getNum());
839 out
->updateFillOpacity(state
);
842 if (obj1
.dictLookup("CA", &obj2
)->isNum()) {
843 state
->setStrokeOpacity(obj2
.getNum());
844 out
->updateStrokeOpacity(state
);
848 // fill/stroke overprint
849 if ((haveFillOP
= (obj1
.dictLookup("op", &obj2
)->isBool()))) {
850 state
->setFillOverprint(obj2
.getBool());
851 out
->updateFillOverprint(state
);
854 if (obj1
.dictLookup("OP", &obj2
)->isBool()) {
855 state
->setStrokeOverprint(obj2
.getBool());
856 out
->updateStrokeOverprint(state
);
858 state
->setFillOverprint(obj2
.getBool());
859 out
->updateFillOverprint(state
);
865 if (obj1
.dictLookup("SA", &obj2
)->isBool()) {
866 state
->setStrokeAdjust(obj2
.getBool());
867 out
->updateStrokeAdjust(state
);
872 if (obj1
.dictLookup("TR2", &obj2
)->isNull()) {
874 obj1
.dictLookup("TR", &obj2
);
876 if (obj2
.isName("Default") ||
877 obj2
.isName("Identity")) {
878 funcs
[0] = funcs
[1] = funcs
[2] = funcs
[3] = NULL
;
879 state
->setTransfer(funcs
);
880 out
->updateTransfer(state
);
881 } else if (obj2
.isArray() && obj2
.arrayGetLength() == 4) {
882 for (i
= 0; i
< 4; ++i
) {
883 obj2
.arrayGet(i
, &obj3
);
884 funcs
[i
] = Function::parse(&obj3
);
891 state
->setTransfer(funcs
);
892 out
->updateTransfer(state
);
894 } else if (obj2
.isName() || obj2
.isDict() || obj2
.isStream()) {
895 if ((funcs
[0] = Function::parse(&obj2
))) {
896 funcs
[1] = funcs
[2] = funcs
[3] = NULL
;
897 state
->setTransfer(funcs
);
898 out
->updateTransfer(state
);
900 } else if (!obj2
.isNull()) {
901 error(getPos(), "Invalid transfer function in ExtGState");
906 if (!obj1
.dictLookup("SMask", &obj2
)->isNull()) {
907 if (obj2
.isName("None")) {
908 out
->clearSoftMask(state
);
909 } else if (obj2
.isDict()) {
910 if (obj2
.dictLookup("S", &obj3
)->isName("Alpha")) {
912 } else { // "Luminosity"
917 if (!obj2
.dictLookup("TR", &obj3
)->isNull()) {
918 funcs
[0] = Function::parse(&obj3
);
919 if (funcs
[0]->getInputSize() != 1 ||
920 funcs
[0]->getOutputSize() != 1) {
922 "Invalid transfer function in soft mask in ExtGState");
928 if ((haveBackdropColor
= obj2
.dictLookup("BC", &obj3
)->isArray())) {
929 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
930 backdropColor
.c
[i
] = 0;
932 for (i
= 0; i
< obj3
.arrayGetLength() && i
< gfxColorMaxComps
; ++i
) {
933 obj3
.arrayGet(i
, &obj4
);
935 backdropColor
.c
[i
] = dblToCol(obj4
.getNum());
941 if (obj2
.dictLookup("G", &obj3
)->isStream()) {
942 if (obj3
.streamGetDict()->lookup("Group", &obj4
)->isDict()) {
943 blendingColorSpace
= NULL
;
944 isolated
= knockout
= gFalse
;
945 if (!obj4
.dictLookup("CS", &obj5
)->isNull()) {
946 blendingColorSpace
= GfxColorSpace::parse(&obj5
);
949 if (obj4
.dictLookup("I", &obj5
)->isBool()) {
950 isolated
= obj5
.getBool();
953 if (obj4
.dictLookup("K", &obj5
)->isBool()) {
954 knockout
= obj5
.getBool();
957 if (!haveBackdropColor
) {
958 if (blendingColorSpace
) {
959 blendingColorSpace
->getDefaultColor(&backdropColor
);
961 //~ need to get the parent or default color space (?)
962 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
963 backdropColor
.c
[i
] = 0;
967 doSoftMask(&obj3
, alpha
, blendingColorSpace
,
968 isolated
, knockout
, funcs
[0], &backdropColor
);
973 error(getPos(), "Invalid soft mask in ExtGState - missing group");
977 error(getPos(), "Invalid soft mask in ExtGState - missing group");
980 } else if (!obj2
.isNull()) {
981 error(getPos(), "Invalid soft mask in ExtGState");
989 void Gfx::doSoftMask(xObject
*str
, GBool alpha
,
990 GfxColorSpace
*blendingColorSpace
,
991 GBool isolated
, GBool knockout
,
992 Function
*transferFunc
, GfxColor
*backdropColor
) {
993 Dict
*dict
, *resDict
;
994 double m
[6], bbox
[4];
998 // check for excessive recursion
999 if (formDepth
> 20) {
1004 dict
= str
->streamGetDict();
1007 dict
->lookup("FormType", &obj1
);
1008 if (!(obj1
.isNull() || (obj1
.isInt() && obj1
.getInt() == 1))) {
1009 error(getPos(), "Unknown form type");
1014 dict
->lookup("BBox", &obj1
);
1015 if (!obj1
.isArray()) {
1017 error(getPos(), "Bad form bounding box");
1020 for (i
= 0; i
< 4; ++i
) {
1021 obj1
.arrayGet(i
, &obj2
);
1022 bbox
[i
] = obj2
.getNum();
1028 dict
->lookup("Matrix", &obj1
);
1029 if (obj1
.isArray()) {
1030 for (i
= 0; i
< 6; ++i
) {
1031 obj1
.arrayGet(i
, &obj2
);
1032 m
[i
] = obj2
.getNum();
1043 dict
->lookup("Resources", &obj1
);
1044 resDict
= obj1
.isDict() ? obj1
.getDict() : (Dict
*)NULL
;
1048 doForm1(str
, resDict
, m
, bbox
, gTrue
, gTrue
,
1049 blendingColorSpace
, isolated
, knockout
,
1050 alpha
, transferFunc
, backdropColor
);
1053 if (blendingColorSpace
) {
1054 delete blendingColorSpace
;
1059 void Gfx::opSetRenderingIntent(xObject args
[], int numArgs
) {
1062 //------------------------------------------------------------------------
1064 //------------------------------------------------------------------------
1066 void Gfx::opSetFillGray(xObject args
[], int numArgs
) {
1069 state
->setFillPattern(NULL
);
1070 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
1071 out
->updateFillColorSpace(state
);
1072 color
.c
[0] = dblToCol(args
[0].getNum());
1073 state
->setFillColor(&color
);
1074 out
->updateFillColor(state
);
1077 void Gfx::opSetStrokeGray(xObject args
[], int numArgs
) {
1080 state
->setStrokePattern(NULL
);
1081 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1082 out
->updateStrokeColorSpace(state
);
1083 color
.c
[0] = dblToCol(args
[0].getNum());
1084 state
->setStrokeColor(&color
);
1085 out
->updateStrokeColor(state
);
1088 void Gfx::opSetFillCMYKColor(xObject args
[], int numArgs
) {
1092 state
->setFillPattern(NULL
);
1093 state
->setFillColorSpace(new GfxDeviceCMYKColorSpace());
1094 out
->updateFillColorSpace(state
);
1095 for (i
= 0; i
< 4; ++i
) {
1096 color
.c
[i
] = dblToCol(args
[i
].getNum());
1098 state
->setFillColor(&color
);
1099 out
->updateFillColor(state
);
1102 void Gfx::opSetStrokeCMYKColor(xObject args
[], int numArgs
) {
1106 state
->setStrokePattern(NULL
);
1107 state
->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
1108 out
->updateStrokeColorSpace(state
);
1109 for (i
= 0; i
< 4; ++i
) {
1110 color
.c
[i
] = dblToCol(args
[i
].getNum());
1112 state
->setStrokeColor(&color
);
1113 out
->updateStrokeColor(state
);
1116 void Gfx::opSetFillRGBColor(xObject args
[], int numArgs
) {
1120 state
->setFillPattern(NULL
);
1121 state
->setFillColorSpace(new GfxDeviceRGBColorSpace());
1122 out
->updateFillColorSpace(state
);
1123 for (i
= 0; i
< 3; ++i
) {
1124 color
.c
[i
] = dblToCol(args
[i
].getNum());
1126 state
->setFillColor(&color
);
1127 out
->updateFillColor(state
);
1130 void Gfx::opSetStrokeRGBColor(xObject args
[], int numArgs
) {
1134 state
->setStrokePattern(NULL
);
1135 state
->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1136 out
->updateStrokeColorSpace(state
);
1137 for (i
= 0; i
< 3; ++i
) {
1138 color
.c
[i
] = dblToCol(args
[i
].getNum());
1140 state
->setStrokeColor(&color
);
1141 out
->updateStrokeColor(state
);
1144 void Gfx::opSetFillColorSpace(xObject args
[], int numArgs
) {
1146 GfxColorSpace
*colorSpace
;
1149 state
->setFillPattern(NULL
);
1150 res
->lookupColorSpace(args
[0].getName(), &obj
);
1152 colorSpace
= GfxColorSpace::parse(&args
[0]);
1154 colorSpace
= GfxColorSpace::parse(&obj
);
1158 state
->setFillColorSpace(colorSpace
);
1159 out
->updateFillColorSpace(state
);
1160 colorSpace
->getDefaultColor(&color
);
1161 state
->setFillColor(&color
);
1162 out
->updateFillColor(state
);
1164 error(getPos(), "Bad color space (fill)");
1168 void Gfx::opSetStrokeColorSpace(xObject args
[], int numArgs
) {
1170 GfxColorSpace
*colorSpace
;
1173 state
->setStrokePattern(NULL
);
1174 res
->lookupColorSpace(args
[0].getName(), &obj
);
1176 colorSpace
= GfxColorSpace::parse(&args
[0]);
1178 colorSpace
= GfxColorSpace::parse(&obj
);
1182 state
->setStrokeColorSpace(colorSpace
);
1183 out
->updateStrokeColorSpace(state
);
1184 colorSpace
->getDefaultColor(&color
);
1185 state
->setStrokeColor(&color
);
1186 out
->updateStrokeColor(state
);
1188 error(getPos(), "Bad color space (stroke)");
1192 void Gfx::opSetFillColor(xObject args
[], int numArgs
) {
1196 if (numArgs
!= state
->getFillColorSpace()->getNComps()) {
1197 error(getPos(), "Incorrect number of arguments in 'sc' command");
1200 state
->setFillPattern(NULL
);
1201 for (i
= 0; i
< numArgs
; ++i
) {
1202 color
.c
[i
] = dblToCol(args
[i
].getNum());
1204 state
->setFillColor(&color
);
1205 out
->updateFillColor(state
);
1208 void Gfx::opSetStrokeColor(xObject args
[], int numArgs
) {
1212 if (numArgs
!= state
->getStrokeColorSpace()->getNComps()) {
1213 error(getPos(), "Incorrect number of arguments in 'SC' command");
1216 state
->setStrokePattern(NULL
);
1217 for (i
= 0; i
< numArgs
; ++i
) {
1218 color
.c
[i
] = dblToCol(args
[i
].getNum());
1220 state
->setStrokeColor(&color
);
1221 out
->updateStrokeColor(state
);
1224 void Gfx::opSetFillColorN(xObject args
[], int numArgs
) {
1226 GfxPattern
*pattern
;
1229 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1231 if (!((GfxPatternColorSpace
*)state
->getFillColorSpace())->getUnder() ||
1232 numArgs
- 1 != ((GfxPatternColorSpace
*)state
->getFillColorSpace())
1233 ->getUnder()->getNComps()) {
1234 error(getPos(), "Incorrect number of arguments in 'scn' command");
1237 for (i
= 0; i
< numArgs
- 1 && i
< gfxColorMaxComps
; ++i
) {
1238 if (args
[i
].isNum()) {
1239 color
.c
[i
] = dblToCol(args
[i
].getNum());
1242 state
->setFillColor(&color
);
1243 out
->updateFillColor(state
);
1245 if (args
[numArgs
-1].isName() &&
1246 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
1247 state
->setFillPattern(pattern
);
1251 if (numArgs
!= state
->getFillColorSpace()->getNComps()) {
1252 error(getPos(), "Incorrect number of arguments in 'scn' command");
1255 state
->setFillPattern(NULL
);
1256 for (i
= 0; i
< numArgs
&& i
< gfxColorMaxComps
; ++i
) {
1257 if (args
[i
].isNum()) {
1258 color
.c
[i
] = dblToCol(args
[i
].getNum());
1261 state
->setFillColor(&color
);
1262 out
->updateFillColor(state
);
1266 void Gfx::opSetStrokeColorN(xObject args
[], int numArgs
) {
1268 GfxPattern
*pattern
;
1271 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1273 if (!((GfxPatternColorSpace
*)state
->getStrokeColorSpace())
1275 numArgs
- 1 != ((GfxPatternColorSpace
*)state
->getStrokeColorSpace())
1276 ->getUnder()->getNComps()) {
1277 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1280 for (i
= 0; i
< numArgs
- 1 && i
< gfxColorMaxComps
; ++i
) {
1281 if (args
[i
].isNum()) {
1282 color
.c
[i
] = dblToCol(args
[i
].getNum());
1285 state
->setStrokeColor(&color
);
1286 out
->updateStrokeColor(state
);
1288 if (args
[numArgs
-1].isName() &&
1289 (pattern
= res
->lookupPattern(args
[numArgs
-1].getName()))) {
1290 state
->setStrokePattern(pattern
);
1294 if (numArgs
!= state
->getStrokeColorSpace()->getNComps()) {
1295 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1298 state
->setStrokePattern(NULL
);
1299 for (i
= 0; i
< numArgs
&& i
< gfxColorMaxComps
; ++i
) {
1300 if (args
[i
].isNum()) {
1301 color
.c
[i
] = dblToCol(args
[i
].getNum());
1304 state
->setStrokeColor(&color
);
1305 out
->updateStrokeColor(state
);
1309 //------------------------------------------------------------------------
1310 // path segment operators
1311 //------------------------------------------------------------------------
1313 void Gfx::opMoveTo(xObject args
[], int numArgs
) {
1314 state
->moveTo(args
[0].getNum(), args
[1].getNum());
1317 void Gfx::opLineTo(xObject args
[], int numArgs
) {
1318 if (!state
->isCurPt()) {
1319 error(getPos(), "No current point in lineto");
1322 state
->lineTo(args
[0].getNum(), args
[1].getNum());
1325 void Gfx::opCurveTo(xObject args
[], int numArgs
) {
1326 double x1
, y1
, x2
, y2
, x3
, y3
;
1328 if (!state
->isCurPt()) {
1329 error(getPos(), "No current point in curveto");
1332 x1
= args
[0].getNum();
1333 y1
= args
[1].getNum();
1334 x2
= args
[2].getNum();
1335 y2
= args
[3].getNum();
1336 x3
= args
[4].getNum();
1337 y3
= args
[5].getNum();
1338 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1341 void Gfx::opCurveTo1(xObject args
[], int numArgs
) {
1342 double x1
, y1
, x2
, y2
, x3
, y3
;
1344 if (!state
->isCurPt()) {
1345 error(getPos(), "No current point in curveto1");
1348 x1
= state
->getCurX();
1349 y1
= state
->getCurY();
1350 x2
= args
[0].getNum();
1351 y2
= args
[1].getNum();
1352 x3
= args
[2].getNum();
1353 y3
= args
[3].getNum();
1354 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1357 void Gfx::opCurveTo2(xObject args
[], int numArgs
) {
1358 double x1
, y1
, x2
, y2
, x3
, y3
;
1360 if (!state
->isCurPt()) {
1361 error(getPos(), "No current point in curveto2");
1364 x1
= args
[0].getNum();
1365 y1
= args
[1].getNum();
1366 x2
= args
[2].getNum();
1367 y2
= args
[3].getNum();
1370 state
->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
1373 void Gfx::opRectangle(xObject args
[], int numArgs
) {
1376 x
= args
[0].getNum();
1377 y
= args
[1].getNum();
1378 w
= args
[2].getNum();
1379 h
= args
[3].getNum();
1380 state
->moveTo(x
, y
);
1381 state
->lineTo(x
+ w
, y
);
1382 state
->lineTo(x
+ w
, y
+ h
);
1383 state
->lineTo(x
, y
+ h
);
1387 void Gfx::opClosePath(xObject args
[], int numArgs
) {
1388 if (!state
->isCurPt()) {
1389 error(getPos(), "No current point in closepath");
1395 //------------------------------------------------------------------------
1396 // path painting operators
1397 //------------------------------------------------------------------------
1399 void Gfx::opEndPath(xObject args
[], int numArgs
) {
1403 void Gfx::opStroke(xObject args
[], int numArgs
) {
1404 if (!state
->isCurPt()) {
1405 //error(getPos(), "No path in stroke");
1408 if (state
->isPath()) {
1409 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1418 void Gfx::opCloseStroke(xObject args
[], int numArgs
) {
1419 if (!state
->isCurPt()) {
1420 //error(getPos(), "No path in closepath/stroke");
1423 if (state
->isPath()) {
1425 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1434 void Gfx::opFill(xObject args
[], int numArgs
) {
1435 if (!state
->isCurPt()) {
1436 //error(getPos(), "No path in fill");
1439 if (state
->isPath()) {
1440 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1441 doPatternFill(gFalse
);
1449 void Gfx::opEOFill(xObject args
[], int numArgs
) {
1450 if (!state
->isCurPt()) {
1451 //error(getPos(), "No path in eofill");
1454 if (state
->isPath()) {
1455 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1456 doPatternFill(gTrue
);
1464 void Gfx::opFillStroke(xObject args
[], int numArgs
) {
1465 if (!state
->isCurPt()) {
1466 //error(getPos(), "No path in fill/stroke");
1469 if (state
->isPath()) {
1470 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1471 doPatternFill(gFalse
);
1475 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1484 void Gfx::opCloseFillStroke(xObject args
[], int numArgs
) {
1485 if (!state
->isCurPt()) {
1486 //error(getPos(), "No path in closepath/fill/stroke");
1489 if (state
->isPath()) {
1491 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1492 doPatternFill(gFalse
);
1496 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1505 void Gfx::opEOFillStroke(xObject args
[], int numArgs
) {
1506 if (!state
->isCurPt()) {
1507 //error(getPos(), "No path in eofill/stroke");
1510 if (state
->isPath()) {
1511 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1512 doPatternFill(gTrue
);
1516 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1525 void Gfx::opCloseEOFillStroke(xObject args
[], int numArgs
) {
1526 if (!state
->isCurPt()) {
1527 //error(getPos(), "No path in closepath/eofill/stroke");
1530 if (state
->isPath()) {
1532 if (state
->getFillColorSpace()->getMode() == csPattern
) {
1533 doPatternFill(gTrue
);
1537 if (state
->getStrokeColorSpace()->getMode() == csPattern
) {
1546 void Gfx::doPatternFill(GBool eoFill
) {
1547 GfxPattern
*pattern
;
1549 // this is a bit of a kludge -- patterns can be really slow, so we
1550 // skip them if we're only doing text extraction, since they almost
1551 // certainly don't contain any text
1552 if (!out
->needNonText()) {
1556 if (!(pattern
= state
->getFillPattern())) {
1559 switch (pattern
->getType()) {
1561 doTilingPatternFill((GfxTilingPattern
*)pattern
, gFalse
, eoFill
);
1564 doShadingPatternFill((GfxShadingPattern
*)pattern
, gFalse
, eoFill
);
1567 error(getPos(), "Unimplemented pattern type (%d) in fill",
1568 pattern
->getType());
1573 void Gfx::doPatternStroke() {
1574 GfxPattern
*pattern
;
1576 // this is a bit of a kludge -- patterns can be really slow, so we
1577 // skip them if we're only doing text extraction, since they almost
1578 // certainly don't contain any text
1579 if (!out
->needNonText()) {
1583 if (!(pattern
= state
->getStrokePattern())) {
1586 switch (pattern
->getType()) {
1588 doTilingPatternFill((GfxTilingPattern
*)pattern
, gTrue
, gFalse
);
1591 doShadingPatternFill((GfxShadingPattern
*)pattern
, gTrue
, gFalse
);
1594 error(getPos(), "Unimplemented pattern type (%d) in stroke",
1595 pattern
->getType());
1600 void Gfx::doTilingPatternFill(GfxTilingPattern
*tPat
,
1601 GBool stroke
, GBool eoFill
) {
1602 GfxPatternColorSpace
*patCS
;
1605 double xMin
, yMin
, xMax
, yMax
, x
, y
, x1
, y1
;
1606 double cxMin
, cyMin
, cxMax
, cyMax
;
1607 int xi0
, yi0
, xi1
, yi1
, xi
, yi
;
1608 double *ctm
, *btm
, *ptm
;
1609 double m
[6], ictm
[6], m1
[6], imb
[6];
1611 double xstep
, ystep
;
1615 patCS
= (GfxPatternColorSpace
*)(stroke
? state
->getStrokeColorSpace()
1616 : state
->getFillColorSpace());
1618 // construct a (pattern space) -> (current space) transform matrix
1619 ctm
= state
->getCTM();
1621 ptm
= tPat
->getMatrix();
1622 // iCTM = invert CTM
1623 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1624 ictm
[0] = ctm
[3] * det
;
1625 ictm
[1] = -ctm
[1] * det
;
1626 ictm
[2] = -ctm
[2] * det
;
1627 ictm
[3] = ctm
[0] * det
;
1628 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1629 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1630 // m1 = PTM * BTM = PTM * base transform matrix
1631 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1632 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1633 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1634 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1635 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1636 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1637 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1638 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1639 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1640 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1641 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1642 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1643 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1645 // construct a (device space) -> (pattern space) transform matrix
1646 det
= 1 / (m1
[0] * m1
[3] - m1
[1] * m1
[2]);
1647 imb
[0] = m1
[3] * det
;
1648 imb
[1] = -m1
[1] * det
;
1649 imb
[2] = -m1
[2] * det
;
1650 imb
[3] = m1
[0] * det
;
1651 imb
[4] = (m1
[2] * m1
[5] - m1
[3] * m1
[4]) * det
;
1652 imb
[5] = (m1
[1] * m1
[4] - m1
[0] * m1
[5]) * det
;
1654 // save current graphics state
1655 savedPath
= state
->getPath()->copy();
1658 // set underlying color space (for uncolored tiling patterns); set
1659 // various other parameters (stroke color, line width) to match
1661 if (tPat
->getPaintType() == 2 && (cs
= patCS
->getUnder())) {
1662 state
->setFillColorSpace(cs
->copy());
1663 out
->updateFillColorSpace(state
);
1664 state
->setStrokeColorSpace(cs
->copy());
1665 out
->updateStrokeColorSpace(state
);
1666 state
->setStrokeColor(state
->getFillColor());
1668 state
->setFillColorSpace(new GfxDeviceGrayColorSpace());
1669 out
->updateFillColorSpace(state
);
1670 state
->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1671 out
->updateStrokeColorSpace(state
);
1673 state
->setFillPattern(NULL
);
1674 out
->updateFillColor(state
);
1675 state
->setStrokePattern(NULL
);
1676 out
->updateStrokeColor(state
);
1678 state
->setLineWidth(0);
1679 out
->updateLineWidth(state
);
1682 // clip to current path
1684 state
->clipToStrokePath();
1685 out
->clipToStrokePath(state
);
1696 // get the clip region, check for empty
1697 state
->getClipBBox(&cxMin
, &cyMin
, &cxMax
, &cyMax
);
1698 if (cxMin
> cxMax
|| cyMin
> cyMax
) {
1702 // transform clip region bbox to pattern space
1703 xMin
= xMax
= cxMin
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1704 yMin
= yMax
= cxMin
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1705 x1
= cxMin
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1706 y1
= cxMin
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1709 } else if (x1
> xMax
) {
1714 } else if (y1
> yMax
) {
1717 x1
= cxMax
* imb
[0] + cyMin
* imb
[2] + imb
[4];
1718 y1
= cxMax
* imb
[1] + cyMin
* imb
[3] + imb
[5];
1721 } else if (x1
> xMax
) {
1726 } else if (y1
> yMax
) {
1729 x1
= cxMax
* imb
[0] + cyMax
* imb
[2] + imb
[4];
1730 y1
= cxMax
* imb
[1] + cyMax
* imb
[3] + imb
[5];
1733 } else if (x1
> xMax
) {
1738 } else if (y1
> yMax
) {
1743 //~ this should treat negative steps differently -- start at right/top
1744 //~ edge instead of left/bottom (?)
1745 xstep
= fabs(tPat
->getXStep());
1746 ystep
= fabs(tPat
->getYStep());
1747 xi0
= (int)ceil((xMin
- tPat
->getBBox()[2]) / xstep
);
1748 xi1
= (int)floor((xMax
- tPat
->getBBox()[0]) / xstep
) + 1;
1749 yi0
= (int)ceil((yMin
- tPat
->getBBox()[3]) / ystep
);
1750 yi1
= (int)floor((yMax
- tPat
->getBBox()[1]) / ystep
) + 1;
1751 for (i
= 0; i
< 4; ++i
) {
1754 if (out
->useTilingPatternFill()) {
1757 out
->tilingPatternFill(state
, tPat
->getContentStream(),
1758 tPat
->getPaintType(), tPat
->getResDict(),
1759 m1
, tPat
->getBBox(),
1760 xi0
, yi0
, xi1
, yi1
, xstep
, ystep
);
1762 for (yi
= yi0
; yi
< yi1
; ++yi
) {
1763 for (xi
= xi0
; xi
< xi1
; ++xi
) {
1766 m1
[4] = x
* m
[0] + y
* m
[2] + m
[4];
1767 m1
[5] = x
* m
[1] + y
* m
[3] + m
[5];
1768 doForm1(tPat
->getContentStream(), tPat
->getResDict(),
1769 m1
, tPat
->getBBox());
1774 // restore graphics state
1777 state
->setPath(savedPath
);
1780 void Gfx::doShadingPatternFill(GfxShadingPattern
*sPat
,
1781 GBool stroke
, GBool eoFill
) {
1782 GfxShading
*shading
;
1784 double *ctm
, *btm
, *ptm
;
1785 double m
[6], ictm
[6], m1
[6];
1786 double xMin
, yMin
, xMax
, yMax
;
1789 shading
= sPat
->getShading();
1791 // save current graphics state
1792 savedPath
= state
->getPath()->copy();
1796 if (shading
->getHasBBox()) {
1797 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1798 state
->moveTo(xMin
, yMin
);
1799 state
->lineTo(xMax
, yMin
);
1800 state
->lineTo(xMax
, yMax
);
1801 state
->lineTo(xMin
, yMax
);
1805 state
->setPath(savedPath
->copy());
1808 // clip to current path
1810 state
->clipToStrokePath();
1811 out
->clipToStrokePath(state
);
1821 // set the color space
1822 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1823 out
->updateFillColorSpace(state
);
1825 // background color fill
1826 if (shading
->getHasBackground()) {
1827 state
->setFillColor(shading
->getBackground());
1828 out
->updateFillColor(state
);
1833 // construct a (pattern space) -> (current space) transform matrix
1834 ctm
= state
->getCTM();
1836 ptm
= sPat
->getMatrix();
1837 // iCTM = invert CTM
1838 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
1839 ictm
[0] = ctm
[3] * det
;
1840 ictm
[1] = -ctm
[1] * det
;
1841 ictm
[2] = -ctm
[2] * det
;
1842 ictm
[3] = ctm
[0] * det
;
1843 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
1844 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
1845 // m1 = PTM * BTM = PTM * base transform matrix
1846 m1
[0] = ptm
[0] * btm
[0] + ptm
[1] * btm
[2];
1847 m1
[1] = ptm
[0] * btm
[1] + ptm
[1] * btm
[3];
1848 m1
[2] = ptm
[2] * btm
[0] + ptm
[3] * btm
[2];
1849 m1
[3] = ptm
[2] * btm
[1] + ptm
[3] * btm
[3];
1850 m1
[4] = ptm
[4] * btm
[0] + ptm
[5] * btm
[2] + btm
[4];
1851 m1
[5] = ptm
[4] * btm
[1] + ptm
[5] * btm
[3] + btm
[5];
1852 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1853 m
[0] = m1
[0] * ictm
[0] + m1
[1] * ictm
[2];
1854 m
[1] = m1
[0] * ictm
[1] + m1
[1] * ictm
[3];
1855 m
[2] = m1
[2] * ictm
[0] + m1
[3] * ictm
[2];
1856 m
[3] = m1
[2] * ictm
[1] + m1
[3] * ictm
[3];
1857 m
[4] = m1
[4] * ictm
[0] + m1
[5] * ictm
[2] + ictm
[4];
1858 m
[5] = m1
[4] * ictm
[1] + m1
[5] * ictm
[3] + ictm
[5];
1860 // set the new matrix
1861 state
->concatCTM(m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1862 out
->updateCTM(state
, m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
1864 #if 1 //~tmp: turn off anti-aliasing temporarily
1865 GBool vaa
= out
->getVectorAntialias();
1867 out
->setVectorAntialias(gFalse
);
1871 // do shading type-specific operations
1872 switch (shading
->getType()) {
1874 doFunctionShFill((GfxFunctionShading
*)shading
);
1877 doAxialShFill((GfxAxialShading
*)shading
);
1880 doRadialShFill((GfxRadialShading
*)shading
);
1884 doGouraudTriangleShFill((GfxGouraudTriangleShading
*)shading
);
1888 doPatchMeshShFill((GfxPatchMeshShading
*)shading
);
1892 #if 1 //~tmp: turn off anti-aliasing temporarily
1894 out
->setVectorAntialias(gTrue
);
1898 // restore graphics state
1900 state
->setPath(savedPath
);
1903 void Gfx::opShFill(xObject args
[], int numArgs
) {
1904 GfxShading
*shading
;
1906 double xMin
, yMin
, xMax
, yMax
;
1908 if (!(shading
= res
->lookupShading(args
[0].getName()))) {
1912 // save current graphics state
1913 savedPath
= state
->getPath()->copy();
1917 if (shading
->getHasBBox()) {
1918 shading
->getBBox(&xMin
, &yMin
, &xMax
, &yMax
);
1919 state
->moveTo(xMin
, yMin
);
1920 state
->lineTo(xMax
, yMin
);
1921 state
->lineTo(xMax
, yMax
);
1922 state
->lineTo(xMin
, yMax
);
1929 // set the color space
1930 state
->setFillColorSpace(shading
->getColorSpace()->copy());
1931 out
->updateFillColorSpace(state
);
1933 #if 1 //~tmp: turn off anti-aliasing temporarily
1934 GBool vaa
= out
->getVectorAntialias();
1936 out
->setVectorAntialias(gFalse
);
1940 // do shading type-specific operations
1941 switch (shading
->getType()) {
1943 doFunctionShFill((GfxFunctionShading
*)shading
);
1946 doAxialShFill((GfxAxialShading
*)shading
);
1949 doRadialShFill((GfxRadialShading
*)shading
);
1953 doGouraudTriangleShFill((GfxGouraudTriangleShading
*)shading
);
1957 doPatchMeshShFill((GfxPatchMeshShading
*)shading
);
1961 #if 1 //~tmp: turn off anti-aliasing temporarily
1963 out
->setVectorAntialias(gTrue
);
1967 // restore graphics state
1969 state
->setPath(savedPath
);
1974 void Gfx::doFunctionShFill(GfxFunctionShading
*shading
) {
1975 double x0
, y0
, x1
, y1
;
1978 if (out
->useShadedFills() &&
1979 out
->functionShadedFill(state
, shading
)) {
1983 shading
->getDomain(&x0
, &y0
, &x1
, &y1
);
1984 shading
->getColor(x0
, y0
, &colors
[0]);
1985 shading
->getColor(x0
, y1
, &colors
[1]);
1986 shading
->getColor(x1
, y0
, &colors
[2]);
1987 shading
->getColor(x1
, y1
, &colors
[3]);
1988 doFunctionShFill1(shading
, x0
, y0
, x1
, y1
, colors
, 0);
1991 void Gfx::doFunctionShFill1(GfxFunctionShading
*shading
,
1992 double x0
, double y0
,
1993 double x1
, double y1
,
1994 GfxColor
*colors
, int depth
) {
1996 GfxColor color0M
, color1M
, colorM0
, colorM1
, colorMM
;
1997 GfxColor colors2
[4];
2002 nComps
= shading
->getColorSpace()->getNComps();
2003 matrix
= shading
->getMatrix();
2005 // compare the four corner colors
2006 for (i
= 0; i
< 4; ++i
) {
2007 for (j
= 0; j
< nComps
; ++j
) {
2008 if (abs(colors
[i
].c
[j
] - colors
[(i
+1)&3].c
[j
]) > functionColorDelta
) {
2017 // center of the rectangle
2018 xM
= 0.5 * (x0
+ x1
);
2019 yM
= 0.5 * (y0
+ y1
);
2021 // the four corner colors are close (or we hit the recursive limit)
2022 // -- fill the rectangle; but require at least one subdivision
2023 // (depth==0) to avoid problems when the four outer corners of the
2024 // shaded region are the same color
2025 if ((i
== 4 && depth
> 0) || depth
== functionMaxDepth
) {
2027 // use the center color
2028 shading
->getColor(xM
, yM
, &fillColor
);
2029 state
->setFillColor(&fillColor
);
2030 out
->updateFillColor(state
);
2032 // fill the rectangle
2033 state
->moveTo(x0
* matrix
[0] + y0
* matrix
[2] + matrix
[4],
2034 x0
* matrix
[1] + y0
* matrix
[3] + matrix
[5]);
2035 state
->lineTo(x1
* matrix
[0] + y0
* matrix
[2] + matrix
[4],
2036 x1
* matrix
[1] + y0
* matrix
[3] + matrix
[5]);
2037 state
->lineTo(x1
* matrix
[0] + y1
* matrix
[2] + matrix
[4],
2038 x1
* matrix
[1] + y1
* matrix
[3] + matrix
[5]);
2039 state
->lineTo(x0
* matrix
[0] + y1
* matrix
[2] + matrix
[4],
2040 x0
* matrix
[1] + y1
* matrix
[3] + matrix
[5]);
2045 // the four corner colors are not close enough -- subdivide the
2049 // colors[0] colorM0 colors[2]
2050 // (x0,y0) (xM,y0) (x1,y0)
2051 // +----------+----------+
2054 // color0M | colorMM | color1M
2055 // (x0,yM) +----------+----------+ (x1,yM)
2059 // +----------+----------+
2060 // colors[1] colorM1 colors[3]
2061 // (x0,y1) (xM,y1) (x1,y1)
2063 shading
->getColor(x0
, yM
, &color0M
);
2064 shading
->getColor(x1
, yM
, &color1M
);
2065 shading
->getColor(xM
, y0
, &colorM0
);
2066 shading
->getColor(xM
, y1
, &colorM1
);
2067 shading
->getColor(xM
, yM
, &colorMM
);
2069 // upper-left sub-rectangle
2070 colors2
[0] = colors
[0];
2071 colors2
[1] = color0M
;
2072 colors2
[2] = colorM0
;
2073 colors2
[3] = colorMM
;
2074 doFunctionShFill1(shading
, x0
, y0
, xM
, yM
, colors2
, depth
+ 1);
2076 // lower-left sub-rectangle
2077 colors2
[0] = color0M
;
2078 colors2
[1] = colors
[1];
2079 colors2
[2] = colorMM
;
2080 colors2
[3] = colorM1
;
2081 doFunctionShFill1(shading
, x0
, yM
, xM
, y1
, colors2
, depth
+ 1);
2083 // upper-right sub-rectangle
2084 colors2
[0] = colorM0
;
2085 colors2
[1] = colorMM
;
2086 colors2
[2] = colors
[2];
2087 colors2
[3] = color1M
;
2088 doFunctionShFill1(shading
, xM
, y0
, x1
, yM
, colors2
, depth
+ 1);
2090 // lower-right sub-rectangle
2091 colors2
[0] = colorMM
;
2092 colors2
[1] = colorM1
;
2093 colors2
[2] = color1M
;
2094 colors2
[3] = colors
[3];
2095 doFunctionShFill1(shading
, xM
, yM
, x1
, y1
, colors2
, depth
+ 1);
2099 void Gfx::doAxialShFill(GfxAxialShading
*shading
) {
2100 double xMin
, yMin
, xMax
, yMax
;
2101 double x0
, y0
, x1
, y1
;
2103 GBool dxZero
, dyZero
;
2104 double tMin
, tMax
, t
, tx
, ty
;
2105 double s
[4], sMin
, sMax
, tmp
;
2106 double ux0
, uy0
, ux1
, uy1
, vx0
, vy0
, vx1
, vy1
;
2108 double ta
[axialMaxSplits
+ 1];
2109 int next
[axialMaxSplits
+ 1];
2110 GfxColor color0
, color1
;
2114 if (out
->useShadedFills() &&
2115 out
->axialShadedFill(state
, shading
)) {
2119 // get the clip region bbox
2120 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
2122 // compute min and max t values, based on the four corners of the
2124 shading
->getCoords(&x0
, &y0
, &x1
, &y1
);
2127 dxZero
= fabs(dx
) < 0.01;
2128 dyZero
= fabs(dy
) < 0.01;
2129 if (dxZero
&& dyZero
) {
2132 mul
= 1 / (dx
* dx
+ dy
* dy
);
2133 tMin
= tMax
= ((xMin
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
2134 t
= ((xMin
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
2137 } else if (t
> tMax
) {
2140 t
= ((xMax
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
2143 } else if (t
> tMax
) {
2146 t
= ((xMax
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
2149 } else if (t
> tMax
) {
2152 if (tMin
< 0 && !shading
->getExtend0()) {
2155 if (tMax
> 1 && !shading
->getExtend1()) {
2160 // get the function domain
2161 t0
= shading
->getDomain0();
2162 t1
= shading
->getDomain1();
2164 // Traverse the t axis and do the shading.
2166 // For each point (tx, ty) on the t axis, consider a line through
2167 // that point perpendicular to the t axis:
2169 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
2170 // y(s) = ty + s * dx --> s = (y - ty) / dx
2172 // Then look at the intersection of this line with the bounding box
2173 // (xMin, yMin, xMax, yMax). In the general case, there are four
2174 // intersection points:
2176 // s0 = (xMin - tx) / -dy
2177 // s1 = (xMax - tx) / -dy
2178 // s2 = (yMin - ty) / dx
2179 // s3 = (yMax - ty) / dx
2181 // and we want the middle two s values.
2183 // In the case where dx = 0, take s0 and s1; in the case where dy =
2184 // 0, take s2 and s3.
2186 // Each filled polygon is bounded by two of these line segments
2187 // perpdendicular to the t axis.
2189 // The t axis is bisected into smaller regions until the color
2190 // difference across a region is small enough, and then the region
2191 // is painted with a single color.
2193 // set up: require at least one split to avoid problems when the two
2194 // ends of the t axis have the same color
2195 nComps
= shading
->getColorSpace()->getNComps();
2197 next
[0] = axialMaxSplits
/ 2;
2198 ta
[axialMaxSplits
/ 2] = 0.5 * (tMin
+ tMax
);
2199 next
[axialMaxSplits
/ 2] = axialMaxSplits
;
2200 ta
[axialMaxSplits
] = tMax
;
2202 // compute the color at t = tMin
2205 } else if (tMin
> 1) {
2208 tt
= t0
+ (t1
- t0
) * tMin
;
2210 shading
->getColor(tt
, &color0
);
2212 // compute the coordinates of the point on the t axis at t = tMin;
2213 // then compute the intersection of the perpendicular line with the
2215 tx
= x0
+ tMin
* dx
;
2216 ty
= y0
+ tMin
* dy
;
2217 if (dxZero
&& dyZero
) {
2219 } else if (dxZero
) {
2220 sMin
= (xMin
- tx
) / -dy
;
2221 sMax
= (xMax
- tx
) / -dy
;
2222 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
2223 } else if (dyZero
) {
2224 sMin
= (yMin
- ty
) / dx
;
2225 sMax
= (yMax
- ty
) / dx
;
2226 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
2228 s
[0] = (yMin
- ty
) / dx
;
2229 s
[1] = (yMax
- ty
) / dx
;
2230 s
[2] = (xMin
- tx
) / -dy
;
2231 s
[3] = (xMax
- tx
) / -dy
;
2232 for (j
= 0; j
< 3; ++j
) {
2234 for (k
= j
+ 1; k
< 4; ++k
) {
2239 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
2244 ux0
= tx
- sMin
* dy
;
2245 uy0
= ty
+ sMin
* dx
;
2246 vx0
= tx
- sMax
* dy
;
2247 vy0
= ty
+ sMax
* dx
;
2250 while (i
< axialMaxSplits
) {
2252 // bisect until color difference is small enough or we hit the
2258 } else if (ta
[j
] > 1) {
2261 tt
= t0
+ (t1
- t0
) * ta
[j
];
2263 shading
->getColor(tt
, &color1
);
2264 for (k
= 0; k
< nComps
; ++k
) {
2265 if (abs(color1
.c
[k
] - color0
.c
[k
]) > axialColorDelta
) {
2273 ta
[k
] = 0.5 * (ta
[i
] + ta
[j
]);
2279 // use the average of the colors of the two sides of the region
2280 for (k
= 0; k
< nComps
; ++k
) {
2281 color0
.c
[k
] = (color0
.c
[k
] + color1
.c
[k
]) / 2;
2284 // compute the coordinates of the point on the t axis; then
2285 // compute the intersection of the perpendicular line with the
2287 tx
= x0
+ ta
[j
] * dx
;
2288 ty
= y0
+ ta
[j
] * dy
;
2289 if (dxZero
&& dyZero
) {
2291 } else if (dxZero
) {
2292 sMin
= (xMin
- tx
) / -dy
;
2293 sMax
= (xMax
- tx
) / -dy
;
2294 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
2295 } else if (dyZero
) {
2296 sMin
= (yMin
- ty
) / dx
;
2297 sMax
= (yMax
- ty
) / dx
;
2298 if (sMin
> sMax
) { tmp
= sMin
; sMin
= sMax
; sMax
= tmp
; }
2300 s
[0] = (yMin
- ty
) / dx
;
2301 s
[1] = (yMax
- ty
) / dx
;
2302 s
[2] = (xMin
- tx
) / -dy
;
2303 s
[3] = (xMax
- tx
) / -dy
;
2304 for (j
= 0; j
< 3; ++j
) {
2306 for (k
= j
+ 1; k
< 4; ++k
) {
2311 tmp
= s
[j
]; s
[j
] = s
[kk
]; s
[kk
] = tmp
;
2316 ux1
= tx
- sMin
* dy
;
2317 uy1
= ty
+ sMin
* dx
;
2318 vx1
= tx
- sMax
* dy
;
2319 vy1
= ty
+ sMax
* dx
;
2322 state
->setFillColor(&color0
);
2323 out
->updateFillColor(state
);
2326 state
->moveTo(ux0
, uy0
);
2327 state
->lineTo(vx0
, vy0
);
2328 state
->lineTo(vx1
, vy1
);
2329 state
->lineTo(ux1
, uy1
);
2334 // set up for next region
2344 void Gfx::doRadialShFill(GfxRadialShading
*shading
) {
2345 double xMin
, yMin
, xMax
, yMax
;
2346 double x0
, y0
, r0
, x1
, y1
, r1
, t0
, t1
;
2348 GfxColor colorA
, colorB
;
2349 double xa
, ya
, xb
, yb
, ra
, rb
;
2350 double ta
, tb
, sa
, sb
;
2351 double sz
, xz
, yz
, sMin
, sMax
;
2355 double theta
, alpha
, angle
, t
;
2357 if (out
->useShadedFills() &&
2358 out
->radialShadedFill(state
, shading
)) {
2362 // get the shading info
2363 shading
->getCoords(&x0
, &y0
, &r0
, &x1
, &y1
, &r1
);
2364 t0
= shading
->getDomain0();
2365 t1
= shading
->getDomain1();
2366 nComps
= shading
->getColorSpace()->getNComps();
2368 // Compute the point at which r(s) = 0; check for the enclosed
2369 // circles case; and compute the angles for the tangent lines.
2370 if (x0
== x1
&& y0
== y1
) {
2372 theta
= 0; // make gcc happy
2373 sz
= 0; // make gcc happy
2374 } else if (r0
== r1
) {
2377 sz
= 0; // make gcc happy
2379 sz
= -r0
/ (r1
- r0
);
2380 xz
= x0
+ sz
* (x1
- x0
);
2381 yz
= y0
+ sz
* (y1
- y0
);
2382 enclosed
= (xz
- x0
) * (xz
- x0
) + (yz
- y0
) * (yz
- y0
) <= r0
* r0
;
2383 theta
= asin(r0
/ sqrt((x0
- xz
) * (x0
- xz
) + (y0
- yz
) * (y0
- yz
)));
2391 alpha
= atan2(y1
- y0
, x1
- x0
);
2394 // compute the (possibly extended) s range
2395 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
2402 // solve for x(s) + r(s) = xMin
2403 if ((x1
+ r1
) - (x0
+ r0
) != 0) {
2404 sa
= (xMin
- (x0
+ r0
)) / ((x1
+ r1
) - (x0
+ r0
));
2407 } else if (sa
> sMax
) {
2411 // solve for x(s) - r(s) = xMax
2412 if ((x1
- r1
) - (x0
- r0
) != 0) {
2413 sa
= (xMax
- (x0
- r0
)) / ((x1
- r1
) - (x0
- r0
));
2416 } else if (sa
> sMax
) {
2420 // solve for y(s) + r(s) = yMin
2421 if ((y1
+ r1
) - (y0
+ r0
) != 0) {
2422 sa
= (yMin
- (y0
+ r0
)) / ((y1
+ r1
) - (y0
+ r0
));
2425 } else if (sa
> sMax
) {
2429 // solve for y(s) - r(s) = yMax
2430 if ((y1
- r1
) - (y0
- r0
) != 0) {
2431 sa
= (yMax
- (y0
- r0
)) / ((y1
- r1
) - (y0
- r0
));
2434 } else if (sa
> sMax
) {
2443 } else if (r0
> r1
) {
2448 // check the 'extend' flags
2449 if (!shading
->getExtend0() && sMin
< 0) {
2452 if (!shading
->getExtend1() && sMax
> 1) {
2457 // compute the number of steps into which circles must be divided to
2458 // achieve a curve flatness of 0.1 pixel in device space for the
2459 // largest circle (note that "device space" is 72 dpi when generating
2460 // PostScript, hence the relatively small 0.1 pixel accuracy)
2461 ctm
= state
->getCTM();
2463 if (fabs(ctm
[1]) > t
) {
2466 if (fabs(ctm
[2]) > t
) {
2469 if (fabs(ctm
[3]) > t
) {
2480 n
= (int)(M_PI
/ acos(1 - 0.1 / t
));
2483 } else if (n
> 200) {
2488 // setup for the start circle
2491 ta
= t0
+ sa
* (t1
- t0
);
2492 xa
= x0
+ sa
* (x1
- x0
);
2493 ya
= y0
+ sa
* (y1
- y0
);
2494 ra
= r0
+ sa
* (r1
- r0
);
2496 shading
->getColor(t0
, &colorA
);
2497 } else if (ta
> t1
) {
2498 shading
->getColor(t1
, &colorA
);
2500 shading
->getColor(ta
, &colorA
);
2504 while (ia
< radialMaxSplits
) {
2506 // go as far along the t axis (toward t1) as we can, such that the
2507 // color difference is within the tolerance (radialColorDelta) --
2508 // this uses bisection (between the current value, t, and t1),
2509 // limited to radialMaxSplits points along the t axis; require at
2510 // least one split to avoid problems when the innermost and
2511 // outermost colors are the same
2512 ib
= radialMaxSplits
;
2514 tb
= t0
+ sb
* (t1
- t0
);
2516 shading
->getColor(t0
, &colorB
);
2517 } else if (tb
> t1
) {
2518 shading
->getColor(t1
, &colorB
);
2520 shading
->getColor(tb
, &colorB
);
2522 while (ib
- ia
> 1) {
2523 for (k
= 0; k
< nComps
; ++k
) {
2524 if (abs(colorB
.c
[k
] - colorA
.c
[k
]) > radialColorDelta
) {
2528 if (k
== nComps
&& ib
< radialMaxSplits
) {
2532 sb
= sMin
+ ((double)ib
/ (double)radialMaxSplits
) * (sMax
- sMin
);
2533 tb
= t0
+ sb
* (t1
- t0
);
2535 shading
->getColor(t0
, &colorB
);
2536 } else if (tb
> t1
) {
2537 shading
->getColor(t1
, &colorB
);
2539 shading
->getColor(tb
, &colorB
);
2543 // compute center and radius of the circle
2544 xb
= x0
+ sb
* (x1
- x0
);
2545 yb
= y0
+ sb
* (y1
- y0
);
2546 rb
= r0
+ sb
* (r1
- r0
);
2548 // use the average of the colors at the two circles
2549 for (k
= 0; k
< nComps
; ++k
) {
2550 colorA
.c
[k
] = (colorA
.c
[k
] + colorB
.c
[k
]) / 2;
2552 state
->setFillColor(&colorA
);
2553 out
->updateFillColor(state
);
2557 // construct path for first circle (counterclockwise)
2558 state
->moveTo(xa
+ ra
, ya
);
2559 for (k
= 1; k
< n
; ++k
) {
2560 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2561 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2565 // construct and append path for second circle (clockwise)
2566 state
->moveTo(xb
+ rb
, yb
);
2567 for (k
= 1; k
< n
; ++k
) {
2568 angle
= -((double)k
/ (double)n
) * 2 * M_PI
;
2569 state
->lineTo(xb
+ rb
* cos(angle
), yb
+ rb
* sin(angle
));
2575 // construct the first subpath (clockwise)
2576 state
->moveTo(xa
+ ra
* cos(alpha
+ theta
+ 0.5 * M_PI
),
2577 ya
+ ra
* sin(alpha
+ theta
+ 0.5 * M_PI
));
2578 for (k
= 0; k
< n
; ++k
) {
2579 angle
= alpha
+ theta
+ 0.5 * M_PI
2580 - ((double)k
/ (double)n
) * (2 * theta
+ M_PI
);
2581 state
->lineTo(xb
+ rb
* cos(angle
), yb
+ rb
* sin(angle
));
2583 for (k
= 0; k
< n
; ++k
) {
2584 angle
= alpha
- theta
- 0.5 * M_PI
2585 + ((double)k
/ (double)n
) * (2 * theta
- M_PI
);
2586 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2590 // construct the second subpath (counterclockwise)
2591 state
->moveTo(xa
+ ra
* cos(alpha
+ theta
+ 0.5 * M_PI
),
2592 ya
+ ra
* sin(alpha
+ theta
+ 0.5 * M_PI
));
2593 for (k
= 0; k
< n
; ++k
) {
2594 angle
= alpha
+ theta
+ 0.5 * M_PI
2595 + ((double)k
/ (double)n
) * (-2 * theta
+ M_PI
);
2596 state
->lineTo(xb
+ rb
* cos(angle
), yb
+ rb
* sin(angle
));
2598 for (k
= 0; k
< n
; ++k
) {
2599 angle
= alpha
- theta
- 0.5 * M_PI
2600 + ((double)k
/ (double)n
) * (2 * theta
+ M_PI
);
2601 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2610 // step to the next value of t
2621 // extend the smaller circle
2622 if ((shading
->getExtend0() && r0
<= r1
) ||
2623 (shading
->getExtend1() && r1
< r0
)) {
2635 shading
->getColor(ta
, &colorA
);
2636 state
->setFillColor(&colorA
);
2637 out
->updateFillColor(state
);
2638 state
->moveTo(xa
+ ra
, ya
);
2639 for (k
= 1; k
< n
; ++k
) {
2640 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2641 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2648 // extend the larger circle
2649 if ((shading
->getExtend0() && r0
> r1
) ||
2650 (shading
->getExtend1() && r1
>= r0
)) {
2662 shading
->getColor(ta
, &colorA
);
2663 state
->setFillColor(&colorA
);
2664 out
->updateFillColor(state
);
2665 state
->moveTo(xMin
, yMin
);
2666 state
->lineTo(xMin
, yMax
);
2667 state
->lineTo(xMax
, yMax
);
2668 state
->lineTo(xMax
, yMin
);
2670 state
->moveTo(xa
+ ra
, ya
);
2671 for (k
= 1; k
< n
; ++k
) {
2672 angle
= ((double)k
/ (double)n
) * 2 * M_PI
;
2673 state
->lineTo(xa
+ ra
* cos(angle
), ya
+ ra
* sin(angle
));
2682 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading
*shading
) {
2683 double x0
, y0
, x1
, y1
, x2
, y2
;
2684 GfxColor color0
, color1
, color2
;
2687 for (i
= 0; i
< shading
->getNTriangles(); ++i
) {
2688 shading
->getTriangle(i
, &x0
, &y0
, &color0
,
2691 gouraudFillTriangle(x0
, y0
, &color0
, x1
, y1
, &color1
, x2
, y2
, &color2
,
2692 shading
->getColorSpace()->getNComps(), 0);
2696 void Gfx::gouraudFillTriangle(double x0
, double y0
, GfxColor
*color0
,
2697 double x1
, double y1
, GfxColor
*color1
,
2698 double x2
, double y2
, GfxColor
*color2
,
2699 int nComps
, int depth
) {
2700 double x01
, y01
, x12
, y12
, x20
, y20
;
2701 GfxColor color01
, color12
, color20
;
2704 for (i
= 0; i
< nComps
; ++i
) {
2705 if (abs(color0
->c
[i
] - color1
->c
[i
]) > gouraudColorDelta
||
2706 abs(color1
->c
[i
] - color2
->c
[i
]) > gouraudColorDelta
) {
2710 if (i
== nComps
|| depth
== gouraudMaxDepth
) {
2711 state
->setFillColor(color0
);
2712 out
->updateFillColor(state
);
2713 state
->moveTo(x0
, y0
);
2714 state
->lineTo(x1
, y1
);
2715 state
->lineTo(x2
, y2
);
2720 x01
= 0.5 * (x0
+ x1
);
2721 y01
= 0.5 * (y0
+ y1
);
2722 x12
= 0.5 * (x1
+ x2
);
2723 y12
= 0.5 * (y1
+ y2
);
2724 x20
= 0.5 * (x2
+ x0
);
2725 y20
= 0.5 * (y2
+ y0
);
2726 //~ if the shading has a Function, this should interpolate on the
2727 //~ function parameter, not on the color components
2728 for (i
= 0; i
< nComps
; ++i
) {
2729 color01
.c
[i
] = (color0
->c
[i
] + color1
->c
[i
]) / 2;
2730 color12
.c
[i
] = (color1
->c
[i
] + color2
->c
[i
]) / 2;
2731 color20
.c
[i
] = (color2
->c
[i
] + color0
->c
[i
]) / 2;
2733 gouraudFillTriangle(x0
, y0
, color0
, x01
, y01
, &color01
,
2734 x20
, y20
, &color20
, nComps
, depth
+ 1);
2735 gouraudFillTriangle(x01
, y01
, &color01
, x1
, y1
, color1
,
2736 x12
, y12
, &color12
, nComps
, depth
+ 1);
2737 gouraudFillTriangle(x01
, y01
, &color01
, x12
, y12
, &color12
,
2738 x20
, y20
, &color20
, nComps
, depth
+ 1);
2739 gouraudFillTriangle(x20
, y20
, &color20
, x12
, y12
, &color12
,
2740 x2
, y2
, color2
, nComps
, depth
+ 1);
2744 void Gfx::doPatchMeshShFill(GfxPatchMeshShading
*shading
) {
2747 if (shading
->getNPatches() > 128) {
2749 } else if (shading
->getNPatches() > 64) {
2751 } else if (shading
->getNPatches() > 16) {
2756 for (i
= 0; i
< shading
->getNPatches(); ++i
) {
2757 fillPatch(shading
->getPatch(i
), shading
->getColorSpace()->getNComps(),
2762 void Gfx::fillPatch(GfxPatch
*patch
, int nComps
, int depth
) {
2763 GfxPatch patch00
, patch01
, patch10
, patch11
;
2764 double xx
[4][8], yy
[4][8];
2768 for (i
= 0; i
< nComps
; ++i
) {
2769 if (abs(patch
->color
[0][0].c
[i
] - patch
->color
[0][1].c
[i
])
2770 > patchColorDelta
||
2771 abs(patch
->color
[0][1].c
[i
] - patch
->color
[1][1].c
[i
])
2772 > patchColorDelta
||
2773 abs(patch
->color
[1][1].c
[i
] - patch
->color
[1][0].c
[i
])
2774 > patchColorDelta
||
2775 abs(patch
->color
[1][0].c
[i
] - patch
->color
[0][0].c
[i
])
2776 > patchColorDelta
) {
2780 if (i
== nComps
|| depth
== patchMaxDepth
) {
2781 state
->setFillColor(&patch
->color
[0][0]);
2782 out
->updateFillColor(state
);
2783 state
->moveTo(patch
->x
[0][0], patch
->y
[0][0]);
2784 state
->curveTo(patch
->x
[0][1], patch
->y
[0][1],
2785 patch
->x
[0][2], patch
->y
[0][2],
2786 patch
->x
[0][3], patch
->y
[0][3]);
2787 state
->curveTo(patch
->x
[1][3], patch
->y
[1][3],
2788 patch
->x
[2][3], patch
->y
[2][3],
2789 patch
->x
[3][3], patch
->y
[3][3]);
2790 state
->curveTo(patch
->x
[3][2], patch
->y
[3][2],
2791 patch
->x
[3][1], patch
->y
[3][1],
2792 patch
->x
[3][0], patch
->y
[3][0]);
2793 state
->curveTo(patch
->x
[2][0], patch
->y
[2][0],
2794 patch
->x
[1][0], patch
->y
[1][0],
2795 patch
->x
[0][0], patch
->y
[0][0]);
2800 for (i
= 0; i
< 4; ++i
) {
2801 xx
[i
][0] = patch
->x
[i
][0];
2802 yy
[i
][0] = patch
->y
[i
][0];
2803 xx
[i
][1] = 0.5 * (patch
->x
[i
][0] + patch
->x
[i
][1]);
2804 yy
[i
][1] = 0.5 * (patch
->y
[i
][0] + patch
->y
[i
][1]);
2805 xxm
= 0.5 * (patch
->x
[i
][1] + patch
->x
[i
][2]);
2806 yym
= 0.5 * (patch
->y
[i
][1] + patch
->y
[i
][2]);
2807 xx
[i
][6] = 0.5 * (patch
->x
[i
][2] + patch
->x
[i
][3]);
2808 yy
[i
][6] = 0.5 * (patch
->y
[i
][2] + patch
->y
[i
][3]);
2809 xx
[i
][2] = 0.5 * (xx
[i
][1] + xxm
);
2810 yy
[i
][2] = 0.5 * (yy
[i
][1] + yym
);
2811 xx
[i
][5] = 0.5 * (xxm
+ xx
[i
][6]);
2812 yy
[i
][5] = 0.5 * (yym
+ yy
[i
][6]);
2813 xx
[i
][3] = xx
[i
][4] = 0.5 * (xx
[i
][2] + xx
[i
][5]);
2814 yy
[i
][3] = yy
[i
][4] = 0.5 * (yy
[i
][2] + yy
[i
][5]);
2815 xx
[i
][7] = patch
->x
[i
][3];
2816 yy
[i
][7] = patch
->y
[i
][3];
2818 for (i
= 0; i
< 4; ++i
) {
2819 patch00
.x
[0][i
] = xx
[0][i
];
2820 patch00
.y
[0][i
] = yy
[0][i
];
2821 patch00
.x
[1][i
] = 0.5 * (xx
[0][i
] + xx
[1][i
]);
2822 patch00
.y
[1][i
] = 0.5 * (yy
[0][i
] + yy
[1][i
]);
2823 xxm
= 0.5 * (xx
[1][i
] + xx
[2][i
]);
2824 yym
= 0.5 * (yy
[1][i
] + yy
[2][i
]);
2825 patch10
.x
[2][i
] = 0.5 * (xx
[2][i
] + xx
[3][i
]);
2826 patch10
.y
[2][i
] = 0.5 * (yy
[2][i
] + yy
[3][i
]);
2827 patch00
.x
[2][i
] = 0.5 * (patch00
.x
[1][i
] + xxm
);
2828 patch00
.y
[2][i
] = 0.5 * (patch00
.y
[1][i
] + yym
);
2829 patch10
.x
[1][i
] = 0.5 * (xxm
+ patch10
.x
[2][i
]);
2830 patch10
.y
[1][i
] = 0.5 * (yym
+ patch10
.y
[2][i
]);
2831 patch00
.x
[3][i
] = 0.5 * (patch00
.x
[2][i
] + patch10
.x
[1][i
]);
2832 patch00
.y
[3][i
] = 0.5 * (patch00
.y
[2][i
] + patch10
.y
[1][i
]);
2833 patch10
.x
[0][i
] = patch00
.x
[3][i
];
2834 patch10
.y
[0][i
] = patch00
.y
[3][i
];
2835 patch10
.x
[3][i
] = xx
[3][i
];
2836 patch10
.y
[3][i
] = yy
[3][i
];
2838 for (i
= 4; i
< 8; ++i
) {
2839 patch01
.x
[0][i
-4] = xx
[0][i
];
2840 patch01
.y
[0][i
-4] = yy
[0][i
];
2841 patch01
.x
[1][i
-4] = 0.5 * (xx
[0][i
] + xx
[1][i
]);
2842 patch01
.y
[1][i
-4] = 0.5 * (yy
[0][i
] + yy
[1][i
]);
2843 xxm
= 0.5 * (xx
[1][i
] + xx
[2][i
]);
2844 yym
= 0.5 * (yy
[1][i
] + yy
[2][i
]);
2845 patch11
.x
[2][i
-4] = 0.5 * (xx
[2][i
] + xx
[3][i
]);
2846 patch11
.y
[2][i
-4] = 0.5 * (yy
[2][i
] + yy
[3][i
]);
2847 patch01
.x
[2][i
-4] = 0.5 * (patch01
.x
[1][i
-4] + xxm
);
2848 patch01
.y
[2][i
-4] = 0.5 * (patch01
.y
[1][i
-4] + yym
);
2849 patch11
.x
[1][i
-4] = 0.5 * (xxm
+ patch11
.x
[2][i
-4]);
2850 patch11
.y
[1][i
-4] = 0.5 * (yym
+ patch11
.y
[2][i
-4]);
2851 patch01
.x
[3][i
-4] = 0.5 * (patch01
.x
[2][i
-4] + patch11
.x
[1][i
-4]);
2852 patch01
.y
[3][i
-4] = 0.5 * (patch01
.y
[2][i
-4] + patch11
.y
[1][i
-4]);
2853 patch11
.x
[0][i
-4] = patch01
.x
[3][i
-4];
2854 patch11
.y
[0][i
-4] = patch01
.y
[3][i
-4];
2855 patch11
.x
[3][i
-4] = xx
[3][i
];
2856 patch11
.y
[3][i
-4] = yy
[3][i
];
2858 //~ if the shading has a Function, this should interpolate on the
2859 //~ function parameter, not on the color components
2860 for (i
= 0; i
< nComps
; ++i
) {
2861 patch00
.color
[0][0].c
[i
] = patch
->color
[0][0].c
[i
];
2862 patch00
.color
[0][1].c
[i
] = (patch
->color
[0][0].c
[i
] +
2863 patch
->color
[0][1].c
[i
]) / 2;
2864 patch01
.color
[0][0].c
[i
] = patch00
.color
[0][1].c
[i
];
2865 patch01
.color
[0][1].c
[i
] = patch
->color
[0][1].c
[i
];
2866 patch01
.color
[1][1].c
[i
] = (patch
->color
[0][1].c
[i
] +
2867 patch
->color
[1][1].c
[i
]) / 2;
2868 patch11
.color
[0][1].c
[i
] = patch01
.color
[1][1].c
[i
];
2869 patch11
.color
[1][1].c
[i
] = patch
->color
[1][1].c
[i
];
2870 patch11
.color
[1][0].c
[i
] = (patch
->color
[1][1].c
[i
] +
2871 patch
->color
[1][0].c
[i
]) / 2;
2872 patch10
.color
[1][1].c
[i
] = patch11
.color
[1][0].c
[i
];
2873 patch10
.color
[1][0].c
[i
] = patch
->color
[1][0].c
[i
];
2874 patch10
.color
[0][0].c
[i
] = (patch
->color
[1][0].c
[i
] +
2875 patch
->color
[0][0].c
[i
]) / 2;
2876 patch00
.color
[1][0].c
[i
] = patch10
.color
[0][0].c
[i
];
2877 patch00
.color
[1][1].c
[i
] = (patch00
.color
[1][0].c
[i
] +
2878 patch01
.color
[1][1].c
[i
]) / 2;
2879 patch01
.color
[1][0].c
[i
] = patch00
.color
[1][1].c
[i
];
2880 patch11
.color
[0][0].c
[i
] = patch00
.color
[1][1].c
[i
];
2881 patch10
.color
[0][1].c
[i
] = patch00
.color
[1][1].c
[i
];
2883 fillPatch(&patch00
, nComps
, depth
+ 1);
2884 fillPatch(&patch10
, nComps
, depth
+ 1);
2885 fillPatch(&patch01
, nComps
, depth
+ 1);
2886 fillPatch(&patch11
, nComps
, depth
+ 1);
2890 void Gfx::doEndPath() {
2891 if (state
->isCurPt() && clip
!= clipNone
) {
2893 if (clip
== clipNormal
) {
2903 //------------------------------------------------------------------------
2904 // path clipping operators
2905 //------------------------------------------------------------------------
2907 void Gfx::opClip(xObject args
[], int numArgs
) {
2911 void Gfx::opEOClip(xObject args
[], int numArgs
) {
2915 //------------------------------------------------------------------------
2916 // text xObject operators
2917 //------------------------------------------------------------------------
2919 void Gfx::opBeginText(xObject args
[], int numArgs
) {
2920 state
->setTextMat(1, 0, 0, 1, 0, 0);
2921 state
->textMoveTo(0, 0);
2922 out
->updateTextMat(state
);
2923 out
->updateTextPos(state
);
2924 fontChanged
= gTrue
;
2927 void Gfx::opEndText(xObject args
[], int numArgs
) {
2928 out
->endTextObject(state
);
2931 //------------------------------------------------------------------------
2932 // text state operators
2933 //------------------------------------------------------------------------
2935 void Gfx::opSetCharSpacing(xObject args
[], int numArgs
) {
2936 state
->setCharSpace(args
[0].getNum());
2937 out
->updateCharSpace(state
);
2940 void Gfx::opSetFont(xObject args
[], int numArgs
) {
2943 if (!(font
= res
->lookupFont(args
[0].getName()))) {
2946 if (printCommands
) {
2947 printf(" font: tag=%s name='%s' %g\n",
2948 font
->getTag()->getCString(),
2949 font
->getName() ? font
->getName()->getCString() : "???",
2953 state
->setFont(font
, args
[1].getNum());
2954 fontChanged
= gTrue
;
2957 void Gfx::opSetTextLeading(xObject args
[], int numArgs
) {
2958 state
->setLeading(args
[0].getNum());
2961 void Gfx::opSetTextRender(xObject args
[], int numArgs
) {
2962 state
->setRender(args
[0].getInt());
2963 out
->updateRender(state
);
2966 void Gfx::opSetTextRise(xObject args
[], int numArgs
) {
2967 state
->setRise(args
[0].getNum());
2968 out
->updateRise(state
);
2971 void Gfx::opSetWordSpacing(xObject args
[], int numArgs
) {
2972 state
->setWordSpace(args
[0].getNum());
2973 out
->updateWordSpace(state
);
2976 void Gfx::opSetHorizScaling(xObject args
[], int numArgs
) {
2977 state
->setHorizScaling(args
[0].getNum());
2978 out
->updateHorizScaling(state
);
2979 fontChanged
= gTrue
;
2982 //------------------------------------------------------------------------
2983 // text positioning operators
2984 //------------------------------------------------------------------------
2986 void Gfx::opTextMove(xObject args
[], int numArgs
) {
2989 tx
= state
->getLineX() + args
[0].getNum();
2990 ty
= state
->getLineY() + args
[1].getNum();
2991 state
->textMoveTo(tx
, ty
);
2992 out
->updateTextPos(state
);
2995 void Gfx::opTextMoveSet(xObject args
[], int numArgs
) {
2998 tx
= state
->getLineX() + args
[0].getNum();
2999 ty
= args
[1].getNum();
3000 state
->setLeading(-ty
);
3001 ty
+= state
->getLineY();
3002 state
->textMoveTo(tx
, ty
);
3003 out
->updateTextPos(state
);
3006 void Gfx::opSetTextMatrix(xObject args
[], int numArgs
) {
3007 state
->setTextMat(args
[0].getNum(), args
[1].getNum(),
3008 args
[2].getNum(), args
[3].getNum(),
3009 args
[4].getNum(), args
[5].getNum());
3010 state
->textMoveTo(0, 0);
3011 out
->updateTextMat(state
);
3012 out
->updateTextPos(state
);
3013 fontChanged
= gTrue
;
3016 void Gfx::opTextNextLine(xObject args
[], int numArgs
) {
3019 tx
= state
->getLineX();
3020 ty
= state
->getLineY() - state
->getLeading();
3021 state
->textMoveTo(tx
, ty
);
3022 out
->updateTextPos(state
);
3025 //------------------------------------------------------------------------
3026 // text string operators
3027 //------------------------------------------------------------------------
3029 void Gfx::opShowText(xObject args
[], int numArgs
) {
3030 if (!state
->getFont()) {
3031 error(getPos(), "No font in show");
3035 out
->updateFont(state
);
3036 fontChanged
= gFalse
;
3038 out
->beginStringOp(state
);
3039 doShowText(args
[0].getString());
3040 out
->endStringOp(state
);
3043 void Gfx::opMoveShowText(xObject args
[], int numArgs
) {
3046 if (!state
->getFont()) {
3047 error(getPos(), "No font in move/show");
3051 out
->updateFont(state
);
3052 fontChanged
= gFalse
;
3054 tx
= state
->getLineX();
3055 ty
= state
->getLineY() - state
->getLeading();
3056 state
->textMoveTo(tx
, ty
);
3057 out
->updateTextPos(state
);
3058 out
->beginStringOp(state
);
3059 doShowText(args
[0].getString());
3060 out
->endStringOp(state
);
3063 void Gfx::opMoveSetShowText(xObject args
[], int numArgs
) {
3066 if (!state
->getFont()) {
3067 error(getPos(), "No font in move/set/show");
3071 out
->updateFont(state
);
3072 fontChanged
= gFalse
;
3074 state
->setWordSpace(args
[0].getNum());
3075 state
->setCharSpace(args
[1].getNum());
3076 tx
= state
->getLineX();
3077 ty
= state
->getLineY() - state
->getLeading();
3078 state
->textMoveTo(tx
, ty
);
3079 out
->updateWordSpace(state
);
3080 out
->updateCharSpace(state
);
3081 out
->updateTextPos(state
);
3082 out
->beginStringOp(state
);
3083 doShowText(args
[2].getString());
3084 out
->endStringOp(state
);
3087 void Gfx::opShowSpaceText(xObject args
[], int numArgs
) {
3093 if (!state
->getFont()) {
3094 error(getPos(), "No font in show/space");
3098 out
->updateFont(state
);
3099 fontChanged
= gFalse
;
3101 out
->beginStringOp(state
);
3102 wMode
= state
->getFont()->getWMode();
3103 a
= args
[0].getArray();
3104 for (i
= 0; i
< a
->getLength(); ++i
) {
3107 // this uses the absolute value of the font size to match
3108 // Acrobat's behavior
3110 state
->textShift(0, -obj
.getNum() * 0.001 *
3111 fabs(state
->getFontSize()));
3113 state
->textShift(-obj
.getNum() * 0.001 *
3114 fabs(state
->getFontSize()), 0);
3116 out
->updateTextShift(state
, obj
.getNum());
3117 } else if (obj
.isString()) {
3118 doShowText(obj
.getString());
3120 error(getPos(), "Element of show/space array must be number or string");
3124 out
->endStringOp(state
);
3127 void Gfx::doShowText(GString
*s
) {
3130 double riseX
, riseY
;
3133 double x
, y
, dx
, dy
, dx2
, dy2
, curX
, curY
, tdx
, tdy
, lineX
, lineY
;
3134 double originX
, originY
, tOriginX
, tOriginY
;
3135 double oldCTM
[6], newCTM
[6];
3141 int len
, n
, uLen
, nChars
, nSpaces
, i
;
3143 font
= state
->getFont();
3144 wMode
= font
->getWMode();
3146 if (out
->useDrawChar()) {
3147 out
->beginString(state
, s
);
3150 // handle a Type 3 char
3151 if (font
->getType() == fontType3
&& out
->interpretType3Chars()) {
3152 mat
= state
->getCTM();
3153 for (i
= 0; i
< 6; ++i
) {
3156 mat
= state
->getTextMat();
3157 newCTM
[0] = mat
[0] * oldCTM
[0] + mat
[1] * oldCTM
[2];
3158 newCTM
[1] = mat
[0] * oldCTM
[1] + mat
[1] * oldCTM
[3];
3159 newCTM
[2] = mat
[2] * oldCTM
[0] + mat
[3] * oldCTM
[2];
3160 newCTM
[3] = mat
[2] * oldCTM
[1] + mat
[3] * oldCTM
[3];
3161 mat
= font
->getFontMatrix();
3162 newCTM
[0] = mat
[0] * newCTM
[0] + mat
[1] * newCTM
[2];
3163 newCTM
[1] = mat
[0] * newCTM
[1] + mat
[1] * newCTM
[3];
3164 newCTM
[2] = mat
[2] * newCTM
[0] + mat
[3] * newCTM
[2];
3165 newCTM
[3] = mat
[2] * newCTM
[1] + mat
[3] * newCTM
[3];
3166 newCTM
[0] *= state
->getFontSize();
3167 newCTM
[1] *= state
->getFontSize();
3168 newCTM
[2] *= state
->getFontSize();
3169 newCTM
[3] *= state
->getFontSize();
3170 newCTM
[0] *= state
->getHorizScaling();
3171 newCTM
[2] *= state
->getHorizScaling();
3172 state
->textTransformDelta(0, state
->getRise(), &riseX
, &riseY
);
3173 curX
= state
->getCurX();
3174 curY
= state
->getCurY();
3175 lineX
= state
->getLineX();
3176 lineY
= state
->getLineY();
3178 p
= s
->getCString();
3179 len
= s
->getLength();
3181 n
= font
->getNextChar(p
, len
, &code
,
3182 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
3183 &dx
, &dy
, &originX
, &originY
);
3184 dx
= dx
* state
->getFontSize() + state
->getCharSpace();
3185 if (n
== 1 && *p
== ' ') {
3186 dx
+= state
->getWordSpace();
3188 dx
*= state
->getHorizScaling();
3189 dy
*= state
->getFontSize();
3190 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
3191 state
->transform(curX
+ riseX
, curY
+ riseY
, &x
, &y
);
3193 state
->setCTM(newCTM
[0], newCTM
[1], newCTM
[2], newCTM
[3], x
, y
);
3194 //~ the CTM concat values here are wrong (but never used)
3195 out
->updateCTM(state
, 1, 0, 0, 1, 0, 0);
3196 if (!out
->beginType3Char(state
, curX
+ riseX
, curY
+ riseY
, tdx
, tdy
,
3198 ((Gfx8BitFont
*)font
)->getCharProc(code
, &charProc
);
3199 if ((resDict
= ((Gfx8BitFont
*)font
)->getResources())) {
3200 pushResources(resDict
);
3202 if (charProc
.isStream()) {
3203 display(&charProc
, gFalse
);
3205 error(getPos(), "Missing or bad Type3 CharProc entry");
3207 out
->endType3Char(state
);
3214 // GfxState::restore() does *not* restore the current position,
3215 // so we deal with it here using (curX, curY) and (lineX, lineY)
3218 state
->moveTo(curX
, curY
);
3219 state
->textSetPos(lineX
, lineY
);
3225 } else if (out
->useDrawChar()) {
3226 state
->textTransformDelta(0, state
->getRise(), &riseX
, &riseY
);
3227 p
= s
->getCString();
3228 len
= s
->getLength();
3230 n
= font
->getNextChar(p
, len
, &code
,
3231 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
3232 &dx
, &dy
, &originX
, &originY
);
3234 dx
*= state
->getFontSize();
3235 dy
= dy
* state
->getFontSize() + state
->getCharSpace();
3236 if (n
== 1 && *p
== ' ') {
3237 dy
+= state
->getWordSpace();
3240 dx
= dx
* state
->getFontSize() + state
->getCharSpace();
3241 if (n
== 1 && *p
== ' ') {
3242 dx
+= state
->getWordSpace();
3244 dx
*= state
->getHorizScaling();
3245 dy
*= state
->getFontSize();
3247 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
3248 originX
*= state
->getFontSize();
3249 originY
*= state
->getFontSize();
3250 state
->textTransformDelta(originX
, originY
, &tOriginX
, &tOriginY
);
3251 out
->drawChar(state
, state
->getCurX() + riseX
, state
->getCurY() + riseY
,
3252 tdx
, tdy
, tOriginX
, tOriginY
, code
, n
, u
, uLen
);
3253 state
->shift(tdx
, tdy
);
3260 p
= s
->getCString();
3261 len
= s
->getLength();
3262 nChars
= nSpaces
= 0;
3264 n
= font
->getNextChar(p
, len
, &code
,
3265 u
, (int)(sizeof(u
) / sizeof(Unicode
)), &uLen
,
3266 &dx2
, &dy2
, &originX
, &originY
);
3269 if (n
== 1 && *p
== ' ') {
3277 dx
*= state
->getFontSize();
3278 dy
= dy
* state
->getFontSize()
3279 + nChars
* state
->getCharSpace()
3280 + nSpaces
* state
->getWordSpace();
3282 dx
= dx
* state
->getFontSize()
3283 + nChars
* state
->getCharSpace()
3284 + nSpaces
* state
->getWordSpace();
3285 dx
*= state
->getHorizScaling();
3286 dy
*= state
->getFontSize();
3288 state
->textTransformDelta(dx
, dy
, &tdx
, &tdy
);
3289 out
->drawString(state
, s
);
3290 state
->shift(tdx
, tdy
);
3293 if (out
->useDrawChar()) {
3294 out
->endString(state
);
3297 updateLevel
+= 10 * s
->getLength();
3300 //------------------------------------------------------------------------
3301 // XObject operators
3302 //------------------------------------------------------------------------
3304 void Gfx::opXObject(xObject args
[], int numArgs
) {
3306 xObject obj1
, obj2
, obj3
, refObj
;
3311 name
= args
[0].getName();
3312 if (!res
->lookupXObject(name
, &obj1
)) {
3315 if (!obj1
.isStream()) {
3316 error(getPos(), "XObject '%s' is wrong type", name
);
3321 obj1
.streamGetDict()->lookup("OPI", &opiDict
);
3322 if (opiDict
.isDict()) {
3323 out
->opiBegin(state
, opiDict
.getDict());
3326 obj1
.streamGetDict()->lookup("Subtype", &obj2
);
3327 if (obj2
.isName("Image")) {
3328 if (out
->needNonText()) {
3329 res
->lookupXObjectNF(name
, &refObj
);
3330 doImage(&refObj
, obj1
.getStream(), gFalse
);
3333 } else if (obj2
.isName("Form")) {
3334 res
->lookupXObjectNF(name
, &refObj
);
3335 if (out
->useDrawForm() && refObj
.isRef()) {
3336 out
->drawForm(refObj
.getRef());
3341 } else if (obj2
.isName("PS")) {
3342 obj1
.streamGetDict()->lookup("Level1", &obj3
);
3343 out
->psXObject(obj1
.getStream(),
3344 obj3
.isStream() ? obj3
.getStream() : (Stream
*)NULL
);
3345 } else if (obj2
.isName()) {
3346 error(getPos(), "Unknown XObject subtype '%s'", obj2
.getName());
3348 error(getPos(), "XObject subtype is missing or wrong type");
3352 if (opiDict
.isDict()) {
3353 out
->opiEnd(state
, opiDict
.getDict());
3360 void Gfx::doImage(xObject
*ref
, Stream
*str
, GBool inlineImg
) {
3361 Dict
*dict
, *maskDict
;
3364 StreamColorSpaceMode csMode
;
3367 GfxColorSpace
*colorSpace
, *maskColorSpace
;
3368 GfxImageColorMap
*colorMap
, *maskColorMap
;
3369 xObject maskObj
, smaskObj
;
3370 GBool haveColorKeyMask
, haveExplicitMask
, haveSoftMask
;
3371 int maskColors
[2*gfxColorMaxComps
];
3372 int maskWidth
, maskHeight
;
3378 // get info from the stream
3380 csMode
= streamCSNone
;
3381 str
->getImageParams(&bits
, &csMode
);
3384 dict
= str
->getDict();
3387 dict
->lookup("Width", &obj1
);
3388 if (obj1
.isNull()) {
3390 dict
->lookup("W", &obj1
);
3394 width
= obj1
.getInt();
3396 dict
->lookup("Height", &obj1
);
3397 if (obj1
.isNull()) {
3399 dict
->lookup("H", &obj1
);
3403 height
= obj1
.getInt();
3407 dict
->lookup("ImageMask", &obj1
);
3408 if (obj1
.isNull()) {
3410 dict
->lookup("IM", &obj1
);
3414 mask
= obj1
.getBool();
3415 else if (!obj1
.isNull())
3421 dict
->lookup("BitsPerComponent", &obj1
);
3422 if (obj1
.isNull()) {
3424 dict
->lookup("BPC", &obj1
);
3427 bits
= obj1
.getInt();
3439 // check for inverted mask
3443 dict
->lookup("Decode", &obj1
);
3444 if (obj1
.isNull()) {
3446 dict
->lookup("D", &obj1
);
3448 if (obj1
.isArray()) {
3449 obj1
.arrayGet(0, &obj2
);
3450 if (obj2
.isInt() && obj2
.getInt() == 1)
3453 } else if (!obj1
.isNull()) {
3459 out
->drawImageMask(state
, ref
, str
, width
, height
, invert
, inlineImg
);
3463 // get color space and color map
3464 dict
->lookup("ColorSpace", &obj1
);
3465 if (obj1
.isNull()) {
3467 dict
->lookup("CS", &obj1
);
3469 if (obj1
.isName()) {
3470 res
->lookupColorSpace(obj1
.getName(), &obj2
);
3471 if (!obj2
.isNull()) {
3478 if (!obj1
.isNull()) {
3479 colorSpace
= GfxColorSpace::parse(&obj1
);
3480 } else if (csMode
== streamCSDeviceGray
) {
3481 colorSpace
= new GfxDeviceGrayColorSpace();
3482 } else if (csMode
== streamCSDeviceRGB
) {
3483 colorSpace
= new GfxDeviceRGBColorSpace();
3484 } else if (csMode
== streamCSDeviceCMYK
) {
3485 colorSpace
= new GfxDeviceCMYKColorSpace();
3493 dict
->lookup("Decode", &obj1
);
3494 if (obj1
.isNull()) {
3496 dict
->lookup("D", &obj1
);
3498 colorMap
= new GfxImageColorMap(bits
, &obj1
, colorSpace
);
3500 if (!colorMap
->isOk()) {
3506 haveColorKeyMask
= haveExplicitMask
= haveSoftMask
= gFalse
;
3507 maskStr
= NULL
; // make gcc happy
3508 maskWidth
= maskHeight
= 0; // make gcc happy
3509 maskInvert
= gFalse
; // make gcc happy
3510 maskColorMap
= NULL
; // make gcc happy
3511 dict
->lookup("Mask", &maskObj
);
3512 dict
->lookup("SMask", &smaskObj
);
3513 if (smaskObj
.isStream()) {
3518 maskStr
= smaskObj
.getStream();
3519 maskDict
= smaskObj
.streamGetDict();
3520 maskDict
->lookup("Width", &obj1
);
3521 if (obj1
.isNull()) {
3523 maskDict
->lookup("W", &obj1
);
3525 if (!obj1
.isInt()) {
3528 maskWidth
= obj1
.getInt();
3530 maskDict
->lookup("Height", &obj1
);
3531 if (obj1
.isNull()) {
3533 maskDict
->lookup("H", &obj1
);
3535 if (!obj1
.isInt()) {
3538 maskHeight
= obj1
.getInt();
3540 maskDict
->lookup("BitsPerComponent", &obj1
);
3541 if (obj1
.isNull()) {
3543 maskDict
->lookup("BPC", &obj1
);
3545 if (!obj1
.isInt()) {
3548 maskBits
= obj1
.getInt();
3550 maskDict
->lookup("ColorSpace", &obj1
);
3551 if (obj1
.isNull()) {
3553 maskDict
->lookup("CS", &obj1
);
3555 if (obj1
.isName()) {
3556 res
->lookupColorSpace(obj1
.getName(), &obj2
);
3557 if (!obj2
.isNull()) {
3564 maskColorSpace
= GfxColorSpace::parse(&obj1
);
3566 if (!maskColorSpace
|| maskColorSpace
->getMode() != csDeviceGray
) {
3569 maskDict
->lookup("Decode", &obj1
);
3570 if (obj1
.isNull()) {
3572 maskDict
->lookup("D", &obj1
);
3574 maskColorMap
= new GfxImageColorMap(maskBits
, &obj1
, maskColorSpace
);
3576 if (!maskColorMap
->isOk()) {
3577 delete maskColorMap
;
3580 //~ handle the Matte entry
3581 haveSoftMask
= gTrue
;
3582 } else if (maskObj
.isArray()) {
3585 i
< maskObj
.arrayGetLength() && i
< 2*gfxColorMaxComps
;
3587 maskObj
.arrayGet(i
, &obj1
);
3588 maskColors
[i
] = obj1
.getInt();
3591 haveColorKeyMask
= gTrue
;
3592 } else if (maskObj
.isStream()) {
3597 maskStr
= maskObj
.getStream();
3598 maskDict
= maskObj
.streamGetDict();
3599 maskDict
->lookup("Width", &obj1
);
3600 if (obj1
.isNull()) {
3602 maskDict
->lookup("W", &obj1
);
3604 if (!obj1
.isInt()) {
3607 maskWidth
= obj1
.getInt();
3609 maskDict
->lookup("Height", &obj1
);
3610 if (obj1
.isNull()) {
3612 maskDict
->lookup("H", &obj1
);
3614 if (!obj1
.isInt()) {
3617 maskHeight
= obj1
.getInt();
3619 maskDict
->lookup("ImageMask", &obj1
);
3620 if (obj1
.isNull()) {
3622 maskDict
->lookup("IM", &obj1
);
3624 if (!obj1
.isBool() || !obj1
.getBool()) {
3628 maskInvert
= gFalse
;
3629 maskDict
->lookup("Decode", &obj1
);
3630 if (obj1
.isNull()) {
3632 maskDict
->lookup("D", &obj1
);
3634 if (obj1
.isArray()) {
3635 obj1
.arrayGet(0, &obj2
);
3636 if (obj2
.isInt() && obj2
.getInt() == 1) {
3640 } else if (!obj1
.isNull()) {
3644 haveExplicitMask
= gTrue
;
3649 out
->drawSoftMaskedImage(state
, ref
, str
, width
, height
, colorMap
,
3650 maskStr
, maskWidth
, maskHeight
, maskColorMap
);
3651 delete maskColorMap
;
3652 } else if (haveExplicitMask
) {
3653 out
->drawMaskedImage(state
, ref
, str
, width
, height
, colorMap
,
3654 maskStr
, maskWidth
, maskHeight
, maskInvert
);
3656 out
->drawImage(state
, ref
, str
, width
, height
, colorMap
,
3657 haveColorKeyMask
? maskColors
: (int *)NULL
, inlineImg
);
3665 if ((i
= width
* height
) > 1000) {
3675 error(getPos(), "Bad image parameters");
3678 void Gfx::doForm(xObject
*str
) {
3680 GBool transpGroup
, isolated
, knockout
;
3681 GfxColorSpace
*blendingColorSpace
;
3682 xObject matrixObj
, bboxObj
;
3683 double m
[6], bbox
[4];
3686 xObject obj1
, obj2
, obj3
;
3689 // check for excessive recursion
3690 if (formDepth
> 20) {
3695 dict
= str
->streamGetDict();
3698 dict
->lookup("FormType", &obj1
);
3699 if (!(obj1
.isNull() || (obj1
.isInt() && obj1
.getInt() == 1))) {
3700 error(getPos(), "Unknown form type");
3705 dict
->lookup("BBox", &bboxObj
);
3706 if (!bboxObj
.isArray()) {
3708 error(getPos(), "Bad form bounding box");
3711 for (i
= 0; i
< 4; ++i
) {
3712 bboxObj
.arrayGet(i
, &obj1
);
3713 bbox
[i
] = obj1
.getNum();
3719 dict
->lookup("Matrix", &matrixObj
);
3720 if (matrixObj
.isArray()) {
3721 for (i
= 0; i
< 6; ++i
) {
3722 matrixObj
.arrayGet(i
, &obj1
);
3723 m
[i
] = obj1
.getNum();
3734 dict
->lookup("Resources", &resObj
);
3735 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
3737 // check for a transparency group
3738 transpGroup
= isolated
= knockout
= gFalse
;
3739 blendingColorSpace
= NULL
;
3740 if (dict
->lookup("Group", &obj1
)->isDict()) {
3741 if (obj1
.dictLookup("S", &obj2
)->isName("Transparency")) {
3742 transpGroup
= gTrue
;
3743 if (!obj1
.dictLookup("CS", &obj3
)->isNull()) {
3744 blendingColorSpace
= GfxColorSpace::parse(&obj3
);
3747 if (obj1
.dictLookup("I", &obj3
)->isBool()) {
3748 isolated
= obj3
.getBool();
3751 if (obj1
.dictLookup("K", &obj3
)->isBool()) {
3752 knockout
= obj3
.getBool();
3762 doForm1(str
, resDict
, m
, bbox
,
3763 transpGroup
, gFalse
, blendingColorSpace
, isolated
, knockout
);
3766 if (blendingColorSpace
) {
3767 delete blendingColorSpace
;
3772 void Gfx::doForm1(xObject
*str
, Dict
*resDict
, double *matrix
, double *bbox
,
3773 GBool transpGroup
, GBool softMask
,
3774 GfxColorSpace
*blendingColorSpace
,
3775 GBool isolated
, GBool knockout
,
3776 GBool alpha
, Function
*transferFunc
,
3777 GfxColor
*backdropColor
) {
3779 double oldBaseMatrix
[6];
3782 // push new resources on stack
3783 pushResources(resDict
);
3785 // save current graphics state
3788 // kill any pre-existing path
3791 // save current parser
3794 // set form transformation matrix
3795 state
->concatCTM(matrix
[0], matrix
[1], matrix
[2],
3796 matrix
[3], matrix
[4], matrix
[5]);
3797 out
->updateCTM(state
, matrix
[0], matrix
[1], matrix
[2],
3798 matrix
[3], matrix
[4], matrix
[5]);
3800 // set form bounding box
3801 state
->moveTo(bbox
[0], bbox
[1]);
3802 state
->lineTo(bbox
[2], bbox
[1]);
3803 state
->lineTo(bbox
[2], bbox
[3]);
3804 state
->lineTo(bbox
[0], bbox
[3]);
3810 if (softMask
|| transpGroup
) {
3811 if (state
->getBlendMode() != gfxBlendNormal
) {
3812 state
->setBlendMode(gfxBlendNormal
);
3813 out
->updateBlendMode(state
);
3815 if (state
->getFillOpacity() != 1) {
3816 state
->setFillOpacity(1);
3817 out
->updateFillOpacity(state
);
3819 if (state
->getStrokeOpacity() != 1) {
3820 state
->setStrokeOpacity(1);
3821 out
->updateStrokeOpacity(state
);
3823 out
->clearSoftMask(state
);
3824 out
->beginTransparencyGroup(state
, bbox
, blendingColorSpace
,
3825 isolated
, knockout
, softMask
);
3828 // set new base matrix
3829 for (i
= 0; i
< 6; ++i
) {
3830 oldBaseMatrix
[i
] = baseMatrix
[i
];
3831 baseMatrix
[i
] = state
->getCTM()[i
];
3835 display(str
, gFalse
);
3837 if (softMask
|| transpGroup
) {
3838 out
->endTransparencyGroup(state
);
3841 // restore base matrix
3842 for (i
= 0; i
< 6; ++i
) {
3843 baseMatrix
[i
] = oldBaseMatrix
[i
];
3849 // restore graphics state
3852 // pop resource stack
3856 out
->setSoftMask(state
, bbox
, alpha
, transferFunc
, backdropColor
);
3857 } else if (transpGroup
) {
3858 out
->paintTransparencyGroup(state
, bbox
);
3864 //------------------------------------------------------------------------
3865 // in-line image operators
3866 //------------------------------------------------------------------------
3868 void Gfx::opBeginImage(xObject args
[], int numArgs
) {
3872 // build dict/stream
3873 str
= buildImageStream();
3875 // display the image
3877 doImage(NULL
, str
, gTrue
);
3880 c1
= str
->getUndecodedStream()->getChar();
3881 c2
= str
->getUndecodedStream()->getChar();
3882 while (!(c1
== 'E' && c2
== 'I') && c2
!= EOF
) {
3884 c2
= str
->getUndecodedStream()->getChar();
3890 Stream
*Gfx::buildImageStream() {
3897 dict
.initDict(xref
);
3898 parser
->getObj(&obj
);
3899 while (!obj
.isCmd("ID") && !obj
.isEOF()) {
3900 if (!obj
.isName()) {
3901 error(getPos(), "Inline image dictionary key must be a name xObject");
3904 key
= copyString(obj
.getName());
3906 parser
->getObj(&obj
);
3907 if (obj
.isEOF() || obj
.isError()) {
3911 dict
.dictAdd(key
, &obj
);
3913 parser
->getObj(&obj
);
3916 error(getPos(), "End of file in inline image");
3924 str
= new EmbedStream(parser
->getStream(), &dict
, gFalse
, 0);
3925 str
= str
->addFilters(&dict
);
3930 void Gfx::opImageData(xObject args
[], int numArgs
) {
3931 error(getPos(), "Internal: got 'ID' operator");
3934 void Gfx::opEndImage(xObject args
[], int numArgs
) {
3935 error(getPos(), "Internal: got 'EI' operator");
3938 //------------------------------------------------------------------------
3939 // type 3 font operators
3940 //------------------------------------------------------------------------
3942 void Gfx::opSetCharWidth(xObject args
[], int numArgs
) {
3943 out
->type3D0(state
, args
[0].getNum(), args
[1].getNum());
3946 void Gfx::opSetCacheDevice(xObject args
[], int numArgs
) {
3947 out
->type3D1(state
, args
[0].getNum(), args
[1].getNum(),
3948 args
[2].getNum(), args
[3].getNum(),
3949 args
[4].getNum(), args
[5].getNum());
3952 //------------------------------------------------------------------------
3953 // compatibility operators
3954 //------------------------------------------------------------------------
3956 void Gfx::opBeginIgnoreUndef(xObject args
[], int numArgs
) {
3960 void Gfx::opEndIgnoreUndef(xObject args
[], int numArgs
) {
3961 if (ignoreUndef
> 0)
3965 //------------------------------------------------------------------------
3966 // marked content operators
3967 //------------------------------------------------------------------------
3969 void Gfx::opBeginMarkedContent(xObject args
[], int numArgs
) {
3970 if (printCommands
) {
3971 printf(" marked content: %s ", args
[0].getName());
3973 args
[2].print(stdout
);
3979 void Gfx::opEndMarkedContent(xObject args
[], int numArgs
) {
3982 void Gfx::opMarkPoint(xObject args
[], int numArgs
) {
3983 if (printCommands
) {
3984 printf(" mark point: %s ", args
[0].getName());
3986 args
[2].print(stdout
);
3992 //------------------------------------------------------------------------
3994 //------------------------------------------------------------------------
3996 void Gfx::drawAnnot(xObject
*str
, AnnotBorderStyle
*borderStyle
,
3997 double xMin
, double yMin
, double xMax
, double yMax
) {
3998 Dict
*dict
, *resDict
;
3999 xObject matrixObj
, bboxObj
, resObj
;
4001 double m
[6], bbox
[4], ictm
[6];
4003 double formX0
, formY0
, formX1
, formY1
;
4004 double annotX0
, annotY0
, annotX1
, annotY1
;
4005 double det
, x
, y
, sx
, sy
;
4008 double *dash
, *dash2
;
4012 //~ can we assume that we're in default user space?
4013 //~ (i.e., baseMatrix = ctm)
4015 // transform the annotation bbox from default user space to user
4016 // space: (bbox * baseMatrix) * iCTM
4017 ctm
= state
->getCTM();
4018 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
4019 ictm
[0] = ctm
[3] * det
;
4020 ictm
[1] = -ctm
[1] * det
;
4021 ictm
[2] = -ctm
[2] * det
;
4022 ictm
[3] = ctm
[0] * det
;
4023 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
4024 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
4025 x
= baseMatrix
[0] * xMin
+ baseMatrix
[2] * yMin
+ baseMatrix
[4];
4026 y
= baseMatrix
[1] * xMin
+ baseMatrix
[3] * yMin
+ baseMatrix
[5];
4027 annotX0
= ictm
[0] * x
+ ictm
[2] * y
+ ictm
[4];
4028 annotY0
= ictm
[1] * x
+ ictm
[3] * y
+ ictm
[5];
4029 x
= baseMatrix
[0] * xMax
+ baseMatrix
[2] * yMax
+ baseMatrix
[4];
4030 y
= baseMatrix
[1] * xMax
+ baseMatrix
[3] * yMax
+ baseMatrix
[5];
4031 annotX1
= ictm
[0] * x
+ ictm
[2] * y
+ ictm
[4];
4032 annotY1
= ictm
[1] * x
+ ictm
[3] * y
+ ictm
[5];
4033 if (annotX0
> annotX1
) {
4034 x
= annotX0
; annotX0
= annotX1
; annotX1
= x
;
4036 if (annotY0
> annotY1
) {
4037 y
= annotY0
; annotY0
= annotY1
; annotY1
= y
;
4040 // draw the appearance stream (if there is one)
4041 if (str
->isStream()) {
4044 dict
= str
->streamGetDict();
4046 // get the form bounding box
4047 dict
->lookup("BBox", &bboxObj
);
4048 if (!bboxObj
.isArray()) {
4050 error(getPos(), "Bad form bounding box");
4053 for (i
= 0; i
< 4; ++i
) {
4054 bboxObj
.arrayGet(i
, &obj1
);
4055 bbox
[i
] = obj1
.getNum();
4060 // get the form matrix
4061 dict
->lookup("Matrix", &matrixObj
);
4062 if (matrixObj
.isArray()) {
4063 for (i
= 0; i
< 6; ++i
) {
4064 matrixObj
.arrayGet(i
, &obj1
);
4065 m
[i
] = obj1
.getNum();
4075 // transform the form bbox from form space to user space
4076 formX0
= bbox
[0] * m
[0] + bbox
[1] * m
[2] + m
[4];
4077 formY0
= bbox
[0] * m
[1] + bbox
[1] * m
[3] + m
[5];
4078 formX1
= bbox
[2] * m
[0] + bbox
[3] * m
[2] + m
[4];
4079 formY1
= bbox
[2] * m
[1] + bbox
[3] * m
[3] + m
[5];
4080 if (formX0
> formX1
) {
4081 x
= formX0
; formX0
= formX1
; formX1
= x
;
4083 if (formY0
> formY1
) {
4084 y
= formY0
; formY0
= formY1
; formY1
= y
;
4087 // scale the form to fit the annotation bbox
4088 if (formX1
== formX0
) {
4089 // this shouldn't happen
4092 sx
= (annotX1
- annotX0
) / (formX1
- formX0
);
4094 if (formY1
== formY0
) {
4095 // this shouldn't happen
4098 sy
= (annotY1
- annotY0
) / (formY1
- formY0
);
4102 m
[4] = (m
[4] - formX0
) * sx
+ annotX0
;
4105 m
[5] = (m
[5] - formY0
) * sy
+ annotY0
;
4108 dict
->lookup("Resources", &resObj
);
4109 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
4112 doForm1(str
, resDict
, m
, bbox
);
4118 if (borderStyle
&& borderStyle
->getWidth() > 0) {
4119 if (state
->getStrokeColorSpace()->getMode() != csDeviceRGB
) {
4120 state
->setStrokePattern(NULL
);
4121 state
->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
4122 out
->updateStrokeColorSpace(state
);
4124 borderStyle
->getColor(&r
, &g
, &b
);
4125 color
.c
[0] = dblToCol(r
);
4126 color
.c
[1] = dblToCol(g
);
4127 color
.c
[2] = dblToCol(b
);
4128 state
->setStrokeColor(&color
);
4129 out
->updateStrokeColor(state
);
4130 // compute the width scale factor when going from default user
4131 // space to user space
4132 x
= (baseMatrix
[0] + baseMatrix
[2]) * ictm
[0] +
4133 (baseMatrix
[1] + baseMatrix
[3]) * ictm
[2];
4134 y
= (baseMatrix
[0] + baseMatrix
[2]) * ictm
[1] +
4135 (baseMatrix
[1] + baseMatrix
[3]) * ictm
[3];
4136 x
= sqrt(0.5 * (x
* x
+ y
* y
));
4137 state
->setLineWidth(x
* borderStyle
->getWidth());
4138 out
->updateLineWidth(state
);
4139 borderStyle
->getDash(&dash
, &dashLength
);
4140 if (borderStyle
->getType() == annotBorderDashed
&& dashLength
> 0) {
4141 dash2
= (double *)gmallocn(dashLength
, sizeof(double));
4142 for (i
= 0; i
< dashLength
; ++i
) {
4143 dash2
[i
] = x
* dash
[i
];
4145 state
->setLineDash(dash2
, dashLength
, 0);
4146 out
->updateLineDash(state
);
4148 //~ this doesn't currently handle the beveled and engraved styles
4150 state
->moveTo(annotX0
, out
->upsideDown() ? annotY1
: annotY0
);
4151 state
->lineTo(annotX1
, out
->upsideDown() ? annotY1
: annotY0
);
4152 if (borderStyle
->getType() != annotBorderUnderlined
) {
4153 state
->lineTo(annotX1
, out
->upsideDown() ? annotY0
: annotY1
);
4154 state
->lineTo(annotX0
, out
->upsideDown() ? annotY0
: annotY1
);
4161 void Gfx::saveState() {
4162 out
->saveState(state
);
4163 state
= state
->save();
4166 void Gfx::restoreState() {
4167 state
= state
->restore();
4168 out
->restoreState(state
);
4171 void Gfx::pushResources(Dict
*resDict
) {
4172 res
= new GfxResources(xref
, resDict
, res
);
4175 void Gfx::popResources() {
4176 GfxResources
*resPtr
;
4178 resPtr
= res
->getNext();