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, Object *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, Object *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, Object *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, Object *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(Object *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
Object args[maxArgs];
564
// scan a sequence of objects
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 object
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(Object *cmd, Object 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(Object *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(Object args[], int numArgs) {
741
void Gfx::opRestore(Object args[], int numArgs) {
745
void Gfx::opConcat(Object 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(Object 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(Object args[], int numArgs) {
778
state->setFlatness((int)args[0].getNum());
779
out->updateFlatness(state);
782
void Gfx::opSetLineJoin(Object args[], int numArgs) {
783
state->setLineJoin(args[0].getInt());
784
out->updateLineJoin(state);
787
void Gfx::opSetLineCap(Object args[], int numArgs) {
788
state->setLineCap(args[0].getInt());
789
out->updateLineCap(state);
792
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
793
state->setMiterLimit(args[0].getNum());
794
out->updateMiterLimit(state);
797
void Gfx::opSetLineWidth(Object args[], int numArgs) {
798
state->setLineWidth(args[0].getNum());
799
out->updateLineWidth(state);
802
void Gfx::opSetExtGState(Object args[], int numArgs) {
803
Object 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(Object *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(Object args[], int numArgs) {
1062
//------------------------------------------------------------------------
1064
//------------------------------------------------------------------------
1066
void Gfx::opSetFillGray(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object args[], int numArgs) {
1314
state->moveTo(args[0].getNum(), args[1].getNum());
1317
void Gfx::opLineTo(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object args[], int numArgs) {
1403
void Gfx::opStroke(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object args[], int numArgs) {
2911
void Gfx::opEOClip(Object args[], int numArgs) {
2915
//------------------------------------------------------------------------
2916
// text object operators
2917
//------------------------------------------------------------------------
2919
void Gfx::opBeginText(Object 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(Object args[], int numArgs) {
2928
out->endTextObject(state);
2931
//------------------------------------------------------------------------
2932
// text state operators
2933
//------------------------------------------------------------------------
2935
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
2936
state->setCharSpace(args[0].getNum());
2937
out->updateCharSpace(state);
2940
void Gfx::opSetFont(Object 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(Object args[], int numArgs) {
2958
state->setLeading(args[0].getNum());
2961
void Gfx::opSetTextRender(Object args[], int numArgs) {
2962
state->setRender(args[0].getInt());
2963
out->updateRender(state);
2966
void Gfx::opSetTextRise(Object args[], int numArgs) {
2967
state->setRise(args[0].getNum());
2968
out->updateRise(state);
2971
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
2972
state->setWordSpace(args[0].getNum());
2973
out->updateWordSpace(state);
2976
void Gfx::opSetHorizScaling(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object 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(Object args[], int numArgs) {
3306
Object 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(Object *ref, Stream *str, GBool inlineImg) {
3361
Dict *dict, *maskDict;
3364
StreamColorSpaceMode csMode;
3367
GfxColorSpace *colorSpace, *maskColorSpace;
3368
GfxImageColorMap *colorMap, *maskColorMap;
3369
Object 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(Object *str) {
3680
GBool transpGroup, isolated, knockout;
3681
GfxColorSpace *blendingColorSpace;
3682
Object matrixObj, bboxObj;
3683
double m[6], bbox[4];
3686
Object 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(Object *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(Object 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 object");
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(Object args[], int numArgs) {
3931
error(getPos(), "Internal: got 'ID' operator");
3934
void Gfx::opEndImage(Object args[], int numArgs) {
3935
error(getPos(), "Internal: got 'EI' operator");
3938
//------------------------------------------------------------------------
3939
// type 3 font operators
3940
//------------------------------------------------------------------------
3942
void Gfx::opSetCharWidth(Object args[], int numArgs) {
3943
out->type3D0(state, args[0].getNum(), args[1].getNum());
3946
void Gfx::opSetCacheDevice(Object 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(Object args[], int numArgs) {
3960
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
3961
if (ignoreUndef > 0)
3965
//------------------------------------------------------------------------
3966
// marked content operators
3967
//------------------------------------------------------------------------
3969
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
3970
if (printCommands) {
3971
printf(" marked content: %s ", args[0].getName());
3973
args[2].print(stdout);
3979
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
3982
void Gfx::opMarkPoint(Object 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(Object *str, AnnotBorderStyle *borderStyle,
3997
double xMin, double yMin, double xMax, double yMax) {
3998
Dict *dict, *resDict;
3999
Object 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();