~matttbe/ubuntu/raring/poppler/lp1072129

« back to all changes in this revision

Viewing changes to .pc/15_output-tiling-patterns-issuing-PS-patterns.patch/poppler/Gfx.cc

  • Committer: Package Import Robot
  • Author(s): Till Kamppeter
  • Date: 2011-08-19 15:10:18 UTC
  • Revision ID: package-import@ubuntu.com-20110819151018-0b11268hfo5pidoe
Tags: 0.16.7-2ubuntu2
debian/patches/15_output-tiling-patterns-issuing-PS-patterns.patch,
debian/patches/17_dont-use-patterns-when-only-one-instance-is-used.patch:
Backported upstream patch to use PostScript Patterns for tiling fill when
output PostScript level >= 2 and to avoid using /PatternType if only one
instance of the pattern is used. This should fix LP: #817049.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//========================================================================
 
2
//
 
3
// Gfx.cc
 
4
//
 
5
// Copyright 1996-2003 Glyph & Cog, LLC
 
6
//
 
7
//========================================================================
 
8
 
 
9
//========================================================================
 
10
//
 
11
// Modified under the Poppler project - http://poppler.freedesktop.org
 
12
//
 
13
// All changes made under the Poppler project to this file are licensed
 
14
// under GPL version 2 or later
 
15
//
 
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>
 
36
//
 
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
 
39
//
 
40
//========================================================================
 
41
 
 
42
#include <config.h>
 
43
 
 
44
#ifdef USE_GCC_PRAGMAS
 
45
#pragma implementation
 
46
#endif
 
47
 
 
48
#include <stdlib.h>
 
49
#include <stdio.h>
 
50
#include <stddef.h>
 
51
#include <string.h>
 
52
#include <math.h>
 
53
#include "goo/gmem.h"
 
54
#include "goo/GooTimer.h"
 
55
#include "goo/GooHash.h"
 
56
#include "GlobalParams.h"
 
57
#include "CharTypes.h"
 
58
#include "Object.h"
 
59
#include "Array.h"
 
60
#include "Dict.h"
 
61
#include "Stream.h"
 
62
#include "Lexer.h"
 
63
#include "Parser.h"
 
64
#include "GfxFont.h"
 
65
#include "GfxState.h"
 
66
#include "OutputDev.h"
 
67
#include "Page.h"
 
68
#include "Annot.h"
 
69
#include "Error.h"
 
70
#include "Gfx.h"
 
71
#include "ProfileData.h"
 
72
#include "Catalog.h"
 
73
#include "OptionalContent.h"
 
74
 
 
75
// the MSVC math.h doesn't define this
 
76
#ifndef M_PI
 
77
#define M_PI 3.14159265358979323846
 
78
#endif
 
79
 
 
80
//------------------------------------------------------------------------
 
81
// constants
 
82
//------------------------------------------------------------------------
 
83
 
 
84
// Max recursive depth for a function shading fill.
 
85
#define functionMaxDepth 6
 
86
 
 
87
// Max delta allowed in any color component for a function shading fill.
 
88
#define functionColorDelta (dblToCol(1 / 256.0))
 
89
 
 
90
// Max number of splits along the t axis for an axial shading fill.
 
91
#define axialMaxSplits 256
 
92
 
 
93
// Max delta allowed in any color component for an axial shading fill.
 
94
#define axialColorDelta (dblToCol(1 / 256.0))
 
95
 
 
96
// Max number of splits along the t axis for a radial shading fill.
 
97
#define radialMaxSplits 256
 
98
 
 
99
// Max delta allowed in any color component for a radial shading fill.
 
100
#define radialColorDelta (dblToCol(1 / 256.0))
 
101
 
 
102
// Max recursive depth for a Gouraud triangle shading fill.
 
103
//
 
104
// Triangles will be split at most gouraudMaxDepth times (each time into 4
 
105
// smaller ones). That makes pow(4,gouraudMaxDepth) many triangles for
 
106
// every triangle.
 
107
#define gouraudMaxDepth 6
 
108
 
 
109
// Max delta allowed in any color component for a Gouraud triangle
 
110
// shading fill.
 
111
#define gouraudColorDelta (dblToCol(3. / 256.0))
 
112
 
 
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
 
116
 
 
117
// Max recursive depth for a patch mesh shading fill.
 
118
#define patchMaxDepth 6
 
119
 
 
120
// Max delta allowed in any color component for a patch mesh shading
 
121
// fill.
 
122
#define patchColorDelta (dblToCol((3. / 256.0)))
 
123
 
 
124
//------------------------------------------------------------------------
 
125
// Operator table
 
126
//------------------------------------------------------------------------
 
127
 
 
128
#ifdef _MSC_VER // this works around a bug in the VC7 compiler
 
129
#  pragma optimize("",off)
 
130
#endif
 
131
 
 
132
Operator Gfx::opTab[] = {
 
133
  {"\"",  3, {tchkNum,    tchkNum,    tchkString},
 
134
          &Gfx::opMoveSetShowText},
 
135
  {"'",   1, {tchkString},
 
136
          &Gfx::opMoveShowText},
 
137
  {"B",   0, {tchkNone},
 
138
          &Gfx::opFillStroke},
 
139
  {"B*",  0, {tchkNone},
 
140
          &Gfx::opEOFillStroke},
 
141
  {"BDC", 2, {tchkName,   tchkProps},
 
142
          &Gfx::opBeginMarkedContent},
 
143
  {"BI",  0, {tchkNone},
 
144
          &Gfx::opBeginImage},
 
145
  {"BMC", 1, {tchkName},
 
146
          &Gfx::opBeginMarkedContent},
 
147
  {"BT",  0, {tchkNone},
 
148
          &Gfx::opBeginText},
 
149
  {"BX",  0, {tchkNone},
 
150
          &Gfx::opBeginIgnoreUndef},
 
151
  {"CS",  1, {tchkName},
 
152
          &Gfx::opSetStrokeColorSpace},
 
153
  {"DP",  2, {tchkName,   tchkProps},
 
154
          &Gfx::opMarkPoint},
 
155
  {"Do",  1, {tchkName},
 
156
          &Gfx::opXObject},
 
157
  {"EI",  0, {tchkNone},
 
158
          &Gfx::opEndImage},
 
159
  {"EMC", 0, {tchkNone},
 
160
          &Gfx::opEndMarkedContent},
 
161
  {"ET",  0, {tchkNone},
 
162
          &Gfx::opEndText},
 
163
  {"EX",  0, {tchkNone},
 
164
          &Gfx::opEndIgnoreUndef},
 
165
  {"F",   0, {tchkNone},
 
166
          &Gfx::opFill},
 
167
  {"G",   1, {tchkNum},
 
168
          &Gfx::opSetStrokeGray},
 
169
  {"ID",  0, {tchkNone},
 
170
          &Gfx::opImageData},
 
171
  {"J",   1, {tchkInt},
 
172
          &Gfx::opSetLineCap},
 
173
  {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
174
          &Gfx::opSetStrokeCMYKColor},
 
175
  {"M",   1, {tchkNum},
 
176
          &Gfx::opSetMiterLimit},
 
177
  {"MP",  1, {tchkName},
 
178
          &Gfx::opMarkPoint},
 
179
  {"Q",   0, {tchkNone},
 
180
          &Gfx::opRestore},
 
181
  {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
 
182
          &Gfx::opSetStrokeRGBColor},
 
183
  {"S",   0, {tchkNone},
 
184
          &Gfx::opStroke},
 
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,
 
195
                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},
 
203
  {"TL",  1, {tchkNum},
 
204
          &Gfx::opSetTextLeading},
 
205
  {"Tc",  1, {tchkNum},
 
206
          &Gfx::opSetCharSpacing},
 
207
  {"Td",  2, {tchkNum,    tchkNum},
 
208
          &Gfx::opTextMove},
 
209
  {"Tf",  2, {tchkName,   tchkNum},
 
210
          &Gfx::opSetFont},
 
211
  {"Tj",  1, {tchkString},
 
212
          &Gfx::opShowText},
 
213
  {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
214
              tchkNum,    tchkNum},
 
215
          &Gfx::opSetTextMatrix},
 
216
  {"Tr",  1, {tchkInt},
 
217
          &Gfx::opSetTextRender},
 
218
  {"Ts",  1, {tchkNum},
 
219
          &Gfx::opSetTextRise},
 
220
  {"Tw",  1, {tchkNum},
 
221
          &Gfx::opSetWordSpacing},
 
222
  {"Tz",  1, {tchkNum},
 
223
          &Gfx::opSetHorizScaling},
 
224
  {"W",   0, {tchkNone},
 
225
          &Gfx::opClip},
 
226
  {"W*",  0, {tchkNone},
 
227
          &Gfx::opEOClip},
 
228
  {"b",   0, {tchkNone},
 
229
          &Gfx::opCloseFillStroke},
 
230
  {"b*",  0, {tchkNone},
 
231
          &Gfx::opCloseEOFillStroke},
 
232
  {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
233
              tchkNum,    tchkNum},
 
234
          &Gfx::opCurveTo},
 
235
  {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
236
              tchkNum,    tchkNum},
 
237
          &Gfx::opConcat},
 
238
  {"cs",  1, {tchkName},
 
239
          &Gfx::opSetFillColorSpace},
 
240
  {"d",   2, {tchkArray,  tchkNum},
 
241
          &Gfx::opSetDash},
 
242
  {"d0",  2, {tchkNum,    tchkNum},
 
243
          &Gfx::opSetCharWidth},
 
244
  {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
245
              tchkNum,    tchkNum},
 
246
          &Gfx::opSetCacheDevice},
 
247
  {"f",   0, {tchkNone},
 
248
          &Gfx::opFill},
 
249
  {"f*",  0, {tchkNone},
 
250
          &Gfx::opEOFill},
 
251
  {"g",   1, {tchkNum},
 
252
          &Gfx::opSetFillGray},
 
253
  {"gs",  1, {tchkName},
 
254
          &Gfx::opSetExtGState},
 
255
  {"h",   0, {tchkNone},
 
256
          &Gfx::opClosePath},
 
257
  {"i",   1, {tchkNum},
 
258
          &Gfx::opSetFlat},
 
259
  {"j",   1, {tchkInt},
 
260
          &Gfx::opSetLineJoin},
 
261
  {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
262
          &Gfx::opSetFillCMYKColor},
 
263
  {"l",   2, {tchkNum,    tchkNum},
 
264
          &Gfx::opLineTo},
 
265
  {"m",   2, {tchkNum,    tchkNum},
 
266
          &Gfx::opMoveTo},
 
267
  {"n",   0, {tchkNone},
 
268
          &Gfx::opEndPath},
 
269
  {"q",   0, {tchkNone},
 
270
          &Gfx::opSave},
 
271
  {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
272
          &Gfx::opRectangle},
 
273
  {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
 
274
          &Gfx::opSetFillRGBColor},
 
275
  {"ri",  1, {tchkName},
 
276
          &Gfx::opSetRenderingIntent},
 
277
  {"s",   0, {tchkNone},
 
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,
 
289
                tchkSCN},
 
290
          &Gfx::opSetFillColorN},
 
291
  {"sh",  1, {tchkName},
 
292
          &Gfx::opShFill},
 
293
  {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
294
          &Gfx::opCurveTo1},
 
295
  {"w",   1, {tchkNum},
 
296
          &Gfx::opSetLineWidth},
 
297
  {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
298
          &Gfx::opCurveTo2},
 
299
};
 
300
 
 
301
#ifdef _MSC_VER // this works around a bug in the VC7 compiler
 
302
#  pragma optimize("",on)
 
303
#endif
 
304
 
 
305
#define numOps (sizeof(opTab) / sizeof(Operator))
 
306
 
 
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) {
 
310
      return false;
 
311
    }
 
312
  }
 
313
  return true;
 
314
}
 
315
 
 
316
//------------------------------------------------------------------------
 
317
// GfxResources
 
318
//------------------------------------------------------------------------
 
319
 
 
320
GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) :
 
321
    gStateCache(2, xref) {
 
322
  Object obj1, obj2;
 
323
  Ref r;
 
324
 
 
325
  if (resDict) {
 
326
 
 
327
    // build font dictionary
 
328
    fonts = NULL;
 
329
    resDict->lookupNF("Font", &obj1);
 
330
    if (obj1.isRef()) {
 
331
      obj1.fetch(xref, &obj2);
 
332
      if (obj2.isDict()) {
 
333
        r = obj1.getRef();
 
334
        fonts = new GfxFontDict(xref, &r, obj2.getDict());
 
335
      }
 
336
      obj2.free();
 
337
    } else if (obj1.isDict()) {
 
338
      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
 
339
    }
 
340
    obj1.free();
 
341
 
 
342
    // get XObject dictionary
 
343
    resDict->lookup("XObject", &xObjDict);
 
344
 
 
345
    // get color space dictionary
 
346
    resDict->lookup("ColorSpace", &colorSpaceDict);
 
347
 
 
348
    // get pattern dictionary
 
349
    resDict->lookup("Pattern", &patternDict);
 
350
 
 
351
    // get shading dictionary
 
352
    resDict->lookup("Shading", &shadingDict);
 
353
 
 
354
    // get graphics state parameter dictionary
 
355
    resDict->lookup("ExtGState", &gStateDict);
 
356
 
 
357
    // get properties dictionary
 
358
    resDict->lookup("Properties", &propertiesDict);
 
359
 
 
360
  } else {
 
361
    fonts = NULL;
 
362
    xObjDict.initNull();
 
363
    colorSpaceDict.initNull();
 
364
    patternDict.initNull();
 
365
    shadingDict.initNull();
 
366
    gStateDict.initNull();
 
367
    propertiesDict.initNull();
 
368
  }
 
369
 
 
370
  next = nextA;
 
371
}
 
372
 
 
373
GfxResources::~GfxResources() {
 
374
  if (fonts) {
 
375
    delete fonts;
 
376
  }
 
377
  xObjDict.free();
 
378
  colorSpaceDict.free();
 
379
  patternDict.free();
 
380
  shadingDict.free();
 
381
  gStateDict.free();
 
382
  propertiesDict.free();
 
383
}
 
384
 
 
385
GfxFont *GfxResources::lookupFont(char *name) {
 
386
  GfxFont *font;
 
387
  GfxResources *resPtr;
 
388
 
 
389
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
390
    if (resPtr->fonts) {
 
391
      if ((font = resPtr->fonts->lookup(name)))
 
392
        return font;
 
393
    }
 
394
  }
 
395
  error(-1, "Unknown font tag '%s'", name);
 
396
  return NULL;
 
397
}
 
398
 
 
399
GBool GfxResources::lookupXObject(char *name, Object *obj) {
 
400
  GfxResources *resPtr;
 
401
 
 
402
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
403
    if (resPtr->xObjDict.isDict()) {
 
404
      if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
 
405
        return gTrue;
 
406
      obj->free();
 
407
    }
 
408
  }
 
409
  error(-1, "XObject '%s' is unknown", name);
 
410
  return gFalse;
 
411
}
 
412
 
 
413
GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
 
414
  GfxResources *resPtr;
 
415
 
 
416
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
417
    if (resPtr->xObjDict.isDict()) {
 
418
      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
 
419
        return gTrue;
 
420
      obj->free();
 
421
    }
 
422
  }
 
423
  error(-1, "XObject '%s' is unknown", name);
 
424
  return gFalse;
 
425
}
 
426
 
 
427
GBool GfxResources::lookupMarkedContentNF(char *name, Object *obj) {
 
428
  GfxResources *resPtr;
 
429
 
 
430
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
431
    if (resPtr->propertiesDict.isDict()) {
 
432
      if (!resPtr->propertiesDict.dictLookupNF(name, obj)->isNull())
 
433
        return gTrue;
 
434
      obj->free();
 
435
    }
 
436
  }
 
437
  error(-1, "Marked Content '%s' is unknown", name);
 
438
  return gFalse;
 
439
}
 
440
 
 
441
void GfxResources::lookupColorSpace(char *name, Object *obj) {
 
442
  GfxResources *resPtr;
 
443
 
 
444
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
445
    if (resPtr->colorSpaceDict.isDict()) {
 
446
      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
 
447
        return;
 
448
      }
 
449
      obj->free();
 
450
    }
 
451
  }
 
452
  obj->initNull();
 
453
}
 
454
 
 
455
GfxPattern *GfxResources::lookupPattern(char *name, Gfx *gfx) {
 
456
  GfxResources *resPtr;
 
457
  GfxPattern *pattern;
 
458
  Object obj;
 
459
 
 
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);
 
464
        obj.free();
 
465
        return pattern;
 
466
      }
 
467
      obj.free();
 
468
    }
 
469
  }
 
470
  error(-1, "Unknown pattern '%s'", name);
 
471
  return NULL;
 
472
}
 
473
 
 
474
GfxShading *GfxResources::lookupShading(char *name, Gfx *gfx) {
 
475
  GfxResources *resPtr;
 
476
  GfxShading *shading;
 
477
  Object obj;
 
478
 
 
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);
 
483
        obj.free();
 
484
        return shading;
 
485
      }
 
486
      obj.free();
 
487
    }
 
488
  }
 
489
  error(-1, "Unknown shading '%s'", name);
 
490
  return NULL;
 
491
}
 
492
 
 
493
GBool GfxResources::lookupGState(char *name, Object *obj) {
 
494
  if (!lookupGStateNF(name, obj))
 
495
    return gFalse;
 
496
 
 
497
  if (!obj->isRef())
 
498
    return gTrue;
 
499
  
 
500
  const Ref ref = obj->getRef();
 
501
  if (!gStateCache.lookup(ref, obj)->isNull())
 
502
    return gTrue;
 
503
  obj->free();
 
504
 
 
505
  gStateCache.put(ref)->copy(obj);
 
506
  return gTrue;
 
507
}
 
508
 
 
509
GBool GfxResources::lookupGStateNF(char *name, Object *obj) {
 
510
  GfxResources *resPtr;
 
511
 
 
512
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
513
    if (resPtr->gStateDict.isDict()) {
 
514
      if (!resPtr->gStateDict.dictLookupNF(name, obj)->isNull()) {
 
515
        return gTrue;
 
516
      }
 
517
      obj->free();
 
518
    }
 
519
  }
 
520
  error(-1, "ExtGState '%s' is unknown", name);
 
521
  return gFalse;
 
522
}
 
523
 
 
524
//------------------------------------------------------------------------
 
525
// Gfx
 
526
//------------------------------------------------------------------------
 
527
 
 
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)
 
533
#ifdef USE_CMS
 
534
 : iccColorSpaceCache(5)
 
535
#endif
 
536
{
 
537
  int i;
 
538
 
 
539
  xref = xrefA;
 
540
  catalog = catalogA;
 
541
  subPage = gFalse;
 
542
  printCommands = globalParams->getPrintCommands();
 
543
  profileCommands = globalParams->getProfileCommands();
 
544
  textHaveCSPattern = gFalse;
 
545
  drawText = gFalse;
 
546
  maskHaveCSPattern = gFalse;
 
547
  mcStack = NULL;
 
548
  parser = NULL;
 
549
 
 
550
  // start the resource stack
 
551
  res = new GfxResources(xref, resDict, NULL);
 
552
 
 
553
  // initialize
 
554
  out = outA;
 
555
  state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
 
556
  stackHeight = 1;
 
557
  pushStateGuard();
 
558
  fontChanged = gFalse;
 
559
  clip = clipNone;
 
560
  ignoreUndef = 0;
 
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];
 
566
  }
 
567
  formDepth = 0;
 
568
  abortCheckCbk = abortCheckCbkA;
 
569
  abortCheckCbkData = abortCheckCbkDataA;
 
570
 
 
571
  // set crop box
 
572
  if (cropBox) {
 
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);
 
577
    state->closePath();
 
578
    state->clip();
 
579
    out->clip(state);
 
580
    state->clearPath();
 
581
  }
 
582
}
 
583
 
 
584
Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict, Catalog *catalogA,
 
585
         PDFRectangle *box, PDFRectangle *cropBox,
 
586
         GBool (*abortCheckCbkA)(void *data),
 
587
         void *abortCheckCbkDataA)
 
588
 #ifdef USE_CMS
 
589
 : iccColorSpaceCache(5)
 
590
#endif
 
591
{
 
592
  int i;
 
593
 
 
594
  xref = xrefA;
 
595
  catalog = catalogA;
 
596
  subPage = gTrue;
 
597
  printCommands = globalParams->getPrintCommands();
 
598
  profileCommands = globalParams->getProfileCommands();
 
599
  textHaveCSPattern = gFalse;
 
600
  drawText = gFalse;
 
601
  maskHaveCSPattern = gFalse;
 
602
  mcStack = NULL;
 
603
  parser = NULL;
 
604
 
 
605
  // start the resource stack
 
606
  res = new GfxResources(xref, resDict, NULL);
 
607
 
 
608
  // initialize
 
609
  out = outA;
 
610
  state = new GfxState(72, 72, box, 0, gFalse);
 
611
  stackHeight = 1;
 
612
  pushStateGuard();
 
613
  fontChanged = gFalse;
 
614
  clip = clipNone;
 
615
  ignoreUndef = 0;
 
616
  for (i = 0; i < 6; ++i) {
 
617
    baseMatrix[i] = state->getCTM()[i];
 
618
  }
 
619
  formDepth = 0;
 
620
  abortCheckCbk = abortCheckCbkA;
 
621
  abortCheckCbkData = abortCheckCbkDataA;
 
622
 
 
623
  // set crop box
 
624
  if (cropBox) {
 
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);
 
629
    state->closePath();
 
630
    state->clip();
 
631
    out->clip(state);
 
632
    state->clearPath();
 
633
  }
 
634
}
 
635
 
 
636
Gfx::~Gfx() {
 
637
  while (stateGuards.size()) {
 
638
    popStateGuard();
 
639
  }
 
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.");
 
643
    restoreState();
 
644
  }
 
645
  if (!subPage) {
 
646
    out->endPage();
 
647
  }
 
648
  while (res) {
 
649
    popResources();
 
650
  }
 
651
  if (state) {
 
652
    delete state;
 
653
  }
 
654
  while (mcStack) {
 
655
    popMarkedContent();
 
656
  }
 
657
}
 
658
 
 
659
void Gfx::display(Object *obj, GBool topLevel) {
 
660
  Object obj2;
 
661
  int i;
 
662
 
 
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");
 
668
        obj2.free();
 
669
        return;
 
670
      }
 
671
      obj2.free();
 
672
    }
 
673
  } else if (!obj->isStream()) {
 
674
    error(-1, "Weird page contents");
 
675
    return;
 
676
  }
 
677
  parser = new Parser(xref, new Lexer(xref, obj), gFalse);
 
678
  go(topLevel);
 
679
  delete parser;
 
680
  parser = NULL;
 
681
}
 
682
 
 
683
void Gfx::go(GBool topLevel) {
 
684
  Object obj;
 
685
  Object args[maxArgs];
 
686
  int numArgs, i;
 
687
  int lastAbortCheck;
 
688
 
 
689
  // scan a sequence of objects
 
690
  pushStateGuard();
 
691
  updateLevel = lastAbortCheck = 0;
 
692
  numArgs = 0;
 
693
  parser->getObj(&obj);
 
694
  while (!obj.isEOF()) {
 
695
    commandAborted = gFalse;
 
696
 
 
697
    // got a command - execute it
 
698
    if (obj.isCmd()) {
 
699
      if (printCommands) {
 
700
        obj.print(stdout);
 
701
        for (i = 0; i < numArgs; ++i) {
 
702
          printf(" ");
 
703
          args[i].print(stdout);
 
704
        }
 
705
        printf("\n");
 
706
        fflush(stdout);
 
707
      }
 
708
      GooTimer timer;
 
709
 
 
710
      // Run the operation
 
711
      execOp(&obj, args, numArgs);
 
712
 
 
713
      // Update the profile information
 
714
      if (profileCommands) {
 
715
        GooHash *hash;
 
716
 
 
717
        hash = out->getProfileHash ();
 
718
        if (hash) {
 
719
          GooString *cmd_g;
 
720
          ProfileData *data_p;
 
721
 
 
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);
 
727
          }
 
728
          
 
729
          data_p->addElement(timer.getElapsed ());
 
730
        }
 
731
      }
 
732
      obj.free();
 
733
      for (i = 0; i < numArgs; ++i)
 
734
        args[i].free();
 
735
      numArgs = 0;
 
736
 
 
737
      // periodically update display
 
738
      if (++updateLevel >= 20000) {
 
739
        out->dump();
 
740
        updateLevel = 0;
 
741
      }
 
742
 
 
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;
 
748
        break;
 
749
      }
 
750
 
 
751
      // check for an abort
 
752
      if (abortCheckCbk) {
 
753
        if (updateLevel - lastAbortCheck > 10) {
 
754
          if ((*abortCheckCbk)(abortCheckCbkData)) {
 
755
            break;
 
756
          }
 
757
          lastAbortCheck = updateLevel;
 
758
        }
 
759
      }
 
760
 
 
761
    // got an argument - save it
 
762
    } else if (numArgs < maxArgs) {
 
763
      args[numArgs++] = obj;
 
764
 
 
765
    // too many arguments - something is wrong
 
766
    } else {
 
767
      error(getPos(), "Too many args in content stream");
 
768
      if (printCommands) {
 
769
        printf("throwing away arg: ");
 
770
        obj.print(stdout);
 
771
        printf("\n");
 
772
        fflush(stdout);
 
773
      }
 
774
      obj.free();
 
775
    }
 
776
 
 
777
    // grab the next object
 
778
    parser->getObj(&obj);
 
779
  }
 
780
  obj.free();
 
781
 
 
782
  // args at end with no command
 
783
  if (numArgs > 0) {
 
784
    error(getPos(), "Leftover args in content stream");
 
785
    if (printCommands) {
 
786
      printf("%d leftovers:", numArgs);
 
787
      for (i = 0; i < numArgs; ++i) {
 
788
        printf(" ");
 
789
        args[i].print(stdout);
 
790
      }
 
791
      printf("\n");
 
792
      fflush(stdout);
 
793
    }
 
794
    for (i = 0; i < numArgs; ++i)
 
795
      args[i].free();
 
796
  }
 
797
 
 
798
  popStateGuard();
 
799
 
 
800
  // update display
 
801
  if (topLevel && updateLevel > 0) {
 
802
    out->dump();
 
803
  }
 
804
}
 
805
 
 
806
void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
 
807
  Operator *op;
 
808
  char *name;
 
809
  Object *argPtr;
 
810
  int i;
 
811
 
 
812
  // find operator
 
813
  name = cmd->getCmd();
 
814
  if (!(op = findOp(name))) {
 
815
    if (ignoreUndef == 0)
 
816
      error(getPos(), "Unknown operator '%s'", name);
 
817
    return;
 
818
  }
 
819
 
 
820
  // type check args
 
821
  argPtr = args;
 
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;
 
826
      return;
 
827
    }
 
828
    if (numArgs > op->numArgs) {
 
829
#if 0
 
830
      error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
 
831
#endif
 
832
      argPtr += numArgs - op->numArgs;
 
833
      numArgs = op->numArgs;
 
834
    }
 
835
  } else {
 
836
    if (numArgs > -op->numArgs) {
 
837
      error(getPos(), "Too many (%d) args to '%s' operator",
 
838
            numArgs, name);
 
839
      return;
 
840
    }
 
841
  }
 
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());
 
846
      return;
 
847
    }
 
848
  }
 
849
 
 
850
  // do it
 
851
  (this->*op->func)(argPtr, numArgs);
 
852
}
 
853
 
 
854
Operator *Gfx::findOp(char *name) {
 
855
  int a, b, m, cmp;
 
856
 
 
857
  a = -1;
 
858
  b = numOps;
 
859
  // invariant: opTab[a] < name < opTab[b]
 
860
  while (b - a > 1) {
 
861
    m = (a + b) / 2;
 
862
    cmp = strcmp(opTab[m].name, name);
 
863
    if (cmp < 0)
 
864
      a = m;
 
865
    else if (cmp > 0)
 
866
      b = m;
 
867
    else
 
868
      a = b = m;
 
869
  }
 
870
  if (cmp != 0)
 
871
    return NULL;
 
872
  return &opTab[a];
 
873
}
 
874
 
 
875
GBool Gfx::checkArg(Object *arg, TchkType type) {
 
876
  switch (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;
 
886
  }
 
887
  return gFalse;
 
888
}
 
889
 
 
890
int Gfx::getPos() {
 
891
  return parser ? parser->getPos() : -1;
 
892
}
 
893
 
 
894
//------------------------------------------------------------------------
 
895
// graphics state operators
 
896
//------------------------------------------------------------------------
 
897
 
 
898
void Gfx::opSave(Object args[], int numArgs) {
 
899
  saveState();
 
900
}
 
901
 
 
902
void Gfx::opRestore(Object args[], int numArgs) {
 
903
  restoreState();
 
904
}
 
905
 
 
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());
 
913
  fontChanged = gTrue;
 
914
}
 
915
 
 
916
void Gfx::opSetDash(Object args[], int numArgs) {
 
917
  Array *a;
 
918
  int length;
 
919
  Object obj;
 
920
  double *dash;
 
921
  int i;
 
922
 
 
923
  a = args[0].getArray();
 
924
  length = a->getLength();
 
925
  if (length == 0) {
 
926
    dash = NULL;
 
927
  } else {
 
928
    dash = (double *)gmallocn(length, sizeof(double));
 
929
    for (i = 0; i < length; ++i) {
 
930
      dash[i] = a->get(i, &obj)->getNum();
 
931
      obj.free();
 
932
    }
 
933
  }
 
934
  state->setLineDash(dash, length, args[1].getNum());
 
935
  out->updateLineDash(state);
 
936
}
 
937
 
 
938
void Gfx::opSetFlat(Object args[], int numArgs) {
 
939
  state->setFlatness((int)args[0].getNum());
 
940
  out->updateFlatness(state);
 
941
}
 
942
 
 
943
void Gfx::opSetLineJoin(Object args[], int numArgs) {
 
944
  state->setLineJoin(args[0].getInt());
 
945
  out->updateLineJoin(state);
 
946
}
 
947
 
 
948
void Gfx::opSetLineCap(Object args[], int numArgs) {
 
949
  state->setLineCap(args[0].getInt());
 
950
  out->updateLineCap(state);
 
951
}
 
952
 
 
953
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
 
954
  state->setMiterLimit(args[0].getNum());
 
955
  out->updateMiterLimit(state);
 
956
}
 
957
 
 
958
void Gfx::opSetLineWidth(Object args[], int numArgs) {
 
959
  state->setLineWidth(args[0].getNum());
 
960
  out->updateLineWidth(state);
 
961
}
 
962
 
 
963
void Gfx::opSetExtGState(Object args[], int numArgs) {
 
964
  Object obj1, obj2, obj3, obj4, obj5;
 
965
  GfxBlendMode mode;
 
966
  GBool haveFillOP;
 
967
  Function *funcs[4];
 
968
  GfxColor backdropColor;
 
969
  GBool haveBackdropColor;
 
970
  GfxColorSpace *blendingColorSpace;
 
971
  GBool alpha, isolated, knockout;
 
972
  int i;
 
973
 
 
974
  if (!res->lookupGState(args[0].getName(), &obj1)) {
 
975
    return;
 
976
  }
 
977
  if (!obj1.isDict()) {
 
978
    error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
 
979
    obj1.free();
 
980
    return;
 
981
  }
 
982
  if (printCommands) {
 
983
    printf("  gfx state dict: ");
 
984
    obj1.print();
 
985
    printf("\n");
 
986
  }
 
987
 
 
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);
 
993
    } else {
 
994
      error(getPos(), "Invalid blend mode in ExtGState");
 
995
    }
 
996
  }
 
997
  obj2.free();
 
998
  if (obj1.dictLookup("ca", &obj2)->isNum()) {
 
999
    state->setFillOpacity(obj2.getNum());
 
1000
    out->updateFillOpacity(state);
 
1001
  }
 
1002
  obj2.free();
 
1003
  if (obj1.dictLookup("CA", &obj2)->isNum()) {
 
1004
    state->setStrokeOpacity(obj2.getNum());
 
1005
    out->updateStrokeOpacity(state);
 
1006
  }
 
1007
  obj2.free();
 
1008
 
 
1009
  // fill/stroke overprint
 
1010
  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
 
1011
    state->setFillOverprint(obj2.getBool());
 
1012
    out->updateFillOverprint(state);
 
1013
  }
 
1014
  obj2.free();
 
1015
  if (obj1.dictLookup("OP", &obj2)->isBool()) {
 
1016
    state->setStrokeOverprint(obj2.getBool());
 
1017
    out->updateStrokeOverprint(state);
 
1018
    if (!haveFillOP) {
 
1019
      state->setFillOverprint(obj2.getBool());
 
1020
      out->updateFillOverprint(state);
 
1021
    }
 
1022
  }
 
1023
  obj2.free();
 
1024
 
 
1025
  // stroke adjust
 
1026
  if (obj1.dictLookup("SA", &obj2)->isBool()) {
 
1027
    state->setStrokeAdjust(obj2.getBool());
 
1028
    out->updateStrokeAdjust(state);
 
1029
  }
 
1030
  obj2.free();
 
1031
 
 
1032
  // transfer function
 
1033
  if (obj1.dictLookup("TR2", &obj2)->isNull()) {
 
1034
    obj2.free();
 
1035
    obj1.dictLookup("TR", &obj2);
 
1036
  }
 
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);
 
1046
      obj3.free();
 
1047
      if (!funcs[i]) {
 
1048
        break;
 
1049
      }
 
1050
    }
 
1051
    if (i == 4) {
 
1052
      state->setTransfer(funcs);
 
1053
      out->updateTransfer(state);
 
1054
    }
 
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);
 
1060
    }
 
1061
  } else if (!obj2.isNull()) {
 
1062
    error(getPos(), "Invalid transfer function in ExtGState");
 
1063
  }
 
1064
  obj2.free();
 
1065
 
 
1066
  // alpha is shape
 
1067
  if (obj1.dictLookup("AIS", &obj2)->isBool()) {
 
1068
    state->setAlphaIsShape(obj2.getBool());
 
1069
    out->updateAlphaIsShape(state);
 
1070
  }
 
1071
  obj2.free();
 
1072
 
 
1073
  // text knockout
 
1074
  if (obj1.dictLookup("TK", &obj2)->isBool()) {
 
1075
    state->setTextKnockout(obj2.getBool());
 
1076
    out->updateTextKnockout(state);
 
1077
  }
 
1078
  obj2.free();
 
1079
 
 
1080
  // soft mask
 
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")) {
 
1086
        alpha = gTrue;
 
1087
      } else { // "Luminosity"
 
1088
        alpha = gFalse;
 
1089
      }
 
1090
      obj3.free();
 
1091
      funcs[0] = NULL;
 
1092
      if (!obj2.dictLookup("TR", &obj3)->isNull()) {
 
1093
        funcs[0] = Function::parse(&obj3);
 
1094
        if (funcs[0]->getInputSize() != 1 ||
 
1095
            funcs[0]->getOutputSize() != 1) {
 
1096
          error(getPos(),
 
1097
                "Invalid transfer function in soft mask in ExtGState");
 
1098
          delete funcs[0];
 
1099
          funcs[0] = NULL;
 
1100
        }
 
1101
      }
 
1102
      obj3.free();
 
1103
      if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
 
1104
        for (i = 0; i < gfxColorMaxComps; ++i) {
 
1105
          backdropColor.c[i] = 0;
 
1106
        }
 
1107
        for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
 
1108
          obj3.arrayGet(i, &obj4);
 
1109
          if (obj4.isNum()) {
 
1110
            backdropColor.c[i] = dblToCol(obj4.getNum());
 
1111
          }
 
1112
          obj4.free();
 
1113
        }
 
1114
      }
 
1115
      obj3.free();
 
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);
 
1122
          }
 
1123
          obj5.free();
 
1124
          if (obj4.dictLookup("I", &obj5)->isBool()) {
 
1125
            isolated = obj5.getBool();
 
1126
          }
 
1127
          obj5.free();
 
1128
          if (obj4.dictLookup("K", &obj5)->isBool()) {
 
1129
            knockout = obj5.getBool();
 
1130
          }
 
1131
          obj5.free();
 
1132
          if (!haveBackdropColor) {
 
1133
            if (blendingColorSpace) {
 
1134
              blendingColorSpace->getDefaultColor(&backdropColor);
 
1135
            } else {
 
1136
              //~ need to get the parent or default color space (?)
 
1137
              for (i = 0; i < gfxColorMaxComps; ++i) {
 
1138
                backdropColor.c[i] = 0;
 
1139
              }
 
1140
            }
 
1141
          }
 
1142
          doSoftMask(&obj3, alpha, blendingColorSpace,
 
1143
                     isolated, knockout, funcs[0], &backdropColor);
 
1144
          if (funcs[0]) {
 
1145
            delete funcs[0];
 
1146
          }
 
1147
        } else {
 
1148
          error(getPos(), "Invalid soft mask in ExtGState - missing group");
 
1149
        }
 
1150
        obj4.free();
 
1151
      } else {
 
1152
        error(getPos(), "Invalid soft mask in ExtGState - missing group");
 
1153
      }
 
1154
      obj3.free();
 
1155
    } else if (!obj2.isNull()) {
 
1156
      error(getPos(), "Invalid soft mask in ExtGState");
 
1157
    }
 
1158
  }
 
1159
  obj2.free();
 
1160
  if (obj1.dictLookup("Font", &obj2)->isArray()) {
 
1161
    GfxFont *font;
 
1162
    if (obj2.arrayGetLength() == 2) {
 
1163
      Object fargs0, fargs1;
 
1164
 
 
1165
      obj2.arrayGetNF(0,&fargs0);
 
1166
      obj2.arrayGet(1,&fargs1);
 
1167
      if (fargs0.isRef() && fargs1.isNum()) {
 
1168
        Object fobj;
 
1169
        Ref r;
 
1170
 
 
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;
 
1177
        }
 
1178
        fobj.free();
 
1179
      }
 
1180
      fargs0.free();
 
1181
      fargs1.free();
 
1182
    } else {
 
1183
      error(getPos(), "Number of args mismatch for /Font in ExtGState");
 
1184
    }
 
1185
  }
 
1186
  obj2.free();
 
1187
  if (obj1.dictLookup("LW", &obj2)->isNum()) {
 
1188
    opSetLineWidth(&obj2,1);
 
1189
  }
 
1190
  obj2.free();
 
1191
  if (obj1.dictLookup("LC", &obj2)->isInt()) {
 
1192
    opSetLineCap(&obj2,1);
 
1193
  }
 
1194
  obj2.free();
 
1195
  if (obj1.dictLookup("LJ", &obj2)->isInt()) {
 
1196
    opSetLineJoin(&obj2,1);
 
1197
  }
 
1198
  obj2.free();
 
1199
  if (obj1.dictLookup("ML", &obj2)->isNum()) {
 
1200
    opSetMiterLimit(&obj2,1);
 
1201
  }
 
1202
  obj2.free();
 
1203
  if (obj1.dictLookup("D", &obj2)->isArray()) {
 
1204
    if (obj2.arrayGetLength() == 2) {
 
1205
      Object dargs[2];
 
1206
 
 
1207
      obj2.arrayGetNF(0,&dargs[0]);
 
1208
      obj2.arrayGet(1,&dargs[1]);
 
1209
      if (dargs[0].isArray() && dargs[1].isInt()) {
 
1210
        opSetDash(dargs,2);
 
1211
      }
 
1212
      dargs[0].free();
 
1213
      dargs[1].free();
 
1214
    } else {
 
1215
      error(getPos(), "Number of args mismatch for /D in ExtGState");
 
1216
    }
 
1217
  }
 
1218
  obj2.free();
 
1219
  if (obj1.dictLookup("RI", &obj2)->isName()) {
 
1220
    opSetRenderingIntent(&obj2,1);
 
1221
  }
 
1222
  obj2.free();
 
1223
  if (obj1.dictLookup("FL", &obj2)->isNum()) {
 
1224
    opSetFlat(&obj2,1);
 
1225
  }
 
1226
  obj2.free();
 
1227
 
 
1228
  obj1.free();
 
1229
}
 
1230
 
 
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];
 
1237
  Object obj1, obj2;
 
1238
  int i;
 
1239
 
 
1240
  // check for excessive recursion
 
1241
  if (formDepth > 20) {
 
1242
    return;
 
1243
  }
 
1244
 
 
1245
  // get stream dict
 
1246
  dict = str->streamGetDict();
 
1247
 
 
1248
  // check form type
 
1249
  dict->lookup("FormType", &obj1);
 
1250
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
 
1251
    error(getPos(), "Unknown form type");
 
1252
  }
 
1253
  obj1.free();
 
1254
 
 
1255
  // get bounding box
 
1256
  dict->lookup("BBox", &obj1);
 
1257
  if (!obj1.isArray()) {
 
1258
    obj1.free();
 
1259
    error(getPos(), "Bad form bounding box");
 
1260
    return;
 
1261
  }
 
1262
  for (i = 0; i < 4; ++i) {
 
1263
    obj1.arrayGet(i, &obj2);
 
1264
    if (likely(obj2.isNum())) bbox[i] = obj2.getNum();
 
1265
    else {
 
1266
      obj2.free();
 
1267
      obj1.free();
 
1268
      error(getPos(), "Bad form bounding box (non number)");
 
1269
      return;
 
1270
    }
 
1271
    obj2.free();
 
1272
  }
 
1273
  obj1.free();
 
1274
 
 
1275
  // get matrix
 
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();
 
1281
      else m[i] = 0;
 
1282
      obj2.free();
 
1283
    }
 
1284
  } else {
 
1285
    m[0] = 1; m[1] = 0;
 
1286
    m[2] = 0; m[3] = 1;
 
1287
    m[4] = 0; m[5] = 0;
 
1288
  }
 
1289
  obj1.free();
 
1290
 
 
1291
  // get resources
 
1292
  dict->lookup("Resources", &obj1);
 
1293
  resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
 
1294
 
 
1295
  // draw it
 
1296
  ++formDepth;
 
1297
  doForm1(str, resDict, m, bbox, gTrue, gTrue,
 
1298
          blendingColorSpace, isolated, knockout,
 
1299
          alpha, transferFunc, backdropColor);
 
1300
  --formDepth;
 
1301
 
 
1302
  if (blendingColorSpace) {
 
1303
    delete blendingColorSpace;
 
1304
  }
 
1305
  obj1.free();
 
1306
}
 
1307
 
 
1308
void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
 
1309
}
 
1310
 
 
1311
//------------------------------------------------------------------------
 
1312
// color operators
 
1313
//------------------------------------------------------------------------
 
1314
 
 
1315
void Gfx::opSetFillGray(Object args[], int numArgs) {
 
1316
  GfxColor color;
 
1317
 
 
1318
  if (textHaveCSPattern && drawText) {
 
1319
    GBool needFill = out->deviceHasTextClip(state);
 
1320
    out->endTextObject(state);
 
1321
    if (needFill) {
 
1322
      doPatternFill(gTrue);
 
1323
    }
 
1324
    out->restoreState(state);
 
1325
  }
 
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;
 
1338
  }
 
1339
}
 
1340
 
 
1341
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
 
1342
  GfxColor color;
 
1343
 
 
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);
 
1350
}
 
1351
 
 
1352
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
 
1353
  GfxColor color;
 
1354
  int i;
 
1355
 
 
1356
  if (textHaveCSPattern && drawText) {
 
1357
    GBool needFill = out->deviceHasTextClip(state);
 
1358
    out->endTextObject(state);
 
1359
    if (needFill) {
 
1360
      doPatternFill(gTrue);
 
1361
    }
 
1362
    out->restoreState(state);
 
1363
  }
 
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());
 
1369
  }
 
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;
 
1378
  }
 
1379
}
 
1380
 
 
1381
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
 
1382
  GfxColor color;
 
1383
  int i;
 
1384
 
 
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());
 
1390
  }
 
1391
  state->setStrokeColor(&color);
 
1392
  out->updateStrokeColor(state);
 
1393
}
 
1394
 
 
1395
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
 
1396
  GfxColor color;
 
1397
  int i;
 
1398
 
 
1399
  if (textHaveCSPattern && drawText) {
 
1400
    GBool needFill = out->deviceHasTextClip(state);
 
1401
    out->endTextObject(state);
 
1402
    if (needFill) {
 
1403
      doPatternFill(gTrue);
 
1404
    }
 
1405
    out->restoreState(state);
 
1406
  }
 
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());
 
1412
  }
 
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;
 
1421
  }
 
1422
}
 
1423
 
 
1424
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
 
1425
  GfxColor color;
 
1426
  int i;
 
1427
 
 
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());
 
1433
  }
 
1434
  state->setStrokeColor(&color);
 
1435
  out->updateStrokeColor(state);
 
1436
}
 
1437
 
 
1438
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
 
1439
  Object obj;
 
1440
  GfxColorSpace *colorSpace;
 
1441
  GfxColor color;
 
1442
 
 
1443
  res->lookupColorSpace(args[0].getName(), &obj);
 
1444
  if (obj.isNull()) {
 
1445
    colorSpace = GfxColorSpace::parse(&args[0], this);
 
1446
  } else {
 
1447
    colorSpace = GfxColorSpace::parse(&obj, this);
 
1448
  }
 
1449
  obj.free();
 
1450
  if (colorSpace) {
 
1451
    if (textHaveCSPattern && drawText) {
 
1452
      GBool needFill = out->deviceHasTextClip(state);
 
1453
      out->endTextObject(state);
 
1454
      if (needFill) {
 
1455
        doPatternFill(gTrue);
 
1456
      }
 
1457
      out->restoreState(state);
 
1458
    }
 
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;
 
1474
    }
 
1475
  } else {
 
1476
    error(getPos(), "Bad color space (fill)");
 
1477
  }
 
1478
}
 
1479
 
 
1480
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
 
1481
  Object obj;
 
1482
  GfxColorSpace *colorSpace;
 
1483
  GfxColor color;
 
1484
 
 
1485
  state->setStrokePattern(NULL);
 
1486
  res->lookupColorSpace(args[0].getName(), &obj);
 
1487
  if (obj.isNull()) {
 
1488
    colorSpace = GfxColorSpace::parse(&args[0], this);
 
1489
  } else {
 
1490
    colorSpace = GfxColorSpace::parse(&obj, this);
 
1491
  }
 
1492
  obj.free();
 
1493
  if (colorSpace) {
 
1494
    state->setStrokeColorSpace(colorSpace);
 
1495
    out->updateStrokeColorSpace(state);
 
1496
    colorSpace->getDefaultColor(&color);
 
1497
    state->setStrokeColor(&color);
 
1498
    out->updateStrokeColor(state);
 
1499
  } else {
 
1500
    error(getPos(), "Bad color space (stroke)");
 
1501
  }
 
1502
}
 
1503
 
 
1504
void Gfx::opSetFillColor(Object args[], int numArgs) {
 
1505
  GfxColor color;
 
1506
  int i;
 
1507
 
 
1508
  if (numArgs != state->getFillColorSpace()->getNComps()) {
 
1509
    error(getPos(), "Incorrect number of arguments in 'sc' command");
 
1510
    return;
 
1511
  }
 
1512
  state->setFillPattern(NULL);
 
1513
  for (i = 0; i < numArgs; ++i) {
 
1514
    color.c[i] = dblToCol(args[i].getNum());
 
1515
  }
 
1516
  state->setFillColor(&color);
 
1517
  out->updateFillColor(state);
 
1518
}
 
1519
 
 
1520
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
 
1521
  GfxColor color;
 
1522
  int i;
 
1523
 
 
1524
  if (numArgs != state->getStrokeColorSpace()->getNComps()) {
 
1525
    error(getPos(), "Incorrect number of arguments in 'SC' command");
 
1526
    return;
 
1527
  }
 
1528
  state->setStrokePattern(NULL);
 
1529
  for (i = 0; i < numArgs; ++i) {
 
1530
    color.c[i] = dblToCol(args[i].getNum());
 
1531
  }
 
1532
  state->setStrokeColor(&color);
 
1533
  out->updateStrokeColor(state);
 
1534
}
 
1535
 
 
1536
void Gfx::opSetFillColorN(Object args[], int numArgs) {
 
1537
  GfxColor color;
 
1538
  GfxPattern *pattern;
 
1539
  int i;
 
1540
 
 
1541
  if (state->getFillColorSpace()->getMode() == csPattern) {
 
1542
    if (numArgs > 1) {
 
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");
 
1547
        return;
 
1548
      }
 
1549
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
 
1550
        if (args[i].isNum()) {
 
1551
          color.c[i] = dblToCol(args[i].getNum());
 
1552
        } else {
 
1553
          color.c[i] = 0; // TODO Investigate if this is what Adobe does
 
1554
        }
 
1555
      }
 
1556
      state->setFillColor(&color);
 
1557
      out->updateFillColor(state);
 
1558
    }
 
1559
    if (args[numArgs-1].isName() &&
 
1560
        (pattern = res->lookupPattern(args[numArgs-1].getName(), this))) {
 
1561
      state->setFillPattern(pattern);
 
1562
    }
 
1563
 
 
1564
  } else {
 
1565
    if (numArgs != state->getFillColorSpace()->getNComps()) {
 
1566
      error(getPos(), "Incorrect number of arguments in 'scn' command");
 
1567
      return;
 
1568
    }
 
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());
 
1573
      } else {
 
1574
        color.c[i] = 0; // TODO Investigate if this is what Adobe does
 
1575
      }
 
1576
    }
 
1577
    state->setFillColor(&color);
 
1578
    out->updateFillColor(state);
 
1579
  }
 
1580
}
 
1581
 
 
1582
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
 
1583
  GfxColor color;
 
1584
  GfxPattern *pattern;
 
1585
  int i;
 
1586
 
 
1587
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1588
    if (numArgs > 1) {
 
1589
      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
 
1590
               ->getUnder() ||
 
1591
          numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
 
1592
                             ->getUnder()->getNComps()) {
 
1593
        error(getPos(), "Incorrect number of arguments in 'SCN' command");
 
1594
        return;
 
1595
      }
 
1596
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
 
1597
        if (args[i].isNum()) {
 
1598
          color.c[i] = dblToCol(args[i].getNum());
 
1599
        } else {
 
1600
          color.c[i] = 0; // TODO Investigate if this is what Adobe does
 
1601
        }
 
1602
      }
 
1603
      state->setStrokeColor(&color);
 
1604
      out->updateStrokeColor(state);
 
1605
    }
 
1606
    if (args[numArgs-1].isName() &&
 
1607
        (pattern = res->lookupPattern(args[numArgs-1].getName(), this))) {
 
1608
      state->setStrokePattern(pattern);
 
1609
    }
 
1610
 
 
1611
  } else {
 
1612
    if (numArgs != state->getStrokeColorSpace()->getNComps()) {
 
1613
      error(getPos(), "Incorrect number of arguments in 'SCN' command");
 
1614
      return;
 
1615
    }
 
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());
 
1620
      } else {
 
1621
        color.c[i] = 0; // TODO Investigate if this is what Adobe does
 
1622
      }
 
1623
    }
 
1624
    state->setStrokeColor(&color);
 
1625
    out->updateStrokeColor(state);
 
1626
  }
 
1627
}
 
1628
 
 
1629
//------------------------------------------------------------------------
 
1630
// path segment operators
 
1631
//------------------------------------------------------------------------
 
1632
 
 
1633
void Gfx::opMoveTo(Object args[], int numArgs) {
 
1634
  state->moveTo(args[0].getNum(), args[1].getNum());
 
1635
}
 
1636
 
 
1637
void Gfx::opLineTo(Object args[], int numArgs) {
 
1638
  if (!state->isCurPt()) {
 
1639
    error(getPos(), "No current point in lineto");
 
1640
    return;
 
1641
  }
 
1642
  state->lineTo(args[0].getNum(), args[1].getNum());
 
1643
}
 
1644
 
 
1645
void Gfx::opCurveTo(Object args[], int numArgs) {
 
1646
  double x1, y1, x2, y2, x3, y3;
 
1647
 
 
1648
  if (!state->isCurPt()) {
 
1649
    error(getPos(), "No current point in curveto");
 
1650
    return;
 
1651
  }
 
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);
 
1659
}
 
1660
 
 
1661
void Gfx::opCurveTo1(Object args[], int numArgs) {
 
1662
  double x1, y1, x2, y2, x3, y3;
 
1663
 
 
1664
  if (!state->isCurPt()) {
 
1665
    error(getPos(), "No current point in curveto1");
 
1666
    return;
 
1667
  }
 
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);
 
1675
}
 
1676
 
 
1677
void Gfx::opCurveTo2(Object args[], int numArgs) {
 
1678
  double x1, y1, x2, y2, x3, y3;
 
1679
 
 
1680
  if (!state->isCurPt()) {
 
1681
    error(getPos(), "No current point in curveto2");
 
1682
    return;
 
1683
  }
 
1684
  x1 = args[0].getNum();
 
1685
  y1 = args[1].getNum();
 
1686
  x2 = args[2].getNum();
 
1687
  y2 = args[3].getNum();
 
1688
  x3 = x2;
 
1689
  y3 = y2;
 
1690
  state->curveTo(x1, y1, x2, y2, x3, y3);
 
1691
}
 
1692
 
 
1693
void Gfx::opRectangle(Object args[], int numArgs) {
 
1694
  double x, y, w, h;
 
1695
 
 
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);
 
1704
  state->closePath();
 
1705
}
 
1706
 
 
1707
void Gfx::opClosePath(Object args[], int numArgs) {
 
1708
  if (!state->isCurPt()) {
 
1709
    error(getPos(), "No current point in closepath");
 
1710
    return;
 
1711
  }
 
1712
  state->closePath();
 
1713
}
 
1714
 
 
1715
//------------------------------------------------------------------------
 
1716
// path painting operators
 
1717
//------------------------------------------------------------------------
 
1718
 
 
1719
void Gfx::opEndPath(Object args[], int numArgs) {
 
1720
  doEndPath();
 
1721
}
 
1722
 
 
1723
void Gfx::opStroke(Object args[], int numArgs) {
 
1724
  if (!state->isCurPt()) {
 
1725
    //error(getPos(), "No path in stroke");
 
1726
    return;
 
1727
  }
 
1728
  if (state->isPath() && !contentIsHidden()) {
 
1729
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1730
      doPatternStroke();
 
1731
    } else {
 
1732
      out->stroke(state);
 
1733
    }
 
1734
  }
 
1735
  doEndPath();
 
1736
}
 
1737
 
 
1738
void Gfx::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
 
1739
  if (!state->isCurPt()) {
 
1740
    //error(getPos(), "No path in closepath/stroke");
 
1741
    return;
 
1742
  }
 
1743
  state->closePath();
 
1744
  if (state->isPath() && !contentIsHidden()) {
 
1745
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1746
      doPatternStroke();
 
1747
    } else {
 
1748
      out->stroke(state);
 
1749
    }
 
1750
  }
 
1751
  doEndPath();
 
1752
}
 
1753
 
 
1754
void Gfx::opFill(Object args[], int numArgs) {
 
1755
  if (!state->isCurPt()) {
 
1756
    //error(getPos(), "No path in fill");
 
1757
    return;
 
1758
  }
 
1759
  if (state->isPath() && !contentIsHidden()) {
 
1760
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1761
      doPatternFill(gFalse);
 
1762
    } else {
 
1763
      out->fill(state);
 
1764
    }
 
1765
  }
 
1766
  doEndPath();
 
1767
}
 
1768
 
 
1769
void Gfx::opEOFill(Object args[], int numArgs) {
 
1770
  if (!state->isCurPt()) {
 
1771
    //error(getPos(), "No path in eofill");
 
1772
    return;
 
1773
  }
 
1774
  if (state->isPath() && !contentIsHidden()) {
 
1775
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1776
      doPatternFill(gTrue);
 
1777
    } else {
 
1778
      out->eoFill(state);
 
1779
    }
 
1780
  }
 
1781
  doEndPath();
 
1782
}
 
1783
 
 
1784
void Gfx::opFillStroke(Object args[], int numArgs) {
 
1785
  if (!state->isCurPt()) {
 
1786
    //error(getPos(), "No path in fill/stroke");
 
1787
    return;
 
1788
  }
 
1789
  if (state->isPath() && !contentIsHidden()) {
 
1790
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1791
      doPatternFill(gFalse);
 
1792
    } else {
 
1793
      out->fill(state);
 
1794
    }
 
1795
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1796
      doPatternStroke();
 
1797
    } else {
 
1798
      out->stroke(state);
 
1799
    }
 
1800
  }
 
1801
  doEndPath();
 
1802
}
 
1803
 
 
1804
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
 
1805
  if (!state->isCurPt()) {
 
1806
    //error(getPos(), "No path in closepath/fill/stroke");
 
1807
    return;
 
1808
  }
 
1809
  if (state->isPath() && !contentIsHidden()) {
 
1810
    state->closePath();
 
1811
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1812
      doPatternFill(gFalse);
 
1813
    } else {
 
1814
      out->fill(state);
 
1815
    }
 
1816
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1817
      doPatternStroke();
 
1818
    } else {
 
1819
      out->stroke(state);
 
1820
    }
 
1821
  }
 
1822
  doEndPath();
 
1823
}
 
1824
 
 
1825
void Gfx::opEOFillStroke(Object args[], int numArgs) {
 
1826
  if (!state->isCurPt()) {
 
1827
    //error(getPos(), "No path in eofill/stroke");
 
1828
    return;
 
1829
  }
 
1830
  if (state->isPath() && !contentIsHidden()) {
 
1831
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1832
      doPatternFill(gTrue);
 
1833
    } else {
 
1834
      out->eoFill(state);
 
1835
    }
 
1836
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1837
      doPatternStroke();
 
1838
    } else {
 
1839
      out->stroke(state);
 
1840
    }
 
1841
  }
 
1842
  doEndPath();
 
1843
}
 
1844
 
 
1845
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
 
1846
  if (!state->isCurPt()) {
 
1847
    //error(getPos(), "No path in closepath/eofill/stroke");
 
1848
    return;
 
1849
  }
 
1850
  if (state->isPath() && !contentIsHidden()) {
 
1851
    state->closePath();
 
1852
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1853
      doPatternFill(gTrue);
 
1854
    } else {
 
1855
      out->eoFill(state);
 
1856
    }
 
1857
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1858
      doPatternStroke();
 
1859
    } else {
 
1860
      out->stroke(state);
 
1861
    }
 
1862
  }
 
1863
  doEndPath();
 
1864
}
 
1865
 
 
1866
void Gfx::doPatternFill(GBool eoFill) {
 
1867
  GfxPattern *pattern;
 
1868
 
 
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()) {
 
1873
    return;
 
1874
  }
 
1875
 
 
1876
  if (!(pattern = state->getFillPattern())) {
 
1877
    return;
 
1878
  }
 
1879
  switch (pattern->getType()) {
 
1880
  case 1:
 
1881
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill);
 
1882
    break;
 
1883
  case 2:
 
1884
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);
 
1885
    break;
 
1886
  default:
 
1887
    error(getPos(), "Unimplemented pattern type (%d) in fill",
 
1888
          pattern->getType());
 
1889
    break;
 
1890
  }
 
1891
}
 
1892
 
 
1893
void Gfx::doPatternStroke() {
 
1894
  GfxPattern *pattern;
 
1895
 
 
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()) {
 
1900
    return;
 
1901
  }
 
1902
 
 
1903
  if (!(pattern = state->getStrokePattern())) {
 
1904
    return;
 
1905
  }
 
1906
  switch (pattern->getType()) {
 
1907
  case 1:
 
1908
    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse);
 
1909
    break;
 
1910
  case 2:
 
1911
    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);
 
1912
    break;
 
1913
  default:
 
1914
    error(getPos(), "Unimplemented pattern type (%d) in stroke",
 
1915
          pattern->getType());
 
1916
    break;
 
1917
  }
 
1918
}
 
1919
 
 
1920
void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
 
1921
                              GBool stroke, GBool eoFill) {
 
1922
  GfxPatternColorSpace *patCS;
 
1923
  GfxColorSpace *cs;
 
1924
  GfxColor color;
 
1925
  GfxPath *savedPath;
 
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];
 
1931
  double det;
 
1932
  double xstep, ystep;
 
1933
  int i;
 
1934
 
 
1935
  // get color space
 
1936
  patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
 
1937
                                          : state->getFillColorSpace());
 
1938
 
 
1939
  // construct a (pattern space) -> (current space) transform matrix
 
1940
  ctm = state->getCTM();
 
1941
  btm = baseMatrix;
 
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];
 
1965
 
 
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;
 
1974
 
 
1975
  // save current graphics state
 
1976
  savedPath = state->getPath()->copy();
 
1977
  saveState();
 
1978
 
 
1979
  // set underlying color space (for uncolored tiling patterns); set
 
1980
  // various other parameters (stroke color, line width) to match
 
1981
  // Adobe's behavior
 
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);
 
1987
    if (stroke) {
 
1988
        state->setFillColor(state->getStrokeColor());
 
1989
    } else {
 
1990
        state->setStrokeColor(state->getFillColor());
 
1991
    }
 
1992
  } else {
 
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);
 
2001
  }
 
2002
  state->setFillPattern(NULL);
 
2003
  out->updateFillColor(state);
 
2004
  state->setStrokePattern(NULL);
 
2005
  out->updateStrokeColor(state);
 
2006
 
 
2007
  // clip to current path
 
2008
  if (stroke) {
 
2009
    state->clipToStrokePath();
 
2010
    out->clipToStrokePath(state);
 
2011
  } else if (!textHaveCSPattern && !maskHaveCSPattern) {
 
2012
    state->clip();
 
2013
    if (eoFill) {
 
2014
      out->eoClip(state);
 
2015
    } else {
 
2016
      out->clip(state);
 
2017
    }
 
2018
  }
 
2019
  state->clearPath();
 
2020
  state->setLineWidth(0);
 
2021
  out->updateLineWidth(state);
 
2022
 
 
2023
  // get the clip region, check for empty
 
2024
  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
 
2025
  if (cxMin > cxMax || cyMin > cyMax) {
 
2026
    goto restore;
 
2027
  }
 
2028
 
 
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];
 
2034
  if (x1 < xMin) {
 
2035
    xMin = x1;
 
2036
  } else if (x1 > xMax) {
 
2037
    xMax = x1;
 
2038
  }
 
2039
  if (y1 < yMin) {
 
2040
    yMin = y1;
 
2041
  } else if (y1 > yMax) {
 
2042
    yMax = y1;
 
2043
  }
 
2044
  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
 
2045
  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
 
2046
  if (x1 < xMin) {
 
2047
    xMin = x1;
 
2048
  } else if (x1 > xMax) {
 
2049
    xMax = x1;
 
2050
  }
 
2051
  if (y1 < yMin) {
 
2052
    yMin = y1;
 
2053
  } else if (y1 > yMax) {
 
2054
    yMax = y1;
 
2055
  }
 
2056
  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
 
2057
  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
 
2058
  if (x1 < xMin) {
 
2059
    xMin = x1;
 
2060
  } else if (x1 > xMax) {
 
2061
    xMax = x1;
 
2062
  }
 
2063
  if (y1 < yMin) {
 
2064
    yMin = y1;
 
2065
  } else if (y1 > yMax) {
 
2066
    yMax = y1;
 
2067
  }
 
2068
 
 
2069
  // draw the pattern
 
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) {
 
2079
    m1[i] = m[i];
 
2080
  }
 
2081
  if (!contentIsHidden()) {
 
2082
    m1[4] = m[4];
 
2083
    m1[5] = m[5];
 
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)) {
 
2089
            goto restore;
 
2090
    } else {
 
2091
      for (yi = yi0; yi < yi1; ++yi) {
 
2092
        for (xi = xi0; xi < xi1; ++xi) {
 
2093
          x = xi * xstep;
 
2094
          y = yi * ystep;
 
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());
 
2099
        }
 
2100
      }
 
2101
    }
 
2102
  }
 
2103
 
 
2104
  // restore graphics state
 
2105
 restore:
 
2106
  restoreState();
 
2107
  state->setPath(savedPath);
 
2108
}
 
2109
 
 
2110
void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
 
2111
                               GBool stroke, GBool eoFill) {
 
2112
  GfxShading *shading;
 
2113
  GfxPath *savedPath;
 
2114
  double *ctm, *btm, *ptm;
 
2115
  double m[6], ictm[6], m1[6];
 
2116
  double xMin, yMin, xMax, yMax;
 
2117
  double det;
 
2118
 
 
2119
  shading = sPat->getShading();
 
2120
 
 
2121
  // save current graphics state
 
2122
  savedPath = state->getPath()->copy();
 
2123
  saveState();
 
2124
 
 
2125
  // clip to bbox
 
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);
 
2132
    state->closePath();
 
2133
    state->clip();
 
2134
    if (!textHaveCSPattern && !maskHaveCSPattern)
 
2135
      out->clip(state);
 
2136
    state->setPath(savedPath->copy());
 
2137
  }
 
2138
 
 
2139
  // clip to current path
 
2140
  if (stroke) {
 
2141
    state->clipToStrokePath();
 
2142
    out->clipToStrokePath(state);
 
2143
  } else if (!textHaveCSPattern && !maskHaveCSPattern) {
 
2144
    state->clip();
 
2145
    if (eoFill) {
 
2146
      out->eoClip(state);
 
2147
    } else {
 
2148
      out->clip(state);
 
2149
    }
 
2150
  }
 
2151
 
 
2152
  // set the color space
 
2153
  state->setFillColorSpace(shading->getColorSpace()->copy());
 
2154
  out->updateFillColorSpace(state);
 
2155
 
 
2156
  // background color fill
 
2157
  if (shading->getHasBackground()) {
 
2158
    state->setFillColor(shading->getBackground());
 
2159
    out->updateFillColor(state);
 
2160
    if (!contentIsHidden())
 
2161
      out->fill(state);
 
2162
  }
 
2163
  state->clearPath();
 
2164
 
 
2165
  // construct a (pattern space) -> (current space) transform matrix
 
2166
  ctm = state->getCTM();
 
2167
  btm = baseMatrix;
 
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];
 
2191
 
 
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]);
 
2195
 
 
2196
#if 1 //~tmp: turn off anti-aliasing temporarily
 
2197
  GBool vaa = out->getVectorAntialias();
 
2198
  if (vaa) {
 
2199
    out->setVectorAntialias(gFalse);
 
2200
  }
 
2201
#endif
 
2202
 
 
2203
  // do shading type-specific operations
 
2204
  switch (shading->getType()) {
 
2205
  case 1:
 
2206
    doFunctionShFill((GfxFunctionShading *)shading);
 
2207
    break;
 
2208
  case 2:
 
2209
    doAxialShFill((GfxAxialShading *)shading);
 
2210
    break;
 
2211
  case 3:
 
2212
    doRadialShFill((GfxRadialShading *)shading);
 
2213
    break;
 
2214
  case 4:
 
2215
  case 5:
 
2216
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
 
2217
    break;
 
2218
  case 6:
 
2219
  case 7:
 
2220
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
 
2221
    break;
 
2222
  }
 
2223
 
 
2224
#if 1 //~tmp: turn off anti-aliasing temporarily
 
2225
  if (vaa) {
 
2226
    out->setVectorAntialias(gTrue);
 
2227
  }
 
2228
#endif
 
2229
 
 
2230
  // restore graphics state
 
2231
  restoreState();
 
2232
  state->setPath(savedPath);
 
2233
}
 
2234
 
 
2235
void Gfx::opShFill(Object args[], int numArgs) {
 
2236
  GfxShading *shading;
 
2237
  GfxPath *savedPath;
 
2238
  double xMin, yMin, xMax, yMax;
 
2239
 
 
2240
  if (!(shading = res->lookupShading(args[0].getName(), this))) {
 
2241
    return;
 
2242
  }
 
2243
 
 
2244
  // save current graphics state
 
2245
  savedPath = state->getPath()->copy();
 
2246
  saveState();
 
2247
 
 
2248
  // clip to bbox
 
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);
 
2255
    state->closePath();
 
2256
    state->clip();
 
2257
    out->clip(state);
 
2258
    state->clearPath();
 
2259
  }
 
2260
 
 
2261
  // set the color space
 
2262
  state->setFillColorSpace(shading->getColorSpace()->copy());
 
2263
  out->updateFillColorSpace(state);
 
2264
 
 
2265
#if 1 //~tmp: turn off anti-aliasing temporarily
 
2266
  GBool vaa = out->getVectorAntialias();
 
2267
  if (vaa) {
 
2268
    out->setVectorAntialias(gFalse);
 
2269
  }
 
2270
#endif
 
2271
 
 
2272
  // do shading type-specific operations
 
2273
  switch (shading->getType()) {
 
2274
  case 1:
 
2275
    doFunctionShFill((GfxFunctionShading *)shading);
 
2276
    break;
 
2277
  case 2:
 
2278
    doAxialShFill((GfxAxialShading *)shading);
 
2279
    break;
 
2280
  case 3:
 
2281
    doRadialShFill((GfxRadialShading *)shading);
 
2282
    break;
 
2283
  case 4:
 
2284
  case 5:
 
2285
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
 
2286
    break;
 
2287
  case 6:
 
2288
  case 7:
 
2289
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
 
2290
    break;
 
2291
  }
 
2292
 
 
2293
#if 1 //~tmp: turn off anti-aliasing temporarily
 
2294
  if (vaa) {
 
2295
    out->setVectorAntialias(gTrue);
 
2296
  }
 
2297
#endif
 
2298
 
 
2299
  // restore graphics state
 
2300
  restoreState();
 
2301
  state->setPath(savedPath);
 
2302
 
 
2303
  delete shading;
 
2304
}
 
2305
 
 
2306
void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
 
2307
  double x0, y0, x1, y1;
 
2308
  GfxColor colors[4];
 
2309
 
 
2310
  if (out->useShadedFills( shading->getType() ) &&
 
2311
      out->functionShadedFill(state, shading)) {
 
2312
    return;
 
2313
  }
 
2314
 
 
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);
 
2321
}
 
2322
 
 
2323
void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
 
2324
                            double x0, double y0,
 
2325
                            double x1, double y1,
 
2326
                            GfxColor *colors, int depth) {
 
2327
  GfxColor fillColor;
 
2328
  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
 
2329
  GfxColor colors2[4];
 
2330
  double *matrix;
 
2331
  double xM, yM;
 
2332
  int nComps, i, j;
 
2333
 
 
2334
  nComps = shading->getColorSpace()->getNComps();
 
2335
  matrix = shading->getMatrix();
 
2336
 
 
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) {
 
2341
        break;
 
2342
      }
 
2343
    }
 
2344
    if (j < nComps) {
 
2345
      break;
 
2346
    }
 
2347
  }
 
2348
 
 
2349
  // center of the rectangle
 
2350
  xM = 0.5 * (x0 + x1);
 
2351
  yM = 0.5 * (y0 + y1);
 
2352
 
 
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) {
 
2358
 
 
2359
    // use the center color
 
2360
    shading->getColor(xM, yM, &fillColor);
 
2361
    state->setFillColor(&fillColor);
 
2362
    out->updateFillColor(state);
 
2363
 
 
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]);
 
2373
    state->closePath();
 
2374
    if (!contentIsHidden())
 
2375
      out->fill(state);
 
2376
    state->clearPath();
 
2377
 
 
2378
  // the four corner colors are not close enough -- subdivide the
 
2379
  // rectangle
 
2380
  } else {
 
2381
 
 
2382
    // colors[0]       colorM0       colors[2]
 
2383
    //   (x0,y0)       (xM,y0)       (x1,y0)
 
2384
    //         +----------+----------+
 
2385
    //         |          |          |
 
2386
    //         |    UL    |    UR    |
 
2387
    // color0M |       colorMM       | color1M
 
2388
    // (x0,yM) +----------+----------+ (x1,yM)
 
2389
    //         |       (xM,yM)       |
 
2390
    //         |    LL    |    LR    |
 
2391
    //         |          |          |
 
2392
    //         +----------+----------+
 
2393
    // colors[1]       colorM1       colors[3]
 
2394
    //   (x0,y1)       (xM,y1)       (x1,y1)
 
2395
 
 
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);
 
2401
 
 
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);
 
2408
    
 
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);
 
2415
    
 
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);
 
2422
 
 
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);
 
2429
  }
 
2430
}
 
2431
 
 
2432
static void bubbleSort(double array[])
 
2433
{
 
2434
  for (int j = 0; j < 3; ++j) {
 
2435
    int kk = j;
 
2436
    for (int k = j + 1; k < 4; ++k) {
 
2437
      if (array[k] < array[kk]) {
 
2438
        kk = k;
 
2439
      }
 
2440
    }
 
2441
    double tmp = array[j];
 
2442
    array[j] = array[kk];
 
2443
    array[kk] = tmp;
 
2444
  }
 
2445
}
 
2446
 
 
2447
void Gfx::doAxialShFill(GfxAxialShading *shading) {
 
2448
  double xMin, yMin, xMax, yMax;
 
2449
  double x0, y0, x1, y1;
 
2450
  double dx, dy, mul;
 
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;
 
2456
  double t0, t1, tt;
 
2457
  double ta[axialMaxSplits + 1];
 
2458
  int next[axialMaxSplits + 1];
 
2459
  GfxColor color0, color1;
 
2460
  int nComps;
 
2461
  int i, j, k;
 
2462
  GBool needExtend = gTrue;
 
2463
 
 
2464
  // get the clip region bbox
 
2465
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
 
2466
 
 
2467
  // compute min and max t values, based on the four corners of the
 
2468
  // clip region bbox
 
2469
  shading->getCoords(&x0, &y0, &x1, &y1);
 
2470
  dx = x1 - x0;
 
2471
  dy = y1 - y0;
 
2472
  dxZero = fabs(dx) < 0.01;
 
2473
  dyZero = fabs(dy) < 0.01;
 
2474
  if (dxZero && dyZero) {
 
2475
    tMin = tMax = 0;
 
2476
  } else {
 
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()) {
 
2486
      tMin = 0;
 
2487
    }
 
2488
    if (tMax > 1 && !shading->getExtend1()) {
 
2489
      tMax = 1;
 
2490
    }
 
2491
  }
 
2492
 
 
2493
  if (out->useShadedFills( shading->getType() ) &&
 
2494
      out->axialShadedFill(state, shading, tMin, tMax)) {
 
2495
          return;
 
2496
  }
 
2497
 
 
2498
  // get the function domain
 
2499
  t0 = shading->getDomain0();
 
2500
  t1 = shading->getDomain1();
 
2501
 
 
2502
  // Traverse the t axis and do the shading.
 
2503
  //
 
2504
  // For each point (tx, ty) on the t axis, consider a line through
 
2505
  // that point perpendicular to the t axis:
 
2506
  //
 
2507
  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
 
2508
  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
 
2509
  //
 
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:
 
2513
  //
 
2514
  //     s0 = (xMin - tx) / -dy
 
2515
  //     s1 = (xMax - tx) / -dy
 
2516
  //     s2 = (yMin - ty) / dx
 
2517
  //     s3 = (yMax - ty) / dx
 
2518
  //
 
2519
  // and we want the middle two s values.
 
2520
  //
 
2521
  // In the case where dx = 0, take s0 and s1; in the case where dy =
 
2522
  // 0, take s2 and s3.
 
2523
  //
 
2524
  // Each filled polygon is bounded by two of these line segments
 
2525
  // perpdendicular to the t axis.
 
2526
  //
 
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.
 
2530
 
 
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();
 
2534
  ta[0] = tMin;
 
2535
  next[0] = axialMaxSplits / 2;
 
2536
  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
 
2537
  next[axialMaxSplits / 2] = axialMaxSplits;
 
2538
  ta[axialMaxSplits] = tMax;
 
2539
 
 
2540
  // compute the color at t = tMin
 
2541
  if (tMin < 0) {
 
2542
    tt = t0;
 
2543
  } else if (tMin > 1) {
 
2544
    tt = t1;
 
2545
  } else {
 
2546
    tt = t0 + (t1 - t0) * tMin;
 
2547
  }
 
2548
  shading->getColor(tt, &color0);
 
2549
 
 
2550
  if (out->useFillColorStop()) {
 
2551
    // make sure we add stop color when t = tMin
 
2552
    state->setFillColor(&color0);
 
2553
    out->updateFillColorStop(state, 0);
 
2554
  }
 
2555
 
 
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
 
2558
  // bounding box
 
2559
  tx = x0 + tMin * dx;
 
2560
  ty = y0 + tMin * dy;
 
2561
  if (dxZero && dyZero) {
 
2562
    sMin = sMax = 0;
 
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; }
 
2571
  } else {
 
2572
    s[0] = (yMin - ty) / dx;
 
2573
    s[1] = (yMax - ty) / dx;
 
2574
    s[2] = (xMin - tx) / -dy;
 
2575
    s[3] = (xMax - tx) / -dy;
 
2576
    bubbleSort(s);
 
2577
    sMin = s[1];
 
2578
    sMax = s[2];
 
2579
  }
 
2580
  ux0 = tx - sMin * dy;
 
2581
  uy0 = ty + sMin * dx;
 
2582
  vx0 = tx - sMax * dy;
 
2583
  vy0 = ty + sMax * dx;
 
2584
 
 
2585
  i = 0;
 
2586
  bool doneBBox1, doneBBox2;
 
2587
  if (dxZero && dyZero) {
 
2588
    doneBBox1 = doneBBox2 = true;
 
2589
  } else {
 
2590
    doneBBox1 = bboxIntersections[1] < tMin;
 
2591
    doneBBox2 = bboxIntersections[2] > tMax;
 
2592
  }
 
2593
 
 
2594
  // If output device doesn't support the extended mode required
 
2595
  // we have to do it here
 
2596
  needExtend = !out->axialShadedSupportExtend(state, shading);
 
2597
 
 
2598
  while (i < axialMaxSplits) {
 
2599
 
 
2600
    // bisect until color difference is small enough or we hit the
 
2601
    // bisection limit
 
2602
    j = next[i];
 
2603
    while (j > i + 1) {
 
2604
      if (ta[j] < 0) {
 
2605
        tt = t0;
 
2606
      } else if (ta[j] > 1) {
 
2607
        tt = t1;
 
2608
      } else {
 
2609
        tt = t0 + (t1 - t0) * ta[j];
 
2610
      }
 
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;
 
2626
           }
 
2627
           else {
 
2628
             teoricalj = j;
 
2629
           }
 
2630
           ta[teoricalj] = bboxIntersections[1];
 
2631
           j = teoricalj;
 
2632
           doneBBox1 = true;
 
2633
         }
 
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;
 
2640
           }
 
2641
           else {
 
2642
             teoricalj = j;
 
2643
           }
 
2644
           ta[teoricalj] = bboxIntersections[2];
 
2645
           j = teoricalj;
 
2646
           doneBBox2 = true;
 
2647
         }
 
2648
         break;
 
2649
      }
 
2650
      k = (i + j) / 2;
 
2651
      ta[k] = 0.5 * (ta[i] + ta[j]);
 
2652
      next[i] = k;
 
2653
      next[k] = j;
 
2654
      j = k;
 
2655
    }
 
2656
 
 
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;
 
2660
    }
 
2661
 
 
2662
    // compute the coordinates of the point on the t axis; then
 
2663
    // compute the intersection of the perpendicular line with the
 
2664
    // bounding box
 
2665
    tx = x0 + ta[j] * dx;
 
2666
    ty = y0 + ta[j] * dy;
 
2667
    if (dxZero && dyZero) {
 
2668
      sMin = sMax = 0;
 
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; }
 
2677
    } else {
 
2678
      s[0] = (yMin - ty) / dx;
 
2679
      s[1] = (yMax - ty) / dx;
 
2680
      s[2] = (xMin - tx) / -dy;
 
2681
      s[3] = (xMax - tx) / -dy;
 
2682
      bubbleSort(s);
 
2683
      sMin = s[1];
 
2684
      sMax = s[2];
 
2685
    }
 
2686
    ux1 = tx - sMin * dy;
 
2687
    uy1 = ty + sMin * dx;
 
2688
    vx1 = tx - sMax * dy;
 
2689
    vy1 = ty + sMax * dx;
 
2690
 
 
2691
    // set the color
 
2692
    state->setFillColor(&color0);
 
2693
    if (out->useFillColorStop())
 
2694
      out->updateFillColorStop(state, (ta[j] - tMin)/(tMax - tMin));
 
2695
    else
 
2696
      out->updateFillColor(state);
 
2697
 
 
2698
    if (needExtend) {
 
2699
      // fill the region
 
2700
      state->moveTo(ux0, uy0);
 
2701
      state->lineTo(vx0, vy0);
 
2702
      state->lineTo(vx1, vy1);
 
2703
      state->lineTo(ux1, uy1);
 
2704
      state->closePath();
 
2705
    }
 
2706
 
 
2707
    if (!out->useFillColorStop()) {
 
2708
      if (!contentIsHidden())
 
2709
        out->fill(state);
 
2710
      state->clearPath();
 
2711
    }
 
2712
 
 
2713
    // set up for next region
 
2714
    ux0 = ux1;
 
2715
    uy0 = uy1;
 
2716
    vx0 = vx1;
 
2717
    vy0 = vy1;
 
2718
    color0 = color1;
 
2719
    i = next[i];
 
2720
  }
 
2721
 
 
2722
  if (out->useFillColorStop()) {
 
2723
    if (!needExtend) {
 
2724
      state->moveTo(xMin, yMin);
 
2725
      state->lineTo(xMin, yMax);
 
2726
      state->lineTo(xMax, yMax);
 
2727
      state->lineTo(xMax, yMin);
 
2728
      state->closePath();
 
2729
    }
 
2730
    if (!contentIsHidden())
 
2731
      out->fill(state);
 
2732
    state->clearPath();
 
2733
  }
 
2734
}
 
2735
 
 
2736
static inline void getShadingColorRadialHelper(double t0, double t1, double t, GfxRadialShading *shading, GfxColor *color)
 
2737
{
 
2738
  if (t0 < t1) {
 
2739
    if (t < t0) {
 
2740
      shading->getColor(t0, color);
 
2741
    } else if (t > t1) {
 
2742
      shading->getColor(t1, color);
 
2743
    } else {
 
2744
      shading->getColor(t, color);
 
2745
    }
 
2746
  } else {
 
2747
    if (t > t0) {
 
2748
      shading->getColor(t0, color);
 
2749
    } else if (t < t1) {
 
2750
      shading->getColor(t1, color);
 
2751
    } else {
 
2752
      shading->getColor(t, color);
 
2753
    }
 
2754
  }
 
2755
}
 
2756
 
 
2757
void Gfx::doRadialShFill(GfxRadialShading *shading) {
 
2758
  double xMin, yMin, xMax, yMax;
 
2759
  double x0, y0, r0, x1, y1, r1, t0, t1;
 
2760
  int nComps;
 
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;
 
2765
  GBool enclosed;
 
2766
  int ia, ib, k, n;
 
2767
  double *ctm;
 
2768
  double theta, alpha, angle, t;
 
2769
  GBool needExtend = gTrue;
 
2770
 
 
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();
 
2776
 
 
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) {
 
2780
    enclosed = gTrue;
 
2781
    theta = 0; // make gcc happy
 
2782
    sz = 0; // make gcc happy
 
2783
  } else if (r0 == r1) {
 
2784
    enclosed = gFalse;
 
2785
    theta = 0;
 
2786
    sz = 0; // make gcc happy
 
2787
  } else {
 
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)));
 
2793
    if (r0 > r1) {
 
2794
      theta = -theta;
 
2795
    }
 
2796
  }
 
2797
  if (enclosed) {
 
2798
    alpha = 0;
 
2799
  } else {
 
2800
    alpha = atan2(y1 - y0, x1 - x0);
 
2801
  }
 
2802
 
 
2803
  // compute the (possibly extended) s range
 
2804
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
 
2805
  if (enclosed) {
 
2806
    sMin = 0;
 
2807
    sMax = 1;
 
2808
  } else {
 
2809
    sMin = 1;
 
2810
    sMax = 0;
 
2811
    // solve for x(s) + r(s) = xMin
 
2812
    if ((x1 + r1) - (x0 + r0) != 0) {
 
2813
      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
 
2814
      if (sa < sMin) {
 
2815
        sMin = sa;
 
2816
      } else if (sa > sMax) {
 
2817
        sMax = sa;
 
2818
      }
 
2819
    }
 
2820
    // solve for x(s) - r(s) = xMax
 
2821
    if ((x1 - r1) - (x0 - r0) != 0) {
 
2822
      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
 
2823
      if (sa < sMin) {
 
2824
        sMin = sa;
 
2825
      } else if (sa > sMax) {
 
2826
        sMax = sa;
 
2827
      }
 
2828
    }
 
2829
    // solve for y(s) + r(s) = yMin
 
2830
    if ((y1 + r1) - (y0 + r0) != 0) {
 
2831
      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
 
2832
      if (sa < sMin) {
 
2833
        sMin = sa;
 
2834
      } else if (sa > sMax) {
 
2835
        sMax = sa;
 
2836
      }
 
2837
    }
 
2838
    // solve for y(s) - r(s) = yMax
 
2839
    if ((y1 - r1) - (y0 - r0) != 0) {
 
2840
      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
 
2841
      if (sa < sMin) {
 
2842
        sMin = sa;
 
2843
      } else if (sa > sMax) {
 
2844
        sMax = sa;
 
2845
      }
 
2846
    }
 
2847
    // check against sz
 
2848
    if (r0 < r1) {
 
2849
      if (sMin < sz) {
 
2850
        sMin = sz;
 
2851
      }
 
2852
    } else if (r0 > r1) {
 
2853
      if (sMax > sz) {
 
2854
        sMax = sz;
 
2855
      }
 
2856
    }
 
2857
    // check the 'extend' flags
 
2858
    if (!shading->getExtend0() && sMin < 0) {
 
2859
      sMin = 0;
 
2860
    }
 
2861
    if (!shading->getExtend1() && sMax > 1) {
 
2862
      sMax = 1;
 
2863
    }
 
2864
  }
 
2865
 
 
2866
  if (out->useShadedFills( shading->getType() ) &&
 
2867
      out->radialShadedFill(state, shading, sMin, sMax)) {
 
2868
    return;
 
2869
  }
 
2870
 
 
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();
 
2876
  t = fabs(ctm[0]);
 
2877
  if (fabs(ctm[1]) > t) {
 
2878
    t = fabs(ctm[1]);
 
2879
  }
 
2880
  if (fabs(ctm[2]) > t) {
 
2881
    t = fabs(ctm[2]);
 
2882
  }
 
2883
  if (fabs(ctm[3]) > t) {
 
2884
    t = fabs(ctm[3]);
 
2885
  }
 
2886
  if (r0 > r1) {
 
2887
    t *= r0;
 
2888
  } else {
 
2889
    t *= r1;
 
2890
  }
 
2891
  if (t < 1) {
 
2892
    n = 3;
 
2893
  } else {
 
2894
    n = (int)(M_PI / acos(1 - 0.1 / t));
 
2895
    if (n < 3) {
 
2896
      n = 3;
 
2897
    } else if (n > 200) {
 
2898
      n = 200;
 
2899
    }
 
2900
  }
 
2901
 
 
2902
  // setup for the start circle
 
2903
  ia = 0;
 
2904
  sa = sMin;
 
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);
 
2910
 
 
2911
  needExtend = !out->radialShadedSupportExtend(state, shading);
 
2912
 
 
2913
  // fill the circles
 
2914
  while (ia < radialMaxSplits) {
 
2915
 
 
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;
 
2923
    sb = sMax;
 
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
 
2930
        int ic = ia + 1;
 
2931
        for (; ic <= ib; ic++) {
 
2932
          GfxColor colorC;
 
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)) {
 
2937
            break;
 
2938
          }
 
2939
        }
 
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);
 
2944
        break;
 
2945
      }
 
2946
      ib = (ia + ib) / 2;
 
2947
      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
 
2948
      tb = t0 + sb * (t1 - t0);
 
2949
      getShadingColorRadialHelper(t0, t1, tb, shading, &colorB);
 
2950
    }
 
2951
 
 
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);
 
2956
 
 
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;
 
2960
    }
 
2961
    state->setFillColor(&colorA);
 
2962
    if (out->useFillColorStop())
 
2963
      out->updateFillColorStop(state, (sa - sMin)/(sMax - sMin));
 
2964
    else
 
2965
      out->updateFillColor(state);
 
2966
 
 
2967
    if (needExtend) {
 
2968
      if (enclosed) {
 
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));
 
2974
        }
 
2975
        state->closePath();
 
2976
 
 
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));
 
2982
        }
 
2983
        state->closePath();
 
2984
      } else {
 
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));
 
2992
        }
 
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));
 
2997
        }
 
2998
        state->closePath();
 
2999
 
 
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));
 
3007
        }
 
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));
 
3012
        }
 
3013
        state->closePath();
 
3014
      }
 
3015
    }
 
3016
 
 
3017
    if (!out->useFillColorStop()) {
 
3018
      // fill the path
 
3019
      if (!contentIsHidden())
 
3020
        out->fill(state);
 
3021
      state->clearPath();
 
3022
    }
 
3023
 
 
3024
    // step to the next value of t
 
3025
    ia = ib;
 
3026
    sa = sb;
 
3027
    ta = tb;
 
3028
    xa = xb;
 
3029
    ya = yb;
 
3030
    ra = rb;
 
3031
    colorA = colorB;
 
3032
  }
 
3033
 
 
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));
 
3038
 
 
3039
    // fill the path
 
3040
    state->moveTo(xMin, yMin);
 
3041
    state->lineTo(xMin, yMax);
 
3042
    state->lineTo(xMax, yMax);
 
3043
    state->lineTo(xMax, yMin);
 
3044
    state->closePath();
 
3045
 
 
3046
    if (!contentIsHidden())
 
3047
      out->fill(state);
 
3048
    state->clearPath();
 
3049
  }
 
3050
 
 
3051
  if (!needExtend)
 
3052
    return;
 
3053
 
 
3054
  if (enclosed) {
 
3055
    // extend the smaller circle
 
3056
    if ((shading->getExtend0() && r0 <= r1) ||
 
3057
        (shading->getExtend1() && r1 < r0)) {
 
3058
      if (r0 <= r1) {
 
3059
        ta = t0;
 
3060
        ra = r0;
 
3061
        xa = x0;
 
3062
        ya = y0;
 
3063
      } else {
 
3064
        ta = t1;
 
3065
        ra = r1;
 
3066
        xa = x1;
 
3067
        ya = y1;
 
3068
      }
 
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));
 
3076
      }
 
3077
      state->closePath();
 
3078
      if (!contentIsHidden())
 
3079
        out->fill(state);
 
3080
      state->clearPath();
 
3081
    }
 
3082
 
 
3083
    // extend the larger circle
 
3084
    if ((shading->getExtend0() && r0 > r1) ||
 
3085
        (shading->getExtend1() && r1 >= r0)) {
 
3086
      if (r0 > r1) {
 
3087
        ta = t0;
 
3088
        ra = r0;
 
3089
        xa = x0;
 
3090
        ya = y0;
 
3091
      } else {
 
3092
        ta = t1;
 
3093
        ra = r1;
 
3094
        xa = x1;
 
3095
        ya = y1;
 
3096
      }
 
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);
 
3104
      state->closePath();
 
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));
 
3109
      }
 
3110
      state->closePath();
 
3111
      if (!contentIsHidden())
 
3112
        out->fill(state);
 
3113
      state->clearPath();
 
3114
    }
 
3115
  }
 
3116
}
 
3117
 
 
3118
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
 
3119
  double x0, y0, x1, y1, x2, y2;
 
3120
  int i;
 
3121
 
 
3122
  if( out->useShadedFills( shading->getType() ) ) {
 
3123
    if( out->gouraudTriangleShadedFill( state, shading ) )
 
3124
      return;
 
3125
  }
 
3126
  // preallocate a path (speed improvements)
 
3127
  state->moveTo(0., 0.);
 
3128
  state->lineTo(1., 0.);
 
3129
  state->lineTo(0., 1.);
 
3130
  state->closePath();
 
3131
 
 
3132
  GfxState::ReusablePathIterator *reusablePath = state->getReusablePath();
 
3133
 
 
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,
 
3142
                              &x1, &y1, &color1,
 
3143
                              &x2, &y2, &color2);
 
3144
      gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath);
 
3145
    }
 
3146
 
 
3147
  } else {
 
3148
    // this always produces output -- even for parameterized ranges.
 
3149
    // But it ignores the parameterized color map (the function). 
 
3150
    //
 
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,
 
3157
                              &x1, &y1, &color1,
 
3158
                              &x2, &y2, &color2);
 
3159
      gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath);
 
3160
    }
 
3161
  }
 
3162
 
 
3163
  delete reusablePath;
 
3164
}
 
3165
 
 
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;
 
3172
  int i;
 
3173
 
 
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) {
 
3177
      break;
 
3178
    }
 
3179
  }
 
3180
  if (i == nComps || depth == gouraudMaxDepth) {
 
3181
    state->setFillColor(color0);
 
3182
    out->updateFillColor(state);
 
3183
 
 
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()); 
 
3189
 
 
3190
    if (!contentIsHidden())
 
3191
      out->fill(state);
 
3192
  } else {
 
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;
 
3203
    }
 
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);
 
3212
  }
 
3213
}
 
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;
 
3219
 
 
3220
  const bool isFineEnough = 
 
3221
       fabs(color0 - meanColor) < refineColorThreshold &&
 
3222
       fabs(color1 - meanColor) < refineColorThreshold &&
 
3223
       fabs(color2 - meanColor) < refineColorThreshold;
 
3224
 
 
3225
  if (isFineEnough || depth == gouraudMaxDepth) {
 
3226
    GfxColor color;
 
3227
 
 
3228
    shading->getParameterizedColor(meanColor, &color);
 
3229
    state->setFillColor(&color);
 
3230
    out->updateFillColor(state);
 
3231
 
 
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()); 
 
3237
 
 
3238
    if (!contentIsHidden())
 
3239
      out->fill(state);
 
3240
  } else {
 
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.;
 
3250
    ++depth;
 
3251
    gouraudFillTriangle(x0, y0, color0, 
 
3252
                        x01, y01, color01,
 
3253
                        x20, y20, color20, 
 
3254
                        refineColorThreshold, depth, shading, path);
 
3255
    gouraudFillTriangle(x01, y01, color01, 
 
3256
                        x1, y1, color1,
 
3257
                        x12, y12, color12, 
 
3258
                        refineColorThreshold, depth, shading, path);
 
3259
    gouraudFillTriangle(x01, y01, color01, 
 
3260
                        x12, y12, color12,
 
3261
                        x20, y20, color20, 
 
3262
                        refineColorThreshold, depth, shading, path);
 
3263
    gouraudFillTriangle(x20, y20, color20, 
 
3264
                        x12, y12, color12,
 
3265
                        x2, y2, color2, 
 
3266
                        refineColorThreshold, depth, shading, path);
 
3267
  }
 
3268
}
 
3269
 
 
3270
void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
 
3271
  int start, i;
 
3272
 
 
3273
  if (shading->getNPatches() > 128) {
 
3274
    start = 3;
 
3275
  } else if (shading->getNPatches() > 64) {
 
3276
    start = 2;
 
3277
  } else if (shading->getNPatches() > 16) {
 
3278
    start = 1;
 
3279
  } else {
 
3280
    start = 0;
 
3281
  }
 
3282
  /*
 
3283
   * Parameterized shadings take one parameter [t_0,t_e]
 
3284
   * and map it into the color space.
 
3285
   *
 
3286
   * Consequently, all color values are stored as doubles.
 
3287
   *
 
3288
   * These color values are interpreted as parameters for parameterized
 
3289
   * shadings and as colorspace entities otherwise.
 
3290
   *
 
3291
   * The only difference is that color space entities are stored into
 
3292
   * DOUBLE arrays, not into arrays of type GfxColorComp.
 
3293
   */
 
3294
  const int colorComps = shading->getColorSpace()->getNComps();
 
3295
  double refineColorThreshold;
 
3296
  if( shading->isParameterized() ) {
 
3297
          refineColorThreshold = gouraudParameterizedColorDelta *
 
3298
                  (shading->getParameterDomainMax() - shading->getParameterDomainMin());
 
3299
 
 
3300
  } else {
 
3301
          refineColorThreshold = patchColorDelta;
 
3302
  }
 
3303
 
 
3304
  for (i = 0; i < shading->getNPatches(); ++i) {
 
3305
    fillPatch(shading->getPatch(i),
 
3306
             colorComps, 
 
3307
             shading->isParameterized() ? 1 : colorComps,
 
3308
             refineColorThreshold,
 
3309
             start,
 
3310
             shading);
 
3311
  }
 
3312
}
 
3313
 
 
3314
 
 
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];
 
3318
  double xxm, yym;
 
3319
  int i;
 
3320
 
 
3321
  for (i = 0; i < patchColorComps; ++i) {
 
3322
    // these comparisons are done in double arithmetics.
 
3323
    //
 
3324
    // For non-parameterized shadings, they are done in color space
 
3325
    // components.
 
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) {
 
3330
      break;
 
3331
    }
 
3332
  }
 
3333
  if (i == patchColorComps || depth == patchMaxDepth) {
 
3334
    GfxColor flatColor;
 
3335
    if( shading->isParameterized() ) {
 
3336
      shading->getParameterizedColor( patch->color[0][0].c[0], &flatColor );
 
3337
    } else {
 
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]);
 
3341
      }
 
3342
    }
 
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]);
 
3358
    state->closePath();
 
3359
    if (!contentIsHidden())
 
3360
      out->fill(state);
 
3361
    state->clearPath();
 
3362
  } else {
 
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];
 
3380
    }
 
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];
 
3400
    }
 
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];
 
3420
    }
 
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];
 
3443
    }
 
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);
 
3448
  }
 
3449
}
 
3450
 
 
3451
void Gfx::doEndPath() {
 
3452
  if (state->isCurPt() && clip != clipNone) {
 
3453
    state->clip();
 
3454
    if (clip == clipNormal) {
 
3455
      out->clip(state);
 
3456
    } else {
 
3457
      out->eoClip(state);
 
3458
    }
 
3459
  }
 
3460
  clip = clipNone;
 
3461
  state->clearPath();
 
3462
}
 
3463
 
 
3464
//------------------------------------------------------------------------
 
3465
// path clipping operators
 
3466
//------------------------------------------------------------------------
 
3467
 
 
3468
void Gfx::opClip(Object args[], int numArgs) {
 
3469
  clip = clipNormal;
 
3470
}
 
3471
 
 
3472
void Gfx::opEOClip(Object args[], int numArgs) {
 
3473
  clip = clipEO;
 
3474
}
 
3475
 
 
3476
//------------------------------------------------------------------------
 
3477
// text object operators
 
3478
//------------------------------------------------------------------------
 
3479
 
 
3480
void Gfx::opBeginText(Object args[], int numArgs) {
 
3481
  out->beginTextObject(state);
 
3482
  drawText = gTrue;
 
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;
 
3490
  }
 
3491
}
 
3492
 
 
3493
void Gfx::opEndText(Object args[], int numArgs) {
 
3494
  GBool needFill = out->deviceHasTextClip(state);
 
3495
  out->endTextObject(state);
 
3496
  drawText = gFalse;
 
3497
  if (textHaveCSPattern) {
 
3498
    if (needFill) {
 
3499
      doPatternFill(gTrue);
 
3500
    }
 
3501
    out->restoreState(state);
 
3502
  }
 
3503
  textHaveCSPattern = gFalse;
 
3504
}
 
3505
 
 
3506
//------------------------------------------------------------------------
 
3507
// text state operators
 
3508
//------------------------------------------------------------------------
 
3509
 
 
3510
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
 
3511
  state->setCharSpace(args[0].getNum());
 
3512
  out->updateCharSpace(state);
 
3513
}
 
3514
 
 
3515
void Gfx::opSetFont(Object args[], int numArgs) {
 
3516
  GfxFont *font;
 
3517
 
 
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;
 
3523
    return;
 
3524
  }
 
3525
  if (printCommands) {
 
3526
    printf("  font: tag=%s name='%s' %g\n",
 
3527
           font->getTag()->getCString(),
 
3528
           font->getName() ? font->getName()->getCString() : "???",
 
3529
           args[1].getNum());
 
3530
    fflush(stdout);
 
3531
  }
 
3532
 
 
3533
  font->incRefCnt();
 
3534
  state->setFont(font, args[1].getNum());
 
3535
  fontChanged = gTrue;
 
3536
}
 
3537
 
 
3538
void Gfx::opSetTextLeading(Object args[], int numArgs) {
 
3539
  state->setLeading(args[0].getNum());
 
3540
}
 
3541
 
 
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);
 
3548
    if (needFill) {
 
3549
      doPatternFill(gTrue);
 
3550
    }
 
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;
 
3559
  }
 
3560
  out->updateRender(state);
 
3561
}
 
3562
 
 
3563
void Gfx::opSetTextRise(Object args[], int numArgs) {
 
3564
  state->setRise(args[0].getNum());
 
3565
  out->updateRise(state);
 
3566
}
 
3567
 
 
3568
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
 
3569
  state->setWordSpace(args[0].getNum());
 
3570
  out->updateWordSpace(state);
 
3571
}
 
3572
 
 
3573
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
 
3574
  state->setHorizScaling(args[0].getNum());
 
3575
  out->updateHorizScaling(state);
 
3576
  fontChanged = gTrue;
 
3577
}
 
3578
 
 
3579
//------------------------------------------------------------------------
 
3580
// text positioning operators
 
3581
//------------------------------------------------------------------------
 
3582
 
 
3583
void Gfx::opTextMove(Object args[], int numArgs) {
 
3584
  double tx, ty;
 
3585
 
 
3586
  tx = state->getLineX() + args[0].getNum();
 
3587
  ty = state->getLineY() + args[1].getNum();
 
3588
  state->textMoveTo(tx, ty);
 
3589
  out->updateTextPos(state);
 
3590
}
 
3591
 
 
3592
void Gfx::opTextMoveSet(Object args[], int numArgs) {
 
3593
  double tx, ty;
 
3594
 
 
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);
 
3601
}
 
3602
 
 
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;
 
3611
}
 
3612
 
 
3613
void Gfx::opTextNextLine(Object args[], int numArgs) {
 
3614
  double tx, ty;
 
3615
 
 
3616
  tx = state->getLineX();
 
3617
  ty = state->getLineY() - state->getLeading();
 
3618
  state->textMoveTo(tx, ty);
 
3619
  out->updateTextPos(state);
 
3620
}
 
3621
 
 
3622
//------------------------------------------------------------------------
 
3623
// text string operators
 
3624
//------------------------------------------------------------------------
 
3625
 
 
3626
void Gfx::opShowText(Object args[], int numArgs) {
 
3627
  if (!state->getFont()) {
 
3628
    error(getPos(), "No font in show");
 
3629
    return;
 
3630
  }
 
3631
  if (fontChanged) {
 
3632
    out->updateFont(state);
 
3633
    fontChanged = gFalse;
 
3634
  }
 
3635
  out->beginStringOp(state);
 
3636
  doShowText(args[0].getString());
 
3637
  out->endStringOp(state);
 
3638
}
 
3639
 
 
3640
void Gfx::opMoveShowText(Object args[], int numArgs) {
 
3641
  double tx, ty;
 
3642
 
 
3643
  if (!state->getFont()) {
 
3644
    error(getPos(), "No font in move/show");
 
3645
    return;
 
3646
  }
 
3647
  if (fontChanged) {
 
3648
    out->updateFont(state);
 
3649
    fontChanged = gFalse;
 
3650
  }
 
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);
 
3658
}
 
3659
 
 
3660
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
 
3661
  double tx, ty;
 
3662
 
 
3663
  if (!state->getFont()) {
 
3664
    error(getPos(), "No font in move/set/show");
 
3665
    return;
 
3666
  }
 
3667
  if (fontChanged) {
 
3668
    out->updateFont(state);
 
3669
    fontChanged = gFalse;
 
3670
  }
 
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);
 
3682
}
 
3683
 
 
3684
void Gfx::opShowSpaceText(Object args[], int numArgs) {
 
3685
  Array *a;
 
3686
  Object obj;
 
3687
  int wMode;
 
3688
  int i;
 
3689
 
 
3690
  if (!state->getFont()) {
 
3691
    error(getPos(), "No font in show/space");
 
3692
    return;
 
3693
  }
 
3694
  if (fontChanged) {
 
3695
    out->updateFont(state);
 
3696
    fontChanged = gFalse;
 
3697
  }
 
3698
  out->beginStringOp(state);
 
3699
  wMode = state->getFont()->getWMode();
 
3700
  a = args[0].getArray();
 
3701
  for (i = 0; i < a->getLength(); ++i) {
 
3702
    a->get(i, &obj);
 
3703
    if (obj.isNum()) {
 
3704
      // this uses the absolute value of the font size to match
 
3705
      // Acrobat's behavior
 
3706
      if (wMode) {
 
3707
        state->textShift(0, -obj.getNum() * 0.001 *
 
3708
                            fabs(state->getFontSize()));
 
3709
      } else {
 
3710
        state->textShift(-obj.getNum() * 0.001 *
 
3711
                         fabs(state->getFontSize()), 0);
 
3712
      }
 
3713
      out->updateTextShift(state, obj.getNum());
 
3714
    } else if (obj.isString()) {
 
3715
      doShowText(obj.getString());
 
3716
    } else {
 
3717
      error(getPos(), "Element of show/space array must be number or string");
 
3718
    }
 
3719
    obj.free();
 
3720
  }
 
3721
  out->endStringOp(state);
 
3722
}
 
3723
 
 
3724
void Gfx::doShowText(GooString *s) {
 
3725
  GfxFont *font;
 
3726
  int wMode;
 
3727
  double riseX, riseY;
 
3728
  CharCode code;
 
3729
  Unicode *u = NULL;
 
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];
 
3733
  double *mat;
 
3734
  Object charProc;
 
3735
  Dict *resDict;
 
3736
  Parser *oldParser;
 
3737
  char *p;
 
3738
  int len, n, uLen, nChars, nSpaces, i;
 
3739
 
 
3740
  font = state->getFont();
 
3741
  wMode = font->getWMode();
 
3742
 
 
3743
  if (out->useDrawChar()) {
 
3744
    out->beginString(state, s);
 
3745
  }
 
3746
 
 
3747
  // handle a Type 3 char
 
3748
  if (font->getType() == fontType3 && out->interpretType3Chars()) {
 
3749
    mat = state->getCTM();
 
3750
    for (i = 0; i < 6; ++i) {
 
3751
      oldCTM[i] = mat[i];
 
3752
    }
 
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();
 
3774
    oldParser = parser;
 
3775
    p = s->getCString();
 
3776
    len = s->getLength();
 
3777
    while (len > 0) {
 
3778
      n = font->getNextChar(p, len, &code,
 
3779
                            &u, &uLen,
 
3780
                            &dx, &dy, &originX, &originY);
 
3781
      dx = dx * state->getFontSize() + state->getCharSpace();
 
3782
      if (n == 1 && *p == ' ') {
 
3783
        dx += state->getWordSpace();
 
3784
      }
 
3785
      dx *= state->getHorizScaling();
 
3786
      dy *= state->getFontSize();
 
3787
      state->textTransformDelta(dx, dy, &tdx, &tdy);
 
3788
      state->transform(curX + riseX, curY + riseY, &x, &y);
 
3789
      saveState();
 
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,
 
3794
                               code, u, uLen)) {
 
3795
        ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
 
3796
        if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
 
3797
          pushResources(resDict);
 
3798
        }
 
3799
        if (charProc.isStream()) {
 
3800
          display(&charProc, gFalse);
 
3801
        } else {
 
3802
          error(getPos(), "Missing or bad Type3 CharProc entry");
 
3803
        }
 
3804
        out->endType3Char(state);
 
3805
        if (resDict) {
 
3806
          popResources();
 
3807
        }
 
3808
        charProc.free();
 
3809
      }
 
3810
      restoreState();
 
3811
      // GfxState::restore() does *not* restore the current position,
 
3812
      // so we deal with it here using (curX, curY) and (lineX, lineY)
 
3813
      curX += tdx;
 
3814
      curY += tdy;
 
3815
      state->moveTo(curX, curY);
 
3816
      state->textSetPos(lineX, lineY);
 
3817
      p += n;
 
3818
      len -= n;
 
3819
    }
 
3820
    parser = oldParser;
 
3821
 
 
3822
  } else if (out->useDrawChar()) {
 
3823
    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
 
3824
    p = s->getCString();
 
3825
    len = s->getLength();
 
3826
    while (len > 0) {
 
3827
      n = font->getNextChar(p, len, &code,
 
3828
                            &u, &uLen,
 
3829
                            &dx, &dy, &originX, &originY);
 
3830
      if (wMode) {
 
3831
        dx *= state->getFontSize();
 
3832
        dy = dy * state->getFontSize() + state->getCharSpace();
 
3833
        if (n == 1 && *p == ' ') {
 
3834
          dy += state->getWordSpace();
 
3835
        }
 
3836
      } else {
 
3837
        dx = dx * state->getFontSize() + state->getCharSpace();
 
3838
        if (n == 1 && *p == ' ') {
 
3839
          dx += state->getWordSpace();
 
3840
        }
 
3841
        dx *= state->getHorizScaling();
 
3842
        dy *= state->getFontSize();
 
3843
      }
 
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);
 
3851
      }
 
3852
      state->shift(tdx, tdy);
 
3853
      p += n;
 
3854
      len -= n;
 
3855
    }
 
3856
 
 
3857
  } else {
 
3858
    dx = dy = 0;
 
3859
    p = s->getCString();
 
3860
    len = s->getLength();
 
3861
    nChars = nSpaces = 0;
 
3862
    while (len > 0) {
 
3863
      n = font->getNextChar(p, len, &code,
 
3864
                            &u, &uLen,
 
3865
                            &dx2, &dy2, &originX, &originY);
 
3866
      dx += dx2;
 
3867
      dy += dy2;
 
3868
      if (n == 1 && *p == ' ') {
 
3869
        ++nSpaces;
 
3870
      }
 
3871
      ++nChars;
 
3872
      p += n;
 
3873
      len -= n;
 
3874
    }
 
3875
    if (wMode) {
 
3876
      dx *= state->getFontSize();
 
3877
      dy = dy * state->getFontSize()
 
3878
           + nChars * state->getCharSpace()
 
3879
           + nSpaces * state->getWordSpace();
 
3880
    } else {
 
3881
      dx = dx * state->getFontSize()
 
3882
           + nChars * state->getCharSpace()
 
3883
           + nSpaces * state->getWordSpace();
 
3884
      dx *= state->getHorizScaling();
 
3885
      dy *= state->getFontSize();
 
3886
    }
 
3887
    state->textTransformDelta(dx, dy, &tdx, &tdy);
 
3888
    if (!contentIsHidden())
 
3889
      out->drawString(state, s);
 
3890
    state->shift(tdx, tdy);
 
3891
  }
 
3892
 
 
3893
  if (out->useDrawChar()) {
 
3894
    out->endString(state);
 
3895
  }
 
3896
 
 
3897
  updateLevel += 10 * s->getLength();
 
3898
}
 
3899
 
 
3900
//------------------------------------------------------------------------
 
3901
// XObject operators
 
3902
//------------------------------------------------------------------------
 
3903
 
 
3904
void Gfx::opXObject(Object args[], int numArgs) {
 
3905
  char *name;
 
3906
  Object obj1, obj2, obj3, refObj;
 
3907
#if OPI_SUPPORT
 
3908
  Object opiDict;
 
3909
#endif
 
3910
 
 
3911
  name = args[0].getName();
 
3912
  if (!res->lookupXObject(name, &obj1)) {
 
3913
    return;
 
3914
  }
 
3915
  if (!obj1.isStream()) {
 
3916
    error(getPos(), "XObject '%s' is wrong type", name);
 
3917
    obj1.free();
 
3918
    return;
 
3919
  }
 
3920
 
 
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 ) ) {
 
3926
      obj2.free();
 
3927
      obj1.free();
 
3928
      return;
 
3929
    }
 
3930
  } else {
 
3931
    error(getPos(), "XObject OC value not null or dict: %i", obj2.getType());
 
3932
  }
 
3933
  obj2.free();
 
3934
 
 
3935
#if OPI_SUPPORT
 
3936
  obj1.streamGetDict()->lookup("OPI", &opiDict);
 
3937
  if (opiDict.isDict()) {
 
3938
    out->opiBegin(state, opiDict.getDict());
 
3939
  }
 
3940
#endif
 
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);
 
3946
      refObj.free();
 
3947
    }
 
3948
  } else if (obj2.isName("Form")) {
 
3949
    res->lookupXObjectNF(name, &refObj);
 
3950
    if (out->useDrawForm() && refObj.isRef()) {
 
3951
      out->drawForm(refObj.getRef());
 
3952
    } else {
 
3953
      doForm(&obj1);
 
3954
    }
 
3955
    refObj.free();
 
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());
 
3962
  } else {
 
3963
    error(getPos(), "XObject subtype is missing or wrong type");
 
3964
  }
 
3965
  obj2.free();
 
3966
#if OPI_SUPPORT
 
3967
  if (opiDict.isDict()) {
 
3968
    out->opiEnd(state, opiDict.getDict());
 
3969
  }
 
3970
  opiDict.free();
 
3971
#endif
 
3972
  obj1.free();
 
3973
}
 
3974
 
 
3975
void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
 
3976
  Dict *dict, *maskDict;
 
3977
  int width, height;
 
3978
  int bits, maskBits;
 
3979
  GBool interpolate;
 
3980
  StreamColorSpaceMode csMode;
 
3981
  GBool mask;
 
3982
  GBool invert;
 
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;
 
3989
  GBool maskInvert;
 
3990
  GBool maskInterpolate;
 
3991
  Stream *maskStr;
 
3992
  Object obj1, obj2;
 
3993
  int i;
 
3994
 
 
3995
  // get info from the stream
 
3996
  bits = 0;
 
3997
  csMode = streamCSNone;
 
3998
  str->getImageParams(&bits, &csMode);
 
3999
 
 
4000
  // get stream dict
 
4001
  dict = str->getDict();
 
4002
 
 
4003
  // get size
 
4004
  dict->lookup("Width", &obj1);
 
4005
  if (obj1.isNull()) {
 
4006
    obj1.free();
 
4007
    dict->lookup("W", &obj1);
 
4008
  }
 
4009
  if (obj1.isInt())
 
4010
    width = obj1.getInt();
 
4011
  else if (obj1.isReal())
 
4012
    width = (int)obj1.getReal();
 
4013
  else
 
4014
    goto err2;
 
4015
  obj1.free();
 
4016
  dict->lookup("Height", &obj1);
 
4017
  if (obj1.isNull()) {
 
4018
    obj1.free();
 
4019
    dict->lookup("H", &obj1);
 
4020
  }
 
4021
  if (obj1.isInt())
 
4022
    height = obj1.getInt();
 
4023
  else if (obj1.isReal())
 
4024
    height = (int)obj1.getReal();
 
4025
  else
 
4026
    goto err2;
 
4027
  obj1.free();
 
4028
 
 
4029
  if (width < 1 || height < 1)
 
4030
    goto err1;
 
4031
 
 
4032
  // image interpolation
 
4033
  dict->lookup("Interpolate", &obj1);
 
4034
  if (obj1.isNull()) {
 
4035
    obj1.free();
 
4036
    dict->lookup("I", &obj1);
 
4037
  }
 
4038
  if (obj1.isBool())
 
4039
    interpolate = obj1.getBool();
 
4040
  else
 
4041
    interpolate = gFalse;
 
4042
  obj1.free();
 
4043
  maskInterpolate = gFalse;
 
4044
 
 
4045
  // image or mask?
 
4046
  dict->lookup("ImageMask", &obj1);
 
4047
  if (obj1.isNull()) {
 
4048
    obj1.free();
 
4049
    dict->lookup("IM", &obj1);
 
4050
  }
 
4051
  mask = gFalse;
 
4052
  if (obj1.isBool())
 
4053
    mask = obj1.getBool();
 
4054
  else if (!obj1.isNull())
 
4055
    goto err2;
 
4056
  obj1.free();
 
4057
 
 
4058
  // bit depth
 
4059
  if (bits == 0) {
 
4060
    dict->lookup("BitsPerComponent", &obj1);
 
4061
    if (obj1.isNull()) {
 
4062
      obj1.free();
 
4063
      dict->lookup("BPC", &obj1);
 
4064
    }
 
4065
    if (obj1.isInt()) {
 
4066
      bits = obj1.getInt();
 
4067
    } else if (mask) {
 
4068
      bits = 1;
 
4069
    } else {
 
4070
      goto err2;
 
4071
    }
 
4072
    obj1.free();
 
4073
  }
 
4074
 
 
4075
  // display a mask
 
4076
  if (mask) {
 
4077
 
 
4078
    // check for inverted mask
 
4079
    if (bits != 1)
 
4080
      goto err1;
 
4081
    invert = gFalse;
 
4082
    dict->lookup("Decode", &obj1);
 
4083
    if (obj1.isNull()) {
 
4084
      obj1.free();
 
4085
      dict->lookup("D", &obj1);
 
4086
    }
 
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)
 
4092
        invert = gTrue;
 
4093
      obj2.free();
 
4094
    } else if (!obj1.isNull()) {
 
4095
      goto err2;
 
4096
    }
 
4097
    obj1.free();
 
4098
 
 
4099
    // draw it
 
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;
 
4107
      }
 
4108
    }
 
4109
  } else {
 
4110
 
 
4111
    // get color space and color map
 
4112
    dict->lookup("ColorSpace", &obj1);
 
4113
    if (obj1.isNull()) {
 
4114
      obj1.free();
 
4115
      dict->lookup("CS", &obj1);
 
4116
    }
 
4117
    if (obj1.isName() && inlineImg) {
 
4118
      res->lookupColorSpace(obj1.getName(), &obj2);
 
4119
      if (!obj2.isNull()) {
 
4120
        obj1.free();
 
4121
        obj1 = obj2;
 
4122
      } else {
 
4123
        obj2.free();
 
4124
      }
 
4125
    }
 
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();
 
4134
    } else {
 
4135
      colorSpace = NULL;
 
4136
    }
 
4137
    obj1.free();
 
4138
    if (!colorSpace) {
 
4139
      goto err1;
 
4140
    }
 
4141
    dict->lookup("Decode", &obj1);
 
4142
    if (obj1.isNull()) {
 
4143
      obj1.free();
 
4144
      dict->lookup("D", &obj1);
 
4145
    }
 
4146
    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
 
4147
    obj1.free();
 
4148
    if (!colorMap->isOk()) {
 
4149
      delete colorMap;
 
4150
      goto err1;
 
4151
    }
 
4152
 
 
4153
    // get the mask
 
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()) {
 
4162
      // soft mask
 
4163
      if (inlineImg) {
 
4164
        goto err1;
 
4165
      }
 
4166
      maskStr = smaskObj.getStream();
 
4167
      maskDict = smaskObj.streamGetDict();
 
4168
      maskDict->lookup("Width", &obj1);
 
4169
      if (obj1.isNull()) {
 
4170
        obj1.free();
 
4171
        maskDict->lookup("W", &obj1);
 
4172
      }
 
4173
      if (!obj1.isInt()) {
 
4174
        goto err2;
 
4175
      }
 
4176
      maskWidth = obj1.getInt();
 
4177
      obj1.free();
 
4178
      maskDict->lookup("Height", &obj1);
 
4179
      if (obj1.isNull()) {
 
4180
        obj1.free();
 
4181
        maskDict->lookup("H", &obj1);
 
4182
      }
 
4183
      if (!obj1.isInt()) {
 
4184
        goto err2;
 
4185
      }
 
4186
      maskHeight = obj1.getInt();
 
4187
      obj1.free();
 
4188
      maskDict->lookup("Interpolate", &obj1);
 
4189
      if (obj1.isNull()) {
 
4190
        obj1.free();
 
4191
        maskDict->lookup("I", &obj1);
 
4192
      }
 
4193
      if (obj1.isBool())
 
4194
        maskInterpolate = obj1.getBool();
 
4195
      else
 
4196
        maskInterpolate = gFalse;
 
4197
      obj1.free();
 
4198
      maskDict->lookup("BitsPerComponent", &obj1);
 
4199
      if (obj1.isNull()) {
 
4200
        obj1.free();
 
4201
        maskDict->lookup("BPC", &obj1);
 
4202
      }
 
4203
      if (!obj1.isInt()) {
 
4204
        goto err2;
 
4205
      }
 
4206
      maskBits = obj1.getInt();
 
4207
      obj1.free();
 
4208
      maskDict->lookup("ColorSpace", &obj1);
 
4209
      if (obj1.isNull()) {
 
4210
        obj1.free();
 
4211
        maskDict->lookup("CS", &obj1);
 
4212
      }
 
4213
      if (obj1.isName()) {
 
4214
        res->lookupColorSpace(obj1.getName(), &obj2);
 
4215
        if (!obj2.isNull()) {
 
4216
          obj1.free();
 
4217
          obj1 = obj2;
 
4218
        } else {
 
4219
          obj2.free();
 
4220
        }
 
4221
      }
 
4222
      maskColorSpace = GfxColorSpace::parse(&obj1, this);
 
4223
      obj1.free();
 
4224
      if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
 
4225
        goto err1;
 
4226
      }
 
4227
      maskDict->lookup("Decode", &obj1);
 
4228
      if (obj1.isNull()) {
 
4229
        obj1.free();
 
4230
        maskDict->lookup("D", &obj1);
 
4231
      }
 
4232
      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
 
4233
      obj1.free();
 
4234
      if (!maskColorMap->isOk()) {
 
4235
        delete maskColorMap;
 
4236
        goto err1;
 
4237
      }
 
4238
      //~ handle the Matte entry
 
4239
      haveSoftMask = gTrue;
 
4240
    } else if (maskObj.isArray()) {
 
4241
      // color key mask
 
4242
      for (i = 0;
 
4243
           i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
 
4244
           ++i) {
 
4245
        maskObj.arrayGet(i, &obj1);
 
4246
        if (obj1.isInt()) {
 
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();
 
4251
        } else {
 
4252
          error(-1, "Mask entry should be an integer but it's of type %d", obj1.getType());
 
4253
          obj1.free();
 
4254
          goto err1;
 
4255
        }
 
4256
        obj1.free();
 
4257
      }
 
4258
      haveColorKeyMask = gTrue;
 
4259
    } else if (maskObj.isStream()) {
 
4260
      // explicit mask
 
4261
      if (inlineImg) {
 
4262
        goto err1;
 
4263
      }
 
4264
      maskStr = maskObj.getStream();
 
4265
      maskDict = maskObj.streamGetDict();
 
4266
      maskDict->lookup("Width", &obj1);
 
4267
      if (obj1.isNull()) {
 
4268
        obj1.free();
 
4269
        maskDict->lookup("W", &obj1);
 
4270
      }
 
4271
      if (!obj1.isInt()) {
 
4272
        goto err2;
 
4273
      }
 
4274
      maskWidth = obj1.getInt();
 
4275
      obj1.free();
 
4276
      maskDict->lookup("Height", &obj1);
 
4277
      if (obj1.isNull()) {
 
4278
        obj1.free();
 
4279
        maskDict->lookup("H", &obj1);
 
4280
      }
 
4281
      if (!obj1.isInt()) {
 
4282
        goto err2;
 
4283
      }
 
4284
      maskHeight = obj1.getInt();
 
4285
      obj1.free();
 
4286
      maskDict->lookup("Interpolate", &obj1);
 
4287
      if (obj1.isNull()) {
 
4288
        obj1.free();
 
4289
        maskDict->lookup("I", &obj1);
 
4290
      }
 
4291
      if (obj1.isBool())
 
4292
        maskInterpolate = obj1.getBool();
 
4293
      else
 
4294
        maskInterpolate = gFalse;
 
4295
      obj1.free();
 
4296
      maskDict->lookup("ImageMask", &obj1);
 
4297
      if (obj1.isNull()) {
 
4298
        obj1.free();
 
4299
        maskDict->lookup("IM", &obj1);
 
4300
      }
 
4301
      if (!obj1.isBool() || !obj1.getBool()) {
 
4302
        goto err2;
 
4303
      }
 
4304
      obj1.free();
 
4305
      maskInvert = gFalse;
 
4306
      maskDict->lookup("Decode", &obj1);
 
4307
      if (obj1.isNull()) {
 
4308
        obj1.free();
 
4309
        maskDict->lookup("D", &obj1);
 
4310
      }
 
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) {
 
4316
          maskInvert = gTrue;
 
4317
        }
 
4318
        obj2.free();
 
4319
      } else if (!obj1.isNull()) {
 
4320
        goto err2;
 
4321
      }
 
4322
      obj1.free();
 
4323
      haveExplicitMask = gTrue;
 
4324
    }
 
4325
 
 
4326
    // draw it
 
4327
    if (haveSoftMask) {
 
4328
      if (!contentIsHidden()) {
 
4329
        out->drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
 
4330
                                 maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
 
4331
      }
 
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);
 
4339
    }
 
4340
    delete colorMap;
 
4341
 
 
4342
    maskObj.free();
 
4343
    smaskObj.free();
 
4344
  }
 
4345
 
 
4346
  if ((i = width * height) > 1000) {
 
4347
    i = 1000;
 
4348
  }
 
4349
  updateLevel += i;
 
4350
 
 
4351
  return;
 
4352
 
 
4353
 err2:
 
4354
  obj1.free();
 
4355
 err1:
 
4356
  error(getPos(), "Bad image parameters");
 
4357
}
 
4358
 
 
4359
void Gfx::doForm(Object *str) {
 
4360
  Dict *dict;
 
4361
  GBool transpGroup, isolated, knockout;
 
4362
  GfxColorSpace *blendingColorSpace;
 
4363
  Object matrixObj, bboxObj;
 
4364
  double m[6], bbox[4];
 
4365
  Object resObj;
 
4366
  Dict *resDict;
 
4367
  Object obj1, obj2, obj3;
 
4368
  int i;
 
4369
 
 
4370
  // check for excessive recursion
 
4371
  if (formDepth > 20) {
 
4372
    return;
 
4373
  }
 
4374
 
 
4375
  // get stream dict
 
4376
  dict = str->streamGetDict();
 
4377
 
 
4378
  // check form type
 
4379
  dict->lookup("FormType", &obj1);
 
4380
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
 
4381
    error(getPos(), "Unknown form type");
 
4382
  }
 
4383
  obj1.free();
 
4384
 
 
4385
  // get bounding box
 
4386
  dict->lookup("BBox", &bboxObj);
 
4387
  if (!bboxObj.isArray()) {
 
4388
    bboxObj.free();
 
4389
    error(getPos(), "Bad form bounding box");
 
4390
    return;
 
4391
  }
 
4392
  for (i = 0; i < 4; ++i) {
 
4393
    bboxObj.arrayGet(i, &obj1);
 
4394
    if (likely(obj1.isNum())) {
 
4395
      bbox[i] = obj1.getNum();
 
4396
      obj1.free();
 
4397
    } else {
 
4398
      obj1.free();
 
4399
      error(getPos(), "Bad form bounding box value");
 
4400
      return;
 
4401
    }
 
4402
  }
 
4403
  bboxObj.free();
 
4404
 
 
4405
  // get matrix
 
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();
 
4411
      else m[i] = 0;
 
4412
      obj1.free();
 
4413
    }
 
4414
  } else {
 
4415
    m[0] = 1; m[1] = 0;
 
4416
    m[2] = 0; m[3] = 1;
 
4417
    m[4] = 0; m[5] = 0;
 
4418
  }
 
4419
  matrixObj.free();
 
4420
 
 
4421
  // get resources
 
4422
  dict->lookup("Resources", &resObj);
 
4423
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
4424
 
 
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);
 
4433
      }
 
4434
      obj3.free();
 
4435
      if (obj1.dictLookup("I", &obj3)->isBool()) {
 
4436
        isolated = obj3.getBool();
 
4437
      }
 
4438
      obj3.free();
 
4439
      if (obj1.dictLookup("K", &obj3)->isBool()) {
 
4440
        knockout = obj3.getBool();
 
4441
      }
 
4442
      obj3.free();
 
4443
    }
 
4444
    obj2.free();
 
4445
  }
 
4446
  obj1.free();
 
4447
 
 
4448
  // draw it
 
4449
  ++formDepth;
 
4450
  doForm1(str, resDict, m, bbox,
 
4451
          transpGroup, gFalse, blendingColorSpace, isolated, knockout);
 
4452
  --formDepth;
 
4453
 
 
4454
  if (blendingColorSpace) {
 
4455
    delete blendingColorSpace;
 
4456
  }
 
4457
  resObj.free();
 
4458
}
 
4459
 
 
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) {
 
4466
  Parser *oldParser;
 
4467
  double oldBaseMatrix[6];
 
4468
  int i;
 
4469
 
 
4470
  // push new resources on stack
 
4471
  pushResources(resDict);
 
4472
 
 
4473
  // save current graphics state
 
4474
  saveState();
 
4475
 
 
4476
  // kill any pre-existing path
 
4477
  state->clearPath();
 
4478
 
 
4479
  // save current parser
 
4480
  oldParser = parser;
 
4481
 
 
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]);
 
4487
 
 
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]);
 
4493
  state->closePath();
 
4494
  state->clip();
 
4495
  out->clip(state);
 
4496
  state->clearPath();
 
4497
 
 
4498
  if (softMask || transpGroup) {
 
4499
    if (state->getBlendMode() != gfxBlendNormal) {
 
4500
      state->setBlendMode(gfxBlendNormal);
 
4501
      out->updateBlendMode(state);
 
4502
    }
 
4503
    if (state->getFillOpacity() != 1) {
 
4504
      state->setFillOpacity(1);
 
4505
      out->updateFillOpacity(state);
 
4506
    }
 
4507
    if (state->getStrokeOpacity() != 1) {
 
4508
      state->setStrokeOpacity(1);
 
4509
      out->updateStrokeOpacity(state);
 
4510
    }
 
4511
    out->clearSoftMask(state);
 
4512
    out->beginTransparencyGroup(state, bbox, blendingColorSpace,
 
4513
                                isolated, knockout, softMask);
 
4514
  }
 
4515
 
 
4516
  // set new base matrix
 
4517
  for (i = 0; i < 6; ++i) {
 
4518
    oldBaseMatrix[i] = baseMatrix[i];
 
4519
    baseMatrix[i] = state->getCTM()[i];
 
4520
  }
 
4521
 
 
4522
  GfxState *stateBefore = state;
 
4523
 
 
4524
  // draw the form
 
4525
  display(str, gFalse);
 
4526
  
 
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) {
 
4531
        restoreState();
 
4532
      }
 
4533
    } else {
 
4534
      error(-1, "There's a form with more Q than q");
 
4535
    }
 
4536
  }
 
4537
 
 
4538
  if (softMask || transpGroup) {
 
4539
    out->endTransparencyGroup(state);
 
4540
  }
 
4541
 
 
4542
  // restore base matrix
 
4543
  for (i = 0; i < 6; ++i) {
 
4544
    baseMatrix[i] = oldBaseMatrix[i];
 
4545
  }
 
4546
 
 
4547
  // restore parser
 
4548
  parser = oldParser;
 
4549
 
 
4550
  // restore graphics state
 
4551
  restoreState();
 
4552
 
 
4553
  // pop resource stack
 
4554
  popResources();
 
4555
 
 
4556
  if (softMask) {
 
4557
    out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
 
4558
  } else if (transpGroup) {
 
4559
    out->paintTransparencyGroup(state, bbox);
 
4560
  }
 
4561
 
 
4562
  return;
 
4563
}
 
4564
 
 
4565
//------------------------------------------------------------------------
 
4566
// in-line image operators
 
4567
//------------------------------------------------------------------------
 
4568
 
 
4569
void Gfx::opBeginImage(Object args[], int numArgs) {
 
4570
  Stream *str;
 
4571
  int c1, c2;
 
4572
 
 
4573
  // build dict/stream
 
4574
  str = buildImageStream();
 
4575
 
 
4576
  // display the image
 
4577
  if (str) {
 
4578
    doImage(NULL, str, gTrue);
 
4579
  
 
4580
    // skip 'EI' tag
 
4581
    c1 = str->getUndecodedStream()->getChar();
 
4582
    c2 = str->getUndecodedStream()->getChar();
 
4583
    while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
 
4584
      c1 = c2;
 
4585
      c2 = str->getUndecodedStream()->getChar();
 
4586
    }
 
4587
    delete str;
 
4588
  }
 
4589
}
 
4590
 
 
4591
Stream *Gfx::buildImageStream() {
 
4592
  Object dict;
 
4593
  Object obj;
 
4594
  char *key;
 
4595
  Stream *str;
 
4596
 
 
4597
  // build dictionary
 
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");
 
4603
      obj.free();
 
4604
    } else {
 
4605
      key = copyString(obj.getName());
 
4606
      obj.free();
 
4607
      parser->getObj(&obj);
 
4608
      if (obj.isEOF() || obj.isError()) {
 
4609
        gfree(key);
 
4610
        break;
 
4611
      }
 
4612
      dict.dictAdd(key, &obj);
 
4613
    }
 
4614
    parser->getObj(&obj);
 
4615
  }
 
4616
  if (obj.isEOF()) {
 
4617
    error(getPos(), "End of file in inline image");
 
4618
    obj.free();
 
4619
    dict.free();
 
4620
    return NULL;
 
4621
  }
 
4622
  obj.free();
 
4623
 
 
4624
  // make stream
 
4625
  if (parser->getStream()) {
 
4626
    str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
 
4627
    str = str->addFilters(&dict);
 
4628
  } else {
 
4629
    str = NULL;
 
4630
    dict.free();
 
4631
  }
 
4632
 
 
4633
  return str;
 
4634
}
 
4635
 
 
4636
void Gfx::opImageData(Object args[], int numArgs) {
 
4637
  error(getPos(), "Internal: got 'ID' operator");
 
4638
}
 
4639
 
 
4640
void Gfx::opEndImage(Object args[], int numArgs) {
 
4641
  error(getPos(), "Internal: got 'EI' operator");
 
4642
}
 
4643
 
 
4644
//------------------------------------------------------------------------
 
4645
// type 3 font operators
 
4646
//------------------------------------------------------------------------
 
4647
 
 
4648
void Gfx::opSetCharWidth(Object args[], int numArgs) {
 
4649
  out->type3D0(state, args[0].getNum(), args[1].getNum());
 
4650
}
 
4651
 
 
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());
 
4656
}
 
4657
 
 
4658
//------------------------------------------------------------------------
 
4659
// compatibility operators
 
4660
//------------------------------------------------------------------------
 
4661
 
 
4662
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
 
4663
  ++ignoreUndef;
 
4664
}
 
4665
 
 
4666
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
 
4667
  if (ignoreUndef > 0)
 
4668
    --ignoreUndef;
 
4669
}
 
4670
 
 
4671
//------------------------------------------------------------------------
 
4672
// marked content operators
 
4673
//------------------------------------------------------------------------
 
4674
 
 
4675
struct MarkedContentStack {
 
4676
  GBool ocSuppressed;       // are we ignoring content based on OptionalContent?
 
4677
  MarkedContentStack *next; // next object on stack
 
4678
};
 
4679
 
 
4680
void Gfx::popMarkedContent() {
 
4681
  MarkedContentStack *mc = mcStack;
 
4682
  mcStack = mc->next;
 
4683
  delete mc;
 
4684
}
 
4685
 
 
4686
void Gfx::pushMarkedContent() {
 
4687
  MarkedContentStack *mc = new MarkedContentStack();
 
4688
  mc->ocSuppressed = gFalse;
 
4689
  mc->next = mcStack;
 
4690
  mcStack = mc;
 
4691
}
 
4692
 
 
4693
GBool Gfx::contentIsHidden() {
 
4694
  MarkedContentStack *mc = mcStack;
 
4695
  bool hidden = mc && mc->ocSuppressed;
 
4696
  while (!hidden && mc && mc->next) {
 
4697
    mc = mc->next;
 
4698
    hidden = mc->ocSuppressed;
 
4699
  }
 
4700
  return hidden;
 
4701
}
 
4702
 
 
4703
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
 
4704
  // push a new stack entry
 
4705
  pushMarkedContent();
 
4706
  
 
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());
 
4713
      }
 
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);
 
4721
       }
 
4722
      } else {
 
4723
        error(getPos(), "DID NOT find %s", name1);
 
4724
      }
 
4725
    } else {
 
4726
      error(getPos(), "insufficient arguments for Marked Content");
 
4727
    }
 
4728
  }
 
4729
 
 
4730
  if (printCommands) {
 
4731
    printf("  marked content: %s ", args[0].getName());
 
4732
    if (numArgs == 2)
 
4733
      args[1].print(stdout);
 
4734
    printf("\n");
 
4735
    fflush(stdout);
 
4736
  }
 
4737
 
 
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);
 
4742
  }
 
4743
}
 
4744
 
 
4745
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
 
4746
  // pop the stack
 
4747
  if (mcStack)
 
4748
    popMarkedContent();
 
4749
  out->endMarkedContent(state);
 
4750
}
 
4751
 
 
4752
void Gfx::opMarkPoint(Object args[], int numArgs) {
 
4753
  if (printCommands) {
 
4754
    printf("  mark point: %s ", args[0].getName());
 
4755
    if (numArgs == 2)
 
4756
      args[1].print(stdout);
 
4757
    printf("\n");
 
4758
    fflush(stdout);
 
4759
  }
 
4760
 
 
4761
  if(numArgs == 2 && args[1].isDict()) {
 
4762
    out->markPoint(args[0].getName(),args[1].getDict());
 
4763
  } else {
 
4764
    out->markPoint(args[0].getName());
 
4765
  }
 
4766
 
 
4767
}
 
4768
 
 
4769
//------------------------------------------------------------------------
 
4770
// misc
 
4771
//------------------------------------------------------------------------
 
4772
 
 
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;
 
4777
  Object obj1;
 
4778
  double m[6], bbox[4], ictm[6];
 
4779
  double *ctm;
 
4780
  double formX0, formY0, formX1, formY1;
 
4781
  double annotX0, annotY0, annotX1, annotY1;
 
4782
  double det, x, y, sx, sy;
 
4783
  double r, g, b;
 
4784
  GfxColor color;
 
4785
  double *dash, *dash2;
 
4786
  int dashLength;
 
4787
  int i;
 
4788
 
 
4789
  //~ can we assume that we're in default user space?
 
4790
  //~ (i.e., baseMatrix = ctm)
 
4791
 
 
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;
 
4812
  }
 
4813
  if (annotY0 > annotY1) {
 
4814
    y = annotY0; annotY0 = annotY1; annotY1 = y;
 
4815
  }
 
4816
 
 
4817
  // draw the appearance stream (if there is one)
 
4818
  if (str->isStream()) {
 
4819
 
 
4820
    // get stream dict
 
4821
    dict = str->streamGetDict();
 
4822
 
 
4823
    // get the form bounding box
 
4824
    dict->lookup("BBox", &bboxObj);
 
4825
    if (!bboxObj.isArray()) {
 
4826
      bboxObj.free();
 
4827
      error(getPos(), "Bad form bounding box");
 
4828
      return;
 
4829
    }
 
4830
    for (i = 0; i < 4; ++i) {
 
4831
      bboxObj.arrayGet(i, &obj1);
 
4832
      if (likely(obj1.isNum())) {
 
4833
        bbox[i] = obj1.getNum();
 
4834
        obj1.free();
 
4835
      } else {
 
4836
        obj1.free();
 
4837
        error(getPos(), "Bad form bounding box value");
 
4838
        return;
 
4839
      }
 
4840
    }
 
4841
    bboxObj.free();
 
4842
 
 
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();
 
4849
        obj1.free();
 
4850
      }
 
4851
    } else {
 
4852
      m[0] = 1; m[1] = 0;
 
4853
      m[2] = 0; m[3] = 1;
 
4854
      m[4] = 0; m[5] = 0;
 
4855
    }
 
4856
    matrixObj.free();
 
4857
 
 
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;
 
4865
    }
 
4866
    if (formY0 > formY1) {
 
4867
      y = formY0; formY0 = formY1; formY1 = y;
 
4868
    }
 
4869
 
 
4870
    // scale the form to fit the annotation bbox
 
4871
    if (formX1 == formX0) {
 
4872
      // this shouldn't happen
 
4873
      sx = 1;
 
4874
    } else {
 
4875
      sx = (annotX1 - annotX0) / (formX1 - formX0);
 
4876
    }
 
4877
    if (formY1 == formY0) {
 
4878
      // this shouldn't happen
 
4879
      sy = 1;
 
4880
    } else {
 
4881
      sy = (annotY1 - annotY0) / (formY1 - formY0);
 
4882
    }
 
4883
    m[0] *= sx;
 
4884
    m[2] *= sx;
 
4885
    m[4] = (m[4] - formX0) * sx + annotX0;
 
4886
    m[1] *= sy;
 
4887
    m[3] *= sy;
 
4888
    m[5] = (m[5] - formY0) * sy + annotY0;
 
4889
 
 
4890
    // get resources
 
4891
    dict->lookup("Resources", &resObj);
 
4892
    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
4893
 
 
4894
    // draw it
 
4895
    doForm1(str, resDict, m, bbox);
 
4896
 
 
4897
    resObj.free();
 
4898
  }
 
4899
 
 
4900
  // draw the border
 
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);
 
4906
    }
 
4907
    if (aColor && (aColor->getSpace() == AnnotColor::colorRGB)) {
 
4908
      const double *values = aColor->getValues();
 
4909
      r = values[0];
 
4910
      g = values[1];
 
4911
      b = values[2];
 
4912
    } else {
 
4913
      r = g = b = 0;
 
4914
    };
 
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];
 
4935
      }
 
4936
      state->setLineDash(dash2, dashLength, 0);
 
4937
      out->updateLineDash(state);
 
4938
    }
 
4939
    //~ this doesn't currently handle the beveled and engraved styles
 
4940
    state->clearPath();
 
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);
 
4946
      state->closePath();
 
4947
    }
 
4948
    out->stroke(state);
 
4949
  }
 
4950
}
 
4951
 
 
4952
int Gfx::bottomGuard() {
 
4953
    return stateGuards[stateGuards.size()-1];
 
4954
}
 
4955
 
 
4956
void Gfx::pushStateGuard() {
 
4957
    stateGuards.push_back(stackHeight);
 
4958
}
 
4959
 
 
4960
void Gfx::popStateGuard() {
 
4961
    while (stackHeight > bottomGuard() && state->hasSaves())
 
4962
        restoreState();
 
4963
    stateGuards.pop_back();
 
4964
}
 
4965
 
 
4966
void Gfx::saveState() {
 
4967
  out->saveState(state);
 
4968
  state = state->save();
 
4969
  stackHeight++;
 
4970
}
 
4971
 
 
4972
void Gfx::restoreState() {
 
4973
  if (stackHeight <= bottomGuard() || !state->hasSaves()) {
 
4974
    error(-1, "Restoring state when no valid states to pop");
 
4975
    commandAborted = gTrue;
 
4976
    return;
 
4977
  }
 
4978
  state = state->restore();
 
4979
  out->restoreState(state);
 
4980
  stackHeight--;
 
4981
}
 
4982
 
 
4983
void Gfx::pushResources(Dict *resDict) {
 
4984
  res = new GfxResources(xref, resDict, res);
 
4985
}
 
4986
 
 
4987
void Gfx::popResources() {
 
4988
  GfxResources *resPtr;
 
4989
 
 
4990
  resPtr = res->getNext();
 
4991
  delete res;
 
4992
  res = resPtr;
 
4993
}
 
4994
 
 
4995
#ifdef USE_CMS
 
4996
PopplerCache *Gfx::getIccColorSpaceCache()
 
4997
{
 
4998
  return &iccColorSpaceCache;
 
4999
}
 
5000
#endif