1
//========================================================================
5
// Copyright 1996-2003 Glyph & Cog, LLC
7
//========================================================================
9
//========================================================================
11
// Modified under the Poppler project - http://poppler.freedesktop.org
13
// All changes made under the Poppler project to this file are licensed
14
// under GPL version 2 or later
16
// Copyright (C) 2005 Jonathan Blandford <jrb@redhat.com>
17
// Copyright (C) 2005-2010 Albert Astals Cid <aacid@kde.org>
18
// Copyright (C) 2006 Thorkild Stray <thorkild@ifi.uio.no>
19
// Copyright (C) 2006 Kristian HĆøgsberg <krh@redhat.com>
20
// Copyright (C) 2006-2010 Carlos Garcia Campos <carlosgc@gnome.org>
21
// Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net>
22
// Copyright (C) 2007, 2008 Brad Hards <bradh@kde.org>
23
// Copyright (C) 2007 Adrian Johnson <ajohnson@redneon.com>
24
// Copyright (C) 2007, 2008 IƱigo MartĆnez <inigomartinez@gmail.com>
25
// Copyright (C) 2007 Koji Otani <sho@bbr.jp>
26
// Copyright (C) 2007 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
27
// Copyright (C) 2008 Pino Toscano <pino@kde.org>
28
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
29
// Copyright (C) 2008 Hib Eris <hib@hiberis.nl>
30
// Copyright (C) 2009 M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
31
// Copyright (C) 2009, 2010 Thomas Freitag <Thomas.Freitag@alfa.de>
32
// Copyright (C) 2009 William Bader <williambader@hotmail.com>
33
// Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu>
34
// Copyright (C) 2010 Nils Hƶglund <nils.hoglund@gmail.com>
35
// Copyright (C) 2010 Christian FeuersƤnger <cfeuersaenger@googlemail.com>
37
// To see a description of the changes please see the Changelog file that
38
// came with your tarball or type make ChangeLog if you are building from git
40
//========================================================================
44
#ifdef USE_GCC_PRAGMAS
45
#pragma implementation
54
#include "goo/GooTimer.h"
55
#include "goo/GooHash.h"
56
#include "GlobalParams.h"
57
#include "CharTypes.h"
66
#include "OutputDev.h"
71
#include "ProfileData.h"
73
#include "OptionalContent.h"
75
// the MSVC math.h doesn't define this
77
#define M_PI 3.14159265358979323846
80
//------------------------------------------------------------------------
82
//------------------------------------------------------------------------
84
// Max recursive depth for a function shading fill.
85
#define functionMaxDepth 6
87
// Max delta allowed in any color component for a function shading fill.
88
#define functionColorDelta (dblToCol(1 / 256.0))
90
// Max number of splits along the t axis for an axial shading fill.
91
#define axialMaxSplits 256
93
// Max delta allowed in any color component for an axial shading fill.
94
#define axialColorDelta (dblToCol(1 / 256.0))
96
// Max number of splits along the t axis for a radial shading fill.
97
#define radialMaxSplits 256
99
// Max delta allowed in any color component for a radial shading fill.
100
#define radialColorDelta (dblToCol(1 / 256.0))
102
// Max recursive depth for a Gouraud triangle shading fill.
104
// Triangles will be split at most gouraudMaxDepth times (each time into 4
105
// smaller ones). That makes pow(4,gouraudMaxDepth) many triangles for
107
#define gouraudMaxDepth 6
109
// Max delta allowed in any color component for a Gouraud triangle
111
#define gouraudColorDelta (dblToCol(3. / 256.0))
113
// Gouraud triangle: if the three color parameters differ by at more than this percend of
114
// the total color parameter range, the triangle will be refined
115
#define gouraudParameterizedColorDelta 5e-3
117
// Max recursive depth for a patch mesh shading fill.
118
#define patchMaxDepth 6
120
// Max delta allowed in any color component for a patch mesh shading
122
#define patchColorDelta (dblToCol((3. / 256.0)))
124
//------------------------------------------------------------------------
126
//------------------------------------------------------------------------
128
#ifdef _MSC_VER // this works around a bug in the VC7 compiler
129
# pragma optimize("",off)
132
Operator Gfx::opTab[] = {
133
{"\"", 3, {tchkNum, tchkNum, tchkString},
134
&Gfx::opMoveSetShowText},
135
{"'", 1, {tchkString},
136
&Gfx::opMoveShowText},
139
{"B*", 0, {tchkNone},
140
&Gfx::opEOFillStroke},
141
{"BDC", 2, {tchkName, tchkProps},
142
&Gfx::opBeginMarkedContent},
143
{"BI", 0, {tchkNone},
145
{"BMC", 1, {tchkName},
146
&Gfx::opBeginMarkedContent},
147
{"BT", 0, {tchkNone},
149
{"BX", 0, {tchkNone},
150
&Gfx::opBeginIgnoreUndef},
151
{"CS", 1, {tchkName},
152
&Gfx::opSetStrokeColorSpace},
153
{"DP", 2, {tchkName, tchkProps},
155
{"Do", 1, {tchkName},
157
{"EI", 0, {tchkNone},
159
{"EMC", 0, {tchkNone},
160
&Gfx::opEndMarkedContent},
161
{"ET", 0, {tchkNone},
163
{"EX", 0, {tchkNone},
164
&Gfx::opEndIgnoreUndef},
168
&Gfx::opSetStrokeGray},
169
{"ID", 0, {tchkNone},
173
{"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
174
&Gfx::opSetStrokeCMYKColor},
176
&Gfx::opSetMiterLimit},
177
{"MP", 1, {tchkName},
181
{"RG", 3, {tchkNum, tchkNum, tchkNum},
182
&Gfx::opSetStrokeRGBColor},
185
{"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
186
&Gfx::opSetStrokeColor},
187
{"SCN", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
188
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
189
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
190
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
191
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
192
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
193
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
194
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
196
&Gfx::opSetStrokeColorN},
197
{"T*", 0, {tchkNone},
198
&Gfx::opTextNextLine},
199
{"TD", 2, {tchkNum, tchkNum},
200
&Gfx::opTextMoveSet},
201
{"TJ", 1, {tchkArray},
202
&Gfx::opShowSpaceText},
204
&Gfx::opSetTextLeading},
206
&Gfx::opSetCharSpacing},
207
{"Td", 2, {tchkNum, tchkNum},
209
{"Tf", 2, {tchkName, tchkNum},
211
{"Tj", 1, {tchkString},
213
{"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
215
&Gfx::opSetTextMatrix},
217
&Gfx::opSetTextRender},
219
&Gfx::opSetTextRise},
221
&Gfx::opSetWordSpacing},
223
&Gfx::opSetHorizScaling},
226
{"W*", 0, {tchkNone},
229
&Gfx::opCloseFillStroke},
230
{"b*", 0, {tchkNone},
231
&Gfx::opCloseEOFillStroke},
232
{"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
235
{"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
238
{"cs", 1, {tchkName},
239
&Gfx::opSetFillColorSpace},
240
{"d", 2, {tchkArray, tchkNum},
242
{"d0", 2, {tchkNum, tchkNum},
243
&Gfx::opSetCharWidth},
244
{"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
246
&Gfx::opSetCacheDevice},
249
{"f*", 0, {tchkNone},
252
&Gfx::opSetFillGray},
253
{"gs", 1, {tchkName},
254
&Gfx::opSetExtGState},
260
&Gfx::opSetLineJoin},
261
{"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
262
&Gfx::opSetFillCMYKColor},
263
{"l", 2, {tchkNum, tchkNum},
265
{"m", 2, {tchkNum, tchkNum},
271
{"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
273
{"rg", 3, {tchkNum, tchkNum, tchkNum},
274
&Gfx::opSetFillRGBColor},
275
{"ri", 1, {tchkName},
276
&Gfx::opSetRenderingIntent},
278
&Gfx::opCloseStroke},
279
{"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
280
&Gfx::opSetFillColor},
281
{"scn", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
282
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
283
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
284
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
285
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
286
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
287
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
288
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
290
&Gfx::opSetFillColorN},
291
{"sh", 1, {tchkName},
293
{"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
296
&Gfx::opSetLineWidth},
297
{"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
301
#ifdef _MSC_VER // this works around a bug in the VC7 compiler
302
# pragma optimize("",on)
305
#define numOps (sizeof(opTab) / sizeof(Operator))
307
static inline GBool isSameGfxColor(const GfxColor &colorA, const GfxColor &colorB, Guint nComps, double delta) {
308
for (Guint k = 0; k < nComps; ++k) {
309
if (abs(colorA.c[k] - colorB.c[k]) > delta) {
316
//------------------------------------------------------------------------
318
//------------------------------------------------------------------------
320
GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) :
321
gStateCache(2, xref) {
327
// build font dictionary
329
resDict->lookupNF("Font", &obj1);
331
obj1.fetch(xref, &obj2);
334
fonts = new GfxFontDict(xref, &r, obj2.getDict());
337
} else if (obj1.isDict()) {
338
fonts = new GfxFontDict(xref, NULL, obj1.getDict());
342
// get XObject dictionary
343
resDict->lookup("XObject", &xObjDict);
345
// get color space dictionary
346
resDict->lookup("ColorSpace", &colorSpaceDict);
348
// get pattern dictionary
349
resDict->lookup("Pattern", &patternDict);
351
// get shading dictionary
352
resDict->lookup("Shading", &shadingDict);
354
// get graphics state parameter dictionary
355
resDict->lookup("ExtGState", &gStateDict);
357
// get properties dictionary
358
resDict->lookup("Properties", &propertiesDict);
363
colorSpaceDict.initNull();
364
patternDict.initNull();
365
shadingDict.initNull();
366
gStateDict.initNull();
367
propertiesDict.initNull();
373
GfxResources::~GfxResources() {
378
colorSpaceDict.free();
382
propertiesDict.free();
385
GfxFont *GfxResources::lookupFont(char *name) {
387
GfxResources *resPtr;
389
for (resPtr = this; resPtr; resPtr = resPtr->next) {
391
if ((font = resPtr->fonts->lookup(name)))
395
error(-1, "Unknown font tag '%s'", name);
399
GBool GfxResources::lookupXObject(char *name, Object *obj) {
400
GfxResources *resPtr;
402
for (resPtr = this; resPtr; resPtr = resPtr->next) {
403
if (resPtr->xObjDict.isDict()) {
404
if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
409
error(-1, "XObject '%s' is unknown", name);
413
GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
414
GfxResources *resPtr;
416
for (resPtr = this; resPtr; resPtr = resPtr->next) {
417
if (resPtr->xObjDict.isDict()) {
418
if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
423
error(-1, "XObject '%s' is unknown", name);
427
GBool GfxResources::lookupMarkedContentNF(char *name, Object *obj) {
428
GfxResources *resPtr;
430
for (resPtr = this; resPtr; resPtr = resPtr->next) {
431
if (resPtr->propertiesDict.isDict()) {
432
if (!resPtr->propertiesDict.dictLookupNF(name, obj)->isNull())
437
error(-1, "Marked Content '%s' is unknown", name);
441
void GfxResources::lookupColorSpace(char *name, Object *obj) {
442
GfxResources *resPtr;
444
for (resPtr = this; resPtr; resPtr = resPtr->next) {
445
if (resPtr->colorSpaceDict.isDict()) {
446
if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
455
GfxPattern *GfxResources::lookupPattern(char *name, Gfx *gfx) {
456
GfxResources *resPtr;
460
for (resPtr = this; resPtr; resPtr = resPtr->next) {
461
if (resPtr->patternDict.isDict()) {
462
if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
463
pattern = GfxPattern::parse(&obj, gfx);
470
error(-1, "Unknown pattern '%s'", name);
474
GfxShading *GfxResources::lookupShading(char *name, Gfx *gfx) {
475
GfxResources *resPtr;
479
for (resPtr = this; resPtr; resPtr = resPtr->next) {
480
if (resPtr->shadingDict.isDict()) {
481
if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
482
shading = GfxShading::parse(&obj, gfx);
489
error(-1, "Unknown shading '%s'", name);
493
GBool GfxResources::lookupGState(char *name, Object *obj) {
494
if (!lookupGStateNF(name, obj))
500
const Ref ref = obj->getRef();
501
if (!gStateCache.lookup(ref, obj)->isNull())
505
gStateCache.put(ref)->copy(obj);
509
GBool GfxResources::lookupGStateNF(char *name, Object *obj) {
510
GfxResources *resPtr;
512
for (resPtr = this; resPtr; resPtr = resPtr->next) {
513
if (resPtr->gStateDict.isDict()) {
514
if (!resPtr->gStateDict.dictLookupNF(name, obj)->isNull()) {
520
error(-1, "ExtGState '%s' is unknown", name);
524
//------------------------------------------------------------------------
526
//------------------------------------------------------------------------
528
Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, Catalog *catalogA,
529
double hDPI, double vDPI, PDFRectangle *box,
530
PDFRectangle *cropBox, int rotate,
531
GBool (*abortCheckCbkA)(void *data),
532
void *abortCheckCbkDataA)
534
: iccColorSpaceCache(5)
542
printCommands = globalParams->getPrintCommands();
543
profileCommands = globalParams->getProfileCommands();
544
textHaveCSPattern = gFalse;
546
maskHaveCSPattern = gFalse;
550
// start the resource stack
551
res = new GfxResources(xref, resDict, NULL);
555
state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
558
fontChanged = gFalse;
561
out->startPage(pageNum, state);
562
out->setDefaultCTM(state->getCTM());
563
out->updateAll(state);
564
for (i = 0; i < 6; ++i) {
565
baseMatrix[i] = state->getCTM()[i];
568
abortCheckCbk = abortCheckCbkA;
569
abortCheckCbkData = abortCheckCbkDataA;
573
state->moveTo(cropBox->x1, cropBox->y1);
574
state->lineTo(cropBox->x2, cropBox->y1);
575
state->lineTo(cropBox->x2, cropBox->y2);
576
state->lineTo(cropBox->x1, cropBox->y2);
584
Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict, Catalog *catalogA,
585
PDFRectangle *box, PDFRectangle *cropBox,
586
GBool (*abortCheckCbkA)(void *data),
587
void *abortCheckCbkDataA)
589
: iccColorSpaceCache(5)
597
printCommands = globalParams->getPrintCommands();
598
profileCommands = globalParams->getProfileCommands();
599
textHaveCSPattern = gFalse;
601
maskHaveCSPattern = gFalse;
605
// start the resource stack
606
res = new GfxResources(xref, resDict, NULL);
610
state = new GfxState(72, 72, box, 0, gFalse);
613
fontChanged = gFalse;
616
for (i = 0; i < 6; ++i) {
617
baseMatrix[i] = state->getCTM()[i];
620
abortCheckCbk = abortCheckCbkA;
621
abortCheckCbkData = abortCheckCbkDataA;
625
state->moveTo(cropBox->x1, cropBox->y1);
626
state->lineTo(cropBox->x2, cropBox->y1);
627
state->lineTo(cropBox->x2, cropBox->y2);
628
state->lineTo(cropBox->x1, cropBox->y2);
637
while (stateGuards.size()) {
640
// There shouldn't be more saves, but pop them if there were any
641
while (state->hasSaves()) {
642
error(-1, "Found state under last state guard. Popping.");
659
void Gfx::display(Object *obj, GBool topLevel) {
663
if (obj->isArray()) {
664
for (i = 0; i < obj->arrayGetLength(); ++i) {
665
obj->arrayGet(i, &obj2);
666
if (!obj2.isStream()) {
667
error(-1, "Weird page contents");
673
} else if (!obj->isStream()) {
674
error(-1, "Weird page contents");
677
parser = new Parser(xref, new Lexer(xref, obj), gFalse);
683
void Gfx::go(GBool topLevel) {
685
Object args[maxArgs];
689
// scan a sequence of objects
691
updateLevel = lastAbortCheck = 0;
693
parser->getObj(&obj);
694
while (!obj.isEOF()) {
695
commandAborted = gFalse;
697
// got a command - execute it
701
for (i = 0; i < numArgs; ++i) {
703
args[i].print(stdout);
711
execOp(&obj, args, numArgs);
713
// Update the profile information
714
if (profileCommands) {
717
hash = out->getProfileHash ();
722
cmd_g = new GooString (obj.getCmd());
723
data_p = (ProfileData *)hash->lookup (cmd_g);
724
if (data_p == NULL) {
725
data_p = new ProfileData();
726
hash->add (cmd_g, data_p);
729
data_p->addElement(timer.getElapsed ());
733
for (i = 0; i < numArgs; ++i)
737
// periodically update display
738
if (++updateLevel >= 20000) {
743
// did the command throw an exception
744
if (commandAborted) {
745
// don't propogate; recursive drawing comes from Form XObjects which
746
// should probably be drawn in a separate context anyway for caching
747
commandAborted = gFalse;
751
// check for an abort
753
if (updateLevel - lastAbortCheck > 10) {
754
if ((*abortCheckCbk)(abortCheckCbkData)) {
757
lastAbortCheck = updateLevel;
761
// got an argument - save it
762
} else if (numArgs < maxArgs) {
763
args[numArgs++] = obj;
765
// too many arguments - something is wrong
767
error(getPos(), "Too many args in content stream");
769
printf("throwing away arg: ");
777
// grab the next object
778
parser->getObj(&obj);
782
// args at end with no command
784
error(getPos(), "Leftover args in content stream");
786
printf("%d leftovers:", numArgs);
787
for (i = 0; i < numArgs; ++i) {
789
args[i].print(stdout);
794
for (i = 0; i < numArgs; ++i)
801
if (topLevel && updateLevel > 0) {
806
void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
813
name = cmd->getCmd();
814
if (!(op = findOp(name))) {
815
if (ignoreUndef == 0)
816
error(getPos(), "Unknown operator '%s'", name);
822
if (op->numArgs >= 0) {
823
if (numArgs < op->numArgs) {
824
error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
825
commandAborted = gTrue;
828
if (numArgs > op->numArgs) {
830
error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
832
argPtr += numArgs - op->numArgs;
833
numArgs = op->numArgs;
836
if (numArgs > -op->numArgs) {
837
error(getPos(), "Too many (%d) args to '%s' operator",
842
for (i = 0; i < numArgs; ++i) {
843
if (!checkArg(&argPtr[i], op->tchk[i])) {
844
error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
845
i, name, argPtr[i].getTypeName());
851
(this->*op->func)(argPtr, numArgs);
854
Operator *Gfx::findOp(char *name) {
859
// invariant: opTab[a] < name < opTab[b]
862
cmp = strcmp(opTab[m].name, name);
875
GBool Gfx::checkArg(Object *arg, TchkType type) {
877
case tchkBool: return arg->isBool();
878
case tchkInt: return arg->isInt();
879
case tchkNum: return arg->isNum();
880
case tchkString: return arg->isString();
881
case tchkName: return arg->isName();
882
case tchkArray: return arg->isArray();
883
case tchkProps: return arg->isDict() || arg->isName();
884
case tchkSCN: return arg->isNum() || arg->isName();
885
case tchkNone: return gFalse;
891
return parser ? parser->getPos() : -1;
894
//------------------------------------------------------------------------
895
// graphics state operators
896
//------------------------------------------------------------------------
898
void Gfx::opSave(Object args[], int numArgs) {
902
void Gfx::opRestore(Object args[], int numArgs) {
906
void Gfx::opConcat(Object args[], int numArgs) {
907
state->concatCTM(args[0].getNum(), args[1].getNum(),
908
args[2].getNum(), args[3].getNum(),
909
args[4].getNum(), args[5].getNum());
910
out->updateCTM(state, args[0].getNum(), args[1].getNum(),
911
args[2].getNum(), args[3].getNum(),
912
args[4].getNum(), args[5].getNum());
916
void Gfx::opSetDash(Object args[], int numArgs) {
923
a = args[0].getArray();
924
length = a->getLength();
928
dash = (double *)gmallocn(length, sizeof(double));
929
for (i = 0; i < length; ++i) {
930
dash[i] = a->get(i, &obj)->getNum();
934
state->setLineDash(dash, length, args[1].getNum());
935
out->updateLineDash(state);
938
void Gfx::opSetFlat(Object args[], int numArgs) {
939
state->setFlatness((int)args[0].getNum());
940
out->updateFlatness(state);
943
void Gfx::opSetLineJoin(Object args[], int numArgs) {
944
state->setLineJoin(args[0].getInt());
945
out->updateLineJoin(state);
948
void Gfx::opSetLineCap(Object args[], int numArgs) {
949
state->setLineCap(args[0].getInt());
950
out->updateLineCap(state);
953
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
954
state->setMiterLimit(args[0].getNum());
955
out->updateMiterLimit(state);
958
void Gfx::opSetLineWidth(Object args[], int numArgs) {
959
state->setLineWidth(args[0].getNum());
960
out->updateLineWidth(state);
963
void Gfx::opSetExtGState(Object args[], int numArgs) {
964
Object obj1, obj2, obj3, obj4, obj5;
968
GfxColor backdropColor;
969
GBool haveBackdropColor;
970
GfxColorSpace *blendingColorSpace;
971
GBool alpha, isolated, knockout;
974
if (!res->lookupGState(args[0].getName(), &obj1)) {
977
if (!obj1.isDict()) {
978
error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
983
printf(" gfx state dict: ");
988
// transparency support: blend mode, fill/stroke opacity
989
if (!obj1.dictLookup("BM", &obj2)->isNull()) {
990
if (state->parseBlendMode(&obj2, &mode)) {
991
state->setBlendMode(mode);
992
out->updateBlendMode(state);
994
error(getPos(), "Invalid blend mode in ExtGState");
998
if (obj1.dictLookup("ca", &obj2)->isNum()) {
999
state->setFillOpacity(obj2.getNum());
1000
out->updateFillOpacity(state);
1003
if (obj1.dictLookup("CA", &obj2)->isNum()) {
1004
state->setStrokeOpacity(obj2.getNum());
1005
out->updateStrokeOpacity(state);
1009
// fill/stroke overprint
1010
if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
1011
state->setFillOverprint(obj2.getBool());
1012
out->updateFillOverprint(state);
1015
if (obj1.dictLookup("OP", &obj2)->isBool()) {
1016
state->setStrokeOverprint(obj2.getBool());
1017
out->updateStrokeOverprint(state);
1019
state->setFillOverprint(obj2.getBool());
1020
out->updateFillOverprint(state);
1026
if (obj1.dictLookup("SA", &obj2)->isBool()) {
1027
state->setStrokeAdjust(obj2.getBool());
1028
out->updateStrokeAdjust(state);
1032
// transfer function
1033
if (obj1.dictLookup("TR2", &obj2)->isNull()) {
1035
obj1.dictLookup("TR", &obj2);
1037
if (obj2.isName("Default") ||
1038
obj2.isName("Identity")) {
1039
funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
1040
state->setTransfer(funcs);
1041
out->updateTransfer(state);
1042
} else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
1043
for (i = 0; i < 4; ++i) {
1044
obj2.arrayGet(i, &obj3);
1045
funcs[i] = Function::parse(&obj3);
1052
state->setTransfer(funcs);
1053
out->updateTransfer(state);
1055
} else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
1056
if ((funcs[0] = Function::parse(&obj2))) {
1057
funcs[1] = funcs[2] = funcs[3] = NULL;
1058
state->setTransfer(funcs);
1059
out->updateTransfer(state);
1061
} else if (!obj2.isNull()) {
1062
error(getPos(), "Invalid transfer function in ExtGState");
1067
if (obj1.dictLookup("AIS", &obj2)->isBool()) {
1068
state->setAlphaIsShape(obj2.getBool());
1069
out->updateAlphaIsShape(state);
1074
if (obj1.dictLookup("TK", &obj2)->isBool()) {
1075
state->setTextKnockout(obj2.getBool());
1076
out->updateTextKnockout(state);
1081
if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
1082
if (obj2.isName("None")) {
1083
out->clearSoftMask(state);
1084
} else if (obj2.isDict()) {
1085
if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
1087
} else { // "Luminosity"
1092
if (!obj2.dictLookup("TR", &obj3)->isNull()) {
1093
funcs[0] = Function::parse(&obj3);
1094
if (funcs[0]->getInputSize() != 1 ||
1095
funcs[0]->getOutputSize() != 1) {
1097
"Invalid transfer function in soft mask in ExtGState");
1103
if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
1104
for (i = 0; i < gfxColorMaxComps; ++i) {
1105
backdropColor.c[i] = 0;
1107
for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
1108
obj3.arrayGet(i, &obj4);
1110
backdropColor.c[i] = dblToCol(obj4.getNum());
1116
if (obj2.dictLookup("G", &obj3)->isStream()) {
1117
if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
1118
blendingColorSpace = NULL;
1119
isolated = knockout = gFalse;
1120
if (!obj4.dictLookup("CS", &obj5)->isNull()) {
1121
blendingColorSpace = GfxColorSpace::parse(&obj5, this);
1124
if (obj4.dictLookup("I", &obj5)->isBool()) {
1125
isolated = obj5.getBool();
1128
if (obj4.dictLookup("K", &obj5)->isBool()) {
1129
knockout = obj5.getBool();
1132
if (!haveBackdropColor) {
1133
if (blendingColorSpace) {
1134
blendingColorSpace->getDefaultColor(&backdropColor);
1136
//~ need to get the parent or default color space (?)
1137
for (i = 0; i < gfxColorMaxComps; ++i) {
1138
backdropColor.c[i] = 0;
1142
doSoftMask(&obj3, alpha, blendingColorSpace,
1143
isolated, knockout, funcs[0], &backdropColor);
1148
error(getPos(), "Invalid soft mask in ExtGState - missing group");
1152
error(getPos(), "Invalid soft mask in ExtGState - missing group");
1155
} else if (!obj2.isNull()) {
1156
error(getPos(), "Invalid soft mask in ExtGState");
1160
if (obj1.dictLookup("Font", &obj2)->isArray()) {
1162
if (obj2.arrayGetLength() == 2) {
1163
Object fargs0, fargs1;
1165
obj2.arrayGetNF(0,&fargs0);
1166
obj2.arrayGet(1,&fargs1);
1167
if (fargs0.isRef() && fargs1.isNum()) {
1171
fargs0.fetch(xref, &fobj);
1172
if (fobj.isDict()) {
1173
r = fargs0.getRef();
1174
font = GfxFont::makeFont(xref,args[0].getName(),r,fobj.getDict());
1175
state->setFont(font,fargs1.getNum());
1176
fontChanged = gTrue;
1183
error(getPos(), "Number of args mismatch for /Font in ExtGState");
1187
if (obj1.dictLookup("LW", &obj2)->isNum()) {
1188
opSetLineWidth(&obj2,1);
1191
if (obj1.dictLookup("LC", &obj2)->isInt()) {
1192
opSetLineCap(&obj2,1);
1195
if (obj1.dictLookup("LJ", &obj2)->isInt()) {
1196
opSetLineJoin(&obj2,1);
1199
if (obj1.dictLookup("ML", &obj2)->isNum()) {
1200
opSetMiterLimit(&obj2,1);
1203
if (obj1.dictLookup("D", &obj2)->isArray()) {
1204
if (obj2.arrayGetLength() == 2) {
1207
obj2.arrayGetNF(0,&dargs[0]);
1208
obj2.arrayGet(1,&dargs[1]);
1209
if (dargs[0].isArray() && dargs[1].isInt()) {
1215
error(getPos(), "Number of args mismatch for /D in ExtGState");
1219
if (obj1.dictLookup("RI", &obj2)->isName()) {
1220
opSetRenderingIntent(&obj2,1);
1223
if (obj1.dictLookup("FL", &obj2)->isNum()) {
1231
void Gfx::doSoftMask(Object *str, GBool alpha,
1232
GfxColorSpace *blendingColorSpace,
1233
GBool isolated, GBool knockout,
1234
Function *transferFunc, GfxColor *backdropColor) {
1235
Dict *dict, *resDict;
1236
double m[6], bbox[4];
1240
// check for excessive recursion
1241
if (formDepth > 20) {
1246
dict = str->streamGetDict();
1249
dict->lookup("FormType", &obj1);
1250
if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
1251
error(getPos(), "Unknown form type");
1256
dict->lookup("BBox", &obj1);
1257
if (!obj1.isArray()) {
1259
error(getPos(), "Bad form bounding box");
1262
for (i = 0; i < 4; ++i) {
1263
obj1.arrayGet(i, &obj2);
1264
if (likely(obj2.isNum())) bbox[i] = obj2.getNum();
1268
error(getPos(), "Bad form bounding box (non number)");
1276
dict->lookup("Matrix", &obj1);
1277
if (obj1.isArray()) {
1278
for (i = 0; i < 6; ++i) {
1279
obj1.arrayGet(i, &obj2);
1280
if (likely(obj2.isNum())) m[i] = obj2.getNum();
1292
dict->lookup("Resources", &obj1);
1293
resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
1297
doForm1(str, resDict, m, bbox, gTrue, gTrue,
1298
blendingColorSpace, isolated, knockout,
1299
alpha, transferFunc, backdropColor);
1302
if (blendingColorSpace) {
1303
delete blendingColorSpace;
1308
void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
1311
//------------------------------------------------------------------------
1313
//------------------------------------------------------------------------
1315
void Gfx::opSetFillGray(Object args[], int numArgs) {
1318
if (textHaveCSPattern && drawText) {
1319
GBool needFill = out->deviceHasTextClip(state);
1320
out->endTextObject(state);
1322
doPatternFill(gTrue);
1324
out->restoreState(state);
1326
state->setFillPattern(NULL);
1327
state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1328
out->updateFillColorSpace(state);
1329
color.c[0] = dblToCol(args[0].getNum());
1330
state->setFillColor(&color);
1331
out->updateFillColor(state);
1332
if (textHaveCSPattern) {
1333
out->beginTextObject(state);
1334
out->updateRender(state);
1335
out->updateTextMat(state);
1336
out->updateTextPos(state);
1337
textHaveCSPattern = gFalse;
1341
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
1344
state->setStrokePattern(NULL);
1345
state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1346
out->updateStrokeColorSpace(state);
1347
color.c[0] = dblToCol(args[0].getNum());
1348
state->setStrokeColor(&color);
1349
out->updateStrokeColor(state);
1352
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
1356
if (textHaveCSPattern && drawText) {
1357
GBool needFill = out->deviceHasTextClip(state);
1358
out->endTextObject(state);
1360
doPatternFill(gTrue);
1362
out->restoreState(state);
1364
state->setFillPattern(NULL);
1365
state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
1366
out->updateFillColorSpace(state);
1367
for (i = 0; i < 4; ++i) {
1368
color.c[i] = dblToCol(args[i].getNum());
1370
state->setFillColor(&color);
1371
out->updateFillColor(state);
1372
if (textHaveCSPattern) {
1373
out->beginTextObject(state);
1374
out->updateRender(state);
1375
out->updateTextMat(state);
1376
out->updateTextPos(state);
1377
textHaveCSPattern = gFalse;
1381
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
1385
state->setStrokePattern(NULL);
1386
state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
1387
out->updateStrokeColorSpace(state);
1388
for (i = 0; i < 4; ++i) {
1389
color.c[i] = dblToCol(args[i].getNum());
1391
state->setStrokeColor(&color);
1392
out->updateStrokeColor(state);
1395
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
1399
if (textHaveCSPattern && drawText) {
1400
GBool needFill = out->deviceHasTextClip(state);
1401
out->endTextObject(state);
1403
doPatternFill(gTrue);
1405
out->restoreState(state);
1407
state->setFillPattern(NULL);
1408
state->setFillColorSpace(new GfxDeviceRGBColorSpace());
1409
out->updateFillColorSpace(state);
1410
for (i = 0; i < 3; ++i) {
1411
color.c[i] = dblToCol(args[i].getNum());
1413
state->setFillColor(&color);
1414
out->updateFillColor(state);
1415
if (textHaveCSPattern) {
1416
out->beginTextObject(state);
1417
out->updateRender(state);
1418
out->updateTextMat(state);
1419
out->updateTextPos(state);
1420
textHaveCSPattern = gFalse;
1424
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
1428
state->setStrokePattern(NULL);
1429
state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1430
out->updateStrokeColorSpace(state);
1431
for (i = 0; i < 3; ++i) {
1432
color.c[i] = dblToCol(args[i].getNum());
1434
state->setStrokeColor(&color);
1435
out->updateStrokeColor(state);
1438
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
1440
GfxColorSpace *colorSpace;
1443
res->lookupColorSpace(args[0].getName(), &obj);
1445
colorSpace = GfxColorSpace::parse(&args[0], this);
1447
colorSpace = GfxColorSpace::parse(&obj, this);
1451
if (textHaveCSPattern && drawText) {
1452
GBool needFill = out->deviceHasTextClip(state);
1453
out->endTextObject(state);
1455
doPatternFill(gTrue);
1457
out->restoreState(state);
1459
state->setFillPattern(NULL);
1460
state->setFillColorSpace(colorSpace);
1461
out->updateFillColorSpace(state);
1462
colorSpace->getDefaultColor(&color);
1463
state->setFillColor(&color);
1464
out->updateFillColor(state);
1465
if (textHaveCSPattern) {
1466
out->beginTextObject(state);
1467
out->updateRender(state);
1468
out->updateTextMat(state);
1469
out->updateTextPos(state);
1470
textHaveCSPattern = colorSpace->getMode() == csPattern;
1471
} else if (drawText && out->supportTextCSPattern(state)) {
1472
out->beginTextObject(state);
1473
textHaveCSPattern = gTrue;
1476
error(getPos(), "Bad color space (fill)");
1480
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
1482
GfxColorSpace *colorSpace;
1485
state->setStrokePattern(NULL);
1486
res->lookupColorSpace(args[0].getName(), &obj);
1488
colorSpace = GfxColorSpace::parse(&args[0], this);
1490
colorSpace = GfxColorSpace::parse(&obj, this);
1494
state->setStrokeColorSpace(colorSpace);
1495
out->updateStrokeColorSpace(state);
1496
colorSpace->getDefaultColor(&color);
1497
state->setStrokeColor(&color);
1498
out->updateStrokeColor(state);
1500
error(getPos(), "Bad color space (stroke)");
1504
void Gfx::opSetFillColor(Object args[], int numArgs) {
1508
if (numArgs != state->getFillColorSpace()->getNComps()) {
1509
error(getPos(), "Incorrect number of arguments in 'sc' command");
1512
state->setFillPattern(NULL);
1513
for (i = 0; i < numArgs; ++i) {
1514
color.c[i] = dblToCol(args[i].getNum());
1516
state->setFillColor(&color);
1517
out->updateFillColor(state);
1520
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
1524
if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1525
error(getPos(), "Incorrect number of arguments in 'SC' command");
1528
state->setStrokePattern(NULL);
1529
for (i = 0; i < numArgs; ++i) {
1530
color.c[i] = dblToCol(args[i].getNum());
1532
state->setStrokeColor(&color);
1533
out->updateStrokeColor(state);
1536
void Gfx::opSetFillColorN(Object args[], int numArgs) {
1538
GfxPattern *pattern;
1541
if (state->getFillColorSpace()->getMode() == csPattern) {
1543
if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1544
numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1545
->getUnder()->getNComps()) {
1546
error(getPos(), "Incorrect number of arguments in 'scn' command");
1549
for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1550
if (args[i].isNum()) {
1551
color.c[i] = dblToCol(args[i].getNum());
1553
color.c[i] = 0; // TODO Investigate if this is what Adobe does
1556
state->setFillColor(&color);
1557
out->updateFillColor(state);
1559
if (args[numArgs-1].isName() &&
1560
(pattern = res->lookupPattern(args[numArgs-1].getName(), this))) {
1561
state->setFillPattern(pattern);
1565
if (numArgs != state->getFillColorSpace()->getNComps()) {
1566
error(getPos(), "Incorrect number of arguments in 'scn' command");
1569
state->setFillPattern(NULL);
1570
for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1571
if (args[i].isNum()) {
1572
color.c[i] = dblToCol(args[i].getNum());
1574
color.c[i] = 0; // TODO Investigate if this is what Adobe does
1577
state->setFillColor(&color);
1578
out->updateFillColor(state);
1582
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
1584
GfxPattern *pattern;
1587
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1589
if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1591
numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1592
->getUnder()->getNComps()) {
1593
error(getPos(), "Incorrect number of arguments in 'SCN' command");
1596
for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1597
if (args[i].isNum()) {
1598
color.c[i] = dblToCol(args[i].getNum());
1600
color.c[i] = 0; // TODO Investigate if this is what Adobe does
1603
state->setStrokeColor(&color);
1604
out->updateStrokeColor(state);
1606
if (args[numArgs-1].isName() &&
1607
(pattern = res->lookupPattern(args[numArgs-1].getName(), this))) {
1608
state->setStrokePattern(pattern);
1612
if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1613
error(getPos(), "Incorrect number of arguments in 'SCN' command");
1616
state->setStrokePattern(NULL);
1617
for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1618
if (args[i].isNum()) {
1619
color.c[i] = dblToCol(args[i].getNum());
1621
color.c[i] = 0; // TODO Investigate if this is what Adobe does
1624
state->setStrokeColor(&color);
1625
out->updateStrokeColor(state);
1629
//------------------------------------------------------------------------
1630
// path segment operators
1631
//------------------------------------------------------------------------
1633
void Gfx::opMoveTo(Object args[], int numArgs) {
1634
state->moveTo(args[0].getNum(), args[1].getNum());
1637
void Gfx::opLineTo(Object args[], int numArgs) {
1638
if (!state->isCurPt()) {
1639
error(getPos(), "No current point in lineto");
1642
state->lineTo(args[0].getNum(), args[1].getNum());
1645
void Gfx::opCurveTo(Object args[], int numArgs) {
1646
double x1, y1, x2, y2, x3, y3;
1648
if (!state->isCurPt()) {
1649
error(getPos(), "No current point in curveto");
1652
x1 = args[0].getNum();
1653
y1 = args[1].getNum();
1654
x2 = args[2].getNum();
1655
y2 = args[3].getNum();
1656
x3 = args[4].getNum();
1657
y3 = args[5].getNum();
1658
state->curveTo(x1, y1, x2, y2, x3, y3);
1661
void Gfx::opCurveTo1(Object args[], int numArgs) {
1662
double x1, y1, x2, y2, x3, y3;
1664
if (!state->isCurPt()) {
1665
error(getPos(), "No current point in curveto1");
1668
x1 = state->getCurX();
1669
y1 = state->getCurY();
1670
x2 = args[0].getNum();
1671
y2 = args[1].getNum();
1672
x3 = args[2].getNum();
1673
y3 = args[3].getNum();
1674
state->curveTo(x1, y1, x2, y2, x3, y3);
1677
void Gfx::opCurveTo2(Object args[], int numArgs) {
1678
double x1, y1, x2, y2, x3, y3;
1680
if (!state->isCurPt()) {
1681
error(getPos(), "No current point in curveto2");
1684
x1 = args[0].getNum();
1685
y1 = args[1].getNum();
1686
x2 = args[2].getNum();
1687
y2 = args[3].getNum();
1690
state->curveTo(x1, y1, x2, y2, x3, y3);
1693
void Gfx::opRectangle(Object args[], int numArgs) {
1696
x = args[0].getNum();
1697
y = args[1].getNum();
1698
w = args[2].getNum();
1699
h = args[3].getNum();
1700
state->moveTo(x, y);
1701
state->lineTo(x + w, y);
1702
state->lineTo(x + w, y + h);
1703
state->lineTo(x, y + h);
1707
void Gfx::opClosePath(Object args[], int numArgs) {
1708
if (!state->isCurPt()) {
1709
error(getPos(), "No current point in closepath");
1715
//------------------------------------------------------------------------
1716
// path painting operators
1717
//------------------------------------------------------------------------
1719
void Gfx::opEndPath(Object args[], int numArgs) {
1723
void Gfx::opStroke(Object args[], int numArgs) {
1724
if (!state->isCurPt()) {
1725
//error(getPos(), "No path in stroke");
1728
if (state->isPath() && !contentIsHidden()) {
1729
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1738
void Gfx::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1739
if (!state->isCurPt()) {
1740
//error(getPos(), "No path in closepath/stroke");
1744
if (state->isPath() && !contentIsHidden()) {
1745
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1754
void Gfx::opFill(Object args[], int numArgs) {
1755
if (!state->isCurPt()) {
1756
//error(getPos(), "No path in fill");
1759
if (state->isPath() && !contentIsHidden()) {
1760
if (state->getFillColorSpace()->getMode() == csPattern) {
1761
doPatternFill(gFalse);
1769
void Gfx::opEOFill(Object args[], int numArgs) {
1770
if (!state->isCurPt()) {
1771
//error(getPos(), "No path in eofill");
1774
if (state->isPath() && !contentIsHidden()) {
1775
if (state->getFillColorSpace()->getMode() == csPattern) {
1776
doPatternFill(gTrue);
1784
void Gfx::opFillStroke(Object args[], int numArgs) {
1785
if (!state->isCurPt()) {
1786
//error(getPos(), "No path in fill/stroke");
1789
if (state->isPath() && !contentIsHidden()) {
1790
if (state->getFillColorSpace()->getMode() == csPattern) {
1791
doPatternFill(gFalse);
1795
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1804
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1805
if (!state->isCurPt()) {
1806
//error(getPos(), "No path in closepath/fill/stroke");
1809
if (state->isPath() && !contentIsHidden()) {
1811
if (state->getFillColorSpace()->getMode() == csPattern) {
1812
doPatternFill(gFalse);
1816
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1825
void Gfx::opEOFillStroke(Object args[], int numArgs) {
1826
if (!state->isCurPt()) {
1827
//error(getPos(), "No path in eofill/stroke");
1830
if (state->isPath() && !contentIsHidden()) {
1831
if (state->getFillColorSpace()->getMode() == csPattern) {
1832
doPatternFill(gTrue);
1836
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1845
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1846
if (!state->isCurPt()) {
1847
//error(getPos(), "No path in closepath/eofill/stroke");
1850
if (state->isPath() && !contentIsHidden()) {
1852
if (state->getFillColorSpace()->getMode() == csPattern) {
1853
doPatternFill(gTrue);
1857
if (state->getStrokeColorSpace()->getMode() == csPattern) {
1866
void Gfx::doPatternFill(GBool eoFill) {
1867
GfxPattern *pattern;
1869
// this is a bit of a kludge -- patterns can be really slow, so we
1870
// skip them if we're only doing text extraction, since they almost
1871
// certainly don't contain any text
1872
if (!out->needNonText()) {
1876
if (!(pattern = state->getFillPattern())) {
1879
switch (pattern->getType()) {
1881
doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill);
1884
doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);
1887
error(getPos(), "Unimplemented pattern type (%d) in fill",
1888
pattern->getType());
1893
void Gfx::doPatternStroke() {
1894
GfxPattern *pattern;
1896
// this is a bit of a kludge -- patterns can be really slow, so we
1897
// skip them if we're only doing text extraction, since they almost
1898
// certainly don't contain any text
1899
if (!out->needNonText()) {
1903
if (!(pattern = state->getStrokePattern())) {
1906
switch (pattern->getType()) {
1908
doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse);
1911
doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);
1914
error(getPos(), "Unimplemented pattern type (%d) in stroke",
1915
pattern->getType());
1920
void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
1921
GBool stroke, GBool eoFill) {
1922
GfxPatternColorSpace *patCS;
1926
double xMin, yMin, xMax, yMax, x, y, x1, y1;
1927
double cxMin, cyMin, cxMax, cyMax;
1928
int xi0, yi0, xi1, yi1, xi, yi;
1929
double *ctm, *btm, *ptm;
1930
double m[6], ictm[6], m1[6], imb[6];
1932
double xstep, ystep;
1936
patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
1937
: state->getFillColorSpace());
1939
// construct a (pattern space) -> (current space) transform matrix
1940
ctm = state->getCTM();
1942
ptm = tPat->getMatrix();
1943
// iCTM = invert CTM
1944
det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1945
ictm[0] = ctm[3] * det;
1946
ictm[1] = -ctm[1] * det;
1947
ictm[2] = -ctm[2] * det;
1948
ictm[3] = ctm[0] * det;
1949
ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1950
ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1951
// m1 = PTM * BTM = PTM * base transform matrix
1952
m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1953
m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1954
m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1955
m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1956
m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1957
m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1958
// m = m1 * iCTM = (PTM * BTM) * (iCTM)
1959
m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1960
m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1961
m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1962
m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1963
m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1964
m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1966
// construct a (device space) -> (pattern space) transform matrix
1967
det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1968
imb[0] = m1[3] * det;
1969
imb[1] = -m1[1] * det;
1970
imb[2] = -m1[2] * det;
1971
imb[3] = m1[0] * det;
1972
imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1973
imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1975
// save current graphics state
1976
savedPath = state->getPath()->copy();
1979
// set underlying color space (for uncolored tiling patterns); set
1980
// various other parameters (stroke color, line width) to match
1982
if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1983
state->setFillColorSpace(cs->copy());
1984
out->updateFillColorSpace(state);
1985
state->setStrokeColorSpace(cs->copy());
1986
out->updateStrokeColorSpace(state);
1988
state->setFillColor(state->getStrokeColor());
1990
state->setStrokeColor(state->getFillColor());
1993
cs = new GfxDeviceGrayColorSpace();
1994
state->setFillColorSpace(cs);
1995
cs->getDefaultColor(&color);
1996
state->setFillColor(&color);
1997
out->updateFillColorSpace(state);
1998
state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1999
state->setStrokeColor(&color);
2000
out->updateStrokeColorSpace(state);
2002
state->setFillPattern(NULL);
2003
out->updateFillColor(state);
2004
state->setStrokePattern(NULL);
2005
out->updateStrokeColor(state);
2007
// clip to current path
2009
state->clipToStrokePath();
2010
out->clipToStrokePath(state);
2011
} else if (!textHaveCSPattern && !maskHaveCSPattern) {
2020
state->setLineWidth(0);
2021
out->updateLineWidth(state);
2023
// get the clip region, check for empty
2024
state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
2025
if (cxMin > cxMax || cyMin > cyMax) {
2029
// transform clip region bbox to pattern space
2030
xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
2031
yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
2032
x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
2033
y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
2036
} else if (x1 > xMax) {
2041
} else if (y1 > yMax) {
2044
x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
2045
y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
2048
} else if (x1 > xMax) {
2053
} else if (y1 > yMax) {
2056
x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
2057
y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
2060
} else if (x1 > xMax) {
2065
} else if (y1 > yMax) {
2070
//~ this should treat negative steps differently -- start at right/top
2071
//~ edge instead of left/bottom (?)
2072
xstep = fabs(tPat->getXStep());
2073
ystep = fabs(tPat->getYStep());
2074
xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);
2075
xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;
2076
yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);
2077
yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1;
2078
for (i = 0; i < 4; ++i) {
2081
if (!contentIsHidden()) {
2084
if (out->useTilingPatternFill() &&
2085
out->tilingPatternFill(state, tPat->getContentStream(),
2086
tPat->getPaintType(), tPat->getResDict(),
2087
m1, tPat->getBBox(),
2088
xi0, yi0, xi1, yi1, xstep, ystep)) {
2091
for (yi = yi0; yi < yi1; ++yi) {
2092
for (xi = xi0; xi < xi1; ++xi) {
2095
m1[4] = x * m[0] + y * m[2] + m[4];
2096
m1[5] = x * m[1] + y * m[3] + m[5];
2097
doForm1(tPat->getContentStream(), tPat->getResDict(),
2098
m1, tPat->getBBox());
2104
// restore graphics state
2107
state->setPath(savedPath);
2110
void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
2111
GBool stroke, GBool eoFill) {
2112
GfxShading *shading;
2114
double *ctm, *btm, *ptm;
2115
double m[6], ictm[6], m1[6];
2116
double xMin, yMin, xMax, yMax;
2119
shading = sPat->getShading();
2121
// save current graphics state
2122
savedPath = state->getPath()->copy();
2126
if (shading->getHasBBox()) {
2127
shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2128
state->moveTo(xMin, yMin);
2129
state->lineTo(xMax, yMin);
2130
state->lineTo(xMax, yMax);
2131
state->lineTo(xMin, yMax);
2134
if (!textHaveCSPattern && !maskHaveCSPattern)
2136
state->setPath(savedPath->copy());
2139
// clip to current path
2141
state->clipToStrokePath();
2142
out->clipToStrokePath(state);
2143
} else if (!textHaveCSPattern && !maskHaveCSPattern) {
2152
// set the color space
2153
state->setFillColorSpace(shading->getColorSpace()->copy());
2154
out->updateFillColorSpace(state);
2156
// background color fill
2157
if (shading->getHasBackground()) {
2158
state->setFillColor(shading->getBackground());
2159
out->updateFillColor(state);
2160
if (!contentIsHidden())
2165
// construct a (pattern space) -> (current space) transform matrix
2166
ctm = state->getCTM();
2168
ptm = sPat->getMatrix();
2169
// iCTM = invert CTM
2170
det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2171
ictm[0] = ctm[3] * det;
2172
ictm[1] = -ctm[1] * det;
2173
ictm[2] = -ctm[2] * det;
2174
ictm[3] = ctm[0] * det;
2175
ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2176
ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2177
// m1 = PTM * BTM = PTM * base transform matrix
2178
m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2179
m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2180
m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2181
m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2182
m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2183
m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2184
// m = m1 * iCTM = (PTM * BTM) * (iCTM)
2185
m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2186
m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2187
m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2188
m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2189
m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2190
m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2192
// set the new matrix
2193
state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
2194
out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
2196
#if 1 //~tmp: turn off anti-aliasing temporarily
2197
GBool vaa = out->getVectorAntialias();
2199
out->setVectorAntialias(gFalse);
2203
// do shading type-specific operations
2204
switch (shading->getType()) {
2206
doFunctionShFill((GfxFunctionShading *)shading);
2209
doAxialShFill((GfxAxialShading *)shading);
2212
doRadialShFill((GfxRadialShading *)shading);
2216
doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
2220
doPatchMeshShFill((GfxPatchMeshShading *)shading);
2224
#if 1 //~tmp: turn off anti-aliasing temporarily
2226
out->setVectorAntialias(gTrue);
2230
// restore graphics state
2232
state->setPath(savedPath);
2235
void Gfx::opShFill(Object args[], int numArgs) {
2236
GfxShading *shading;
2238
double xMin, yMin, xMax, yMax;
2240
if (!(shading = res->lookupShading(args[0].getName(), this))) {
2244
// save current graphics state
2245
savedPath = state->getPath()->copy();
2249
if (shading->getHasBBox()) {
2250
shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2251
state->moveTo(xMin, yMin);
2252
state->lineTo(xMax, yMin);
2253
state->lineTo(xMax, yMax);
2254
state->lineTo(xMin, yMax);
2261
// set the color space
2262
state->setFillColorSpace(shading->getColorSpace()->copy());
2263
out->updateFillColorSpace(state);
2265
#if 1 //~tmp: turn off anti-aliasing temporarily
2266
GBool vaa = out->getVectorAntialias();
2268
out->setVectorAntialias(gFalse);
2272
// do shading type-specific operations
2273
switch (shading->getType()) {
2275
doFunctionShFill((GfxFunctionShading *)shading);
2278
doAxialShFill((GfxAxialShading *)shading);
2281
doRadialShFill((GfxRadialShading *)shading);
2285
doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
2289
doPatchMeshShFill((GfxPatchMeshShading *)shading);
2293
#if 1 //~tmp: turn off anti-aliasing temporarily
2295
out->setVectorAntialias(gTrue);
2299
// restore graphics state
2301
state->setPath(savedPath);
2306
void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
2307
double x0, y0, x1, y1;
2310
if (out->useShadedFills( shading->getType() ) &&
2311
out->functionShadedFill(state, shading)) {
2315
shading->getDomain(&x0, &y0, &x1, &y1);
2316
shading->getColor(x0, y0, &colors[0]);
2317
shading->getColor(x0, y1, &colors[1]);
2318
shading->getColor(x1, y0, &colors[2]);
2319
shading->getColor(x1, y1, &colors[3]);
2320
doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
2323
void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
2324
double x0, double y0,
2325
double x1, double y1,
2326
GfxColor *colors, int depth) {
2328
GfxColor color0M, color1M, colorM0, colorM1, colorMM;
2329
GfxColor colors2[4];
2334
nComps = shading->getColorSpace()->getNComps();
2335
matrix = shading->getMatrix();
2337
// compare the four corner colors
2338
for (i = 0; i < 4; ++i) {
2339
for (j = 0; j < nComps; ++j) {
2340
if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
2349
// center of the rectangle
2350
xM = 0.5 * (x0 + x1);
2351
yM = 0.5 * (y0 + y1);
2353
// the four corner colors are close (or we hit the recursive limit)
2354
// -- fill the rectangle; but require at least one subdivision
2355
// (depth==0) to avoid problems when the four outer corners of the
2356
// shaded region are the same color
2357
if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
2359
// use the center color
2360
shading->getColor(xM, yM, &fillColor);
2361
state->setFillColor(&fillColor);
2362
out->updateFillColor(state);
2364
// fill the rectangle
2365
state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
2366
x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
2367
state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
2368
x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
2369
state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
2370
x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
2371
state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
2372
x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
2374
if (!contentIsHidden())
2378
// the four corner colors are not close enough -- subdivide the
2382
// colors[0] colorM0 colors[2]
2383
// (x0,y0) (xM,y0) (x1,y0)
2384
// +----------+----------+
2387
// color0M | colorMM | color1M
2388
// (x0,yM) +----------+----------+ (x1,yM)
2392
// +----------+----------+
2393
// colors[1] colorM1 colors[3]
2394
// (x0,y1) (xM,y1) (x1,y1)
2396
shading->getColor(x0, yM, &color0M);
2397
shading->getColor(x1, yM, &color1M);
2398
shading->getColor(xM, y0, &colorM0);
2399
shading->getColor(xM, y1, &colorM1);
2400
shading->getColor(xM, yM, &colorMM);
2402
// upper-left sub-rectangle
2403
colors2[0] = colors[0];
2404
colors2[1] = color0M;
2405
colors2[2] = colorM0;
2406
colors2[3] = colorMM;
2407
doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
2409
// lower-left sub-rectangle
2410
colors2[0] = color0M;
2411
colors2[1] = colors[1];
2412
colors2[2] = colorMM;
2413
colors2[3] = colorM1;
2414
doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
2416
// upper-right sub-rectangle
2417
colors2[0] = colorM0;
2418
colors2[1] = colorMM;
2419
colors2[2] = colors[2];
2420
colors2[3] = color1M;
2421
doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
2423
// lower-right sub-rectangle
2424
colors2[0] = colorMM;
2425
colors2[1] = colorM1;
2426
colors2[2] = color1M;
2427
colors2[3] = colors[3];
2428
doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
2432
static void bubbleSort(double array[])
2434
for (int j = 0; j < 3; ++j) {
2436
for (int k = j + 1; k < 4; ++k) {
2437
if (array[k] < array[kk]) {
2441
double tmp = array[j];
2442
array[j] = array[kk];
2447
void Gfx::doAxialShFill(GfxAxialShading *shading) {
2448
double xMin, yMin, xMax, yMax;
2449
double x0, y0, x1, y1;
2451
GBool dxZero, dyZero;
2452
double bboxIntersections[4];
2453
double tMin, tMax, tx, ty;
2454
double s[4], sMin, sMax, tmp;
2455
double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
2457
double ta[axialMaxSplits + 1];
2458
int next[axialMaxSplits + 1];
2459
GfxColor color0, color1;
2462
GBool needExtend = gTrue;
2464
// get the clip region bbox
2465
state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2467
// compute min and max t values, based on the four corners of the
2469
shading->getCoords(&x0, &y0, &x1, &y1);
2472
dxZero = fabs(dx) < 0.01;
2473
dyZero = fabs(dy) < 0.01;
2474
if (dxZero && dyZero) {
2477
mul = 1 / (dx * dx + dy * dy);
2478
bboxIntersections[0] = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
2479
bboxIntersections[1] = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
2480
bboxIntersections[2] = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
2481
bboxIntersections[3] = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
2482
bubbleSort(bboxIntersections);
2483
tMin = bboxIntersections[0];
2484
tMax = bboxIntersections[3];
2485
if (tMin < 0 && !shading->getExtend0()) {
2488
if (tMax > 1 && !shading->getExtend1()) {
2493
if (out->useShadedFills( shading->getType() ) &&
2494
out->axialShadedFill(state, shading, tMin, tMax)) {
2498
// get the function domain
2499
t0 = shading->getDomain0();
2500
t1 = shading->getDomain1();
2502
// Traverse the t axis and do the shading.
2504
// For each point (tx, ty) on the t axis, consider a line through
2505
// that point perpendicular to the t axis:
2507
// x(s) = tx + s * -dy --> s = (x - tx) / -dy
2508
// y(s) = ty + s * dx --> s = (y - ty) / dx
2510
// Then look at the intersection of this line with the bounding box
2511
// (xMin, yMin, xMax, yMax). In the general case, there are four
2512
// intersection points:
2514
// s0 = (xMin - tx) / -dy
2515
// s1 = (xMax - tx) / -dy
2516
// s2 = (yMin - ty) / dx
2517
// s3 = (yMax - ty) / dx
2519
// and we want the middle two s values.
2521
// In the case where dx = 0, take s0 and s1; in the case where dy =
2522
// 0, take s2 and s3.
2524
// Each filled polygon is bounded by two of these line segments
2525
// perpdendicular to the t axis.
2527
// The t axis is bisected into smaller regions until the color
2528
// difference across a region is small enough, and then the region
2529
// is painted with a single color.
2531
// set up: require at least one split to avoid problems when the two
2532
// ends of the t axis have the same color
2533
nComps = shading->getColorSpace()->getNComps();
2535
next[0] = axialMaxSplits / 2;
2536
ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
2537
next[axialMaxSplits / 2] = axialMaxSplits;
2538
ta[axialMaxSplits] = tMax;
2540
// compute the color at t = tMin
2543
} else if (tMin > 1) {
2546
tt = t0 + (t1 - t0) * tMin;
2548
shading->getColor(tt, &color0);
2550
if (out->useFillColorStop()) {
2551
// make sure we add stop color when t = tMin
2552
state->setFillColor(&color0);
2553
out->updateFillColorStop(state, 0);
2556
// compute the coordinates of the point on the t axis at t = tMin;
2557
// then compute the intersection of the perpendicular line with the
2559
tx = x0 + tMin * dx;
2560
ty = y0 + tMin * dy;
2561
if (dxZero && dyZero) {
2563
} else if (dxZero) {
2564
sMin = (xMin - tx) / -dy;
2565
sMax = (xMax - tx) / -dy;
2566
if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2567
} else if (dyZero) {
2568
sMin = (yMin - ty) / dx;
2569
sMax = (yMax - ty) / dx;
2570
if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2572
s[0] = (yMin - ty) / dx;
2573
s[1] = (yMax - ty) / dx;
2574
s[2] = (xMin - tx) / -dy;
2575
s[3] = (xMax - tx) / -dy;
2580
ux0 = tx - sMin * dy;
2581
uy0 = ty + sMin * dx;
2582
vx0 = tx - sMax * dy;
2583
vy0 = ty + sMax * dx;
2586
bool doneBBox1, doneBBox2;
2587
if (dxZero && dyZero) {
2588
doneBBox1 = doneBBox2 = true;
2590
doneBBox1 = bboxIntersections[1] < tMin;
2591
doneBBox2 = bboxIntersections[2] > tMax;
2594
// If output device doesn't support the extended mode required
2595
// we have to do it here
2596
needExtend = !out->axialShadedSupportExtend(state, shading);
2598
while (i < axialMaxSplits) {
2600
// bisect until color difference is small enough or we hit the
2606
} else if (ta[j] > 1) {
2609
tt = t0 + (t1 - t0) * ta[j];
2611
shading->getColor(tt, &color1);
2612
if (isSameGfxColor(color1, color0, nComps, axialColorDelta)) {
2613
// in these two if what we guarantee is that if we are skipping lots of
2614
// positions because the colors are the same, we still create a region
2615
// with vertexs passing by bboxIntersections[1] and bboxIntersections[2]
2616
// otherwise we can have empty regions that should really be painted
2617
// like happened in bug 19896
2618
// What we do to ensure that we pass a line through this points
2619
// is making sure use the exact bboxIntersections[] value as one of the used ta[] values
2620
if (!doneBBox1 && ta[i] < bboxIntersections[1] && ta[j] > bboxIntersections[1]) {
2621
int teoricalj = (int) ((bboxIntersections[1] - tMin) * axialMaxSplits / (tMax - tMin));
2622
if (teoricalj <= i) teoricalj = i + 1;
2623
if (teoricalj < j) {
2624
next[i] = teoricalj;
2625
next[teoricalj] = j;
2630
ta[teoricalj] = bboxIntersections[1];
2634
if (!doneBBox2 && ta[i] < bboxIntersections[2] && ta[j] > bboxIntersections[2]) {
2635
int teoricalj = (int) ((bboxIntersections[2] - tMin) * axialMaxSplits / (tMax - tMin));
2636
if (teoricalj <= i) teoricalj = i + 1;
2637
if (teoricalj < j) {
2638
next[i] = teoricalj;
2639
next[teoricalj] = j;
2644
ta[teoricalj] = bboxIntersections[2];
2651
ta[k] = 0.5 * (ta[i] + ta[j]);
2657
// use the average of the colors of the two sides of the region
2658
for (k = 0; k < nComps; ++k) {
2659
color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
2662
// compute the coordinates of the point on the t axis; then
2663
// compute the intersection of the perpendicular line with the
2665
tx = x0 + ta[j] * dx;
2666
ty = y0 + ta[j] * dy;
2667
if (dxZero && dyZero) {
2669
} else if (dxZero) {
2670
sMin = (xMin - tx) / -dy;
2671
sMax = (xMax - tx) / -dy;
2672
if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2673
} else if (dyZero) {
2674
sMin = (yMin - ty) / dx;
2675
sMax = (yMax - ty) / dx;
2676
if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
2678
s[0] = (yMin - ty) / dx;
2679
s[1] = (yMax - ty) / dx;
2680
s[2] = (xMin - tx) / -dy;
2681
s[3] = (xMax - tx) / -dy;
2686
ux1 = tx - sMin * dy;
2687
uy1 = ty + sMin * dx;
2688
vx1 = tx - sMax * dy;
2689
vy1 = ty + sMax * dx;
2692
state->setFillColor(&color0);
2693
if (out->useFillColorStop())
2694
out->updateFillColorStop(state, (ta[j] - tMin)/(tMax - tMin));
2696
out->updateFillColor(state);
2700
state->moveTo(ux0, uy0);
2701
state->lineTo(vx0, vy0);
2702
state->lineTo(vx1, vy1);
2703
state->lineTo(ux1, uy1);
2707
if (!out->useFillColorStop()) {
2708
if (!contentIsHidden())
2713
// set up for next region
2722
if (out->useFillColorStop()) {
2724
state->moveTo(xMin, yMin);
2725
state->lineTo(xMin, yMax);
2726
state->lineTo(xMax, yMax);
2727
state->lineTo(xMax, yMin);
2730
if (!contentIsHidden())
2736
static inline void getShadingColorRadialHelper(double t0, double t1, double t, GfxRadialShading *shading, GfxColor *color)
2740
shading->getColor(t0, color);
2741
} else if (t > t1) {
2742
shading->getColor(t1, color);
2744
shading->getColor(t, color);
2748
shading->getColor(t0, color);
2749
} else if (t < t1) {
2750
shading->getColor(t1, color);
2752
shading->getColor(t, color);
2757
void Gfx::doRadialShFill(GfxRadialShading *shading) {
2758
double xMin, yMin, xMax, yMax;
2759
double x0, y0, r0, x1, y1, r1, t0, t1;
2761
GfxColor colorA, colorB;
2762
double xa, ya, xb, yb, ra, rb;
2763
double ta, tb, sa, sb;
2764
double sz, xz, yz, sMin, sMax;
2768
double theta, alpha, angle, t;
2769
GBool needExtend = gTrue;
2771
// get the shading info
2772
shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2773
t0 = shading->getDomain0();
2774
t1 = shading->getDomain1();
2775
nComps = shading->getColorSpace()->getNComps();
2777
// Compute the point at which r(s) = 0; check for the enclosed
2778
// circles case; and compute the angles for the tangent lines.
2779
if (x0 == x1 && y0 == y1) {
2781
theta = 0; // make gcc happy
2782
sz = 0; // make gcc happy
2783
} else if (r0 == r1) {
2786
sz = 0; // make gcc happy
2788
sz = (r1 > r0) ? -r0 / (r1 - r0) : -r1 / (r0 - r1);
2789
xz = x0 + sz * (x1 - x0);
2790
yz = y0 + sz * (y1 - y0);
2791
enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
2792
theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
2800
alpha = atan2(y1 - y0, x1 - x0);
2803
// compute the (possibly extended) s range
2804
state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2811
// solve for x(s) + r(s) = xMin
2812
if ((x1 + r1) - (x0 + r0) != 0) {
2813
sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
2816
} else if (sa > sMax) {
2820
// solve for x(s) - r(s) = xMax
2821
if ((x1 - r1) - (x0 - r0) != 0) {
2822
sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
2825
} else if (sa > sMax) {
2829
// solve for y(s) + r(s) = yMin
2830
if ((y1 + r1) - (y0 + r0) != 0) {
2831
sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
2834
} else if (sa > sMax) {
2838
// solve for y(s) - r(s) = yMax
2839
if ((y1 - r1) - (y0 - r0) != 0) {
2840
sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
2843
} else if (sa > sMax) {
2852
} else if (r0 > r1) {
2857
// check the 'extend' flags
2858
if (!shading->getExtend0() && sMin < 0) {
2861
if (!shading->getExtend1() && sMax > 1) {
2866
if (out->useShadedFills( shading->getType() ) &&
2867
out->radialShadedFill(state, shading, sMin, sMax)) {
2871
// compute the number of steps into which circles must be divided to
2872
// achieve a curve flatness of 0.1 pixel in device space for the
2873
// largest circle (note that "device space" is 72 dpi when generating
2874
// PostScript, hence the relatively small 0.1 pixel accuracy)
2875
ctm = state->getCTM();
2877
if (fabs(ctm[1]) > t) {
2880
if (fabs(ctm[2]) > t) {
2883
if (fabs(ctm[3]) > t) {
2894
n = (int)(M_PI / acos(1 - 0.1 / t));
2897
} else if (n > 200) {
2902
// setup for the start circle
2905
ta = t0 + sa * (t1 - t0);
2906
xa = x0 + sa * (x1 - x0);
2907
ya = y0 + sa * (y1 - y0);
2908
ra = r0 + sa * (r1 - r0);
2909
getShadingColorRadialHelper(t0, t1, ta, shading, &colorA);
2911
needExtend = !out->radialShadedSupportExtend(state, shading);
2914
while (ia < radialMaxSplits) {
2916
// go as far along the t axis (toward t1) as we can, such that the
2917
// color difference is within the tolerance (radialColorDelta) --
2918
// this uses bisection (between the current value, t, and t1),
2919
// limited to radialMaxSplits points along the t axis; require at
2920
// least one split to avoid problems when the innermost and
2921
// outermost colors are the same
2922
ib = radialMaxSplits;
2924
tb = t0 + sb * (t1 - t0);
2925
getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
2926
while (ib - ia > 1) {
2927
if (isSameGfxColor(colorB, colorA, nComps, radialColorDelta)) {
2928
// The shading is not necessarily lineal so having two points with the
2929
// same color does not mean all the areas in between have the same color too
2931
for (; ic <= ib; ic++) {
2933
const double sc = sMin + ((double)ic / (double)radialMaxSplits) * (sMax - sMin);
2934
const double tc = t0 + sc * (t1 - t0);
2935
getShadingColorRadialHelper(t0, t1, tc, shading, &colorC);
2936
if (!isSameGfxColor(colorC, colorA, nComps, radialColorDelta)) {
2940
ib = (ic > ia + 1) ? ic - 1 : ia + 1;
2941
sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2942
tb = t0 + sb * (t1 - t0);
2943
getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
2947
sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2948
tb = t0 + sb * (t1 - t0);
2949
getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
2952
// compute center and radius of the circle
2953
xb = x0 + sb * (x1 - x0);
2954
yb = y0 + sb * (y1 - y0);
2955
rb = r0 + sb * (r1 - r0);
2957
// use the average of the colors at the two circles
2958
for (k = 0; k < nComps; ++k) {
2959
colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
2961
state->setFillColor(&colorA);
2962
if (out->useFillColorStop())
2963
out->updateFillColorStop(state, (sa - sMin)/(sMax - sMin));
2965
out->updateFillColor(state);
2969
// construct path for first circle (counterclockwise)
2970
state->moveTo(xa + ra, ya);
2971
for (k = 1; k < n; ++k) {
2972
angle = ((double)k / (double)n) * 2 * M_PI;
2973
state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2977
// construct and append path for second circle (clockwise)
2978
state->moveTo(xb + rb, yb);
2979
for (k = 1; k < n; ++k) {
2980
angle = -((double)k / (double)n) * 2 * M_PI;
2981
state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2985
// construct the first subpath (clockwise)
2986
state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
2987
ya + ra * sin(alpha + theta + 0.5 * M_PI));
2988
for (k = 0; k < n; ++k) {
2989
angle = alpha + theta + 0.5 * M_PI
2990
- ((double)k / (double)n) * (2 * theta + M_PI);
2991
state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2993
for (k = 0; k < n; ++k) {
2994
angle = alpha - theta - 0.5 * M_PI
2995
+ ((double)k / (double)n) * (2 * theta - M_PI);
2996
state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3000
// construct the second subpath (counterclockwise)
3001
state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3002
ya + ra * sin(alpha + theta + 0.5 * M_PI));
3003
for (k = 0; k < n; ++k) {
3004
angle = alpha + theta + 0.5 * M_PI
3005
+ ((double)k / (double)n) * (-2 * theta + M_PI);
3006
state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3008
for (k = 0; k < n; ++k) {
3009
angle = alpha - theta - 0.5 * M_PI
3010
+ ((double)k / (double)n) * (2 * theta + M_PI);
3011
state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3017
if (!out->useFillColorStop()) {
3019
if (!contentIsHidden())
3024
// step to the next value of t
3034
if (out->useFillColorStop()) {
3035
// make sure we add stop color when sb = sMax
3036
state->setFillColor(&colorA);
3037
out->updateFillColorStop(state, (sb - sMin)/(sMax - sMin));
3040
state->moveTo(xMin, yMin);
3041
state->lineTo(xMin, yMax);
3042
state->lineTo(xMax, yMax);
3043
state->lineTo(xMax, yMin);
3046
if (!contentIsHidden())
3055
// extend the smaller circle
3056
if ((shading->getExtend0() && r0 <= r1) ||
3057
(shading->getExtend1() && r1 < r0)) {
3069
shading->getColor(ta, &colorA);
3070
state->setFillColor(&colorA);
3071
out->updateFillColor(state);
3072
state->moveTo(xa + ra, ya);
3073
for (k = 1; k < n; ++k) {
3074
angle = ((double)k / (double)n) * 2 * M_PI;
3075
state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3078
if (!contentIsHidden())
3083
// extend the larger circle
3084
if ((shading->getExtend0() && r0 > r1) ||
3085
(shading->getExtend1() && r1 >= r0)) {
3097
shading->getColor(ta, &colorA);
3098
state->setFillColor(&colorA);
3099
out->updateFillColor(state);
3100
state->moveTo(xMin, yMin);
3101
state->lineTo(xMin, yMax);
3102
state->lineTo(xMax, yMax);
3103
state->lineTo(xMax, yMin);
3105
state->moveTo(xa + ra, ya);
3106
for (k = 1; k < n; ++k) {
3107
angle = ((double)k / (double)n) * 2 * M_PI;
3108
state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3111
if (!contentIsHidden())
3118
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
3119
double x0, y0, x1, y1, x2, y2;
3122
if( out->useShadedFills( shading->getType() ) ) {
3123
if( out->gouraudTriangleShadedFill( state, shading ) )
3126
// preallocate a path (speed improvements)
3127
state->moveTo(0., 0.);
3128
state->lineTo(1., 0.);
3129
state->lineTo(0., 1.);
3132
GfxState::ReusablePathIterator *reusablePath = state->getReusablePath();
3134
if (shading->isParameterized()) {
3135
// work with parameterized values:
3136
double color0, color1, color2;
3137
// a relative threshold:
3138
const double refineColorThreshold = gouraudParameterizedColorDelta *
3139
(shading->getParameterDomainMax() - shading->getParameterDomainMin());
3140
for (i = 0; i < shading->getNTriangles(); ++i) {
3141
shading->getTriangle(i, &x0, &y0, &color0,
3144
gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath);
3148
// this always produces output -- even for parameterized ranges.
3149
// But it ignores the parameterized color map (the function).
3151
// Note that using this code in for parameterized shadings might be
3152
// correct in circumstances (namely if the function is linear in the actual
3153
// triangle), but in general, it will simply be wrong.
3154
GfxColor color0, color1, color2;
3155
for (i = 0; i < shading->getNTriangles(); ++i) {
3156
shading->getTriangle(i, &x0, &y0, &color0,
3159
gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath);
3163
delete reusablePath;
3166
void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
3167
double x1, double y1, GfxColor *color1,
3168
double x2, double y2, GfxColor *color2,
3169
int nComps, int depth, GfxState::ReusablePathIterator *path) {
3170
double x01, y01, x12, y12, x20, y20;
3171
GfxColor color01, color12, color20;
3174
for (i = 0; i < nComps; ++i) {
3175
if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
3176
abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
3180
if (i == nComps || depth == gouraudMaxDepth) {
3181
state->setFillColor(color0);
3182
out->updateFillColor(state);
3184
path->reset(); assert(!path->isEnd());
3185
path->setCoord(x0,y0); path->next(); assert(!path->isEnd());
3186
path->setCoord(x1,y1); path->next(); assert(!path->isEnd());
3187
path->setCoord(x2,y2); path->next(); assert(!path->isEnd());
3188
path->setCoord(x0,y0); path->next(); assert( path->isEnd());
3190
if (!contentIsHidden())
3193
x01 = 0.5 * (x0 + x1);
3194
y01 = 0.5 * (y0 + y1);
3195
x12 = 0.5 * (x1 + x2);
3196
y12 = 0.5 * (y1 + y2);
3197
x20 = 0.5 * (x2 + x0);
3198
y20 = 0.5 * (y2 + y0);
3199
for (i = 0; i < nComps; ++i) {
3200
color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
3201
color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
3202
color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
3204
gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
3205
x20, y20, &color20, nComps, depth + 1, path);
3206
gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
3207
x12, y12, &color12, nComps, depth + 1, path);
3208
gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
3209
x20, y20, &color20, nComps, depth + 1, path);
3210
gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
3211
x2, y2, color2, nComps, depth + 1, path);
3214
void Gfx::gouraudFillTriangle(double x0, double y0, double color0,
3215
double x1, double y1, double color1,
3216
double x2, double y2, double color2,
3217
double refineColorThreshold, int depth, GfxGouraudTriangleShading *shading, GfxState::ReusablePathIterator *path) {
3218
const double meanColor = (color0 + color1 + color2) / 3;
3220
const bool isFineEnough =
3221
fabs(color0 - meanColor) < refineColorThreshold &&
3222
fabs(color1 - meanColor) < refineColorThreshold &&
3223
fabs(color2 - meanColor) < refineColorThreshold;
3225
if (isFineEnough || depth == gouraudMaxDepth) {
3228
shading->getParameterizedColor(meanColor, &color);
3229
state->setFillColor(&color);
3230
out->updateFillColor(state);
3232
path->reset(); assert(!path->isEnd());
3233
path->setCoord(x0,y0); path->next(); assert(!path->isEnd());
3234
path->setCoord(x1,y1); path->next(); assert(!path->isEnd());
3235
path->setCoord(x2,y2); path->next(); assert(!path->isEnd());
3236
path->setCoord(x0,y0); path->next(); assert( path->isEnd());
3238
if (!contentIsHidden())
3241
const double x01 = 0.5 * (x0 + x1);
3242
const double y01 = 0.5 * (y0 + y1);
3243
const double x12 = 0.5 * (x1 + x2);
3244
const double y12 = 0.5 * (y1 + y2);
3245
const double x20 = 0.5 * (x2 + x0);
3246
const double y20 = 0.5 * (y2 + y0);
3247
const double color01 = (color0 + color1) / 2.;
3248
const double color12 = (color1 + color2) / 2.;
3249
const double color20 = (color2 + color0) / 2.;
3251
gouraudFillTriangle(x0, y0, color0,
3254
refineColorThreshold, depth, shading, path);
3255
gouraudFillTriangle(x01, y01, color01,
3258
refineColorThreshold, depth, shading, path);
3259
gouraudFillTriangle(x01, y01, color01,
3262
refineColorThreshold, depth, shading, path);
3263
gouraudFillTriangle(x20, y20, color20,
3266
refineColorThreshold, depth, shading, path);
3270
void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
3273
if (shading->getNPatches() > 128) {
3275
} else if (shading->getNPatches() > 64) {
3277
} else if (shading->getNPatches() > 16) {
3283
* Parameterized shadings take one parameter [t_0,t_e]
3284
* and map it into the color space.
3286
* Consequently, all color values are stored as doubles.
3288
* These color values are interpreted as parameters for parameterized
3289
* shadings and as colorspace entities otherwise.
3291
* The only difference is that color space entities are stored into
3292
* DOUBLE arrays, not into arrays of type GfxColorComp.
3294
const int colorComps = shading->getColorSpace()->getNComps();
3295
double refineColorThreshold;
3296
if( shading->isParameterized() ) {
3297
refineColorThreshold = gouraudParameterizedColorDelta *
3298
(shading->getParameterDomainMax() - shading->getParameterDomainMin());
3301
refineColorThreshold = patchColorDelta;
3304
for (i = 0; i < shading->getNPatches(); ++i) {
3305
fillPatch(shading->getPatch(i),
3307
shading->isParameterized() ? 1 : colorComps,
3308
refineColorThreshold,
3315
void Gfx::fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, GfxPatchMeshShading *shading) {
3316
GfxPatch patch00, patch01, patch10, patch11;
3317
double xx[4][8], yy[4][8];
3321
for (i = 0; i < patchColorComps; ++i) {
3322
// these comparisons are done in double arithmetics.
3324
// For non-parameterized shadings, they are done in color space
3326
if (fabs(patch->color[0][0].c[i] - patch->color[0][1].c[i]) > refineColorThreshold ||
3327
fabs(patch->color[0][1].c[i] - patch->color[1][1].c[i]) > refineColorThreshold ||
3328
fabs(patch->color[1][1].c[i] - patch->color[1][0].c[i]) > refineColorThreshold ||
3329
fabs(patch->color[1][0].c[i] - patch->color[0][0].c[i]) > refineColorThreshold) {
3333
if (i == patchColorComps || depth == patchMaxDepth) {
3335
if( shading->isParameterized() ) {
3336
shading->getParameterizedColor( patch->color[0][0].c[0], &flatColor );
3338
for( i = 0; i<colorComps; ++i ) {
3339
// simply cast to the desired type; that's all what is needed.
3340
flatColor.c[i] = GfxColorComp(patch->color[0][0].c[i]);
3343
state->setFillColor(&flatColor);
3344
out->updateFillColor(state);
3345
state->moveTo(patch->x[0][0], patch->y[0][0]);
3346
state->curveTo(patch->x[0][1], patch->y[0][1],
3347
patch->x[0][2], patch->y[0][2],
3348
patch->x[0][3], patch->y[0][3]);
3349
state->curveTo(patch->x[1][3], patch->y[1][3],
3350
patch->x[2][3], patch->y[2][3],
3351
patch->x[3][3], patch->y[3][3]);
3352
state->curveTo(patch->x[3][2], patch->y[3][2],
3353
patch->x[3][1], patch->y[3][1],
3354
patch->x[3][0], patch->y[3][0]);
3355
state->curveTo(patch->x[2][0], patch->y[2][0],
3356
patch->x[1][0], patch->y[1][0],
3357
patch->x[0][0], patch->y[0][0]);
3359
if (!contentIsHidden())
3363
for (i = 0; i < 4; ++i) {
3364
xx[i][0] = patch->x[i][0];
3365
yy[i][0] = patch->y[i][0];
3366
xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
3367
yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
3368
xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
3369
yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
3370
xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
3371
yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
3372
xx[i][2] = 0.5 * (xx[i][1] + xxm);
3373
yy[i][2] = 0.5 * (yy[i][1] + yym);
3374
xx[i][5] = 0.5 * (xxm + xx[i][6]);
3375
yy[i][5] = 0.5 * (yym + yy[i][6]);
3376
xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
3377
yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
3378
xx[i][7] = patch->x[i][3];
3379
yy[i][7] = patch->y[i][3];
3381
for (i = 0; i < 4; ++i) {
3382
patch00.x[0][i] = xx[0][i];
3383
patch00.y[0][i] = yy[0][i];
3384
patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
3385
patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
3386
xxm = 0.5 * (xx[1][i] + xx[2][i]);
3387
yym = 0.5 * (yy[1][i] + yy[2][i]);
3388
patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
3389
patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
3390
patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
3391
patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
3392
patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
3393
patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
3394
patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
3395
patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
3396
patch10.x[0][i] = patch00.x[3][i];
3397
patch10.y[0][i] = patch00.y[3][i];
3398
patch10.x[3][i] = xx[3][i];
3399
patch10.y[3][i] = yy[3][i];
3401
for (i = 4; i < 8; ++i) {
3402
patch01.x[0][i-4] = xx[0][i];
3403
patch01.y[0][i-4] = yy[0][i];
3404
patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
3405
patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
3406
xxm = 0.5 * (xx[1][i] + xx[2][i]);
3407
yym = 0.5 * (yy[1][i] + yy[2][i]);
3408
patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
3409
patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
3410
patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
3411
patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
3412
patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
3413
patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
3414
patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
3415
patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
3416
patch11.x[0][i-4] = patch01.x[3][i-4];
3417
patch11.y[0][i-4] = patch01.y[3][i-4];
3418
patch11.x[3][i-4] = xx[3][i];
3419
patch11.y[3][i-4] = yy[3][i];
3421
for (i = 0; i < patchColorComps; ++i) {
3422
patch00.color[0][0].c[i] = patch->color[0][0].c[i];
3423
patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
3424
patch->color[0][1].c[i]) / 2;
3425
patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
3426
patch01.color[0][1].c[i] = patch->color[0][1].c[i];
3427
patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
3428
patch->color[1][1].c[i]) / 2;
3429
patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
3430
patch11.color[1][1].c[i] = patch->color[1][1].c[i];
3431
patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
3432
patch->color[1][0].c[i]) / 2;
3433
patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
3434
patch10.color[1][0].c[i] = patch->color[1][0].c[i];
3435
patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
3436
patch->color[0][0].c[i]) / 2;
3437
patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
3438
patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
3439
patch01.color[1][1].c[i]) / 2;
3440
patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
3441
patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
3442
patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
3444
fillPatch(&patch00, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3445
fillPatch(&patch10, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3446
fillPatch(&patch01, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3447
fillPatch(&patch11, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
3451
void Gfx::doEndPath() {
3452
if (state->isCurPt() && clip != clipNone) {
3454
if (clip == clipNormal) {
3464
//------------------------------------------------------------------------
3465
// path clipping operators
3466
//------------------------------------------------------------------------
3468
void Gfx::opClip(Object args[], int numArgs) {
3472
void Gfx::opEOClip(Object args[], int numArgs) {
3476
//------------------------------------------------------------------------
3477
// text object operators
3478
//------------------------------------------------------------------------
3480
void Gfx::opBeginText(Object args[], int numArgs) {
3481
out->beginTextObject(state);
3483
state->setTextMat(1, 0, 0, 1, 0, 0);
3484
state->textMoveTo(0, 0);
3485
out->updateTextMat(state);
3486
out->updateTextPos(state);
3487
fontChanged = gTrue;
3488
if (!(state->getRender() & 4) && out->supportTextCSPattern(state)) {
3489
textHaveCSPattern = gTrue;
3493
void Gfx::opEndText(Object args[], int numArgs) {
3494
GBool needFill = out->deviceHasTextClip(state);
3495
out->endTextObject(state);
3497
if (textHaveCSPattern) {
3499
doPatternFill(gTrue);
3501
out->restoreState(state);
3503
textHaveCSPattern = gFalse;
3506
//------------------------------------------------------------------------
3507
// text state operators
3508
//------------------------------------------------------------------------
3510
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
3511
state->setCharSpace(args[0].getNum());
3512
out->updateCharSpace(state);
3515
void Gfx::opSetFont(Object args[], int numArgs) {
3518
if (!(font = res->lookupFont(args[0].getName()))) {
3519
// unsetting the font (drawing no text) is better than using the
3520
// previous one and drawing random glyphs from it
3521
state->setFont(NULL, args[1].getNum());
3522
fontChanged = gTrue;
3525
if (printCommands) {
3526
printf(" font: tag=%s name='%s' %g\n",
3527
font->getTag()->getCString(),
3528
font->getName() ? font->getName()->getCString() : "???",
3534
state->setFont(font, args[1].getNum());
3535
fontChanged = gTrue;
3538
void Gfx::opSetTextLeading(Object args[], int numArgs) {
3539
state->setLeading(args[0].getNum());
3542
void Gfx::opSetTextRender(Object args[], int numArgs) {
3543
int rm = state->getRender();
3544
state->setRender(args[0].getInt());
3545
if ((args[0].getInt() & 4) && textHaveCSPattern && drawText) {
3546
GBool needFill = out->deviceHasTextClip(state);
3547
out->endTextObject(state);
3549
doPatternFill(gTrue);
3551
out->restoreState(state);
3552
out->beginTextObject(state);
3553
out->updateTextMat(state);
3554
out->updateTextPos(state);
3555
textHaveCSPattern = gFalse;
3556
} else if ((rm & 4) && !(args[0].getInt() & 4) && out->supportTextCSPattern(state) && drawText) {
3557
out->beginTextObject(state);
3558
textHaveCSPattern = gTrue;
3560
out->updateRender(state);
3563
void Gfx::opSetTextRise(Object args[], int numArgs) {
3564
state->setRise(args[0].getNum());
3565
out->updateRise(state);
3568
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
3569
state->setWordSpace(args[0].getNum());
3570
out->updateWordSpace(state);
3573
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
3574
state->setHorizScaling(args[0].getNum());
3575
out->updateHorizScaling(state);
3576
fontChanged = gTrue;
3579
//------------------------------------------------------------------------
3580
// text positioning operators
3581
//------------------------------------------------------------------------
3583
void Gfx::opTextMove(Object args[], int numArgs) {
3586
tx = state->getLineX() + args[0].getNum();
3587
ty = state->getLineY() + args[1].getNum();
3588
state->textMoveTo(tx, ty);
3589
out->updateTextPos(state);
3592
void Gfx::opTextMoveSet(Object args[], int numArgs) {
3595
tx = state->getLineX() + args[0].getNum();
3596
ty = args[1].getNum();
3597
state->setLeading(-ty);
3598
ty += state->getLineY();
3599
state->textMoveTo(tx, ty);
3600
out->updateTextPos(state);
3603
void Gfx::opSetTextMatrix(Object args[], int numArgs) {
3604
state->setTextMat(args[0].getNum(), args[1].getNum(),
3605
args[2].getNum(), args[3].getNum(),
3606
args[4].getNum(), args[5].getNum());
3607
state->textMoveTo(0, 0);
3608
out->updateTextMat(state);
3609
out->updateTextPos(state);
3610
fontChanged = gTrue;
3613
void Gfx::opTextNextLine(Object args[], int numArgs) {
3616
tx = state->getLineX();
3617
ty = state->getLineY() - state->getLeading();
3618
state->textMoveTo(tx, ty);
3619
out->updateTextPos(state);
3622
//------------------------------------------------------------------------
3623
// text string operators
3624
//------------------------------------------------------------------------
3626
void Gfx::opShowText(Object args[], int numArgs) {
3627
if (!state->getFont()) {
3628
error(getPos(), "No font in show");
3632
out->updateFont(state);
3633
fontChanged = gFalse;
3635
out->beginStringOp(state);
3636
doShowText(args[0].getString());
3637
out->endStringOp(state);
3640
void Gfx::opMoveShowText(Object args[], int numArgs) {
3643
if (!state->getFont()) {
3644
error(getPos(), "No font in move/show");
3648
out->updateFont(state);
3649
fontChanged = gFalse;
3651
tx = state->getLineX();
3652
ty = state->getLineY() - state->getLeading();
3653
state->textMoveTo(tx, ty);
3654
out->updateTextPos(state);
3655
out->beginStringOp(state);
3656
doShowText(args[0].getString());
3657
out->endStringOp(state);
3660
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
3663
if (!state->getFont()) {
3664
error(getPos(), "No font in move/set/show");
3668
out->updateFont(state);
3669
fontChanged = gFalse;
3671
state->setWordSpace(args[0].getNum());
3672
state->setCharSpace(args[1].getNum());
3673
tx = state->getLineX();
3674
ty = state->getLineY() - state->getLeading();
3675
state->textMoveTo(tx, ty);
3676
out->updateWordSpace(state);
3677
out->updateCharSpace(state);
3678
out->updateTextPos(state);
3679
out->beginStringOp(state);
3680
doShowText(args[2].getString());
3681
out->endStringOp(state);
3684
void Gfx::opShowSpaceText(Object args[], int numArgs) {
3690
if (!state->getFont()) {
3691
error(getPos(), "No font in show/space");
3695
out->updateFont(state);
3696
fontChanged = gFalse;
3698
out->beginStringOp(state);
3699
wMode = state->getFont()->getWMode();
3700
a = args[0].getArray();
3701
for (i = 0; i < a->getLength(); ++i) {
3704
// this uses the absolute value of the font size to match
3705
// Acrobat's behavior
3707
state->textShift(0, -obj.getNum() * 0.001 *
3708
fabs(state->getFontSize()));
3710
state->textShift(-obj.getNum() * 0.001 *
3711
fabs(state->getFontSize()), 0);
3713
out->updateTextShift(state, obj.getNum());
3714
} else if (obj.isString()) {
3715
doShowText(obj.getString());
3717
error(getPos(), "Element of show/space array must be number or string");
3721
out->endStringOp(state);
3724
void Gfx::doShowText(GooString *s) {
3727
double riseX, riseY;
3730
double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
3731
double originX, originY, tOriginX, tOriginY;
3732
double oldCTM[6], newCTM[6];
3738
int len, n, uLen, nChars, nSpaces, i;
3740
font = state->getFont();
3741
wMode = font->getWMode();
3743
if (out->useDrawChar()) {
3744
out->beginString(state, s);
3747
// handle a Type 3 char
3748
if (font->getType() == fontType3 && out->interpretType3Chars()) {
3749
mat = state->getCTM();
3750
for (i = 0; i < 6; ++i) {
3753
mat = state->getTextMat();
3754
newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
3755
newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
3756
newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
3757
newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
3758
mat = font->getFontMatrix();
3759
newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
3760
newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
3761
newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
3762
newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
3763
newCTM[0] *= state->getFontSize();
3764
newCTM[1] *= state->getFontSize();
3765
newCTM[2] *= state->getFontSize();
3766
newCTM[3] *= state->getFontSize();
3767
newCTM[0] *= state->getHorizScaling();
3768
newCTM[2] *= state->getHorizScaling();
3769
state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3770
curX = state->getCurX();
3771
curY = state->getCurY();
3772
lineX = state->getLineX();
3773
lineY = state->getLineY();
3775
p = s->getCString();
3776
len = s->getLength();
3778
n = font->getNextChar(p, len, &code,
3780
&dx, &dy, &originX, &originY);
3781
dx = dx * state->getFontSize() + state->getCharSpace();
3782
if (n == 1 && *p == ' ') {
3783
dx += state->getWordSpace();
3785
dx *= state->getHorizScaling();
3786
dy *= state->getFontSize();
3787
state->textTransformDelta(dx, dy, &tdx, &tdy);
3788
state->transform(curX + riseX, curY + riseY, &x, &y);
3790
state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
3791
//~ the CTM concat values here are wrong (but never used)
3792
out->updateCTM(state, 1, 0, 0, 1, 0, 0);
3793
if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
3795
((Gfx8BitFont *)font)->getCharProc(code, &charProc);
3796
if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
3797
pushResources(resDict);
3799
if (charProc.isStream()) {
3800
display(&charProc, gFalse);
3802
error(getPos(), "Missing or bad Type3 CharProc entry");
3804
out->endType3Char(state);
3811
// GfxState::restore() does *not* restore the current position,
3812
// so we deal with it here using (curX, curY) and (lineX, lineY)
3815
state->moveTo(curX, curY);
3816
state->textSetPos(lineX, lineY);
3822
} else if (out->useDrawChar()) {
3823
state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3824
p = s->getCString();
3825
len = s->getLength();
3827
n = font->getNextChar(p, len, &code,
3829
&dx, &dy, &originX, &originY);
3831
dx *= state->getFontSize();
3832
dy = dy * state->getFontSize() + state->getCharSpace();
3833
if (n == 1 && *p == ' ') {
3834
dy += state->getWordSpace();
3837
dx = dx * state->getFontSize() + state->getCharSpace();
3838
if (n == 1 && *p == ' ') {
3839
dx += state->getWordSpace();
3841
dx *= state->getHorizScaling();
3842
dy *= state->getFontSize();
3844
state->textTransformDelta(dx, dy, &tdx, &tdy);
3845
originX *= state->getFontSize();
3846
originY *= state->getFontSize();
3847
state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
3848
if (!contentIsHidden()) {
3849
out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
3850
tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
3852
state->shift(tdx, tdy);
3859
p = s->getCString();
3860
len = s->getLength();
3861
nChars = nSpaces = 0;
3863
n = font->getNextChar(p, len, &code,
3865
&dx2, &dy2, &originX, &originY);
3868
if (n == 1 && *p == ' ') {
3876
dx *= state->getFontSize();
3877
dy = dy * state->getFontSize()
3878
+ nChars * state->getCharSpace()
3879
+ nSpaces * state->getWordSpace();
3881
dx = dx * state->getFontSize()
3882
+ nChars * state->getCharSpace()
3883
+ nSpaces * state->getWordSpace();
3884
dx *= state->getHorizScaling();
3885
dy *= state->getFontSize();
3887
state->textTransformDelta(dx, dy, &tdx, &tdy);
3888
if (!contentIsHidden())
3889
out->drawString(state, s);
3890
state->shift(tdx, tdy);
3893
if (out->useDrawChar()) {
3894
out->endString(state);
3897
updateLevel += 10 * s->getLength();
3900
//------------------------------------------------------------------------
3901
// XObject operators
3902
//------------------------------------------------------------------------
3904
void Gfx::opXObject(Object args[], int numArgs) {
3906
Object obj1, obj2, obj3, refObj;
3911
name = args[0].getName();
3912
if (!res->lookupXObject(name, &obj1)) {
3915
if (!obj1.isStream()) {
3916
error(getPos(), "XObject '%s' is wrong type", name);
3921
obj1.streamGetDict()->lookupNF("OC", &obj2);
3922
if (obj2.isNull()) {
3923
// No OC entry - so we proceed as normal
3924
} else if (obj2.isRef()) {
3925
if ( catalog->getOptContentConfig() && ! catalog->getOptContentConfig()->optContentIsVisible( &obj2 ) ) {
3931
error(getPos(), "XObject OC value not null or dict: %i", obj2.getType());
3936
obj1.streamGetDict()->lookup("OPI", &opiDict);
3937
if (opiDict.isDict()) {
3938
out->opiBegin(state, opiDict.getDict());
3941
obj1.streamGetDict()->lookup("Subtype", &obj2);
3942
if (obj2.isName("Image")) {
3943
if (out->needNonText()) {
3944
res->lookupXObjectNF(name, &refObj);
3945
doImage(&refObj, obj1.getStream(), gFalse);
3948
} else if (obj2.isName("Form")) {
3949
res->lookupXObjectNF(name, &refObj);
3950
if (out->useDrawForm() && refObj.isRef()) {
3951
out->drawForm(refObj.getRef());
3956
} else if (obj2.isName("PS")) {
3957
obj1.streamGetDict()->lookup("Level1", &obj3);
3958
out->psXObject(obj1.getStream(),
3959
obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
3960
} else if (obj2.isName()) {
3961
error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
3963
error(getPos(), "XObject subtype is missing or wrong type");
3967
if (opiDict.isDict()) {
3968
out->opiEnd(state, opiDict.getDict());
3975
void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
3976
Dict *dict, *maskDict;
3980
StreamColorSpaceMode csMode;
3983
GfxColorSpace *colorSpace, *maskColorSpace;
3984
GfxImageColorMap *colorMap, *maskColorMap;
3985
Object maskObj, smaskObj;
3986
GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
3987
int maskColors[2*gfxColorMaxComps];
3988
int maskWidth, maskHeight;
3990
GBool maskInterpolate;
3995
// get info from the stream
3997
csMode = streamCSNone;
3998
str->getImageParams(&bits, &csMode);
4001
dict = str->getDict();
4004
dict->lookup("Width", &obj1);
4005
if (obj1.isNull()) {
4007
dict->lookup("W", &obj1);
4010
width = obj1.getInt();
4011
else if (obj1.isReal())
4012
width = (int)obj1.getReal();
4016
dict->lookup("Height", &obj1);
4017
if (obj1.isNull()) {
4019
dict->lookup("H", &obj1);
4022
height = obj1.getInt();
4023
else if (obj1.isReal())
4024
height = (int)obj1.getReal();
4029
if (width < 1 || height < 1)
4032
// image interpolation
4033
dict->lookup("Interpolate", &obj1);
4034
if (obj1.isNull()) {
4036
dict->lookup("I", &obj1);
4039
interpolate = obj1.getBool();
4041
interpolate = gFalse;
4043
maskInterpolate = gFalse;
4046
dict->lookup("ImageMask", &obj1);
4047
if (obj1.isNull()) {
4049
dict->lookup("IM", &obj1);
4053
mask = obj1.getBool();
4054
else if (!obj1.isNull())
4060
dict->lookup("BitsPerComponent", &obj1);
4061
if (obj1.isNull()) {
4063
dict->lookup("BPC", &obj1);
4066
bits = obj1.getInt();
4078
// check for inverted mask
4082
dict->lookup("Decode", &obj1);
4083
if (obj1.isNull()) {
4085
dict->lookup("D", &obj1);
4087
if (obj1.isArray()) {
4088
obj1.arrayGet(0, &obj2);
4089
// Table 4.39 says /Decode must be [1 0] or [0 1]. Adobe
4090
// accepts [1.0 0.0] as well.
4091
if (obj2.isNum() && obj2.getNum() >= 0.9)
4094
} else if (!obj1.isNull()) {
4100
if (!contentIsHidden()) {
4101
out->drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
4102
if (out->fillMaskCSPattern(state)) {
4103
maskHaveCSPattern = gTrue;
4104
doPatternFill(gTrue);
4105
out->endMaskClip(state);
4106
maskHaveCSPattern = gFalse;
4111
// get color space and color map
4112
dict->lookup("ColorSpace", &obj1);
4113
if (obj1.isNull()) {
4115
dict->lookup("CS", &obj1);
4117
if (obj1.isName() && inlineImg) {
4118
res->lookupColorSpace(obj1.getName(), &obj2);
4119
if (!obj2.isNull()) {
4126
if (!obj1.isNull()) {
4127
colorSpace = GfxColorSpace::parse(&obj1, this);
4128
} else if (csMode == streamCSDeviceGray) {
4129
colorSpace = new GfxDeviceGrayColorSpace();
4130
} else if (csMode == streamCSDeviceRGB) {
4131
colorSpace = new GfxDeviceRGBColorSpace();
4132
} else if (csMode == streamCSDeviceCMYK) {
4133
colorSpace = new GfxDeviceCMYKColorSpace();
4141
dict->lookup("Decode", &obj1);
4142
if (obj1.isNull()) {
4144
dict->lookup("D", &obj1);
4146
colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
4148
if (!colorMap->isOk()) {
4154
haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
4155
maskStr = NULL; // make gcc happy
4156
maskWidth = maskHeight = 0; // make gcc happy
4157
maskInvert = gFalse; // make gcc happy
4158
maskColorMap = NULL; // make gcc happy
4159
dict->lookup("Mask", &maskObj);
4160
dict->lookup("SMask", &smaskObj);
4161
if (smaskObj.isStream()) {
4166
maskStr = smaskObj.getStream();
4167
maskDict = smaskObj.streamGetDict();
4168
maskDict->lookup("Width", &obj1);
4169
if (obj1.isNull()) {
4171
maskDict->lookup("W", &obj1);
4173
if (!obj1.isInt()) {
4176
maskWidth = obj1.getInt();
4178
maskDict->lookup("Height", &obj1);
4179
if (obj1.isNull()) {
4181
maskDict->lookup("H", &obj1);
4183
if (!obj1.isInt()) {
4186
maskHeight = obj1.getInt();
4188
maskDict->lookup("Interpolate", &obj1);
4189
if (obj1.isNull()) {
4191
maskDict->lookup("I", &obj1);
4194
maskInterpolate = obj1.getBool();
4196
maskInterpolate = gFalse;
4198
maskDict->lookup("BitsPerComponent", &obj1);
4199
if (obj1.isNull()) {
4201
maskDict->lookup("BPC", &obj1);
4203
if (!obj1.isInt()) {
4206
maskBits = obj1.getInt();
4208
maskDict->lookup("ColorSpace", &obj1);
4209
if (obj1.isNull()) {
4211
maskDict->lookup("CS", &obj1);
4213
if (obj1.isName()) {
4214
res->lookupColorSpace(obj1.getName(), &obj2);
4215
if (!obj2.isNull()) {
4222
maskColorSpace = GfxColorSpace::parse(&obj1, this);
4224
if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
4227
maskDict->lookup("Decode", &obj1);
4228
if (obj1.isNull()) {
4230
maskDict->lookup("D", &obj1);
4232
maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
4234
if (!maskColorMap->isOk()) {
4235
delete maskColorMap;
4238
//~ handle the Matte entry
4239
haveSoftMask = gTrue;
4240
} else if (maskObj.isArray()) {
4243
i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
4245
maskObj.arrayGet(i, &obj1);
4247
maskColors[i] = obj1.getInt();
4248
} else if (obj1.isReal()) {
4249
error(-1, "Mask entry should be an integer but it's a real, trying to use it");
4250
maskColors[i] = (int) obj1.getReal();
4252
error(-1, "Mask entry should be an integer but it's of type %d", obj1.getType());
4258
haveColorKeyMask = gTrue;
4259
} else if (maskObj.isStream()) {
4264
maskStr = maskObj.getStream();
4265
maskDict = maskObj.streamGetDict();
4266
maskDict->lookup("Width", &obj1);
4267
if (obj1.isNull()) {
4269
maskDict->lookup("W", &obj1);
4271
if (!obj1.isInt()) {
4274
maskWidth = obj1.getInt();
4276
maskDict->lookup("Height", &obj1);
4277
if (obj1.isNull()) {
4279
maskDict->lookup("H", &obj1);
4281
if (!obj1.isInt()) {
4284
maskHeight = obj1.getInt();
4286
maskDict->lookup("Interpolate", &obj1);
4287
if (obj1.isNull()) {
4289
maskDict->lookup("I", &obj1);
4292
maskInterpolate = obj1.getBool();
4294
maskInterpolate = gFalse;
4296
maskDict->lookup("ImageMask", &obj1);
4297
if (obj1.isNull()) {
4299
maskDict->lookup("IM", &obj1);
4301
if (!obj1.isBool() || !obj1.getBool()) {
4305
maskInvert = gFalse;
4306
maskDict->lookup("Decode", &obj1);
4307
if (obj1.isNull()) {
4309
maskDict->lookup("D", &obj1);
4311
if (obj1.isArray()) {
4312
obj1.arrayGet(0, &obj2);
4313
// Table 4.39 says /Decode must be [1 0] or [0 1]. Adobe
4314
// accepts [1.0 0.0] as well.
4315
if (obj2.isNum() && obj2.getNum() >= 0.9) {
4319
} else if (!obj1.isNull()) {
4323
haveExplicitMask = gTrue;
4328
if (!contentIsHidden()) {
4329
out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
4330
maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
4332
delete maskColorMap;
4333
} else if (haveExplicitMask && !contentIsHidden ()) {
4334
out->drawMaskedImage(state, ref, str, width, height, colorMap, interpolate,
4335
maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate);
4336
} else if (!contentIsHidden()) {
4337
out->drawImage(state, ref, str, width, height, colorMap, interpolate,
4338
haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
4346
if ((i = width * height) > 1000) {
4356
error(getPos(), "Bad image parameters");
4359
void Gfx::doForm(Object *str) {
4361
GBool transpGroup, isolated, knockout;
4362
GfxColorSpace *blendingColorSpace;
4363
Object matrixObj, bboxObj;
4364
double m[6], bbox[4];
4367
Object obj1, obj2, obj3;
4370
// check for excessive recursion
4371
if (formDepth > 20) {
4376
dict = str->streamGetDict();
4379
dict->lookup("FormType", &obj1);
4380
if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
4381
error(getPos(), "Unknown form type");
4386
dict->lookup("BBox", &bboxObj);
4387
if (!bboxObj.isArray()) {
4389
error(getPos(), "Bad form bounding box");
4392
for (i = 0; i < 4; ++i) {
4393
bboxObj.arrayGet(i, &obj1);
4394
if (likely(obj1.isNum())) {
4395
bbox[i] = obj1.getNum();
4399
error(getPos(), "Bad form bounding box value");
4406
dict->lookup("Matrix", &matrixObj);
4407
if (matrixObj.isArray()) {
4408
for (i = 0; i < 6; ++i) {
4409
matrixObj.arrayGet(i, &obj1);
4410
if (likely(obj1.isNum())) m[i] = obj1.getNum();
4422
dict->lookup("Resources", &resObj);
4423
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4425
// check for a transparency group
4426
transpGroup = isolated = knockout = gFalse;
4427
blendingColorSpace = NULL;
4428
if (dict->lookup("Group", &obj1)->isDict()) {
4429
if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
4430
transpGroup = gTrue;
4431
if (!obj1.dictLookup("CS", &obj3)->isNull()) {
4432
blendingColorSpace = GfxColorSpace::parse(&obj3, this);
4435
if (obj1.dictLookup("I", &obj3)->isBool()) {
4436
isolated = obj3.getBool();
4439
if (obj1.dictLookup("K", &obj3)->isBool()) {
4440
knockout = obj3.getBool();
4450
doForm1(str, resDict, m, bbox,
4451
transpGroup, gFalse, blendingColorSpace, isolated, knockout);
4454
if (blendingColorSpace) {
4455
delete blendingColorSpace;
4460
void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
4461
GBool transpGroup, GBool softMask,
4462
GfxColorSpace *blendingColorSpace,
4463
GBool isolated, GBool knockout,
4464
GBool alpha, Function *transferFunc,
4465
GfxColor *backdropColor) {
4467
double oldBaseMatrix[6];
4470
// push new resources on stack
4471
pushResources(resDict);
4473
// save current graphics state
4476
// kill any pre-existing path
4479
// save current parser
4482
// set form transformation matrix
4483
state->concatCTM(matrix[0], matrix[1], matrix[2],
4484
matrix[3], matrix[4], matrix[5]);
4485
out->updateCTM(state, matrix[0], matrix[1], matrix[2],
4486
matrix[3], matrix[4], matrix[5]);
4488
// set form bounding box
4489
state->moveTo(bbox[0], bbox[1]);
4490
state->lineTo(bbox[2], bbox[1]);
4491
state->lineTo(bbox[2], bbox[3]);
4492
state->lineTo(bbox[0], bbox[3]);
4498
if (softMask || transpGroup) {
4499
if (state->getBlendMode() != gfxBlendNormal) {
4500
state->setBlendMode(gfxBlendNormal);
4501
out->updateBlendMode(state);
4503
if (state->getFillOpacity() != 1) {
4504
state->setFillOpacity(1);
4505
out->updateFillOpacity(state);
4507
if (state->getStrokeOpacity() != 1) {
4508
state->setStrokeOpacity(1);
4509
out->updateStrokeOpacity(state);
4511
out->clearSoftMask(state);
4512
out->beginTransparencyGroup(state, bbox, blendingColorSpace,
4513
isolated, knockout, softMask);
4516
// set new base matrix
4517
for (i = 0; i < 6; ++i) {
4518
oldBaseMatrix[i] = baseMatrix[i];
4519
baseMatrix[i] = state->getCTM()[i];
4522
GfxState *stateBefore = state;
4525
display(str, gFalse);
4527
if (stateBefore != state) {
4528
if (state->isParentState(stateBefore)) {
4529
error(-1, "There's a form with more q than Q, trying to fix");
4530
while (stateBefore != state) {
4534
error(-1, "There's a form with more Q than q");
4538
if (softMask || transpGroup) {
4539
out->endTransparencyGroup(state);
4542
// restore base matrix
4543
for (i = 0; i < 6; ++i) {
4544
baseMatrix[i] = oldBaseMatrix[i];
4550
// restore graphics state
4553
// pop resource stack
4557
out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
4558
} else if (transpGroup) {
4559
out->paintTransparencyGroup(state, bbox);
4565
//------------------------------------------------------------------------
4566
// in-line image operators
4567
//------------------------------------------------------------------------
4569
void Gfx::opBeginImage(Object args[], int numArgs) {
4573
// build dict/stream
4574
str = buildImageStream();
4576
// display the image
4578
doImage(NULL, str, gTrue);
4581
c1 = str->getUndecodedStream()->getChar();
4582
c2 = str->getUndecodedStream()->getChar();
4583
while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
4585
c2 = str->getUndecodedStream()->getChar();
4591
Stream *Gfx::buildImageStream() {
4598
dict.initDict(xref);
4599
parser->getObj(&obj);
4600
while (!obj.isCmd("ID") && !obj.isEOF()) {
4601
if (!obj.isName()) {
4602
error(getPos(), "Inline image dictionary key must be a name object");
4605
key = copyString(obj.getName());
4607
parser->getObj(&obj);
4608
if (obj.isEOF() || obj.isError()) {
4612
dict.dictAdd(key, &obj);
4614
parser->getObj(&obj);
4617
error(getPos(), "End of file in inline image");
4625
if (parser->getStream()) {
4626
str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
4627
str = str->addFilters(&dict);
4636
void Gfx::opImageData(Object args[], int numArgs) {
4637
error(getPos(), "Internal: got 'ID' operator");
4640
void Gfx::opEndImage(Object args[], int numArgs) {
4641
error(getPos(), "Internal: got 'EI' operator");
4644
//------------------------------------------------------------------------
4645
// type 3 font operators
4646
//------------------------------------------------------------------------
4648
void Gfx::opSetCharWidth(Object args[], int numArgs) {
4649
out->type3D0(state, args[0].getNum(), args[1].getNum());
4652
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
4653
out->type3D1(state, args[0].getNum(), args[1].getNum(),
4654
args[2].getNum(), args[3].getNum(),
4655
args[4].getNum(), args[5].getNum());
4658
//------------------------------------------------------------------------
4659
// compatibility operators
4660
//------------------------------------------------------------------------
4662
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
4666
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
4667
if (ignoreUndef > 0)
4671
//------------------------------------------------------------------------
4672
// marked content operators
4673
//------------------------------------------------------------------------
4675
struct MarkedContentStack {
4676
GBool ocSuppressed; // are we ignoring content based on OptionalContent?
4677
MarkedContentStack *next; // next object on stack
4680
void Gfx::popMarkedContent() {
4681
MarkedContentStack *mc = mcStack;
4686
void Gfx::pushMarkedContent() {
4687
MarkedContentStack *mc = new MarkedContentStack();
4688
mc->ocSuppressed = gFalse;
4693
GBool Gfx::contentIsHidden() {
4694
MarkedContentStack *mc = mcStack;
4695
bool hidden = mc && mc->ocSuppressed;
4696
while (!hidden && mc && mc->next) {
4698
hidden = mc->ocSuppressed;
4703
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
4704
// push a new stack entry
4705
pushMarkedContent();
4707
OCGs *contentConfig = catalog->getOptContentConfig();
4708
char* name0 = args[0].getName();
4709
if ( strncmp( name0, "OC", 2) == 0 && contentConfig) {
4710
if ( numArgs >= 2 ) {
4711
if (!args[1].isName()) {
4712
error(getPos(), "Unexpected MC Type: %i", args[1].getType());
4714
char* name1 = args[1].getName();
4715
Object markedContent;
4716
if ( res->lookupMarkedContentNF( name1, &markedContent ) ) {
4717
if ( markedContent.isRef() ) {
4718
bool visible = contentConfig->optContentIsVisible( &markedContent );
4719
MarkedContentStack *mc = mcStack;
4720
mc->ocSuppressed = !(visible);
4723
error(getPos(), "DID NOT find %s", name1);
4726
error(getPos(), "insufficient arguments for Marked Content");
4730
if (printCommands) {
4731
printf(" marked content: %s ", args[0].getName());
4733
args[1].print(stdout);
4738
if(numArgs == 2 && args[1].isDict ()) {
4739
out->beginMarkedContent(args[0].getName(),args[1].getDict());
4740
} else if(numArgs == 1) {
4741
out->beginMarkedContent(args[0].getName(),NULL);
4745
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
4749
out->endMarkedContent(state);
4752
void Gfx::opMarkPoint(Object args[], int numArgs) {
4753
if (printCommands) {
4754
printf(" mark point: %s ", args[0].getName());
4756
args[1].print(stdout);
4761
if(numArgs == 2 && args[1].isDict()) {
4762
out->markPoint(args[0].getName(),args[1].getDict());
4764
out->markPoint(args[0].getName());
4769
//------------------------------------------------------------------------
4771
//------------------------------------------------------------------------
4773
void Gfx::drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor,
4774
double xMin, double yMin, double xMax, double yMax) {
4775
Dict *dict, *resDict;
4776
Object matrixObj, bboxObj, resObj;
4778
double m[6], bbox[4], ictm[6];
4780
double formX0, formY0, formX1, formY1;
4781
double annotX0, annotY0, annotX1, annotY1;
4782
double det, x, y, sx, sy;
4785
double *dash, *dash2;
4789
//~ can we assume that we're in default user space?
4790
//~ (i.e., baseMatrix = ctm)
4792
// transform the annotation bbox from default user space to user
4793
// space: (bbox * baseMatrix) * iCTM
4794
ctm = state->getCTM();
4795
det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
4796
ictm[0] = ctm[3] * det;
4797
ictm[1] = -ctm[1] * det;
4798
ictm[2] = -ctm[2] * det;
4799
ictm[3] = ctm[0] * det;
4800
ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
4801
ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
4802
x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
4803
y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
4804
annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
4805
annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
4806
x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
4807
y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
4808
annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
4809
annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
4810
if (annotX0 > annotX1) {
4811
x = annotX0; annotX0 = annotX1; annotX1 = x;
4813
if (annotY0 > annotY1) {
4814
y = annotY0; annotY0 = annotY1; annotY1 = y;
4817
// draw the appearance stream (if there is one)
4818
if (str->isStream()) {
4821
dict = str->streamGetDict();
4823
// get the form bounding box
4824
dict->lookup("BBox", &bboxObj);
4825
if (!bboxObj.isArray()) {
4827
error(getPos(), "Bad form bounding box");
4830
for (i = 0; i < 4; ++i) {
4831
bboxObj.arrayGet(i, &obj1);
4832
if (likely(obj1.isNum())) {
4833
bbox[i] = obj1.getNum();
4837
error(getPos(), "Bad form bounding box value");
4843
// get the form matrix
4844
dict->lookup("Matrix", &matrixObj);
4845
if (matrixObj.isArray() && matrixObj.arrayGetLength() >= 6) {
4846
for (i = 0; i < 6; ++i) {
4847
matrixObj.arrayGet(i, &obj1);
4848
m[i] = obj1.getNum();
4858
// transform the form bbox from form space to user space
4859
formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
4860
formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
4861
formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
4862
formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
4863
if (formX0 > formX1) {
4864
x = formX0; formX0 = formX1; formX1 = x;
4866
if (formY0 > formY1) {
4867
y = formY0; formY0 = formY1; formY1 = y;
4870
// scale the form to fit the annotation bbox
4871
if (formX1 == formX0) {
4872
// this shouldn't happen
4875
sx = (annotX1 - annotX0) / (formX1 - formX0);
4877
if (formY1 == formY0) {
4878
// this shouldn't happen
4881
sy = (annotY1 - annotY0) / (formY1 - formY0);
4885
m[4] = (m[4] - formX0) * sx + annotX0;
4888
m[5] = (m[5] - formY0) * sy + annotY0;
4891
dict->lookup("Resources", &resObj);
4892
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4895
doForm1(str, resDict, m, bbox);
4901
if (border && border->getWidth() > 0) {
4902
if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
4903
state->setStrokePattern(NULL);
4904
state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
4905
out->updateStrokeColorSpace(state);
4907
if (aColor && (aColor->getSpace() == AnnotColor::colorRGB)) {
4908
const double *values = aColor->getValues();
4915
color.c[0] = dblToCol(r);
4916
color.c[1] = dblToCol(g);
4917
color.c[2] = dblToCol(b);
4918
state->setStrokeColor(&color);
4919
out->updateStrokeColor(state);
4920
// compute the width scale factor when going from default user
4921
// space to user space
4922
x = (baseMatrix[0] + baseMatrix[2]) * ictm[0] +
4923
(baseMatrix[1] + baseMatrix[3]) * ictm[2];
4924
y = (baseMatrix[0] + baseMatrix[2]) * ictm[1] +
4925
(baseMatrix[1] + baseMatrix[3]) * ictm[3];
4926
x = sqrt(0.5 * (x * x + y * y));
4927
state->setLineWidth(x * border->getWidth());
4928
out->updateLineWidth(state);
4929
dashLength = border->getDashLength();
4930
dash = border->getDash();
4931
if (border->getStyle() == AnnotBorder::borderDashed && dashLength > 0) {
4932
dash2 = (double *)gmallocn(dashLength, sizeof(double));
4933
for (i = 0; i < dashLength; ++i) {
4934
dash2[i] = x * dash[i];
4936
state->setLineDash(dash2, dashLength, 0);
4937
out->updateLineDash(state);
4939
//~ this doesn't currently handle the beveled and engraved styles
4941
state->moveTo(annotX0, out->upsideDown() ? annotY0 : annotY1);
4942
state->lineTo(annotX1, out->upsideDown() ? annotY0 : annotY1);
4943
if (border->getStyle() != AnnotBorder::borderUnderlined) {
4944
state->lineTo(annotX1, out->upsideDown() ? annotY1 : annotY0);
4945
state->lineTo(annotX0, out->upsideDown() ? annotY1 : annotY0);
4952
int Gfx::bottomGuard() {
4953
return stateGuards[stateGuards.size()-1];
4956
void Gfx::pushStateGuard() {
4957
stateGuards.push_back(stackHeight);
4960
void Gfx::popStateGuard() {
4961
while (stackHeight > bottomGuard() && state->hasSaves())
4963
stateGuards.pop_back();
4966
void Gfx::saveState() {
4967
out->saveState(state);
4968
state = state->save();
4972
void Gfx::restoreState() {
4973
if (stackHeight <= bottomGuard() || !state->hasSaves()) {
4974
error(-1, "Restoring state when no valid states to pop");
4975
commandAborted = gTrue;
4978
state = state->restore();
4979
out->restoreState(state);
4983
void Gfx::pushResources(Dict *resDict) {
4984
res = new GfxResources(xref, resDict, res);
4987
void Gfx::popResources() {
4988
GfxResources *resPtr;
4990
resPtr = res->getNext();
4996
PopplerCache *Gfx::getIccColorSpaceCache()
4998
return &iccColorSpaceCache;