Coverage Report

Created: 2026-08-31 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/Gfx.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Gfx.cc
4
//
5
// Copyright 1996-2016 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <stdio.h>
13
#include <stddef.h>
14
#include <string.h>
15
#include <math.h>
16
#include "gmem.h"
17
#include "gmempp.h"
18
#include "GString.h"
19
#include "GList.h"
20
#include "Trace.h"
21
#include "GlobalParams.h"
22
#include "CharTypes.h"
23
#include "Object.h"
24
#include "PDFDoc.h"
25
#include "Array.h"
26
#include "Dict.h"
27
#include "Stream.h"
28
#include "Lexer.h"
29
#include "Parser.h"
30
#include "GfxFont.h"
31
#include "GfxState.h"
32
#include "OutputDev.h"
33
#include "Page.h"
34
#include "Annot.h"
35
#include "OptionalContent.h"
36
#include "Error.h"
37
#include "TextString.h"
38
#include "Gfx.h"
39
40
// the MSVC math.h doesn't define this
41
#ifndef M_PI
42
#define M_PI 3.14159265358979323846
43
#endif
44
45
//------------------------------------------------------------------------
46
// constants
47
//------------------------------------------------------------------------
48
49
// Max recursive depth for a function shading fill.
50
0
#define functionMaxDepth 6
51
52
// Max delta allowed in any color component for a function shading fill.
53
0
#define functionColorDelta (dblToCol(1 / 256.0))
54
55
// Number of splits along the t axis for an axial shading fill.
56
0
#define axialSplits 256
57
58
// Max delta allowed in any color component for an axial shading fill.
59
0
#define axialColorDelta (dblToCol(1 / 256.0))
60
61
// Max number of splits along the t axis for a radial shading fill.
62
0
#define radialMaxSplits 256
63
64
// Max delta allowed in any color component for a radial shading fill.
65
0
#define radialColorDelta (dblToCol(1 / 256.0))
66
67
// Max recursive depth for a Gouraud triangle shading fill.
68
0
#define gouraudMaxDepth 6
69
70
// Max delta allowed in any color component for a Gouraud triangle
71
// shading fill.
72
0
#define gouraudColorDelta (dblToCol(1 / 256.0))
73
74
// Max recursive depth for a patch mesh shading fill.
75
0
#define patchMaxDepth 6
76
77
// Max delta allowed in any color component for a patch mesh shading
78
// fill.
79
0
#define patchColorDelta (dblToCol(1 / 256.0))
80
81
// Max errors (undefined operator, wrong number of args) allowed before
82
// giving up on a content stream.
83
10.5M
#define contentStreamErrorLimit 500
84
85
//------------------------------------------------------------------------
86
// Operator table
87
//------------------------------------------------------------------------
88
89
#ifdef _WIN32 // this works around a bug in the VC7 compiler
90
#  pragma optimize("",off)
91
#endif
92
93
Operator Gfx::opTab[] = {
94
  {"\"",  3, {tchkNum,    tchkNum,    tchkString},
95
          &Gfx::opMoveSetShowText},
96
  {"'",   1, {tchkString},
97
          &Gfx::opMoveShowText},
98
  {"B",   0, {tchkNone},
99
          &Gfx::opFillStroke},
100
  {"B*",  0, {tchkNone},
101
          &Gfx::opEOFillStroke},
102
  {"BDC", 2, {tchkName,   tchkProps},
103
          &Gfx::opBeginMarkedContent},
104
  {"BI",  0, {tchkNone},
105
          &Gfx::opBeginImage},
106
  {"BMC", 1, {tchkName},
107
          &Gfx::opBeginMarkedContent},
108
  {"BT",  0, {tchkNone},
109
          &Gfx::opBeginText},
110
  {"BX",  0, {tchkNone},
111
          &Gfx::opBeginIgnoreUndef},
112
  {"CS",  1, {tchkName},
113
          &Gfx::opSetStrokeColorSpace},
114
  {"DP",  2, {tchkName,   tchkProps},
115
          &Gfx::opMarkPoint},
116
  {"Do",  1, {tchkName},
117
          &Gfx::opXObject},
118
  {"EI",  0, {tchkNone},
119
          &Gfx::opEndImage},
120
  {"EMC", 0, {tchkNone},
121
          &Gfx::opEndMarkedContent},
122
  {"ET",  0, {tchkNone},
123
          &Gfx::opEndText},
124
  {"EX",  0, {tchkNone},
125
          &Gfx::opEndIgnoreUndef},
126
  {"F",   0, {tchkNone},
127
          &Gfx::opFill},
128
  {"G",   1, {tchkNum},
129
          &Gfx::opSetStrokeGray},
130
  {"ID",  0, {tchkNone},
131
          &Gfx::opImageData},
132
  {"J",   1, {tchkInt},
133
          &Gfx::opSetLineCap},
134
  {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
135
          &Gfx::opSetStrokeCMYKColor},
136
  {"M",   1, {tchkNum},
137
          &Gfx::opSetMiterLimit},
138
  {"MP",  1, {tchkName},
139
          &Gfx::opMarkPoint},
140
  {"Q",   0, {tchkNone},
141
          &Gfx::opRestore},
142
  {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
143
          &Gfx::opSetStrokeRGBColor},
144
  {"S",   0, {tchkNone},
145
          &Gfx::opStroke},
146
  {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum,
147
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
148
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
149
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
150
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
151
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
152
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
153
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
154
         tchkNum},
155
          &Gfx::opSetStrokeColor},
156
  {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
157
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
158
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
159
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
160
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
161
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
162
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
163
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
164
          tchkSCN},
165
          &Gfx::opSetStrokeColorN},
166
  {"T*",  0, {tchkNone},
167
          &Gfx::opTextNextLine},
168
  {"TD",  2, {tchkNum,    tchkNum},
169
          &Gfx::opTextMoveSet},
170
  {"TJ",  1, {tchkArray},
171
          &Gfx::opShowSpaceText},
172
  {"TL",  1, {tchkNum},
173
          &Gfx::opSetTextLeading},
174
  {"Tc",  1, {tchkNum},
175
          &Gfx::opSetCharSpacing},
176
  {"Td",  2, {tchkNum,    tchkNum},
177
          &Gfx::opTextMove},
178
  {"Tf",  2, {tchkName,   tchkNum},
179
          &Gfx::opSetFont},
180
  {"Tj",  1, {tchkString},
181
          &Gfx::opShowText},
182
  {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
183
        tchkNum,    tchkNum},
184
          &Gfx::opSetTextMatrix},
185
  {"Tr",  1, {tchkInt},
186
          &Gfx::opSetTextRender},
187
  {"Ts",  1, {tchkNum},
188
          &Gfx::opSetTextRise},
189
  {"Tw",  1, {tchkNum},
190
          &Gfx::opSetWordSpacing},
191
  {"Tz",  1, {tchkNum},
192
          &Gfx::opSetHorizScaling},
193
  {"W",   0, {tchkNone},
194
          &Gfx::opClip},
195
  {"W*",  0, {tchkNone},
196
          &Gfx::opEOClip},
197
  {"b",   0, {tchkNone},
198
          &Gfx::opCloseFillStroke},
199
  {"b*",  0, {tchkNone},
200
          &Gfx::opCloseEOFillStroke},
201
  {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
202
        tchkNum,    tchkNum},
203
          &Gfx::opCurveTo},
204
  {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
205
        tchkNum,    tchkNum},
206
          &Gfx::opConcat},
207
  {"cs",  1, {tchkName},
208
          &Gfx::opSetFillColorSpace},
209
  {"d",   2, {tchkArray,  tchkNum},
210
          &Gfx::opSetDash},
211
  {"d0",  2, {tchkNum,    tchkNum},
212
          &Gfx::opSetCharWidth},
213
  {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
214
        tchkNum,    tchkNum},
215
          &Gfx::opSetCacheDevice},
216
  {"f",   0, {tchkNone},
217
          &Gfx::opFill},
218
  {"f*",  0, {tchkNone},
219
          &Gfx::opEOFill},
220
  {"g",   1, {tchkNum},
221
          &Gfx::opSetFillGray},
222
  {"gs",  1, {tchkName},
223
          &Gfx::opSetExtGState},
224
  {"h",   0, {tchkNone},
225
          &Gfx::opClosePath},
226
  {"i",   1, {tchkNum},
227
          &Gfx::opSetFlat},
228
  {"j",   1, {tchkInt},
229
          &Gfx::opSetLineJoin},
230
  {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
231
          &Gfx::opSetFillCMYKColor},
232
  {"l",   2, {tchkNum,    tchkNum},
233
          &Gfx::opLineTo},
234
  {"m",   2, {tchkNum,    tchkNum},
235
          &Gfx::opMoveTo},
236
  {"n",   0, {tchkNone},
237
          &Gfx::opEndPath},
238
  {"q",   0, {tchkNone},
239
          &Gfx::opSave},
240
  {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
241
          &Gfx::opRectangle},
242
  {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
243
          &Gfx::opSetFillRGBColor},
244
  {"ri",  1, {tchkName},
245
          &Gfx::opSetRenderingIntent},
246
  {"s",   0, {tchkNone},
247
          &Gfx::opCloseStroke},
248
  {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum,
249
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
250
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
251
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
252
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
253
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
254
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
255
         tchkNum,   tchkNum,    tchkNum,    tchkNum,
256
         tchkNum},
257
          &Gfx::opSetFillColor},
258
  {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
259
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
260
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
261
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
262
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
263
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
264
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
265
          tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
266
          tchkSCN},
267
          &Gfx::opSetFillColorN},
268
  {"sh",  1, {tchkName},
269
          &Gfx::opShFill},
270
  {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
271
          &Gfx::opCurveTo1},
272
  {"w",   1, {tchkNum},
273
          &Gfx::opSetLineWidth},
274
  {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
275
          &Gfx::opCurveTo2},
276
};
277
278
#ifdef _WIN32 // this works around a bug in the VC7 compiler
279
#  pragma optimize("",on)
280
#endif
281
282
10.5M
#define numOps (sizeof(opTab) / sizeof(Operator))
283
284
//------------------------------------------------------------------------
285
// GfxResources
286
//------------------------------------------------------------------------
287
288
2.03M
GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
289
2.03M
  Object obj1, obj2;
290
2.03M
  Ref r;
291
292
2.03M
  if (resDict) {
293
1.89M
    valid = gTrue;
294
295
    // build font dictionary
296
1.89M
    fonts = NULL;
297
1.89M
    resDict->lookupNF("Font", &obj1);
298
1.89M
    if (obj1.isRef()) {
299
609
      obj1.fetch(xref, &obj2);
300
609
      if (obj2.isDict()) {
301
400
  r = obj1.getRef();
302
400
  fonts = new GfxFontDict(xref, &r, obj2.getDict());
303
400
      }
304
609
      obj2.free();
305
1.89M
    } else if (obj1.isDict()) {
306
141k
      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
307
141k
    }
308
1.89M
    obj1.free();
309
310
    // get XObject dictionary
311
1.89M
    resDict->lookup("XObject", &xObjDict);
312
313
    // get color space dictionary
314
1.89M
    resDict->lookup("ColorSpace", &colorSpaceDict);
315
316
    // get pattern dictionary
317
1.89M
    resDict->lookup("Pattern", &patternDict);
318
319
    // get shading dictionary
320
1.89M
    resDict->lookup("Shading", &shadingDict);
321
322
    // get graphics state parameter dictionary
323
1.89M
    resDict->lookup("ExtGState", &gStateDict);
324
325
    // get properties dictionary
326
1.89M
    resDict->lookup("Properties", &propsDict);
327
328
1.89M
  } else {
329
144k
    valid = gFalse;
330
144k
    fonts = NULL;
331
144k
    xObjDict.initNull();
332
144k
    colorSpaceDict.initNull();
333
144k
    patternDict.initNull();
334
144k
    shadingDict.initNull();
335
144k
    gStateDict.initNull();
336
144k
    propsDict.initNull();
337
144k
  }
338
339
2.03M
  next = nextA;
340
2.03M
}
341
342
2.03M
GfxResources::~GfxResources() {
343
2.03M
  if (fonts) {
344
142k
    delete fonts;
345
142k
  }
346
2.03M
  xObjDict.free();
347
2.03M
  colorSpaceDict.free();
348
2.03M
  patternDict.free();
349
2.03M
  shadingDict.free();
350
2.03M
  gStateDict.free();
351
2.03M
  propsDict.free();
352
2.03M
}
353
354
131k
GfxFont *GfxResources::lookupFont(char *name) {
355
131k
  GfxFont *font;
356
131k
  GfxResources *resPtr;
357
358
227k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
359
173k
    if (resPtr->fonts) {
360
141k
      if ((font = resPtr->fonts->lookup(name))) {
361
76.6k
  return font;
362
76.6k
      }
363
141k
    }
364
173k
  }
365
54.7k
  error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name);
366
54.7k
  return NULL;
367
131k
}
368
369
22
GfxFont *GfxResources::lookupFontByRef(Ref ref) {
370
22
  GfxFont *font;
371
22
  GfxResources *resPtr;
372
373
41
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
374
22
    if (resPtr->fonts) {
375
8
      if ((font = resPtr->fonts->lookupByRef(ref))) {
376
3
  return font;
377
3
      }
378
8
    }
379
22
  }
380
19
  error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen);
381
19
  return NULL;
382
22
}
383
384
0
GBool GfxResources::lookupXObject(const char *name, Object *obj) {
385
0
  GfxResources *resPtr;
386
387
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
388
0
    if (resPtr->xObjDict.isDict()) {
389
0
      if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
390
0
  return gTrue;
391
0
      obj->free();
392
0
    }
393
0
  }
394
0
  error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
395
0
  return gFalse;
396
0
}
397
398
48.1k
GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
399
48.1k
  GfxResources *resPtr;
400
401
92.1k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
402
81.7k
    if (resPtr->xObjDict.isDict()) {
403
64.6k
      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
404
37.6k
  return gTrue;
405
26.9k
      obj->free();
406
26.9k
    }
407
81.7k
  }
408
10.4k
  error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
409
10.4k
  return gFalse;
410
48.1k
}
411
412
void GfxResources::lookupColorSpace(const char *name, Object *obj,
413
80.4k
            GBool inherit) {
414
80.4k
  GfxResources *resPtr;
415
416
  //~ should also test for G, RGB, and CMYK - but only in inline images (?)
417
80.4k
  if (!strcmp(name, "DeviceGray") ||
418
74.1k
      !strcmp(name, "DeviceRGB") ||
419
69.7k
      !strcmp(name, "DeviceCMYK")) {
420
10.7k
    obj->initNull();
421
10.7k
    return;
422
10.7k
  }
423
150k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
424
96.5k
    if (resPtr->colorSpaceDict.isDict()) {
425
24.0k
      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
426
16.1k
  return;
427
16.1k
      }
428
7.83k
      obj->free();
429
7.83k
    }
430
80.4k
    if (!inherit && valid) {
431
0
      break;
432
0
    }
433
80.4k
  }
434
53.4k
  obj->initNull();
435
53.4k
}
436
437
GfxPattern *GfxResources::lookupPattern(const char *name
438
48.8k
          ) {
439
48.8k
  GfxResources *resPtr;
440
48.8k
  GfxPattern *pattern;
441
48.8k
  Object objRef, obj;
442
443
68.2k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
444
51.0k
    if (resPtr->patternDict.isDict()) {
445
35.8k
      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
446
31.6k
  resPtr->patternDict.dictLookupNF(name, &objRef);
447
31.6k
  pattern = GfxPattern::parse(&objRef, &obj
448
31.6k
            );
449
31.6k
  objRef.free();
450
31.6k
  obj.free();
451
31.6k
  return pattern;
452
31.6k
      }
453
4.17k
      obj.free();
454
4.17k
    }
455
51.0k
  }
456
17.2k
  error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name);
457
17.2k
  return NULL;
458
48.8k
}
459
460
GfxShading *GfxResources::lookupShading(const char *name
461
19.4k
          ) {
462
19.4k
  GfxResources *resPtr;
463
19.4k
  GfxShading *shading;
464
19.4k
  Object obj;
465
466
48.0k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
467
33.1k
    if (resPtr->shadingDict.isDict()) {
468
5.86k
      if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
469
4.62k
  shading = GfxShading::parse(&obj
470
4.62k
            );
471
4.62k
  obj.free();
472
4.62k
  return shading;
473
4.62k
      }
474
1.23k
      obj.free();
475
1.23k
    }
476
33.1k
  }
477
14.8k
  error(errSyntaxError, -1, "Unknown shading '{0:s}'", name);
478
14.8k
  return NULL;
479
19.4k
}
480
481
40.9k
GBool GfxResources::lookupGState(const char *name, Object *obj) {
482
40.9k
  GfxResources *resPtr;
483
484
120k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
485
92.2k
    if (resPtr->gStateDict.isDict()) {
486
23.9k
      if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
487
13.1k
  return gTrue;
488
13.1k
      }
489
10.8k
      obj->free();
490
10.8k
    }
491
92.2k
  }
492
27.7k
  error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name);
493
27.7k
  return gFalse;
494
40.9k
}
495
496
14.4k
GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) {
497
14.4k
  GfxResources *resPtr;
498
499
20.5k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
500
14.4k
    if (resPtr->propsDict.isDict()) {
501
13.8k
      if (!resPtr->propsDict.dictLookupNF(name, obj)->isNull()) {
502
8.38k
  return gTrue;
503
8.38k
      }
504
5.48k
      obj->free();
505
5.48k
    }
506
14.4k
  }
507
6.03k
  error(errSyntaxError, -1, "Properties '{0:s}' is unknown", name);
508
6.03k
  return gFalse;
509
14.4k
}
510
511
//------------------------------------------------------------------------
512
// Gfx
513
//------------------------------------------------------------------------
514
515
Gfx::Gfx(PDFDoc *docA, OutputDev *outA, LocalParams *localParams,
516
   int pageNum, Dict *resDict,
517
   double hDPI, double vDPI, PDFRectangle *box,
518
   PDFRectangle *cropBox, int rotate,
519
   GBool (*abortCheckCbkA)(void *data),
520
230k
   void *abortCheckCbkDataA) {
521
230k
  int i;
522
523
230k
  doc = docA;
524
230k
  xref = doc->getXRef();
525
230k
  subPage = gFalse;
526
230k
  printCommands = globalParams->getPrintCommands();
527
230k
  defaultFont = NULL;
528
529
  // start the resource stack
530
230k
  res = new GfxResources(xref, resDict, NULL);
531
532
  // initialize
533
230k
  out = outA;
534
230k
  state = new GfxState(localParams, hDPI, vDPI,
535
230k
           box, rotate, out->upsideDown());
536
230k
  fontChanged = gFalse;
537
230k
  haveSavedClipPath = gFalse;
538
230k
  clip = clipNone;
539
230k
  ignoreUndef = 0;
540
230k
  out->startPage(pageNum, state);
541
230k
  out->setDefaultCTM(state->getCTM());
542
230k
  out->updateAll(state);
543
1.61M
  for (i = 0; i < 6; ++i) {
544
1.38M
    baseMatrix[i] = state->getCTM()[i];
545
1.38M
  }
546
230k
  formDepth = 0;
547
230k
  markedContentStack = new GList();
548
230k
  ocState = gTrue;
549
230k
  parser = NULL;
550
230k
  contentStreamStack = new GList();
551
230k
  abortCheckCbk = abortCheckCbkA;
552
230k
  abortCheckCbkData = abortCheckCbkDataA;
553
554
  // set crop box
555
230k
  if (cropBox) {
556
16.8k
    state->moveTo(cropBox->x1, cropBox->y1);
557
16.8k
    state->lineTo(cropBox->x2, cropBox->y1);
558
16.8k
    state->lineTo(cropBox->x2, cropBox->y2);
559
16.8k
    state->lineTo(cropBox->x1, cropBox->y2);
560
16.8k
    state->closePath();
561
16.8k
    state->clip();
562
16.8k
    out->clip(state);
563
16.8k
    state->clearPath();
564
16.8k
  }
565
230k
}
566
567
Gfx::Gfx(PDFDoc *docA, OutputDev *outA, LocalParams *localParams,
568
   Dict *resDict, PDFRectangle *box, PDFRectangle *cropBox,
569
   GBool (*abortCheckCbkA)(void *data),
570
0
   void *abortCheckCbkDataA) {
571
0
  int i;
572
573
0
  doc = docA;
574
0
  xref = doc->getXRef();
575
0
  subPage = gTrue;
576
0
  printCommands = globalParams->getPrintCommands();
577
0
  defaultFont = NULL;
578
579
  // start the resource stack
580
0
  res = new GfxResources(xref, resDict, NULL);
581
582
  // initialize
583
0
  out = outA;
584
0
  state = new GfxState(localParams, 72, 72, box, 0, gFalse);
585
0
  fontChanged = gFalse;
586
0
  haveSavedClipPath = gFalse;
587
0
  clip = clipNone;
588
0
  ignoreUndef = 0;
589
0
  for (i = 0; i < 6; ++i) {
590
0
    baseMatrix[i] = state->getCTM()[i];
591
0
  }
592
0
  formDepth = 0;
593
0
  markedContentStack = new GList();
594
0
  ocState = gTrue;
595
0
  parser = NULL;
596
0
  contentStreamStack = new GList();
597
0
  abortCheckCbk = abortCheckCbkA;
598
0
  abortCheckCbkData = abortCheckCbkDataA;
599
600
  // set crop box
601
0
  if (cropBox) {
602
0
    state->moveTo(cropBox->x1, cropBox->y1);
603
0
    state->lineTo(cropBox->x2, cropBox->y1);
604
0
    state->lineTo(cropBox->x2, cropBox->y2);
605
0
    state->lineTo(cropBox->x1, cropBox->y2);
606
0
    state->closePath();
607
0
    state->clip();
608
0
    out->clip(state);
609
0
    state->clearPath();
610
0
  }
611
0
}
612
613
230k
Gfx::~Gfx() {
614
230k
  if (defaultFont) {
615
10.6k
    delete defaultFont;
616
10.6k
  }
617
230k
  if (!subPage) {
618
230k
    out->endPage();
619
230k
  }
620
230k
  while (state->hasSaves()) {
621
0
    restoreState();
622
0
  }
623
230k
  delete state;
624
460k
  while (res) {
625
230k
    popResources();
626
230k
  }
627
230k
  deleteGList(markedContentStack, GfxMarkedContent);
628
230k
  delete contentStreamStack;
629
230k
}
630
631
245k
void Gfx::display(Object *objRef, GBool topLevel) {
632
245k
  Object obj1, obj2;
633
245k
  int i;
634
635
245k
  objRef->fetch(xref, &obj1);
636
245k
  if (obj1.isArray()) {
637
4.26k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
638
3.21k
      obj1.arrayGetNF(i, &obj2);
639
3.21k
      if (checkForContentStreamLoop(&obj2)) {
640
0
  obj2.free();
641
0
  obj1.free();
642
0
  return;
643
0
      }
644
3.21k
      obj2.free();
645
3.21k
    }
646
3.35k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
647
2.41k
      obj1.arrayGet(i, &obj2);
648
2.41k
      if (!obj2.isStream()) {
649
110
  error(errSyntaxError, -1, "Invalid object type for content stream");
650
110
  obj2.free();
651
110
  obj1.free();
652
110
  return;
653
110
      }
654
2.30k
      obj2.free();
655
2.30k
    }
656
941
    contentStreamStack->append(&obj1);
657
244k
  } else if (obj1.isStream()) {
658
243k
    if (checkForContentStreamLoop(objRef)) {
659
93.9k
      obj1.free();
660
93.9k
      return;
661
93.9k
    }
662
149k
    contentStreamStack->append(objRef);
663
149k
  } else {
664
837
    error(errSyntaxError, -1, "Invalid object type for content stream");
665
837
    obj1.free();
666
837
    return;
667
837
  }
668
150k
  parser = new Parser(xref, new Lexer(xref, &obj1), gFalse);
669
150k
  go(topLevel);
670
150k
  delete parser;
671
150k
  parser = NULL;
672
150k
  contentStreamStack->del(contentStreamStack->getLength() - 1);
673
150k
  obj1.free();
674
150k
}
675
676
// If <ref> is already on contentStreamStack, i.e., if there is a loop
677
// in the content streams, report an error, and return true.
678
246k
GBool Gfx::checkForContentStreamLoop(Object *ref) {
679
246k
  Object *objPtr;
680
246k
  Object obj1;
681
246k
  int i, j;
682
683
246k
  if (ref->isRef()) {
684
495k
    for (i = 0; i < contentStreamStack->getLength(); ++i) {
685
345k
      objPtr = (Object *)contentStreamStack->get(i);
686
345k
      if (objPtr->isRef()) {
687
339k
  if (ref->getRefNum() == objPtr->getRefNum() &&
688
93.9k
      ref->getRefGen() == objPtr->getRefGen()) {
689
93.9k
    error(errSyntaxError, -1, "Loop in content streams");
690
93.9k
    return gTrue;
691
93.9k
  }
692
339k
      } else if (objPtr->isArray()) {
693
12.2k
  for (j = 0; j < objPtr->arrayGetLength(); ++j) {
694
6.71k
    objPtr->arrayGetNF(j, &obj1);
695
6.71k
    if (obj1.isRef()) {
696
6.71k
      if (ref->getRefNum() == obj1.getRefNum() &&
697
0
    ref->getRefGen() == obj1.getRefGen()) {
698
0
        error(errSyntaxError, -1, "Loop in content streams");
699
0
        obj1.free();
700
0
        return gTrue;
701
0
      }
702
6.71k
    }
703
6.71k
    obj1.free();
704
6.71k
  }
705
5.54k
      }
706
345k
    }
707
243k
  }
708
152k
  return gFalse;
709
246k
}
710
711
150k
void Gfx::go(GBool topLevel) {
712
150k
  Object obj;
713
150k
  Object args[maxArgs];
714
150k
  GBool aborted;
715
150k
  int numArgs, i;
716
150k
  int errCount;
717
718
  // scan a sequence of objects
719
150k
  opCounter = 0;
720
150k
  aborted = gFalse;
721
150k
  errCount = 0;
722
150k
  numArgs = 0;
723
150k
  getContentObj(&obj);
724
38.1M
  while (!obj.isEOF()) {
725
726
    // check for an abort
727
37.9M
    ++opCounter;
728
37.9M
    if (abortCheckCbk && opCounter > 100) {
729
0
      if ((*abortCheckCbk)(abortCheckCbkData)) {
730
0
  aborted = gTrue;
731
0
  break;
732
0
      }
733
0
      opCounter = 0;
734
0
    }
735
736
    // got a command - execute it
737
37.9M
    if (obj.isCmd()) {
738
10.5M
      if (printCommands) {
739
0
  obj.print(stdout);
740
0
  for (i = 0; i < numArgs; ++i) {
741
0
    printf(" ");
742
0
    args[i].print(stdout);
743
0
  }
744
0
  printf("\n");
745
0
  fflush(stdout);
746
0
      }
747
10.5M
      if (!execOp(&obj, args, numArgs)) {
748
5.52M
  ++errCount;
749
5.52M
      }
750
10.5M
      obj.free();
751
36.5M
      for (i = 0; i < numArgs; ++i)
752
26.0M
  args[i].free();
753
10.5M
      numArgs = 0;
754
755
      // check for too many errors
756
10.5M
      if (errCount > contentStreamErrorLimit) {
757
3.73k
  error(errSyntaxError, -1,
758
3.73k
        "Too many errors - giving up on this content stream");
759
3.73k
  break;
760
3.73k
      }
761
762
    // got an argument - save it
763
27.4M
    } else if (numArgs < maxArgs) {
764
26.1M
      args[numArgs++] = obj;
765
766
    // too many arguments - something is wrong
767
26.1M
    } else {
768
1.30M
      error(errSyntaxError, getPos(), "Too many args in content stream");
769
1.30M
      if (printCommands) {
770
0
  printf("throwing away arg: ");
771
0
  obj.print(stdout);
772
0
  printf("\n");
773
0
  fflush(stdout);
774
0
      }
775
1.30M
      obj.free();
776
1.30M
    }
777
778
    // grab the next object
779
37.9M
    getContentObj(&obj);
780
37.9M
  }
781
150k
  obj.free();
782
783
  // args at end with no command
784
150k
  if (numArgs > 0) {
785
31.3k
    if (!aborted) {
786
31.3k
      error(errSyntaxError, getPos(), "Leftover args in content stream");
787
31.3k
      if (printCommands) {
788
0
  printf("%d leftovers:", numArgs);
789
0
  for (i = 0; i < numArgs; ++i) {
790
0
    printf(" ");
791
0
    args[i].print(stdout);
792
0
  }
793
0
  printf("\n");
794
0
  fflush(stdout);
795
0
      }
796
31.3k
    }
797
99.9k
    for (i = 0; i < numArgs; ++i) {
798
68.6k
      args[i].free();
799
68.6k
    }
800
31.3k
  }
801
150k
}
802
803
39.3M
void Gfx::getContentObj(Object *obj) {
804
39.3M
  parser->getObj(obj);
805
39.3M
  if (obj->isRef()) {
806
33.1k
    error(errSyntaxError, getPos(), "Indirect reference in content stream");
807
33.1k
    obj->free();
808
33.1k
    obj->initError();
809
33.1k
  }
810
39.3M
}
811
812
// Returns true if successful, false on error.
813
10.5M
GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
814
10.5M
  Operator *op;
815
10.5M
  char *name;
816
10.5M
  Object *argPtr;
817
10.5M
  int i;
818
819
  // find operator
820
10.5M
  name = cmd->getCmd();
821
10.5M
  if (!(op = findOp(name))) {
822
4.47M
    if (ignoreUndef > 0) {
823
110k
      return gTrue;
824
110k
    }
825
4.36M
    error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
826
4.36M
    return gFalse;
827
4.47M
  }
828
829
  // type check args
830
6.04M
  argPtr = args;
831
6.04M
  if (op->numArgs >= 0) {
832
5.96M
    if (numArgs < op->numArgs) {
833
996k
      error(errSyntaxError, getPos(),
834
996k
      "Too few ({0:d}) args to '{1:s}' operator", numArgs, name);
835
996k
      return gFalse;
836
996k
    }
837
4.96M
    if (numArgs > op->numArgs) {
838
#if 0
839
      error(errSyntaxWarning, getPos(),
840
      "Too many ({0:d}) args to '{1:s}' operator", numArgs, name);
841
#endif
842
1.43M
      argPtr += numArgs - op->numArgs;
843
1.43M
      numArgs = op->numArgs;
844
1.43M
    }
845
4.96M
  } else {
846
88.8k
    if (numArgs > -op->numArgs) {
847
3.22k
      error(errSyntaxWarning, getPos(),
848
3.22k
      "Too many ({0:d}) args to '{1:s}' operator",
849
3.22k
      numArgs, name);
850
3.22k
    }
851
88.8k
  }
852
11.1M
  for (i = 0; i < numArgs; ++i) {
853
6.29M
    if (!checkArg(&argPtr[i], op->tchk[i])) {
854
159k
      error(errSyntaxError, getPos(),
855
159k
      "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
856
159k
      i, name, argPtr[i].getTypeName());
857
159k
      return gFalse;
858
159k
    }
859
6.29M
  }
860
861
  // do it
862
4.89M
  (this->*op->func)(argPtr, numArgs);
863
864
4.89M
  return gTrue;
865
5.05M
}
866
867
10.5M
Operator *Gfx::findOp(char *name) {
868
10.5M
  int a, b, m, cmp;
869
870
10.5M
  a = -1;
871
10.5M
  b = numOps;
872
10.5M
  cmp = 0; // make gcc happy
873
  // invariant: opTab[a] < name < opTab[b]
874
70.8M
  while (b - a > 1) {
875
60.3M
    m = (a + b) / 2;
876
60.3M
    cmp = strcmp(opTab[m].name, name);
877
60.3M
    if (cmp < 0)
878
32.9M
      a = m;
879
27.4M
    else if (cmp > 0)
880
21.3M
      b = m;
881
6.04M
    else
882
6.04M
      a = b = m;
883
60.3M
  }
884
10.5M
  if (cmp != 0)
885
4.47M
    return NULL;
886
6.04M
  return &opTab[a];
887
10.5M
}
888
889
6.29M
GBool Gfx::checkArg(Object *arg, TchkType type) {
890
6.29M
  switch (type) {
891
0
  case tchkBool:   return arg->isBool();
892
87.6k
  case tchkInt:    return arg->isInt();
893
5.29M
  case tchkNum:    return arg->isNum();
894
379k
  case tchkString: return arg->isString();
895
374k
  case tchkName:   return arg->isName();
896
75.1k
  case tchkArray:  return arg->isArray();
897
17.4k
  case tchkProps:  return arg->isDict() || arg->isName();
898
70.0k
  case tchkSCN:    return arg->isNum() || arg->isName();
899
0
  case tchkNone:   return gFalse;
900
6.29M
  }
901
0
  return gFalse;
902
6.29M
}
903
904
10.0M
GFileOffset Gfx::getPos() {
905
10.0M
  return parser ? parser->getPos() : -1;
906
10.0M
}
907
908
//------------------------------------------------------------------------
909
// graphics state operators
910
//------------------------------------------------------------------------
911
912
247k
void Gfx::opSave(Object args[], int numArgs) {
913
247k
  saveState();
914
247k
}
915
916
266k
void Gfx::opRestore(Object args[], int numArgs) {
917
266k
  restoreState();
918
266k
}
919
920
95.0k
void Gfx::opConcat(Object args[], int numArgs) {
921
95.0k
  state->concatCTM(args[0].getNum(), args[1].getNum(),
922
95.0k
       args[2].getNum(), args[3].getNum(),
923
95.0k
       args[4].getNum(), args[5].getNum());
924
95.0k
  out->updateCTM(state, args[0].getNum(), args[1].getNum(),
925
95.0k
     args[2].getNum(), args[3].getNum(),
926
95.0k
     args[4].getNum(), args[5].getNum());
927
95.0k
  fontChanged = gTrue;
928
95.0k
}
929
930
6.44k
void Gfx::opSetDash(Object args[], int numArgs) {
931
6.44k
  Array *a;
932
6.44k
  int length;
933
6.44k
  Object obj;
934
6.44k
  double *dash;
935
6.44k
  int i;
936
937
6.44k
  a = args[0].getArray();
938
6.44k
  length = a->getLength();
939
6.44k
  if (length == 0) {
940
5.44k
    dash = NULL;
941
5.44k
  } else {
942
995
    dash = (double *)gmallocn(length, sizeof(double));
943
19.4k
    for (i = 0; i < length; ++i) {
944
18.4k
      dash[i] = a->get(i, &obj)->getNum();
945
18.4k
      obj.free();
946
18.4k
    }
947
995
  }
948
6.44k
  state->setLineDash(dash, length, args[1].getNum());
949
6.44k
  out->updateLineDash(state);
950
6.44k
}
951
952
131k
void Gfx::opSetFlat(Object args[], int numArgs) {
953
131k
  state->setFlatness((int)args[0].getNum());
954
131k
  out->updateFlatness(state);
955
131k
}
956
957
11.3k
void Gfx::opSetLineJoin(Object args[], int numArgs) {
958
11.3k
  int lineJoin;
959
960
11.3k
  lineJoin = args[0].getInt();
961
11.3k
  if (lineJoin < 0 || lineJoin > 2) {
962
4.75k
    lineJoin = 0;
963
4.75k
  }
964
11.3k
  state->setLineJoin(lineJoin);
965
11.3k
  out->updateLineJoin(state);
966
11.3k
}
967
968
58.2k
void Gfx::opSetLineCap(Object args[], int numArgs) {
969
58.2k
  int lineCap;
970
971
58.2k
  lineCap = args[0].getInt();
972
58.2k
  if (lineCap < 0 || lineCap > 2) {
973
16.8k
    lineCap = 0;
974
16.8k
  }
975
58.2k
  state->setLineCap(lineCap);
976
58.2k
  out->updateLineCap(state);
977
58.2k
}
978
979
26.4k
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
980
26.4k
  state->setMiterLimit(args[0].getNum());
981
26.4k
  out->updateMiterLimit(state);
982
26.4k
}
983
984
94.9k
void Gfx::opSetLineWidth(Object args[], int numArgs) {
985
94.9k
  state->setLineWidth(args[0].getNum());
986
94.9k
  out->updateLineWidth(state);
987
94.9k
}
988
989
40.9k
void Gfx::opSetExtGState(Object args[], int numArgs) {
990
40.9k
  Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj;
991
40.9k
  Object args2[2];
992
40.9k
  GfxBlendMode mode;
993
40.9k
  GBool haveFillOP;
994
40.9k
  Function *funcs[4];
995
40.9k
  GBool alpha, knockout;
996
40.9k
  double opac;
997
40.9k
  int i;
998
999
40.9k
  if (!res->lookupGState(args[0].getName(), &obj1)) {
1000
27.7k
    return;
1001
27.7k
  }
1002
13.1k
  if (!obj1.isDict()) {
1003
1.70k
    error(errSyntaxError, getPos(),
1004
1.70k
    "ExtGState '{0:s}' is wrong type", args[0].getName());
1005
1.70k
    obj1.free();
1006
1.70k
    return;
1007
1.70k
  }
1008
11.4k
  if (printCommands) {
1009
0
    printf("  gfx state dict: ");
1010
0
    obj1.print();
1011
0
    printf("\n");
1012
0
  }
1013
1014
  // parameters that are also set by individual PDF operators
1015
11.4k
  if (obj1.dictLookup("LW", &obj2)->isNum()) {
1016
267
    opSetLineWidth(&obj2, 1);
1017
267
  }
1018
11.4k
  obj2.free();
1019
11.4k
  if (obj1.dictLookup("LC", &obj2)->isInt()) {
1020
265
    opSetLineCap(&obj2, 1);
1021
265
  }
1022
11.4k
  obj2.free();
1023
11.4k
  if (obj1.dictLookup("LJ", &obj2)->isInt()) {
1024
263
    opSetLineJoin(&obj2, 1);
1025
263
  }
1026
11.4k
  obj2.free();
1027
11.4k
  if (obj1.dictLookup("ML", &obj2)->isNum()) {
1028
251
    opSetMiterLimit(&obj2, 1);
1029
251
  }
1030
11.4k
  obj2.free();
1031
11.4k
  if (obj1.dictLookup("D", &obj2)->isArray() &&
1032
167
      obj2.arrayGetLength() == 2) {
1033
141
    obj2.arrayGet(0, &args2[0]);
1034
141
    obj2.arrayGet(1, &args2[1]);
1035
141
    if (args2[0].isArray() && args2[1].isNum()) {
1036
18
      opSetDash(args2, 2);
1037
18
    }
1038
141
    args2[0].free();
1039
141
    args2[1].free();
1040
141
  }
1041
11.4k
  obj2.free();
1042
11.4k
  if (obj1.dictLookup("FL", &obj2)->isNum()) {
1043
1
    opSetFlat(&obj2, 1);
1044
1
  }
1045
11.4k
  obj2.free();
1046
11.4k
  if (obj1.dictLookup("RI", &obj2)->isName()) {
1047
0
    opSetRenderingIntent(&obj2, 1);
1048
0
  }
1049
11.4k
  obj2.free();
1050
1051
  // font
1052
11.4k
  if (obj1.dictLookup("Font", &obj2)->isArray() &&
1053
51
      obj2.arrayGetLength() == 2) {
1054
37
    obj2.arrayGetNF(0, &obj3);
1055
37
    obj2.arrayGetNF(1, &obj4);
1056
37
    if (obj3.isRef() && obj4.isNum()) {
1057
22
      doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum());
1058
22
    }
1059
37
    obj3.free();
1060
37
    obj4.free();
1061
37
  }
1062
11.4k
  obj2.free();
1063
1064
  // transparency support: blend mode, fill/stroke opacity
1065
11.4k
  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
1066
5.90k
    if (state->parseBlendMode(&obj2, &mode)) {
1067
5.29k
      state->setBlendMode(mode);
1068
5.29k
      out->updateBlendMode(state);
1069
5.29k
    } else {
1070
610
      error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState");
1071
610
    }
1072
5.90k
  }
1073
11.4k
  obj2.free();
1074
11.4k
  if (obj1.dictLookup("ca", &obj2)->isNum()) {
1075
5.66k
    opac = obj2.getNum();
1076
5.66k
    state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
1077
5.66k
    out->updateFillOpacity(state);
1078
5.66k
  }
1079
11.4k
  obj2.free();
1080
11.4k
  if (obj1.dictLookup("CA", &obj2)->isNum()) {
1081
5.62k
    opac = obj2.getNum();
1082
5.62k
    state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
1083
5.62k
    out->updateStrokeOpacity(state);
1084
5.62k
  }
1085
11.4k
  obj2.free();
1086
1087
  // fill/stroke overprint, overprint mode
1088
11.4k
  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
1089
2.34k
    if (!state->getIgnoreColorOps()) {
1090
2.30k
      state->setFillOverprint(obj2.getBool());
1091
2.30k
      out->updateFillOverprint(state);
1092
2.30k
    } else {
1093
35
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1094
35
      " in uncolored Type 3 char or tiling pattern");
1095
35
    }
1096
2.34k
  }
1097
11.4k
  obj2.free();
1098
11.4k
  if (obj1.dictLookup("OP", &obj2)->isBool()) {
1099
1.84k
    if (!state->getIgnoreColorOps()) {
1100
1.81k
      state->setStrokeOverprint(obj2.getBool());
1101
1.81k
      out->updateStrokeOverprint(state);
1102
1.81k
      if (!haveFillOP) {
1103
391
  state->setFillOverprint(obj2.getBool());
1104
391
  out->updateFillOverprint(state);
1105
391
      }
1106
1.81k
    } else {
1107
35
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1108
35
      " in uncolored Type 3 char or tiling pattern");
1109
35
    }
1110
1.84k
  }
1111
11.4k
  obj2.free();
1112
11.4k
  if (obj1.dictLookup("OPM", &obj2)->isInt()) {
1113
1.85k
    if (!state->getIgnoreColorOps()) {
1114
1.83k
      state->setOverprintMode(obj2.getInt());
1115
1.83k
      out->updateOverprintMode(state);
1116
1.83k
    } else {
1117
20
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1118
20
      " in uncolored Type 3 char or tiling pattern");
1119
20
    }
1120
1.85k
  }
1121
11.4k
  obj2.free();
1122
1123
  // stroke adjust
1124
11.4k
  if (obj1.dictLookup("SA", &obj2)->isBool()) {
1125
2.11k
    state->setStrokeAdjust(obj2.getBool());
1126
2.11k
    out->updateStrokeAdjust(state);
1127
2.11k
  }
1128
11.4k
  obj2.free();
1129
1130
  // transfer function
1131
11.4k
  if (obj1.dictLookup("TR2", &obj2)->isNull()) {
1132
11.2k
    obj2.free();
1133
11.2k
    obj1.dictLookup("TR", &obj2);
1134
11.2k
  }
1135
11.4k
  if (!obj2.isNull()) {
1136
1.41k
    if (!state->getIgnoreColorOps()) {
1137
1.41k
      if (obj2.isName("Default") ||
1138
1.27k
    obj2.isName("Identity")) {
1139
403
  funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
1140
403
  state->setTransfer(funcs);
1141
403
  out->updateTransfer(state);
1142
1.01k
      } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
1143
95
  for (i = 0; i < 4; ++i) {
1144
81
    obj2.arrayGet(i, &obj3);
1145
81
    funcs[i] = Function::parse(&obj3, 1, 1);
1146
81
    obj3.free();
1147
81
    if (!funcs[i]) {
1148
19
      break;
1149
19
    }
1150
81
  }
1151
33
  if (i == 4) {
1152
14
    state->setTransfer(funcs);
1153
14
    out->updateTransfer(state);
1154
14
  }
1155
979
      } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
1156
579
  if ((funcs[0] = Function::parse(&obj2, 1, 1))) {
1157
173
    funcs[1] = funcs[2] = funcs[3] = NULL;
1158
173
    state->setTransfer(funcs);
1159
173
    out->updateTransfer(state);
1160
173
  }
1161
579
      } else {
1162
400
  error(errSyntaxError, getPos(),
1163
400
        "Invalid transfer function in ExtGState");
1164
400
      }
1165
1.41k
    } else {
1166
0
      error(errSyntaxWarning, getPos(), "Ignoring transfer function setting"
1167
0
      " in uncolored Type 3 char or tiling pattern");
1168
0
    }
1169
1.41k
  }
1170
11.4k
  obj2.free();
1171
1172
  // alpha is shape
1173
11.4k
  if (obj1.dictLookup("AIS", &obj2)->isBool()) {
1174
3.90k
    state->setAlphaIsShape(obj2.getBool());
1175
3.90k
    out->updateAlphaIsShape(state);
1176
3.90k
  }
1177
11.4k
  obj2.free();
1178
1179
  // soft mask
1180
11.4k
  if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
1181
3.86k
    if (obj2.isName("None")) {
1182
1.01k
      out->clearSoftMask(state);
1183
2.85k
    } else if (obj2.isDict()) {
1184
2.24k
      obj2.dictLookup("S", &obj3);
1185
2.24k
      if (obj3.isName("Alpha")) {
1186
261
  alpha = gTrue;
1187
1.98k
      } else if (obj3.isName("Luminosity")) {
1188
683
  alpha = gFalse;
1189
1.29k
      } else {
1190
1.29k
  error(errSyntaxError, getPos(),
1191
1.29k
        "Missing S (subtype) entry in soft mask");
1192
1.29k
  alpha = gFalse;
1193
1.29k
      }
1194
2.24k
      obj3.free();
1195
2.24k
      funcs[0] = NULL;
1196
2.24k
      if (!obj2.dictLookup("TR", &obj3)->isNull()) {
1197
1.03k
  if (obj3.isName("Default") ||
1198
1.03k
      obj3.isName("Identity")) {
1199
0
    funcs[0] = NULL;
1200
1.03k
  } else {
1201
1.03k
    if (!(funcs[0] = Function::parse(&obj3, 1, 1))) {
1202
1.02k
      error(errSyntaxError, getPos(),
1203
1.02k
      "Invalid transfer function in soft mask in ExtGState");
1204
1.02k
      delete funcs[0];
1205
1.02k
      funcs[0] = NULL;
1206
1.02k
    }
1207
1.03k
  }
1208
1.03k
      }
1209
2.24k
      obj3.free();
1210
2.24k
      obj2.dictLookup("BC", &backdropColorObj);
1211
2.24k
      if (obj2.dictLookup("G", &obj3)->isStream()) {
1212
1.44k
  if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
1213
1.15k
    knockout = gFalse;
1214
1.15k
    if (obj4.dictLookup("K", &obj5)->isBool()) {
1215
735
      knockout = obj5.getBool();
1216
735
    }
1217
1.15k
    obj5.free();
1218
1.15k
    obj2.dictLookupNF("G", &objRef3);
1219
    // it doesn't make sense for softmasks to be non-isolated,
1220
    // because they're blended with a backdrop color, rather
1221
    // than the original backdrop
1222
1.15k
    doSoftMask(&obj3, &objRef3, alpha, gTrue, knockout, funcs[0],
1223
1.15k
         &backdropColorObj);
1224
1.15k
    objRef3.free();
1225
1.15k
    if (funcs[0]) {
1226
12
      delete funcs[0];
1227
12
    }
1228
1.15k
  } else {
1229
286
    error(errSyntaxError, getPos(),
1230
286
    "Invalid soft mask in ExtGState - missing group");
1231
286
  }
1232
1.44k
  obj4.free();
1233
1.44k
      } else {
1234
801
  error(errSyntaxError, getPos(),
1235
801
        "Invalid soft mask in ExtGState - missing group");
1236
801
      }
1237
2.24k
      obj3.free();
1238
2.24k
      backdropColorObj.free();
1239
2.24k
    } else if (!obj2.isNull()) {
1240
611
      error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState");
1241
611
    }
1242
3.86k
  }
1243
11.4k
  obj2.free();
1244
1245
11.4k
  obj1.free();
1246
11.4k
}
1247
1248
void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
1249
         GBool isolated, GBool knockout,
1250
1.15k
         Function *transferFunc, Object *backdropColorObj) {
1251
1.15k
  Dict *dict, *resDict;
1252
1.15k
  double m[6], bbox[4];
1253
1.15k
  Object obj1, obj2;
1254
1.15k
  int i;
1255
1256
  // get stream dict
1257
1.15k
  dict = str->streamGetDict();
1258
1259
  // check form type
1260
1.15k
  dict->lookup("FormType", &obj1);
1261
1.15k
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
1262
153
    error(errSyntaxError, getPos(), "Unknown form type");
1263
153
  }
1264
1.15k
  obj1.free();
1265
1266
  // get bounding box
1267
1.15k
  dict->lookup("BBox", &obj1);
1268
1.15k
  if (!obj1.isArray()) {
1269
47
    obj1.free();
1270
47
    error(errSyntaxError, getPos(), "Bad form bounding box");
1271
47
    return;
1272
47
  }
1273
5.53k
  for (i = 0; i < 4; ++i) {
1274
4.42k
    obj1.arrayGet(i, &obj2);
1275
4.42k
    bbox[i] = obj2.getNum();
1276
4.42k
    obj2.free();
1277
4.42k
  }
1278
1.10k
  obj1.free();
1279
1280
  // get matrix
1281
1.10k
  dict->lookup("Matrix", &obj1);
1282
1.10k
  if (obj1.isArray()) {
1283
1.07k
    for (i = 0; i < 6; ++i) {
1284
924
      obj1.arrayGet(i, &obj2);
1285
924
      m[i] = obj2.getNum();
1286
924
      obj2.free();
1287
924
    }
1288
953
  } else {
1289
953
    m[0] = 1; m[1] = 0;
1290
953
    m[2] = 0; m[3] = 1;
1291
953
    m[4] = 0; m[5] = 0;
1292
953
  }
1293
1.10k
  obj1.free();
1294
1295
  // get resources
1296
1.10k
  dict->lookup("Resources", &obj1);
1297
1.10k
  resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
1298
1299
  // draw it
1300
1.10k
  drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout,
1301
1.10k
     alpha, transferFunc, backdropColorObj);
1302
1303
1.10k
  obj1.free();
1304
1.10k
}
1305
1306
1.84k
void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
1307
1.84k
  GfxRenderingIntent ri;
1308
1309
1.84k
  if (state->getIgnoreColorOps()) {
1310
37
    error(errSyntaxWarning, getPos(), "Ignoring rendering intent setting"
1311
37
    " in uncolored Type 3 char or tiling pattern");
1312
37
    return;
1313
37
  }
1314
1.81k
  ri = parseRenderingIntent(args[0].getName());
1315
1.81k
  state->setRenderingIntent(ri);
1316
1.81k
  out->updateRenderingIntent(state);
1317
1.81k
}
1318
1319
1.81k
GfxRenderingIntent Gfx::parseRenderingIntent(const char *name) {
1320
1.81k
  if (!strcmp(name, "AbsoluteColorimetric")) {
1321
0
    return gfxRenderingIntentAbsoluteColorimetric;
1322
0
  }
1323
1.81k
  if (!strcmp(name, "Saturation")) {
1324
0
    return gfxRenderingIntentSaturation;
1325
0
  }
1326
1.81k
  if (!strcmp(name, "Perceptual")) {
1327
944
    return gfxRenderingIntentPerceptual;
1328
944
  }
1329
867
  return gfxRenderingIntentRelativeColorimetric;
1330
1.81k
}
1331
1332
//------------------------------------------------------------------------
1333
// color operators
1334
//------------------------------------------------------------------------
1335
1336
47.3k
void Gfx::opSetFillGray(Object args[], int numArgs) {
1337
47.3k
  GfxColor color;
1338
1339
47.3k
  if (state->getIgnoreColorOps()) {
1340
2.98k
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1341
2.98k
    " in uncolored Type 3 char or tiling pattern");
1342
2.98k
    return;
1343
2.98k
  }
1344
44.3k
  state->setFillPattern(NULL);
1345
44.3k
  state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
1346
44.3k
  out->updateFillColorSpace(state);
1347
44.3k
  color.c[0] = dblToCol(args[0].getNum());
1348
44.3k
  state->setFillColor(&color);
1349
44.3k
  out->updateFillColor(state);
1350
44.3k
}
1351
1352
83.7k
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
1353
83.7k
  GfxColor color;
1354
1355
83.7k
  if (state->getIgnoreColorOps()) {
1356
2.12k
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1357
2.12k
    " in uncolored Type 3 char or tiling pattern");
1358
2.12k
    return;
1359
2.12k
  }
1360
81.5k
  state->setStrokePattern(NULL);
1361
81.5k
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
1362
81.5k
  out->updateStrokeColorSpace(state);
1363
81.5k
  color.c[0] = dblToCol(args[0].getNum());
1364
81.5k
  state->setStrokeColor(&color);
1365
81.5k
  out->updateStrokeColor(state);
1366
81.5k
}
1367
1368
9.81k
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
1369
9.81k
  GfxColor color;
1370
9.81k
  int i;
1371
1372
9.81k
  if (state->getIgnoreColorOps()) {
1373
230
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1374
230
    " in uncolored Type 3 char or tiling pattern");
1375
230
    return;
1376
230
  }
1377
9.58k
  state->setFillPattern(NULL);
1378
9.58k
  state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK));
1379
9.58k
  out->updateFillColorSpace(state);
1380
47.9k
  for (i = 0; i < 4; ++i) {
1381
38.3k
    color.c[i] = dblToCol(args[i].getNum());
1382
38.3k
  }
1383
9.58k
  state->setFillColor(&color);
1384
9.58k
  out->updateFillColor(state);
1385
9.58k
}
1386
1387
2.49k
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
1388
2.49k
  GfxColor color;
1389
2.49k
  int i;
1390
1391
2.49k
  if (state->getIgnoreColorOps()) {
1392
259
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1393
259
    " in uncolored Type 3 char or tiling pattern");
1394
259
    return;
1395
259
  }
1396
2.23k
  state->setStrokePattern(NULL);
1397
2.23k
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
1398
2.23k
  out->updateStrokeColorSpace(state);
1399
11.1k
  for (i = 0; i < 4; ++i) {
1400
8.94k
    color.c[i] = dblToCol(args[i].getNum());
1401
8.94k
  }
1402
2.23k
  state->setStrokeColor(&color);
1403
2.23k
  out->updateStrokeColor(state);
1404
2.23k
}
1405
1406
87.6k
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
1407
87.6k
  GfxColor color;
1408
87.6k
  int i;
1409
1410
87.6k
  if (state->getIgnoreColorOps()) {
1411
503
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1412
503
    " in uncolored Type 3 char or tiling pattern");
1413
503
    return;
1414
503
  }
1415
87.1k
  state->setFillPattern(NULL);
1416
87.1k
  state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB));
1417
87.1k
  out->updateFillColorSpace(state);
1418
348k
  for (i = 0; i < 3; ++i) {
1419
261k
    color.c[i] = dblToCol(args[i].getNum());
1420
261k
  }
1421
87.1k
  state->setFillColor(&color);
1422
87.1k
  out->updateFillColor(state);
1423
87.1k
}
1424
1425
32.2k
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
1426
32.2k
  GfxColor color;
1427
32.2k
  int i;
1428
1429
32.2k
  if (state->getIgnoreColorOps()) {
1430
675
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1431
675
    " in uncolored Type 3 char or tiling pattern");
1432
675
    return;
1433
675
  }
1434
31.5k
  state->setStrokePattern(NULL);
1435
31.5k
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
1436
31.5k
  out->updateStrokeColorSpace(state);
1437
126k
  for (i = 0; i < 3; ++i) {
1438
94.5k
    color.c[i] = dblToCol(args[i].getNum());
1439
94.5k
  }
1440
31.5k
  state->setStrokeColor(&color);
1441
31.5k
  out->updateStrokeColor(state);
1442
31.5k
}
1443
1444
61.1k
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
1445
61.1k
  Object obj;
1446
61.1k
  GfxColorSpace *colorSpace;
1447
61.1k
  GfxColor color;
1448
1449
61.1k
  if (state->getIgnoreColorOps()) {
1450
26
    error(errSyntaxWarning, getPos(), "Ignoring color space setting"
1451
26
    " in uncolored Type 3 char or tiling pattern");
1452
26
    return;
1453
26
  }
1454
61.1k
  state->setFillPattern(NULL);
1455
61.1k
  res->lookupColorSpace(args[0].getName(), &obj);
1456
61.1k
  if (obj.isNull()) {
1457
45.3k
    colorSpace = GfxColorSpace::parse(&args[0]
1458
45.3k
              );
1459
45.3k
  } else {
1460
15.8k
    colorSpace = GfxColorSpace::parse(&obj
1461
15.8k
              );
1462
15.8k
  }
1463
61.1k
  obj.free();
1464
61.1k
  if (colorSpace) {
1465
45.7k
    state->setFillColorSpace(colorSpace);
1466
45.7k
    out->updateFillColorSpace(state);
1467
45.7k
    colorSpace->getDefaultColor(&color);
1468
45.7k
    state->setFillColor(&color);
1469
45.7k
    out->updateFillColor(state);
1470
45.7k
  } else {
1471
15.3k
    error(errSyntaxError, getPos(), "Bad color space (fill)");
1472
15.3k
  }
1473
61.1k
}
1474
1475
1.04k
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
1476
1.04k
  Object obj;
1477
1.04k
  GfxColorSpace *colorSpace;
1478
1.04k
  GfxColor color;
1479
1480
1.04k
  if (state->getIgnoreColorOps()) {
1481
89
    error(errSyntaxWarning, getPos(), "Ignoring color space setting"
1482
89
    " in uncolored Type 3 char or tiling pattern");
1483
89
    return;
1484
89
  }
1485
957
  state->setStrokePattern(NULL);
1486
957
  res->lookupColorSpace(args[0].getName(), &obj);
1487
957
  if (obj.isNull()) {
1488
633
    colorSpace = GfxColorSpace::parse(&args[0]
1489
633
              );
1490
633
  } else {
1491
324
    colorSpace = GfxColorSpace::parse(&obj
1492
324
              );
1493
324
  }
1494
957
  obj.free();
1495
957
  if (colorSpace) {
1496
330
    state->setStrokeColorSpace(colorSpace);
1497
330
    out->updateStrokeColorSpace(state);
1498
330
    colorSpace->getDefaultColor(&color);
1499
330
    state->setStrokeColor(&color);
1500
330
    out->updateStrokeColor(state);
1501
627
  } else {
1502
627
    error(errSyntaxError, getPos(), "Bad color space (stroke)");
1503
627
  }
1504
957
}
1505
1506
// Technically, per the PDF spec, the 'sc' operator can only be used
1507
// with device, CIE, and Indexed color spaces.  Some buggy PDF
1508
// generators use it for DeviceN, so we also allow that (same as
1509
// 'scn', minus the optional pattern name argument).
1510
17.6k
void Gfx::opSetFillColor(Object args[], int numArgs) {
1511
17.6k
  GfxColor color;
1512
17.6k
  int i;
1513
1514
17.6k
  if (state->getIgnoreColorOps()) {
1515
75
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1516
75
    " in uncolored Type 3 char or tiling pattern");
1517
75
    return;
1518
75
  }
1519
17.5k
  if (numArgs != state->getFillColorSpace()->getNComps()) {
1520
12.2k
    error(errSyntaxError, getPos(),
1521
12.2k
    "Incorrect number of arguments in 'sc' command");
1522
12.2k
    return;
1523
12.2k
  }
1524
5.24k
  state->setFillPattern(NULL);
1525
8.91k
  for (i = 0; i < numArgs; ++i) {
1526
3.67k
    color.c[i] = dblToCol(args[i].getNum());
1527
3.67k
  }
1528
5.24k
  state->setFillColor(&color);
1529
5.24k
  out->updateFillColor(state);
1530
5.24k
}
1531
1532
// Technically, per the PDF spec, the 'SC' operator can only be used
1533
// with device, CIE, and Indexed color spaces.  Some buggy PDF
1534
// generators use it for DeviceN, so we also allow that (same as
1535
// 'SCN', minus the optional pattern name argument).
1536
2.55k
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
1537
2.55k
  GfxColor color;
1538
2.55k
  int i;
1539
1540
2.55k
  if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1541
1.79k
    error(errSyntaxError, getPos(),
1542
1.79k
    "Incorrect number of arguments in 'SC' command");
1543
1.79k
    return;
1544
1.79k
  }
1545
764
  state->setStrokePattern(NULL);
1546
2.36k
  for (i = 0; i < numArgs; ++i) {
1547
1.59k
    color.c[i] = dblToCol(args[i].getNum());
1548
1.59k
  }
1549
764
  state->setStrokeColor(&color);
1550
764
  out->updateStrokeColor(state);
1551
764
}
1552
1553
66.6k
void Gfx::opSetFillColorN(Object args[], int numArgs) {
1554
66.6k
  GfxColor color;
1555
66.6k
  GfxPattern *pattern;
1556
66.6k
  int i;
1557
1558
66.6k
  if (state->getIgnoreColorOps()) {
1559
147
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1560
147
    " in uncolored Type 3 char or tiling pattern");
1561
147
    return;
1562
147
  }
1563
66.4k
  if (state->getFillColorSpace()->getMode() == csPattern) {
1564
61.2k
    if (numArgs == 0 || !args[numArgs-1].isName()) {
1565
11.6k
      error(errSyntaxError, getPos(),
1566
11.6k
      "Invalid arguments in 'scn' command");
1567
11.6k
      return;
1568
11.6k
    }
1569
49.5k
    if (numArgs > 1) {
1570
978
      if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1571
235
    numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1572
856
                       ->getUnder()->getNComps()) {
1573
856
  error(errSyntaxError, getPos(),
1574
856
        "Incorrect number of arguments in 'scn' command");
1575
856
  return;
1576
856
      }
1577
488
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1578
366
  if (args[i].isNum()) {
1579
287
    color.c[i] = dblToCol(args[i].getNum());
1580
287
  }
1581
366
      }
1582
122
      state->setFillColor(&color);
1583
122
      out->updateFillColor(state);
1584
122
    }
1585
48.6k
    if ((pattern = res->lookupPattern(args[numArgs-1].getName()
1586
48.6k
              ))) {
1587
20.4k
      state->setFillPattern(pattern);
1588
20.4k
    }
1589
1590
48.6k
  } else {
1591
5.25k
    if (numArgs != state->getFillColorSpace()->getNComps()) {
1592
2.40k
      error(errSyntaxError, getPos(),
1593
2.40k
      "Incorrect number of arguments in 'scn' command");
1594
2.40k
      return;
1595
2.40k
    }
1596
2.85k
    state->setFillPattern(NULL);
1597
6.31k
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1598
3.46k
      if (args[i].isNum()) {
1599
2.27k
  color.c[i] = dblToCol(args[i].getNum());
1600
2.27k
      }
1601
3.46k
    }
1602
2.85k
    state->setFillColor(&color);
1603
2.85k
    out->updateFillColor(state);
1604
2.85k
  }
1605
66.4k
}
1606
1607
992
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
1608
992
  GfxColor color;
1609
992
  GfxPattern *pattern;
1610
992
  int i;
1611
1612
992
  if (state->getIgnoreColorOps()) {
1613
87
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1614
87
    " in uncolored Type 3 char or tiling pattern");
1615
87
    return;
1616
87
  }
1617
905
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1618
310
    if (numArgs == 0 || !args[numArgs-1].isName()) {
1619
41
      error(errSyntaxError, getPos(),
1620
41
      "Invalid arguments in 'SCN' command");
1621
41
      return;
1622
41
    }
1623
269
    if (numArgs > 1) {
1624
56
      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1625
56
         ->getUnder() ||
1626
0
    numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1627
56
                       ->getUnder()->getNComps()) {
1628
56
  error(errSyntaxError, getPos(),
1629
56
        "Incorrect number of arguments in 'SCN' command");
1630
56
  return;
1631
56
      }
1632
0
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1633
0
  if (args[i].isNum()) {
1634
0
    color.c[i] = dblToCol(args[i].getNum());
1635
0
  }
1636
0
      }
1637
0
      state->setStrokeColor(&color);
1638
0
      out->updateStrokeColor(state);
1639
0
    }
1640
213
    if ((pattern = res->lookupPattern(args[numArgs-1].getName()
1641
213
              ))) {
1642
152
      state->setStrokePattern(pattern);
1643
152
    }
1644
1645
595
  } else {
1646
595
    if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1647
286
      error(errSyntaxError, getPos(),
1648
286
      "Incorrect number of arguments in 'SCN' command");
1649
286
      return;
1650
286
    }
1651
309
    state->setStrokePattern(NULL);
1652
658
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1653
349
      if (args[i].isNum()) {
1654
330
  color.c[i] = dblToCol(args[i].getNum());
1655
330
      }
1656
349
    }
1657
309
    state->setStrokeColor(&color);
1658
309
    out->updateStrokeColor(state);
1659
309
  }
1660
905
}
1661
1662
//------------------------------------------------------------------------
1663
// path segment operators
1664
//------------------------------------------------------------------------
1665
1666
214k
void Gfx::opMoveTo(Object args[], int numArgs) {
1667
214k
  state->moveTo(args[0].getNum(), args[1].getNum());
1668
214k
}
1669
1670
224k
void Gfx::opLineTo(Object args[], int numArgs) {
1671
224k
  if (!state->isCurPt()) {
1672
100k
    error(errSyntaxError, getPos(), "No current point in lineto");
1673
100k
    return;
1674
100k
  }
1675
124k
  state->lineTo(args[0].getNum(), args[1].getNum());
1676
124k
}
1677
1678
114k
void Gfx::opCurveTo(Object args[], int numArgs) {
1679
114k
  double x1, y1, x2, y2, x3, y3;
1680
1681
114k
  if (!state->isCurPt()) {
1682
21.5k
    error(errSyntaxError, getPos(), "No current point in curveto");
1683
21.5k
    return;
1684
21.5k
  }
1685
92.8k
  x1 = args[0].getNum();
1686
92.8k
  y1 = args[1].getNum();
1687
92.8k
  x2 = args[2].getNum();
1688
92.8k
  y2 = args[3].getNum();
1689
92.8k
  x3 = args[4].getNum();
1690
92.8k
  y3 = args[5].getNum();
1691
92.8k
  state->curveTo(x1, y1, x2, y2, x3, y3);
1692
92.8k
}
1693
1694
15.6k
void Gfx::opCurveTo1(Object args[], int numArgs) {
1695
15.6k
  double x1, y1, x2, y2, x3, y3;
1696
1697
15.6k
  if (!state->isCurPt()) {
1698
12.9k
    error(errSyntaxError, getPos(), "No current point in curveto1");
1699
12.9k
    return;
1700
12.9k
  }
1701
2.72k
  x1 = state->getCurX();
1702
2.72k
  y1 = state->getCurY();
1703
2.72k
  x2 = args[0].getNum();
1704
2.72k
  y2 = args[1].getNum();
1705
2.72k
  x3 = args[2].getNum();
1706
2.72k
  y3 = args[3].getNum();
1707
2.72k
  state->curveTo(x1, y1, x2, y2, x3, y3);
1708
2.72k
}
1709
1710
17.4k
void Gfx::opCurveTo2(Object args[], int numArgs) {
1711
17.4k
  double x1, y1, x2, y2, x3, y3;
1712
1713
17.4k
  if (!state->isCurPt()) {
1714
11.6k
    error(errSyntaxError, getPos(), "No current point in curveto2");
1715
11.6k
    return;
1716
11.6k
  }
1717
5.81k
  x1 = args[0].getNum();
1718
5.81k
  y1 = args[1].getNum();
1719
5.81k
  x2 = args[2].getNum();
1720
5.81k
  y2 = args[3].getNum();
1721
5.81k
  x3 = x2;
1722
5.81k
  y3 = y2;
1723
5.81k
  state->curveTo(x1, y1, x2, y2, x3, y3);
1724
5.81k
}
1725
1726
131k
void Gfx::opRectangle(Object args[], int numArgs) {
1727
131k
  double x, y, w, h;
1728
1729
131k
  x = args[0].getNum();
1730
131k
  y = args[1].getNum();
1731
131k
  w = args[2].getNum();
1732
131k
  h = args[3].getNum();
1733
131k
  state->moveTo(x, y);
1734
131k
  state->lineTo(x + w, y);
1735
131k
  state->lineTo(x + w, y + h);
1736
131k
  state->lineTo(x, y + h);
1737
131k
  state->closePath();
1738
131k
}
1739
1740
68.5k
void Gfx::opClosePath(Object args[], int numArgs) {
1741
68.5k
  if (!state->isCurPt()) {
1742
32.3k
    error(errSyntaxError, getPos(), "No current point in closepath");
1743
32.3k
    return;
1744
32.3k
  }
1745
36.2k
  state->closePath();
1746
36.2k
}
1747
1748
//------------------------------------------------------------------------
1749
// path painting operators
1750
//------------------------------------------------------------------------
1751
1752
255k
void Gfx::opEndPath(Object args[], int numArgs) {
1753
255k
  doEndPath();
1754
255k
}
1755
1756
92.3k
void Gfx::opStroke(Object args[], int numArgs) {
1757
92.3k
  if (!state->isCurPt()) {
1758
    //error(errSyntaxError, getPos(), "No path in stroke");
1759
72.6k
    return;
1760
72.6k
  }
1761
19.6k
  if (state->isPath()) {
1762
14.6k
    if (ocState) {
1763
14.5k
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
1764
195
  doPatternStroke();
1765
14.3k
      } else {
1766
14.3k
  out->stroke(state);
1767
14.3k
      }
1768
14.5k
    }
1769
14.6k
  }
1770
19.6k
  doEndPath();
1771
19.6k
}
1772
1773
44.3k
void Gfx::opCloseStroke(Object args[], int numArgs) {
1774
44.3k
  if (!state->isCurPt()) {
1775
    //error(errSyntaxError, getPos(), "No path in closepath/stroke");
1776
32.9k
    return;
1777
32.9k
  }
1778
11.4k
  if (state->isPath()) {
1779
9.12k
    state->closePath();
1780
9.12k
    if (ocState) {
1781
9.05k
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
1782
3
  doPatternStroke();
1783
9.05k
      } else {
1784
9.05k
  out->stroke(state);
1785
9.05k
      }
1786
9.05k
    }
1787
9.12k
  }
1788
11.4k
  doEndPath();
1789
11.4k
}
1790
1791
186k
void Gfx::opFill(Object args[], int numArgs) {
1792
186k
  if (!state->isCurPt()) {
1793
    //error(errSyntaxError, getPos(), "No path in fill");
1794
87.7k
    return;
1795
87.7k
  }
1796
98.3k
  if (state->isPath()) {
1797
83.8k
    if (ocState) {
1798
81.9k
      if (state->getFillColorSpace()->getMode() == csPattern) {
1799
7.89k
  doPatternFill(gFalse);
1800
74.0k
      } else {
1801
74.0k
  out->fill(state);
1802
74.0k
      }
1803
81.9k
    }
1804
83.8k
  }
1805
98.3k
  doEndPath();
1806
98.3k
}
1807
1808
5.76k
void Gfx::opEOFill(Object args[], int numArgs) {
1809
5.76k
  if (!state->isCurPt()) {
1810
    //error(errSyntaxError, getPos(), "No path in eofill");
1811
1.83k
    return;
1812
1.83k
  }
1813
3.93k
  if (state->isPath()) {
1814
3.72k
    if (ocState) {
1815
3.72k
      if (state->getFillColorSpace()->getMode() == csPattern) {
1816
3.54k
  doPatternFill(gTrue);
1817
3.54k
      } else {
1818
177
  out->eoFill(state);
1819
177
      }
1820
3.72k
    }
1821
3.72k
  }
1822
3.93k
  doEndPath();
1823
3.93k
}
1824
1825
251k
void Gfx::opFillStroke(Object args[], int numArgs) {
1826
251k
  if (!state->isCurPt()) {
1827
    //error(errSyntaxError, getPos(), "No path in fill/stroke");
1828
205k
    return;
1829
205k
  }
1830
45.7k
  if (state->isPath()) {
1831
35.5k
    if (ocState) {
1832
35.4k
      if (state->getFillColorSpace()->getMode() == csPattern ||
1833
23.0k
    state->getStrokeColorSpace()->getMode() == csPattern) {
1834
23.0k
  if (state->getFillColorSpace()->getMode() == csPattern) {
1835
23.0k
    doPatternFill(gFalse);
1836
23.0k
  } else {
1837
5
    out->fill(state);
1838
5
  }
1839
23.0k
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1840
5
    doPatternStroke();
1841
23.0k
  } else {
1842
23.0k
    out->stroke(state);
1843
23.0k
  }
1844
23.0k
      } else {
1845
12.4k
  out->fillStroke(state, gFalse);
1846
12.4k
      }
1847
35.4k
    }
1848
35.5k
  }
1849
45.7k
  doEndPath();
1850
45.7k
}
1851
1852
93.0k
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1853
93.0k
  if (!state->isCurPt()) {
1854
    //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke");
1855
58.6k
    return;
1856
58.6k
  }
1857
34.4k
  if (state->isPath()) {
1858
26.7k
    state->closePath();
1859
26.7k
    if (ocState) {
1860
26.7k
      if (state->getFillColorSpace()->getMode() == csPattern ||
1861
24.2k
    state->getStrokeColorSpace()->getMode() == csPattern) {
1862
24.2k
  if (state->getFillColorSpace()->getMode() == csPattern) {
1863
24.2k
    doPatternFill(gFalse);
1864
24.2k
  } else {
1865
4
    out->fill(state);
1866
4
  }
1867
24.2k
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1868
4
    doPatternStroke();
1869
24.2k
  } else {
1870
24.2k
    out->stroke(state);
1871
24.2k
  }
1872
24.2k
      } else {
1873
2.45k
  out->fillStroke(state, gFalse);
1874
2.45k
      }
1875
26.7k
    }
1876
26.7k
  }
1877
34.4k
  doEndPath();
1878
34.4k
}
1879
1880
1.01k
void Gfx::opEOFillStroke(Object args[], int numArgs) {
1881
1.01k
  if (!state->isCurPt()) {
1882
    //error(errSyntaxError, getPos(), "No path in eofill/stroke");
1883
132
    return;
1884
132
  }
1885
885
  if (state->isPath()) {
1886
115
    if (ocState) {
1887
114
      if (state->getFillColorSpace()->getMode() == csPattern ||
1888
87
    state->getStrokeColorSpace()->getMode() == csPattern) {
1889
27
  if (state->getFillColorSpace()->getMode() == csPattern) {
1890
27
    doPatternFill(gTrue);
1891
27
  } else {
1892
0
    out->eoFill(state);
1893
0
  }
1894
27
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1895
0
    doPatternStroke();
1896
27
  } else {
1897
27
    out->stroke(state);
1898
27
  }
1899
87
      } else {
1900
87
  out->fillStroke(state, gTrue);
1901
87
      }
1902
114
    }
1903
115
  }
1904
885
  doEndPath();
1905
885
}
1906
1907
170
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1908
170
  if (!state->isCurPt()) {
1909
    //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke");
1910
127
    return;
1911
127
  }
1912
43
  if (state->isPath()) {
1913
31
    state->closePath();
1914
31
    if (ocState) {
1915
30
      if (state->getFillColorSpace()->getMode() == csPattern ||
1916
18
    state->getStrokeColorSpace()->getMode() == csPattern) {
1917
18
  if (state->getFillColorSpace()->getMode() == csPattern) {
1918
18
    doPatternFill(gTrue);
1919
18
  } else {
1920
0
    out->eoFill(state);
1921
0
  }
1922
18
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1923
0
    doPatternStroke();
1924
18
  } else {
1925
18
    out->stroke(state);
1926
18
  }
1927
18
      } else {
1928
12
  out->fillStroke(state, gTrue);
1929
12
      }
1930
30
    }
1931
31
  }
1932
43
  doEndPath();
1933
43
}
1934
1935
58.7k
void Gfx::doPatternFill(GBool eoFill) {
1936
58.7k
  GfxPattern *pattern;
1937
1938
  // this is a bit of a kludge -- patterns can be really slow, so we
1939
  // skip them if we're only doing text extraction, since they almost
1940
  // certainly don't contain any text
1941
58.7k
  if (!out->needNonText()) {
1942
0
    return;
1943
0
  }
1944
1945
58.7k
  if (!(pattern = state->getFillPattern())) {
1946
28.9k
    return;
1947
28.9k
  }
1948
29.8k
  switch (pattern->getType()) {
1949
29.4k
  case 1:
1950
29.4k
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse);
1951
29.4k
    break;
1952
378
  case 2:
1953
378
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse);
1954
378
    break;
1955
0
  default:
1956
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
1957
0
    pattern->getType());
1958
0
    break;
1959
29.8k
  }
1960
29.8k
}
1961
1962
207
void Gfx::doPatternStroke() {
1963
207
  GfxPattern *pattern;
1964
1965
  // this is a bit of a kludge -- patterns can be really slow, so we
1966
  // skip them if we're only doing text extraction, since they almost
1967
  // certainly don't contain any text
1968
207
  if (!out->needNonText()) {
1969
0
    return;
1970
0
  }
1971
1972
207
  if (!(pattern = state->getStrokePattern())) {
1973
54
    return;
1974
54
  }
1975
153
  switch (pattern->getType()) {
1976
137
  case 1:
1977
137
    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse);
1978
137
    break;
1979
16
  case 2:
1980
16
    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse);
1981
16
    break;
1982
0
  default:
1983
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke",
1984
0
    pattern->getType());
1985
0
    break;
1986
153
  }
1987
153
}
1988
1989
37.3k
void Gfx::doPatternText(GBool stroke) {
1990
37.3k
  GfxPattern *pattern;
1991
1992
  // this is a bit of a kludge -- patterns can be really slow, so we
1993
  // skip them if we're only doing text extraction, since they almost
1994
  // certainly don't contain any text
1995
37.3k
  if (!out->needNonText()) {
1996
0
    return;
1997
0
  }
1998
1999
37.3k
  pattern = stroke ? state->getStrokePattern() : state->getFillPattern();
2000
37.3k
  if (!pattern) {
2001
33.8k
    return;
2002
33.8k
  }
2003
3.51k
  switch (pattern->getType()) {
2004
2.94k
  case 1:
2005
2.94k
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue);
2006
2.94k
    break;
2007
570
  case 2:
2008
570
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue);
2009
570
    break;
2010
0
  default:
2011
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
2012
0
    pattern->getType());
2013
0
    break;
2014
3.51k
  }
2015
3.51k
}
2016
2017
void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
2018
11
           GBool invert, GBool inlineImg, GBool interpolate) {
2019
11
  saveState();
2020
2021
11
  out->setSoftMaskFromImageMask(state, ref, str,
2022
11
        width, height, invert, inlineImg, interpolate);
2023
2024
11
  state->clearPath();
2025
11
  state->moveTo(0, 0);
2026
11
  state->lineTo(1, 0);
2027
11
  state->lineTo(1, 1);
2028
11
  state->lineTo(0, 1);
2029
11
  state->closePath();
2030
11
  doPatternFill(gTrue);
2031
11
  state->clearPath();
2032
2033
11
  restoreState();
2034
11
}
2035
2036
void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
2037
32.5k
            GBool stroke, GBool eoFill, GBool text) {
2038
32.5k
  GfxPatternColorSpace *patCS;
2039
32.5k
  GfxColorSpace *cs;
2040
32.5k
  GfxColor color;
2041
32.5k
  GfxState *savedState;
2042
32.5k
  double xMin, yMin, xMax, yMax, x, y, x1, y1, t;
2043
32.5k
  double cxMin, cyMin, cxMax, cyMax;
2044
32.5k
  int xi0, yi0, xi1, yi1, xi, yi;
2045
32.5k
  double *ctm, *btm, *ptm;
2046
32.5k
  double bbox[4], m[6], ictm[6], m1[6], imb[6];
2047
32.5k
  double det;
2048
32.5k
  double xstep, ystep;
2049
32.5k
  int abortCheckCounter, i;
2050
2051
  // get color space
2052
32.5k
  patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
2053
32.5k
                  : state->getFillColorSpace());
2054
2055
  // construct a (pattern space) -> (current space) transform matrix
2056
32.5k
  ctm = state->getCTM();
2057
32.5k
  btm = baseMatrix;
2058
32.5k
  ptm = tPat->getMatrix();
2059
  // iCTM = invert CTM
2060
32.5k
  det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2061
32.5k
  if (fabs(det) <= 1e-10) {
2062
389
    error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2063
389
    return;
2064
389
  }
2065
32.1k
  det = 1 / det;
2066
32.1k
  ictm[0] = ctm[3] * det;
2067
32.1k
  ictm[1] = -ctm[1] * det;
2068
32.1k
  ictm[2] = -ctm[2] * det;
2069
32.1k
  ictm[3] = ctm[0] * det;
2070
32.1k
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2071
32.1k
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2072
  // m1 = PTM * BTM = PTM * base transform matrix
2073
32.1k
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2074
32.1k
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2075
32.1k
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2076
32.1k
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2077
32.1k
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2078
32.1k
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2079
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2080
32.1k
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2081
32.1k
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2082
32.1k
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2083
32.1k
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2084
32.1k
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2085
32.1k
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2086
2087
  // construct a (device space) -> (pattern space) transform matrix
2088
32.1k
  det = m1[0] * m1[3] - m1[1] * m1[2];
2089
32.1k
  if (fabs(det) <= 1e-10) {
2090
821
    error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2091
821
    return;
2092
821
  }
2093
31.3k
  det = 1 / det;
2094
31.3k
  imb[0] = m1[3] * det;
2095
31.3k
  imb[1] = -m1[1] * det;
2096
31.3k
  imb[2] = -m1[2] * det;
2097
31.3k
  imb[3] = m1[0] * det;
2098
31.3k
  imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
2099
31.3k
  imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
2100
2101
  // save current graphics state
2102
31.3k
  savedState = saveStateStack();
2103
2104
  // set underlying color space (for uncolored tiling patterns); set
2105
  // various other parameters (stroke color, line width) to match
2106
  // Adobe's behavior
2107
31.3k
  state->setFillPattern(NULL);
2108
31.3k
  state->setStrokePattern(NULL);
2109
31.3k
  state->setFillOverprint(gFalse);
2110
31.3k
  state->setStrokeOverprint(gFalse);
2111
31.3k
  state->setOverprintMode(0);
2112
31.3k
  if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
2113
16
    state->setFillColorSpace(cs->copy());
2114
16
    out->updateFillColorSpace(state);
2115
16
    state->setStrokeColorSpace(cs->copy());
2116
16
    out->updateStrokeColorSpace(state);
2117
16
    if (stroke) {
2118
0
      state->setFillColor(state->getStrokeColor());
2119
16
    } else {
2120
16
      state->setStrokeColor(state->getFillColor());
2121
16
    }
2122
16
    out->updateFillColor(state);
2123
16
    out->updateStrokeColor(state);
2124
16
    state->setIgnoreColorOps(gTrue);
2125
31.3k
  } else {
2126
31.3k
    state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
2127
31.3k
    out->updateFillColorSpace(state);
2128
31.3k
    state->getFillColorSpace()->getDefaultColor(&color);
2129
31.3k
    state->setFillColor(&color);
2130
31.3k
    out->updateFillColor(state);
2131
31.3k
    state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
2132
31.3k
    out->updateStrokeColorSpace(state);
2133
31.3k
    state->getStrokeColorSpace()->getDefaultColor(&color);
2134
31.3k
    state->setStrokeColor(&color);
2135
31.3k
    out->updateStrokeColor(state);
2136
31.3k
  }
2137
31.3k
  if (!stroke) {
2138
31.2k
    state->setLineWidth(0);
2139
31.2k
    out->updateLineWidth(state);
2140
31.2k
    state->setLineDash(NULL, 0, 0);
2141
31.2k
    out->updateLineDash(state);
2142
31.2k
  }
2143
2144
  // clip to current path
2145
31.3k
  if (stroke) {
2146
133
    state->clipToStrokePath();
2147
133
    out->clipToStrokePath(state);
2148
31.2k
  } else if (!text) {
2149
29.4k
    state->clip();
2150
29.4k
    if (eoFill) {
2151
83
      out->eoClip(state);
2152
29.3k
    } else {
2153
29.3k
      out->clip(state);
2154
29.3k
    }
2155
29.4k
  }
2156
31.3k
  state->clearPath();
2157
2158
  // get the clip region, check for empty
2159
31.3k
  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
2160
31.3k
  if (cxMin > cxMax || cyMin > cyMax) {
2161
28.9k
    goto err;
2162
28.9k
  }
2163
2164
  // transform clip region bbox to pattern space
2165
2.37k
  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
2166
2.37k
  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
2167
2.37k
  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
2168
2.37k
  y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
2169
2.37k
  if (x1 < xMin) {
2170
0
    xMin = x1;
2171
2.37k
  } else if (x1 > xMax) {
2172
0
    xMax = x1;
2173
0
  }
2174
2.37k
  if (y1 < yMin) {
2175
338
    yMin = y1;
2176
2.04k
  } else if (y1 > yMax) {
2177
0
    yMax = y1;
2178
0
  }
2179
2.37k
  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
2180
2.37k
  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
2181
2.37k
  if (x1 < xMin) {
2182
0
    xMin = x1;
2183
2.37k
  } else if (x1 > xMax) {
2184
341
    xMax = x1;
2185
341
  }
2186
2.37k
  if (y1 < yMin) {
2187
0
    yMin = y1;
2188
2.37k
  } else if (y1 > yMax) {
2189
0
    yMax = y1;
2190
0
  }
2191
2.37k
  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
2192
2.37k
  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
2193
2.37k
  if (x1 < xMin) {
2194
0
    xMin = x1;
2195
2.37k
  } else if (x1 > xMax) {
2196
0
    xMax = x1;
2197
0
  }
2198
2.37k
  if (y1 < yMin) {
2199
0
    yMin = y1;
2200
2.37k
  } else if (y1 > yMax) {
2201
0
    yMax = y1;
2202
0
  }
2203
2204
  // draw the pattern
2205
  //~ this should treat negative steps differently -- start at right/top
2206
  //~ edge instead of left/bottom (?)
2207
2.37k
  bbox[0] = tPat->getBBox()[0];
2208
2.37k
  bbox[1] = tPat->getBBox()[1];
2209
2.37k
  bbox[2] = tPat->getBBox()[2];
2210
2.37k
  bbox[3] = tPat->getBBox()[3];
2211
2.37k
  if (bbox[0] > bbox[2]) {
2212
3
    t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t;
2213
3
  }
2214
2.37k
  if (bbox[1] > bbox[3]) {
2215
23
    t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t;
2216
23
  }
2217
2.37k
  xstep = tPat->getXStep();
2218
2.37k
  ystep = tPat->getYStep();
2219
2.37k
  if (xstep == 0 || ystep == 0) {
2220
70
    error(errSyntaxError, getPos(), "Zero x or y step in tiling pattern fill");
2221
70
    goto err;
2222
70
  }
2223
2.30k
  if (xstep > 0) {
2224
2.29k
    xi0 = (int)ceil((xMin - bbox[2]) / xstep);
2225
2.29k
    xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1;
2226
2.29k
  } else {
2227
12
    xi0 = (int)ceil((xMax - bbox[0]) / xstep);
2228
12
    xi1 = (int)floor((xMin - bbox[2]) / xstep) + 1;
2229
12
  }
2230
2.30k
  if (ystep > 0) {
2231
1.74k
    yi0 = (int)ceil((yMin - bbox[3]) / ystep);
2232
1.74k
    yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1;
2233
1.74k
  } else {
2234
560
    yi0 = (int)ceil((yMax - bbox[1]) / ystep);
2235
560
    yi1 = (int)floor((yMin - bbox[3]) / ystep) + 1;
2236
560
  }
2237
11.5k
  for (i = 0; i < 4; ++i) {
2238
9.23k
    m1[i] = m[i];
2239
9.23k
  }
2240
2.30k
  if (out->useTilingPatternFill() && !tPat->usesBlendMode(xref)) {
2241
2.30k
    m1[4] = m[4];
2242
2.30k
    m1[5] = m[5];
2243
2.30k
    out->tilingPatternFill(state, this, tPat->getContentStreamRef(),
2244
2.30k
         tPat->getPaintType(), tPat->getTilingType(),
2245
2.30k
         tPat->getResDict(),
2246
2.30k
         m1, bbox,
2247
2.30k
         xi0, yi0, xi1, yi1, xstep, ystep);
2248
2.30k
  } else {
2249
0
    abortCheckCounter = 0;
2250
0
    for (yi = yi0; yi < yi1; ++yi) {
2251
0
      for (xi = xi0; xi < xi1; ++xi) {
2252
0
  if (abortCheckCbk) {
2253
0
    ++abortCheckCounter;
2254
0
    if (abortCheckCounter > 100) {
2255
0
      if ((*abortCheckCbk)(abortCheckCbkData)) {
2256
0
        goto err;
2257
0
      }
2258
0
      abortCheckCounter = 0;
2259
0
    }
2260
0
  }
2261
0
  x = xi * xstep;
2262
0
  y = yi * ystep;
2263
0
  m1[4] = x * m[0] + y * m[2] + m[4];
2264
0
  m1[5] = x * m[1] + y * m[3] + m[5];
2265
0
  drawForm(tPat->getContentStreamRef(), tPat->getResDict(),
2266
0
     m1, bbox);
2267
0
      }
2268
0
    }
2269
0
  }
2270
2271
  // restore graphics state
2272
31.3k
 err:
2273
31.3k
  restoreStateStack(savedState);
2274
31.3k
}
2275
2276
void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
2277
964
             GBool stroke, GBool eoFill, GBool text) {
2278
964
  GfxShading *shading;
2279
964
  GfxState *savedState;
2280
964
  double *ctm, *btm, *ptm;
2281
964
  double m[6], ictm[6], m1[6];
2282
964
  double xMin, yMin, xMax, yMax;
2283
964
  double det;
2284
2285
964
  shading = sPat->getShading();
2286
2287
  // save current graphics state
2288
964
  savedState = saveStateStack();
2289
2290
  // clip to current path
2291
964
  if (stroke) {
2292
16
    state->clipToStrokePath();
2293
16
    out->clipToStrokePath(state);
2294
16
    state->setFillOverprint(state->getStrokeOverprint());
2295
948
  } else if (!text) {
2296
378
    state->clip();
2297
378
    if (eoFill) {
2298
0
      out->eoClip(state);
2299
378
    } else {
2300
378
      out->clip(state);
2301
378
    }
2302
378
  }
2303
964
  state->clearPath();
2304
2305
  // construct a (pattern space) -> (current space) transform matrix
2306
964
  ctm = state->getCTM();
2307
964
  btm = baseMatrix;
2308
964
  ptm = sPat->getMatrix();
2309
  // iCTM = invert CTM
2310
964
  det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2311
964
  if (fabs(det) <= 1e-10) {
2312
93
    error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
2313
93
    restoreStateStack(savedState);
2314
93
    return;
2315
93
  }
2316
871
  det = 1 / det;
2317
871
  ictm[0] = ctm[3] * det;
2318
871
  ictm[1] = -ctm[1] * det;
2319
871
  ictm[2] = -ctm[2] * det;
2320
871
  ictm[3] = ctm[0] * det;
2321
871
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2322
871
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2323
  // m1 = PTM * BTM = PTM * base transform matrix
2324
871
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2325
871
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2326
871
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2327
871
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2328
871
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2329
871
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2330
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2331
871
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2332
871
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2333
871
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2334
871
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2335
871
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2336
871
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2337
2338
  // set the new matrix
2339
871
  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
2340
871
  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
2341
2342
  // clip to bbox
2343
871
  if (shading->getHasBBox()) {
2344
1
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2345
1
    state->moveTo(xMin, yMin);
2346
1
    state->lineTo(xMax, yMin);
2347
1
    state->lineTo(xMax, yMax);
2348
1
    state->lineTo(xMin, yMax);
2349
1
    state->closePath();
2350
1
    state->clip();
2351
1
    out->clip(state);
2352
1
    state->clearPath();
2353
1
  }
2354
2355
  // set the color space
2356
871
  state->setFillColorSpace(shading->getColorSpace()->copy());
2357
871
  out->updateFillColorSpace(state);
2358
2359
  // background color fill
2360
871
  if (shading->getHasBackground()) {
2361
0
    state->setFillColor(shading->getBackground());
2362
0
    out->updateFillColor(state);
2363
0
    state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2364
0
    state->moveTo(xMin, yMin);
2365
0
    state->lineTo(xMax, yMin);
2366
0
    state->lineTo(xMax, yMax);
2367
0
    state->lineTo(xMin, yMax);
2368
0
    state->closePath();
2369
0
    out->fill(state);
2370
0
    state->clearPath();
2371
0
  }
2372
2373
  // perform the fill
2374
871
  doShFill(shading);
2375
2376
  // restore graphics state
2377
871
  restoreStateStack(savedState);
2378
871
}
2379
2380
19.8k
void Gfx::opShFill(Object args[], int numArgs) {
2381
19.8k
  GfxShading *shading;
2382
19.8k
  GfxState *savedState;
2383
19.8k
  double xMin, yMin, xMax, yMax;
2384
2385
19.8k
  if (state->getIgnoreColorOps()) {
2386
430
    error(errSyntaxWarning, getPos(), "Ignoring shaded fill"
2387
430
    " in uncolored Type 3 char or tiling pattern");
2388
430
    return;
2389
430
  }
2390
2391
19.4k
  if (!out->needNonText()) {
2392
0
    return;
2393
0
  }
2394
2395
19.4k
  if (!ocState) {
2396
0
    return;
2397
0
  }
2398
2399
19.4k
  if (!(shading = res->lookupShading(args[0].getName()
2400
19.4k
             ))) {
2401
16.9k
    return;
2402
16.9k
  }
2403
2404
  // save current graphics state
2405
2.46k
  savedState = saveStateStack();
2406
2407
  // clip to bbox
2408
2.46k
  if (shading->getHasBBox()) {
2409
6
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2410
6
    state->moveTo(xMin, yMin);
2411
6
    state->lineTo(xMax, yMin);
2412
6
    state->lineTo(xMax, yMax);
2413
6
    state->lineTo(xMin, yMax);
2414
6
    state->closePath();
2415
6
    state->clip();
2416
6
    out->clip(state);
2417
6
    state->clearPath();
2418
6
  }
2419
2420
  // set the color space
2421
2.46k
  state->setFillColorSpace(shading->getColorSpace()->copy());
2422
2.46k
  out->updateFillColorSpace(state);
2423
2424
  // perform the fill
2425
2.46k
  doShFill(shading);
2426
2427
  // restore graphics state
2428
2.46k
  restoreStateStack(savedState);
2429
2430
2.46k
  delete shading;
2431
2.46k
}
2432
2433
3.33k
void Gfx::doShFill(GfxShading *shading) {
2434
3.33k
  if (out->shadedFill(state, shading)) {
2435
3.29k
    return;
2436
3.29k
  }
2437
2438
  // do shading type-specific operations
2439
42
  switch (shading->getType()) {
2440
0
  case 1:
2441
0
    doFunctionShFill((GfxFunctionShading *)shading);
2442
0
    break;
2443
0
  case 2:
2444
0
    doAxialShFill((GfxAxialShading *)shading);
2445
0
    break;
2446
0
  case 3:
2447
0
    doRadialShFill((GfxRadialShading *)shading);
2448
0
    break;
2449
0
  case 4:
2450
0
  case 5:
2451
0
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
2452
0
    break;
2453
0
  case 6:
2454
0
  case 7:
2455
0
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
2456
0
    break;
2457
42
  }
2458
42
}
2459
2460
0
void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
2461
0
  double x0, y0, x1, y1;
2462
0
  GfxColor colors[4];
2463
2464
0
  shading->getDomain(&x0, &y0, &x1, &y1);
2465
0
  shading->getColor(x0, y0, &colors[0]);
2466
0
  shading->getColor(x0, y1, &colors[1]);
2467
0
  shading->getColor(x1, y0, &colors[2]);
2468
0
  shading->getColor(x1, y1, &colors[3]);
2469
0
  doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
2470
0
}
2471
2472
void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
2473
          double x0, double y0,
2474
          double x1, double y1,
2475
0
          GfxColor *colors, int depth) {
2476
0
  GfxColor fillColor;
2477
0
  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
2478
0
  GfxColor colors2[4];
2479
0
  double *matrix;
2480
0
  double xM, yM;
2481
0
  int nComps, i, j;
2482
2483
0
  nComps = shading->getColorSpace()->getNComps();
2484
0
  matrix = shading->getMatrix();
2485
2486
  // compare the four corner colors
2487
0
  for (i = 0; i < 4; ++i) {
2488
0
    for (j = 0; j < nComps; ++j) {
2489
0
      if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
2490
0
  break;
2491
0
      }
2492
0
    }
2493
0
    if (j < nComps) {
2494
0
      break;
2495
0
    }
2496
0
  }
2497
2498
  // center of the rectangle
2499
0
  xM = 0.5 * (x0 + x1);
2500
0
  yM = 0.5 * (y0 + y1);
2501
2502
  // the four corner colors are close (or we hit the recursive limit)
2503
  // -- fill the rectangle; but require at least one subdivision
2504
  // (depth==0) to avoid problems when the four outer corners of the
2505
  // shaded region are the same color
2506
0
  if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
2507
2508
    // use the center color
2509
0
    shading->getColor(xM, yM, &fillColor);
2510
0
    state->setFillColor(&fillColor);
2511
0
    out->updateFillColor(state);
2512
2513
    // fill the rectangle
2514
0
    state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
2515
0
      x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
2516
0
    state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
2517
0
      x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
2518
0
    state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
2519
0
      x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
2520
0
    state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
2521
0
      x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
2522
0
    state->closePath();
2523
0
    out->fill(state);
2524
0
    state->clearPath();
2525
2526
  // the four corner colors are not close enough -- subdivide the
2527
  // rectangle
2528
0
  } else {
2529
2530
    // colors[0]       colorM0       colors[2]
2531
    //   (x0,y0)       (xM,y0)       (x1,y0)
2532
    //         +----------+----------+
2533
    //         |          |          |
2534
    //         |    UL    |    UR    |
2535
    // color0M |       colorMM       | color1M
2536
    // (x0,yM) +----------+----------+ (x1,yM)
2537
    //         |       (xM,yM)       |
2538
    //         |    LL    |    LR    |
2539
    //         |          |          |
2540
    //         +----------+----------+
2541
    // colors[1]       colorM1       colors[3]
2542
    //   (x0,y1)       (xM,y1)       (x1,y1)
2543
2544
0
    shading->getColor(x0, yM, &color0M);
2545
0
    shading->getColor(x1, yM, &color1M);
2546
0
    shading->getColor(xM, y0, &colorM0);
2547
0
    shading->getColor(xM, y1, &colorM1);
2548
0
    shading->getColor(xM, yM, &colorMM);
2549
2550
    // upper-left sub-rectangle
2551
0
    colors2[0] = colors[0];
2552
0
    colors2[1] = color0M;
2553
0
    colors2[2] = colorM0;
2554
0
    colors2[3] = colorMM;
2555
0
    doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
2556
    
2557
    // lower-left sub-rectangle
2558
0
    colors2[0] = color0M;
2559
0
    colors2[1] = colors[1];
2560
0
    colors2[2] = colorMM;
2561
0
    colors2[3] = colorM1;
2562
0
    doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
2563
    
2564
    // upper-right sub-rectangle
2565
0
    colors2[0] = colorM0;
2566
0
    colors2[1] = colorMM;
2567
0
    colors2[2] = colors[2];
2568
0
    colors2[3] = color1M;
2569
0
    doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
2570
2571
    // lower-right sub-rectangle
2572
0
    colors2[0] = colorMM;
2573
0
    colors2[1] = colorM1;
2574
0
    colors2[2] = color1M;
2575
0
    colors2[3] = colors[3];
2576
0
    doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
2577
0
  }
2578
0
}
2579
2580
0
void Gfx::doAxialShFill(GfxAxialShading *shading) {
2581
0
  double xMin, yMin, xMax, yMax;
2582
0
  double x0, y0, x1, y1;
2583
0
  double dx, dy, mul;
2584
0
  GBool dxdyZero, horiz;
2585
0
  double tMin, tMax, tMinExt, tMaxExt, t, tx, ty;
2586
0
  double sMin, sMax, tmp;
2587
0
  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
2588
0
  double t0, t1, tt;
2589
0
  GfxColor colors[axialSplits];
2590
0
  int abortCheckCounter, nComps, i, j, k;
2591
2592
  // get the clip region bbox
2593
0
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2594
2595
  // compute min and max t values, based on the four corners of the
2596
  // clip region bbox
2597
0
  shading->getCoords(&x0, &y0, &x1, &y1);
2598
0
  dx = x1 - x0;
2599
0
  dy = y1 - y0;
2600
0
  dxdyZero = fabs(dx) < 0.0001 && fabs(dy) < 0.0001;
2601
0
  horiz = fabs(dy) < fabs(dx);
2602
0
  if (dxdyZero) {
2603
0
    tMinExt = tMaxExt = 0;
2604
0
    tMin = tMax = 0;
2605
0
  } else {
2606
0
    mul = 1 / (dx * dx + dy * dy);
2607
0
    tMinExt = tMaxExt = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
2608
0
    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
2609
0
    if (t < tMinExt) {
2610
0
      tMinExt = t;
2611
0
    } else if (t > tMaxExt) {
2612
0
      tMaxExt = t;
2613
0
    }
2614
0
    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
2615
0
    if (t < tMinExt) {
2616
0
      tMinExt = t;
2617
0
    } else if (t > tMaxExt) {
2618
0
      tMaxExt = t;
2619
0
    }
2620
0
    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
2621
0
    if (t < tMinExt) {
2622
0
      tMinExt = t;
2623
0
    } else if (t > tMaxExt) {
2624
0
      tMaxExt = t;
2625
0
    }
2626
0
    if ((tMin = tMinExt) < 0) {
2627
0
      tMin = 0;
2628
0
    }
2629
0
    if (!shading->getExtend0()) {
2630
0
      tMinExt = tMin;
2631
0
    }
2632
0
    if ((tMax = tMaxExt) > 1) {
2633
0
      tMax = 1;
2634
0
    }
2635
0
    if (!shading->getExtend1()) {
2636
0
      tMaxExt = tMax;
2637
0
    }
2638
0
  }
2639
2640
  // get the function domain
2641
0
  t0 = shading->getDomain0();
2642
0
  t1 = shading->getDomain1();
2643
2644
  // Traverse the t axis and do the shading.
2645
  //
2646
  // For each point (tx, ty) on the t axis, consider a line through
2647
  // that point perpendicular to the t axis:
2648
  //
2649
  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
2650
  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
2651
  //
2652
  // Then look at the intersection of this line with the bounding box
2653
  // (xMin, yMin, xMax, yMax).  For -1 < |dy/dx| < 1, look at the
2654
  // intersection with yMin, yMax:
2655
  //
2656
  //     s0 = (yMin - ty) / dx
2657
  //     s1 = (yMax - ty) / dx
2658
  //
2659
  // else look at the intersection with xMin, xMax:
2660
  //
2661
  //     s0 = (xMin - tx) / -dy
2662
  //     s1 = (xMax - tx) / -dy
2663
  //
2664
  // Each filled polygon is bounded by two of these line segments
2665
  // perpdendicular to the t axis.
2666
  //
2667
  // The t axis is bisected into smaller regions until the color
2668
  // difference across a region is small enough, and then the region
2669
  // is painted with a single color.
2670
2671
  // compute the coordinates of the point on the t axis at t = tMin;
2672
  // then compute the intersection of the perpendicular line with the
2673
  // bounding box
2674
0
  tx = x0 + tMin * dx;
2675
0
  ty = y0 + tMin * dy;
2676
0
  if (dxdyZero) {
2677
0
    sMin = sMax = 0;
2678
0
  } else {
2679
0
    if (horiz) {
2680
0
      sMin = (yMin - ty) / dx;
2681
0
      sMax = (yMax - ty) / dx;
2682
0
    } else {
2683
0
      sMin = (xMin - tx) / -dy;
2684
0
      sMax = (xMax - tx) / -dy;
2685
0
    }
2686
0
    if (sMin > sMax) {
2687
0
      tmp = sMin; sMin = sMax; sMax = tmp;
2688
0
    }
2689
0
  }
2690
0
  ux0 = tx - sMin * dy;
2691
0
  uy0 = ty + sMin * dx;
2692
0
  vx0 = tx - sMax * dy;
2693
0
  vy0 = ty + sMax * dx;
2694
2695
  // fill the extension at t0
2696
0
  if (shading->getExtend0() && tMinExt < tMin) {
2697
2698
    // compute the color at t0
2699
0
    shading->getColor(t0, &colors[0]);
2700
2701
    // compute the coordinates of the point on the t axis at t =
2702
    // tMinExt; then compute the intersection of the perpendicular
2703
    // line with the bounding box
2704
0
    tx = x0 + tMinExt * dx;
2705
0
    ty = y0 + tMinExt * dy;
2706
0
    if (dxdyZero) {
2707
0
      sMin = sMax = 0;
2708
0
    } else {
2709
0
      if (horiz) {
2710
0
  sMin = (yMin - ty) / dx;
2711
0
  sMax = (yMax - ty) / dx;
2712
0
      } else {
2713
0
  sMin = (xMin - tx) / -dy;
2714
0
  sMax = (xMax - tx) / -dy;
2715
0
      }
2716
0
      if (sMin > sMax) {
2717
0
  tmp = sMin; sMin = sMax; sMax = tmp;
2718
0
      }
2719
0
    }
2720
0
    ux1 = tx - sMin * dy;
2721
0
    uy1 = ty + sMin * dx;
2722
0
    vx1 = tx - sMax * dy;
2723
0
    vy1 = ty + sMax * dx;
2724
2725
    // set the color
2726
0
    state->setFillColor(&colors[0]);
2727
0
    out->updateFillColor(state);
2728
2729
    // fill the region
2730
0
    state->moveTo(ux1, uy1);
2731
0
    state->lineTo(vx1, vy1);
2732
0
    state->lineTo(vx0, vy0);
2733
0
    state->lineTo(ux0, uy0);
2734
0
    state->closePath();
2735
0
    out->fill(state);
2736
0
    state->clearPath();
2737
0
  }
2738
2739
  // traverse the t axis, splitting [tMin, tMax] into axialSplits regions
2740
2741
  // compute the color in the center of each region
2742
0
  for (i = 0; i < axialSplits; ++i) {
2743
0
    t = tMin + (tMax - tMin) * (i + 0.5) / axialSplits;
2744
0
    tt = t0 + (t1 - t0) * t;
2745
0
    shading->getColor(tt, &colors[i]);
2746
0
  }
2747
2748
  // each iteration draws one or more regions, starting at i --
2749
  // if the colors are similar, it will combine regions i, i+1, ...
2750
0
  nComps = shading->getColorSpace()->getNComps();
2751
0
  abortCheckCounter = 0;
2752
0
  i = 0;
2753
0
  while (i < axialSplits) {
2754
2755
0
    if (abortCheckCbk) {
2756
0
      ++abortCheckCounter;
2757
0
      if (abortCheckCounter > 100) {
2758
0
        if ((*abortCheckCbk)(abortCheckCbkData)) {
2759
0
          break;
2760
0
        }
2761
0
        abortCheckCounter = 0;
2762
0
      }
2763
0
    }
2764
2765
    // check for similar colors
2766
0
    for (j = i + 1; j < axialSplits; ++j) {
2767
0
      for (k = 0; k < nComps; ++k) {
2768
0
  if (abs(colors[j].c[k] - colors[i].c[k]) > axialColorDelta) {
2769
0
    break;
2770
0
  }
2771
0
      }
2772
0
      if (k < nComps) {
2773
0
  break;
2774
0
      }
2775
0
    }
2776
2777
    // compute the coordinates of the point on the t axis; then
2778
    // compute the intersection of the perpendicular line with the
2779
    // bounding box
2780
0
    t = tMin + (tMax - tMin) * (double)j / axialSplits;
2781
0
    tx = x0 + t * dx;
2782
0
    ty = y0 + t * dy;
2783
0
    if (dxdyZero) {
2784
0
      sMin = sMax = 0;
2785
0
    } else {
2786
0
      if (horiz) {
2787
0
  sMin = (yMin - ty) / dx;
2788
0
  sMax = (yMax - ty) / dx;
2789
0
      } else {
2790
0
  sMin = (xMin - tx) / -dy;
2791
0
  sMax = (xMax - tx) / -dy;
2792
0
      }
2793
0
      if (sMin > sMax) {
2794
0
  tmp = sMin; sMin = sMax; sMax = tmp;
2795
0
      }
2796
0
    }
2797
0
    ux1 = tx - sMin * dy;
2798
0
    uy1 = ty + sMin * dx;
2799
0
    vx1 = tx - sMax * dy;
2800
0
    vy1 = ty + sMax * dx;
2801
2802
    // set the color
2803
0
    state->setFillColor(&colors[i]);
2804
0
    out->updateFillColor(state);
2805
2806
    // fill the region
2807
0
    state->moveTo(ux0, uy0);
2808
0
    state->lineTo(vx0, vy0);
2809
0
    state->lineTo(vx1, vy1);
2810
0
    state->lineTo(ux1, uy1);
2811
0
    state->closePath();
2812
0
    out->fill(state);
2813
0
    state->clearPath();
2814
2815
    // set up for next region
2816
0
    ux0 = ux1;
2817
0
    uy0 = uy1;
2818
0
    vx0 = vx1;
2819
0
    vy0 = vy1;
2820
2821
0
    i = j;
2822
0
  }
2823
2824
  // fill the extension at t1
2825
0
  if (shading->getExtend1() && tMaxExt > tMax) {
2826
2827
    // compute the color at t1
2828
0
    shading->getColor(t1, &colors[0]);
2829
2830
    // compute the coordinates of the point on the t axis at t =
2831
    // tMaxExt; then compute the intersection of the perpendicular
2832
    // line with the bounding box
2833
0
    tx = x0 + tMaxExt * dx;
2834
0
    ty = y0 + tMaxExt * dy;
2835
0
    if (dxdyZero) {
2836
0
      sMin = sMax = 0;
2837
0
    } else {
2838
0
      if (horiz) {
2839
0
  sMin = (yMin - ty) / dx;
2840
0
  sMax = (yMax - ty) / dx;
2841
0
      } else {
2842
0
  sMin = (xMin - tx) / -dy;
2843
0
  sMax = (xMax - tx) / -dy;
2844
0
      }
2845
0
      if (sMin > sMax) {
2846
0
  tmp = sMin; sMin = sMax; sMax = tmp;
2847
0
      }
2848
0
    }
2849
0
    ux1 = tx - sMin * dy;
2850
0
    uy1 = ty + sMin * dx;
2851
0
    vx1 = tx - sMax * dy;
2852
0
    vy1 = ty + sMax * dx;
2853
2854
    // set the color
2855
0
    state->setFillColor(&colors[0]);
2856
0
    out->updateFillColor(state);
2857
2858
    // fill the region
2859
0
    state->moveTo(ux0, uy0);
2860
0
    state->lineTo(vx0, vy0);
2861
0
    state->lineTo(vx1, vy1);
2862
0
    state->lineTo(ux1, uy1);
2863
0
    state->closePath();
2864
0
    out->fill(state);
2865
0
    state->clearPath();
2866
0
  }
2867
0
}
2868
2869
#if defined(__GNUC__) && !defined(__clang__)
2870
// this function makes a lot of sin()/cos() calls, which are slow
2871
// with glibc 2.16 and newer on x86; accuracy isn't terribly
2872
// important here, so tell gcc to use the fast version
2873
#pragma GCC optimize ("fast-math")
2874
#endif
2875
2876
0
void Gfx::doRadialShFill(GfxRadialShading *shading) {
2877
0
  double xMin, yMin, xMax, yMax;
2878
0
  double x0, y0, r0, x1, y1, r1, t0, t1;
2879
0
  int nComps;
2880
0
  GfxColor colorA, colorB;
2881
0
  double xa, ya, xb, yb, ra, rb;
2882
0
  double ta, tb, sa, sb;
2883
0
  double sMin, sMax, h;
2884
0
  double sLeft, sRight, sTop, sBottom, sZero, sDiag;
2885
0
  GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
2886
0
  GBool haveSMin, haveSMax;
2887
0
  GBool enclosed;
2888
0
  double *ctm;
2889
0
  double theta, alpha, angle, t;
2890
0
  int abortCheckCounter, ia, ib, k, n;
2891
2892
  // get the shading info
2893
0
  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2894
0
  t0 = shading->getDomain0();
2895
0
  t1 = shading->getDomain1();
2896
0
  nComps = shading->getColorSpace()->getNComps();
2897
2898
  // Compute the point at which r(s) = 0; check for the enclosed
2899
  // circles case; and compute the angles for the tangent lines.
2900
0
  h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
2901
0
  if (h == 0) {
2902
0
    enclosed = gTrue;
2903
0
    theta = 0; // make gcc happy
2904
0
  } else if (r1 - r0 == 0) {
2905
0
    enclosed = gFalse;
2906
0
    theta = 0;
2907
0
  } else if (fabs(r1 - r0) >= h - 0.0001) {
2908
0
    enclosed = gTrue;
2909
0
    theta = 0; // make gcc happy
2910
0
  } else {
2911
0
    enclosed = gFalse;
2912
0
    theta = asin((r1 - r0) / h);
2913
0
  }
2914
0
  if (enclosed) {
2915
0
    alpha = 0;
2916
0
  } else {
2917
0
    alpha = atan2(y1 - y0, x1 - x0);
2918
0
  }
2919
2920
  // compute the (possibly extended) s range
2921
0
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2922
0
  if (enclosed) {
2923
0
    sMin = 0;
2924
0
    sMax = 1;
2925
0
  } else {
2926
    // solve x(sLeft) + r(sLeft) = xMin
2927
0
    if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
2928
0
      sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
2929
0
    } else {
2930
0
      sLeft = 0; // make gcc happy
2931
0
    }
2932
    // solve x(sRight) - r(sRight) = xMax
2933
0
    if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
2934
0
      sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
2935
0
    } else {
2936
0
      sRight = 0; // make gcc happy
2937
0
    }
2938
    // solve y(sBottom) + r(sBottom) = yMin
2939
0
    if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
2940
0
      sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
2941
0
    } else {
2942
0
      sBottom = 0; // make gcc happy
2943
0
    }
2944
    // solve y(sTop) - r(sTop) = yMax
2945
0
    if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
2946
0
      sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
2947
0
    } else {
2948
0
      sTop = 0; // make gcc happy
2949
0
    }
2950
    // solve r(sZero) = 0
2951
0
    if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
2952
0
      sZero = -r0 / (r1 - r0);
2953
0
    } else {
2954
0
      sZero = 0; // make gcc happy
2955
0
    }
2956
    // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
2957
0
    if (haveSZero) {
2958
0
      sDiag = (sqrt((xMax - xMin) * (xMax - xMin) +
2959
0
        (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
2960
0
    } else {
2961
0
      sDiag = 0; // make gcc happy
2962
0
    }
2963
    // compute sMin
2964
0
    if (shading->getExtend0()) {
2965
0
      sMin = 0;
2966
0
      haveSMin = gFalse;
2967
0
      if (x0 < x1 && haveSLeft && sLeft < 0) {
2968
0
  sMin = sLeft;
2969
0
  haveSMin = gTrue;
2970
0
      } else if (x0 > x1 && haveSRight && sRight < 0) {
2971
0
  sMin = sRight;
2972
0
  haveSMin = gTrue;
2973
0
      }
2974
0
      if (y0 < y1 && haveSBottom && sBottom < 0) {
2975
0
  if (!haveSMin || sBottom > sMin) {
2976
0
    sMin = sBottom;
2977
0
    haveSMin = gTrue;
2978
0
  }
2979
0
      } else if (y0 > y1 && haveSTop && sTop < 0) {
2980
0
  if (!haveSMin || sTop > sMin) {
2981
0
    sMin = sTop;
2982
0
    haveSMin = gTrue;
2983
0
  }
2984
0
      }
2985
0
      if (haveSZero && sZero <= 0) {
2986
0
  if (!haveSMin || sZero > sMin) {
2987
0
    sMin = sZero;
2988
0
  }
2989
0
      }
2990
0
    } else {
2991
0
      sMin = 0;
2992
0
    }
2993
    // compute sMax
2994
0
    if (shading->getExtend1()) {
2995
0
      sMax = 1;
2996
0
      haveSMax = gFalse;
2997
0
      if (x1 < x0 && haveSLeft && sLeft > 1) {
2998
0
  sMax = sLeft;
2999
0
  haveSMax = gTrue;
3000
0
      } else if (x1 > x0 && haveSRight && sRight > 1) {
3001
0
  sMax = sRight;
3002
0
  haveSMax = gTrue;
3003
0
      }
3004
0
      if (y1 < y0 && haveSBottom && sBottom > 1) {
3005
0
  if (!haveSMax || sBottom < sMax) {
3006
0
    sMax = sBottom;
3007
0
    haveSMax = gTrue;
3008
0
  }
3009
0
      } else if (y1 > y0 && haveSTop && sTop > 1) {
3010
0
  if (!haveSMax || sTop < sMax) {
3011
0
    sMax = sTop;
3012
0
    haveSMax = gTrue;
3013
0
  }
3014
0
      }
3015
0
      if (haveSZero && sDiag > 1) {
3016
0
  if (!haveSMax || sDiag < sMax) {
3017
0
    sMax = sDiag;
3018
0
  }
3019
0
      }
3020
0
    } else {
3021
0
      sMax = 1;
3022
0
    }
3023
0
  }
3024
3025
  // compute the number of steps into which circles must be divided to
3026
  // achieve a curve flatness of 0.1 pixel in device space for the
3027
  // largest circle (note that "device space" is 72 dpi when generating
3028
  // PostScript, hence the relatively small 0.1 pixel accuracy)
3029
0
  ctm = state->getCTM();
3030
0
  t = fabs(ctm[0]);
3031
0
  if (fabs(ctm[1]) > t) {
3032
0
    t = fabs(ctm[1]);
3033
0
  }
3034
0
  if (fabs(ctm[2]) > t) {
3035
0
    t = fabs(ctm[2]);
3036
0
  }
3037
0
  if (fabs(ctm[3]) > t) {
3038
0
    t = fabs(ctm[3]);
3039
0
  }
3040
0
  if (r0 > r1) {
3041
0
    t *= r0;
3042
0
  } else {
3043
0
    t *= r1;
3044
0
  }
3045
0
  if (t < 1) {
3046
0
    n = 3;
3047
0
  } else {
3048
0
    n = (int)(M_PI / acos(1 - 0.1 / t));
3049
0
    if (n < 3) {
3050
0
      n = 3;
3051
0
    } else if (n > 200) {
3052
0
      n = 200;
3053
0
    }
3054
0
  }
3055
3056
  // setup for the start circle
3057
0
  ia = 0;
3058
0
  sa = sMin;
3059
0
  ta = t0 + sa * (t1 - t0);
3060
0
  xa = x0 + sa * (x1 - x0);
3061
0
  ya = y0 + sa * (y1 - y0);
3062
0
  ra = r0 + sa * (r1 - r0);
3063
0
  if (ta < t0) {
3064
0
    shading->getColor(t0, &colorA);
3065
0
  } else if (ta > t1) {
3066
0
    shading->getColor(t1, &colorA);
3067
0
  } else {
3068
0
    shading->getColor(ta, &colorA);
3069
0
  }
3070
3071
  // fill the circles
3072
0
  abortCheckCounter = 0;
3073
0
  while (ia < radialMaxSplits) {
3074
3075
0
    if (abortCheckCbk) {
3076
0
      ++abortCheckCounter;
3077
0
      if (abortCheckCounter > 100) {
3078
0
  if ((*abortCheckCbk)(abortCheckCbkData)) {
3079
0
    break;
3080
0
  }
3081
0
  abortCheckCounter = 0;
3082
0
      }
3083
0
    }
3084
3085
    // go as far along the t axis (toward t1) as we can, such that the
3086
    // color difference is within the tolerance (radialColorDelta) --
3087
    // this uses bisection (between the current value, t, and t1),
3088
    // limited to radialMaxSplits points along the t axis; require at
3089
    // least one split to avoid problems when the innermost and
3090
    // outermost colors are the same
3091
0
    ib = radialMaxSplits;
3092
0
    sb = sMax;
3093
0
    tb = t0 + sb * (t1 - t0);
3094
0
    if (tb < t0) {
3095
0
      shading->getColor(t0, &colorB);
3096
0
    } else if (tb > t1) {
3097
0
      shading->getColor(t1, &colorB);
3098
0
    } else {
3099
0
      shading->getColor(tb, &colorB);
3100
0
    }
3101
0
    while (ib - ia > 1) {
3102
0
      for (k = 0; k < nComps; ++k) {
3103
0
  if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
3104
0
    break;
3105
0
  }
3106
0
      }
3107
0
      if (k == nComps && ib < radialMaxSplits) {
3108
0
  break;
3109
0
      }
3110
0
      ib = (ia + ib) / 2;
3111
0
      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
3112
0
      tb = t0 + sb * (t1 - t0);
3113
0
      if (tb < t0) {
3114
0
  shading->getColor(t0, &colorB);
3115
0
      } else if (tb > t1) {
3116
0
  shading->getColor(t1, &colorB);
3117
0
      } else {
3118
0
  shading->getColor(tb, &colorB);
3119
0
      }
3120
0
    }
3121
3122
    // compute center and radius of the circle
3123
0
    xb = x0 + sb * (x1 - x0);
3124
0
    yb = y0 + sb * (y1 - y0);
3125
0
    rb = r0 + sb * (r1 - r0);
3126
3127
    // use the average of the colors at the two circles
3128
0
    for (k = 0; k < nComps; ++k) {
3129
0
      colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
3130
0
    }
3131
0
    state->setFillColor(&colorA);
3132
0
    out->updateFillColor(state);
3133
3134
0
    if (enclosed) {
3135
3136
      // construct path for first circle (counterclockwise)
3137
0
      state->moveTo(xa + ra, ya);
3138
0
      for (k = 1; k < n; ++k) {
3139
0
  angle = ((double)k / (double)n) * 2 * M_PI;
3140
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3141
0
      }
3142
0
      state->closePath();
3143
3144
      // construct and append path for second circle (clockwise)
3145
0
      state->moveTo(xb + rb, yb);
3146
0
      for (k = 1; k < n; ++k) {
3147
0
  angle = -((double)k / (double)n) * 2 * M_PI;
3148
0
  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3149
0
      }
3150
0
      state->closePath();
3151
3152
0
    } else {
3153
3154
      // construct the first subpath (clockwise)
3155
0
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3156
0
        ya + ra * sin(alpha + theta + 0.5 * M_PI));
3157
0
      for (k = 0; k < n; ++k) {
3158
0
  angle = alpha + theta + 0.5 * M_PI
3159
0
    - ((double)k / (double)n) * (2 * theta + M_PI);
3160
0
  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3161
0
      }
3162
0
      for (k = 0; k < n; ++k) {
3163
0
  angle = alpha - theta - 0.5 * M_PI
3164
0
    + ((double)k / (double)n) * (2 * theta - M_PI);
3165
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3166
0
      }
3167
0
      state->closePath();
3168
3169
      // construct the second subpath (counterclockwise)
3170
0
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3171
0
        ya + ra * sin(alpha + theta + 0.5 * M_PI));
3172
0
      for (k = 0; k < n; ++k) {
3173
0
  angle = alpha + theta + 0.5 * M_PI
3174
0
          + ((double)k / (double)n) * (-2 * theta + M_PI);
3175
0
  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3176
0
      }
3177
0
      for (k = 0; k < n; ++k) {
3178
0
  angle = alpha - theta - 0.5 * M_PI
3179
0
          + ((double)k / (double)n) * (2 * theta + M_PI);
3180
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3181
0
      }
3182
0
      state->closePath();
3183
0
    }
3184
3185
    // fill the path
3186
0
    out->fill(state);
3187
0
    state->clearPath();
3188
3189
    // step to the next value of t
3190
0
    ia = ib;
3191
0
    sa = sb;
3192
0
    ta = tb;
3193
0
    xa = xb;
3194
0
    ya = yb;
3195
0
    ra = rb;
3196
0
    colorA = colorB;
3197
0
  }
3198
3199
0
  if (enclosed) {
3200
    // extend the smaller circle
3201
0
    if ((shading->getExtend0() && r0 <= r1) ||
3202
0
  (shading->getExtend1() && r1 < r0)) {
3203
0
      if (r0 <= r1) {
3204
0
  ta = t0;
3205
0
  ra = r0;
3206
0
  xa = x0;
3207
0
  ya = y0;
3208
0
      } else {
3209
0
  ta = t1;
3210
0
  ra = r1;
3211
0
  xa = x1;
3212
0
  ya = y1;
3213
0
      }
3214
0
      shading->getColor(ta, &colorA);
3215
0
      state->setFillColor(&colorA);
3216
0
      out->updateFillColor(state);
3217
0
      state->moveTo(xa + ra, ya);
3218
0
      for (k = 1; k < n; ++k) {
3219
0
  angle = ((double)k / (double)n) * 2 * M_PI;
3220
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3221
0
      }
3222
0
      state->closePath();
3223
0
      out->fill(state);
3224
0
      state->clearPath();
3225
0
    }
3226
3227
    // extend the larger circle
3228
0
    if ((shading->getExtend0() && r0 > r1) ||
3229
0
  (shading->getExtend1() && r1 >= r0)) {
3230
0
      if (r0 > r1) {
3231
0
  ta = t0;
3232
0
  ra = r0;
3233
0
  xa = x0;
3234
0
  ya = y0;
3235
0
      } else {
3236
0
  ta = t1;
3237
0
  ra = r1;
3238
0
  xa = x1;
3239
0
  ya = y1;
3240
0
      }
3241
0
      shading->getColor(ta, &colorA);
3242
0
      state->setFillColor(&colorA);
3243
0
      out->updateFillColor(state);
3244
0
      state->moveTo(xMin, yMin);
3245
0
      state->lineTo(xMin, yMax);
3246
0
      state->lineTo(xMax, yMax);
3247
0
      state->lineTo(xMax, yMin);
3248
0
      state->closePath();
3249
0
      state->moveTo(xa + ra, ya);
3250
0
      for (k = 1; k < n; ++k) {
3251
0
  angle = ((double)k / (double)n) * 2 * M_PI;
3252
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3253
0
      }
3254
0
      state->closePath();
3255
0
      out->fill(state);
3256
0
      state->clearPath();
3257
0
    }
3258
0
  }
3259
0
}
3260
3261
#if defined(__GNUC__) && !defined(__clang__)
3262
#pragma GCC reset_options
3263
#endif
3264
3265
0
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
3266
0
  double x0, y0, x1, y1, x2, y2;
3267
0
  double color0[gfxColorMaxComps];
3268
0
  double color1[gfxColorMaxComps];
3269
0
  double color2[gfxColorMaxComps];
3270
0
  int abortCheckCounter, i;
3271
3272
0
  abortCheckCounter = 0;
3273
0
  for (i = 0; i < shading->getNTriangles(); ++i) {
3274
0
    if (abortCheckCbk) {
3275
0
      ++abortCheckCounter;
3276
0
      if (abortCheckCounter > 25) {
3277
0
  if ((*abortCheckCbk)(abortCheckCbkData)) {
3278
0
    break;
3279
0
  }
3280
0
  abortCheckCounter = 0;
3281
0
      }
3282
0
    }
3283
0
    shading->getTriangle(i, &x0, &y0, color0,
3284
0
       &x1, &y1, color1,
3285
0
       &x2, &y2, color2);
3286
0
    gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2,
3287
0
      shading, 0);
3288
0
  }
3289
0
}
3290
3291
void Gfx::gouraudFillTriangle(double x0, double y0, double *color0,
3292
            double x1, double y1, double *color1,
3293
            double x2, double y2, double *color2,
3294
0
            GfxGouraudTriangleShading *shading, int depth) {
3295
0
  double dx0, dy0, dx1, dy1, dx2, dy2;
3296
0
  double x01, y01, x12, y12, x20, y20;
3297
0
  double color01[gfxColorMaxComps];
3298
0
  double color12[gfxColorMaxComps];
3299
0
  double color20[gfxColorMaxComps];
3300
0
  GfxColor c0, c1, c2;
3301
0
  int nComps, i;
3302
3303
  // recursion ends when:
3304
  // (1) color difference is smaller than gouraudColorDelta; or
3305
  // (2) triangles are smaller than 0.5 pixel (note that "device
3306
  //     space" is 72dpi when generating PostScript); or
3307
  // (3) max recursion depth (gouraudMaxDepth) is hit.
3308
0
  nComps = shading->getColorSpace()->getNComps();
3309
0
  shading->getColor(color0, &c0);
3310
0
  shading->getColor(color1, &c1);
3311
0
  shading->getColor(color2, &c2);
3312
0
  for (i = 0; i < nComps; ++i) {
3313
0
    if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta ||
3314
0
  abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) {
3315
0
      break;
3316
0
    }
3317
0
  }
3318
0
  state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0);
3319
0
  state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1);
3320
0
  state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2);
3321
0
  if (i == nComps ||
3322
0
      depth == gouraudMaxDepth ||
3323
0
      (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 &&
3324
0
       fabs(dx1) < 0.5 && fabs(dy1) < 0.5 &&
3325
0
       fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) {
3326
0
    state->setFillColor(&c0);
3327
0
    out->updateFillColor(state);
3328
0
    state->moveTo(x0, y0);
3329
0
    state->lineTo(x1, y1);
3330
0
    state->lineTo(x2, y2);
3331
0
    state->closePath();
3332
0
    out->fill(state);
3333
0
    state->clearPath();
3334
0
  } else {
3335
0
    x01 = 0.5 * (x0 + x1);
3336
0
    y01 = 0.5 * (y0 + y1);
3337
0
    x12 = 0.5 * (x1 + x2);
3338
0
    y12 = 0.5 * (y1 + y2);
3339
0
    x20 = 0.5 * (x2 + x0);
3340
0
    y20 = 0.5 * (y2 + y0);
3341
0
    for (i = 0; i < shading->getNComps(); ++i) {
3342
0
      color01[i] = 0.5 * (color0[i] + color1[i]);
3343
0
      color12[i] = 0.5 * (color1[i] + color2[i]);
3344
0
      color20[i] = 0.5 * (color2[i] + color0[i]);
3345
0
    }
3346
0
    gouraudFillTriangle(x0, y0, color0, x01, y01, color01,
3347
0
      x20, y20, color20, shading, depth + 1);
3348
0
    gouraudFillTriangle(x01, y01, color01, x1, y1, color1,
3349
0
      x12, y12, color12, shading, depth + 1);
3350
0
    gouraudFillTriangle(x01, y01, color01, x12, y12, color12,
3351
0
      x20, y20, color20, shading, depth + 1);
3352
0
    gouraudFillTriangle(x20, y20, color20, x12, y12, color12,
3353
0
      x2, y2, color2, shading, depth + 1);
3354
0
  }
3355
0
}
3356
3357
0
void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
3358
0
  int start, abortCheckCounter, i;
3359
3360
0
  if (shading->getNPatches() > 128) {
3361
0
    start = 3;
3362
0
  } else if (shading->getNPatches() > 64) {
3363
0
    start = 2;
3364
0
  } else if (shading->getNPatches() > 16) {
3365
0
    start = 1;
3366
0
  } else {
3367
0
    start = 0;
3368
0
  }
3369
0
  abortCheckCounter = 0;
3370
0
  for (i = 0; i < shading->getNPatches(); ++i) {
3371
0
    if (abortCheckCbk) {
3372
0
      ++abortCheckCounter;
3373
0
      if (abortCheckCounter > 25) {
3374
0
  if ((*abortCheckCbk)(abortCheckCbkData)) {
3375
0
    break;
3376
0
  }
3377
0
  abortCheckCounter = 0;
3378
0
      }
3379
0
    }
3380
0
    fillPatch(shading->getPatch(i), shading, start);
3381
0
  }
3382
0
}
3383
3384
0
void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) {
3385
0
  GfxPatch patch00, patch01, patch10, patch11;
3386
0
  GfxColor c00, c01, c10, c11;
3387
0
  double xx[4][8], yy[4][8];
3388
0
  double x, y, xMin, yMin, xMax, yMax, xxm, yym;
3389
0
  int nComps, i, j;
3390
0
  GBool stop;
3391
3392
0
  shading->getColor(patch->color[0][0], &c00);
3393
0
  stop = gFalse;
3394
3395
  // stop subdivision at max depth
3396
0
  if (depth == patchMaxDepth) {
3397
0
    stop = gTrue;
3398
0
  }
3399
3400
  // stop subdivision if colors are close enough
3401
0
  if (!stop) {
3402
0
    nComps = shading->getColorSpace()->getNComps();
3403
0
    shading->getColor(patch->color[0][1], &c01);
3404
0
    shading->getColor(patch->color[1][0], &c10);
3405
0
    shading->getColor(patch->color[1][1], &c11);
3406
0
    for (i = 0; i < nComps; ++i) {
3407
0
      if (abs(c00.c[i] - c01.c[i]) > patchColorDelta ||
3408
0
    abs(c01.c[i] - c11.c[i]) > patchColorDelta ||
3409
0
    abs(c11.c[i] - c10.c[i]) > patchColorDelta ||
3410
0
    abs(c10.c[i] - c00.c[i]) > patchColorDelta) {
3411
0
  break;
3412
0
      }
3413
0
    }
3414
0
    if (i == nComps) {
3415
0
      stop = gTrue;
3416
0
    }
3417
0
  }
3418
3419
  // stop subdivision if patch is small enough
3420
0
  if (!stop) {
3421
0
    xMin = yMin = xMax = yMax = 0;
3422
0
    for (j = 0; j < 4; ++j) {
3423
0
      for (i = 0; i < 4; ++i) {
3424
0
  state->transformDelta(patch->x[i][j], patch->y[i][j], &x, &y);
3425
0
  if (i == 0 && j == 0) {
3426
0
    xMin = xMax = x;
3427
0
    yMin = yMax = y;
3428
0
  } else {
3429
0
    if (x < xMin) {
3430
0
      xMin = x;
3431
0
    } else if (x > xMax) {
3432
0
      xMax = x;
3433
0
    }
3434
0
    if (y < yMin) {
3435
0
      yMin = y;
3436
0
    } else if (y > yMax) {
3437
0
      yMax = y;
3438
0
    }
3439
0
  }
3440
0
      }
3441
0
    }
3442
0
    if (xMax - xMin < 1 && yMax - yMin < 1) {
3443
0
      stop = gTrue;
3444
0
    }
3445
0
  }
3446
3447
  // draw the patch
3448
0
  if (stop) {
3449
0
    state->setFillColor(&c00);
3450
0
    out->updateFillColor(state);
3451
0
    state->moveTo(patch->x[0][0], patch->y[0][0]);
3452
0
    state->curveTo(patch->x[0][1], patch->y[0][1],
3453
0
       patch->x[0][2], patch->y[0][2],
3454
0
       patch->x[0][3], patch->y[0][3]);
3455
0
    state->curveTo(patch->x[1][3], patch->y[1][3],
3456
0
       patch->x[2][3], patch->y[2][3],
3457
0
       patch->x[3][3], patch->y[3][3]);
3458
0
    state->curveTo(patch->x[3][2], patch->y[3][2],
3459
0
       patch->x[3][1], patch->y[3][1],
3460
0
       patch->x[3][0], patch->y[3][0]);
3461
0
    state->curveTo(patch->x[2][0], patch->y[2][0],
3462
0
       patch->x[1][0], patch->y[1][0],
3463
0
       patch->x[0][0], patch->y[0][0]);
3464
0
    state->closePath();
3465
0
    out->fill(state);
3466
0
    state->clearPath();
3467
3468
  // subdivide the patch
3469
0
  } else {
3470
0
    for (i = 0; i < 4; ++i) {
3471
0
      xx[i][0] = patch->x[i][0];
3472
0
      yy[i][0] = patch->y[i][0];
3473
0
      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
3474
0
      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
3475
0
      xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
3476
0
      yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
3477
0
      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
3478
0
      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
3479
0
      xx[i][2] = 0.5 * (xx[i][1] + xxm);
3480
0
      yy[i][2] = 0.5 * (yy[i][1] + yym);
3481
0
      xx[i][5] = 0.5 * (xxm + xx[i][6]);
3482
0
      yy[i][5] = 0.5 * (yym + yy[i][6]);
3483
0
      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
3484
0
      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
3485
0
      xx[i][7] = patch->x[i][3];
3486
0
      yy[i][7] = patch->y[i][3];
3487
0
    }
3488
0
    for (i = 0; i < 4; ++i) {
3489
0
      patch00.x[0][i] = xx[0][i];
3490
0
      patch00.y[0][i] = yy[0][i];
3491
0
      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
3492
0
      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
3493
0
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
3494
0
      yym = 0.5 * (yy[1][i] + yy[2][i]);
3495
0
      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
3496
0
      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
3497
0
      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
3498
0
      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
3499
0
      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
3500
0
      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
3501
0
      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
3502
0
      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
3503
0
      patch10.x[0][i] = patch00.x[3][i];
3504
0
      patch10.y[0][i] = patch00.y[3][i];
3505
0
      patch10.x[3][i] = xx[3][i];
3506
0
      patch10.y[3][i] = yy[3][i];
3507
0
    }
3508
0
    for (i = 4; i < 8; ++i) {
3509
0
      patch01.x[0][i-4] = xx[0][i];
3510
0
      patch01.y[0][i-4] = yy[0][i];
3511
0
      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
3512
0
      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
3513
0
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
3514
0
      yym = 0.5 * (yy[1][i] + yy[2][i]);
3515
0
      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
3516
0
      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
3517
0
      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
3518
0
      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
3519
0
      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
3520
0
      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
3521
0
      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
3522
0
      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
3523
0
      patch11.x[0][i-4] = patch01.x[3][i-4];
3524
0
      patch11.y[0][i-4] = patch01.y[3][i-4];
3525
0
      patch11.x[3][i-4] = xx[3][i];
3526
0
      patch11.y[3][i-4] = yy[3][i];
3527
0
    }
3528
0
    for (i = 0; i < shading->getNComps(); ++i) {
3529
0
      patch00.color[0][0][i] = patch->color[0][0][i];
3530
0
      patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
3531
0
              patch->color[0][1][i]);
3532
0
      patch01.color[0][0][i] = patch00.color[0][1][i];
3533
0
      patch01.color[0][1][i] = patch->color[0][1][i];
3534
0
      patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] +
3535
0
              patch->color[1][1][i]);
3536
0
      patch11.color[0][1][i] = patch01.color[1][1][i];
3537
0
      patch11.color[1][1][i] = patch->color[1][1][i];
3538
0
      patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] +
3539
0
              patch->color[1][0][i]);
3540
0
      patch10.color[1][1][i] = patch11.color[1][0][i];
3541
0
      patch10.color[1][0][i] = patch->color[1][0][i];
3542
0
      patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] +
3543
0
              patch->color[0][0][i]);
3544
0
      patch00.color[1][0][i] = patch10.color[0][0][i];
3545
0
      patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] +
3546
0
              patch01.color[1][1][i]);
3547
0
      patch01.color[1][0][i] = patch00.color[1][1][i];
3548
0
      patch11.color[0][0][i] = patch00.color[1][1][i];
3549
0
      patch10.color[0][1][i] = patch00.color[1][1][i];
3550
0
    }
3551
0
    fillPatch(&patch00, shading, depth + 1);
3552
0
    fillPatch(&patch10, shading, depth + 1);
3553
0
    fillPatch(&patch01, shading, depth + 1);
3554
0
    fillPatch(&patch11, shading, depth + 1);
3555
0
  }
3556
0
}
3557
3558
470k
void Gfx::doEndPath() {
3559
470k
  if (clip != clipNone) {
3560
93.4k
    if (state->isCurPt()) {
3561
43.2k
      state->clip();
3562
43.2k
      if (clip == clipNormal) {
3563
36.5k
  out->clip(state);
3564
36.5k
      } else {
3565
6.73k
  out->eoClip(state);
3566
6.73k
      }
3567
50.1k
    } else {
3568
50.1k
      error(errSyntaxError, getPos(), "Empty path in clip");
3569
50.1k
    }
3570
93.4k
  }
3571
470k
  clip = clipNone;
3572
470k
  state->clearPath();
3573
470k
}
3574
3575
//------------------------------------------------------------------------
3576
// path clipping operators
3577
//------------------------------------------------------------------------
3578
3579
138k
void Gfx::opClip(Object args[], int numArgs) {
3580
138k
  clip = clipNormal;
3581
138k
}
3582
3583
7.15k
void Gfx::opEOClip(Object args[], int numArgs) {
3584
7.15k
  clip = clipEO;
3585
7.15k
}
3586
3587
//------------------------------------------------------------------------
3588
// text object operators
3589
//------------------------------------------------------------------------
3590
3591
271k
void Gfx::opBeginText(Object args[], int numArgs) {
3592
271k
  state->setTextMat(1, 0, 0, 1, 0, 0);
3593
271k
  state->textMoveTo(0, 0);
3594
271k
  out->updateTextMat(state);
3595
271k
  out->updateTextPos(state);
3596
271k
  fontChanged = gTrue;
3597
271k
  haveSavedClipPath = gFalse;
3598
271k
}
3599
3600
271k
void Gfx::opEndText(Object args[], int numArgs) {
3601
271k
  if (haveSavedClipPath) {
3602
2.14k
    out->clipToSavedClipPath(state);
3603
2.14k
    haveSavedClipPath = gFalse;
3604
2.14k
  }
3605
3606
271k
  out->endTextObject(state);
3607
271k
}
3608
3609
//------------------------------------------------------------------------
3610
// text state operators
3611
//------------------------------------------------------------------------
3612
3613
24.0k
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
3614
24.0k
  state->setCharSpace(args[0].getNum());
3615
24.0k
  out->updateCharSpace(state);
3616
24.0k
}
3617
3618
131k
void Gfx::opSetFont(Object args[], int numArgs) {
3619
131k
  doSetFont(res->lookupFont(args[0].getName()), args[1].getNum());
3620
131k
}
3621
3622
131k
void Gfx::doSetFont(GfxFont *font, double size) {
3623
131k
  if (!font) {
3624
54.7k
    if (!defaultFont) {
3625
10.6k
      defaultFont = GfxFont::makeDefaultFont(xref);
3626
10.6k
    }
3627
54.7k
    font = defaultFont;
3628
54.7k
  }
3629
131k
  if (printCommands) {
3630
0
    printf("  font: tag=%s name='%s' %g\n",
3631
0
     font->getTag()->getCString(),
3632
0
     font->getName() ? font->getName()->getCString() : "???",
3633
0
     size);
3634
0
    fflush(stdout);
3635
0
  }
3636
131k
  state->setFont(font, size);
3637
131k
  fontChanged = gTrue;
3638
131k
}
3639
3640
11.9k
void Gfx::opSetTextLeading(Object args[], int numArgs) {
3641
11.9k
  state->setLeading(args[0].getNum());
3642
11.9k
}
3643
3644
14.4k
void Gfx::opSetTextRender(Object args[], int numArgs) {
3645
14.4k
  state->setRender(args[0].getInt());
3646
14.4k
  out->updateRender(state);
3647
14.4k
}
3648
3649
1.48k
void Gfx::opSetTextRise(Object args[], int numArgs) {
3650
1.48k
  state->setRise(args[0].getNum());
3651
1.48k
  out->updateRise(state);
3652
1.48k
}
3653
3654
19.8k
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
3655
19.8k
  state->setWordSpace(args[0].getNum());
3656
19.8k
  out->updateWordSpace(state);
3657
19.8k
}
3658
3659
9.83k
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
3660
9.83k
  state->setHorizScaling(args[0].getNum());
3661
9.83k
  out->updateHorizScaling(state);
3662
9.83k
  fontChanged = gTrue;
3663
9.83k
}
3664
3665
//------------------------------------------------------------------------
3666
// text positioning operators
3667
//------------------------------------------------------------------------
3668
3669
81.7k
void Gfx::opTextMove(Object args[], int numArgs) {
3670
81.7k
  double tx, ty;
3671
3672
81.7k
  tx = state->getLineX() + args[0].getNum();
3673
81.7k
  ty = state->getLineY() + args[1].getNum();
3674
81.7k
  state->textMoveTo(tx, ty);
3675
81.7k
  out->updateTextPos(state);
3676
81.7k
}
3677
3678
41.7k
void Gfx::opTextMoveSet(Object args[], int numArgs) {
3679
41.7k
  double tx, ty;
3680
3681
41.7k
  tx = state->getLineX() + args[0].getNum();
3682
41.7k
  ty = args[1].getNum();
3683
41.7k
  state->setLeading(-ty);
3684
41.7k
  ty += state->getLineY();
3685
41.7k
  state->textMoveTo(tx, ty);
3686
41.7k
  out->updateTextPos(state);
3687
41.7k
}
3688
3689
179k
void Gfx::opSetTextMatrix(Object args[], int numArgs) {
3690
179k
  state->setTextMat(args[0].getNum(), args[1].getNum(),
3691
179k
        args[2].getNum(), args[3].getNum(),
3692
179k
        args[4].getNum(), args[5].getNum());
3693
179k
  state->textMoveTo(0, 0);
3694
179k
  out->updateTextMat(state);
3695
179k
  out->updateTextPos(state);
3696
179k
  fontChanged = gTrue;
3697
179k
}
3698
3699
2.28k
void Gfx::opTextNextLine(Object args[], int numArgs) {
3700
2.28k
  double tx, ty;
3701
3702
2.28k
  tx = state->getLineX();
3703
2.28k
  ty = state->getLineY() - state->getLeading();
3704
2.28k
  state->textMoveTo(tx, ty);
3705
2.28k
  out->updateTextPos(state);
3706
2.28k
}
3707
3708
//------------------------------------------------------------------------
3709
// text string operators
3710
//------------------------------------------------------------------------
3711
3712
325k
void Gfx::opShowText(Object args[], int numArgs) {
3713
325k
  if (!state->getFont()) {
3714
21.9k
    error(errSyntaxError, getPos(), "No font in show");
3715
21.9k
    return;
3716
21.9k
  }
3717
304k
  if (fontChanged) {
3718
238k
    out->updateFont(state);
3719
238k
    fontChanged = gFalse;
3720
238k
  }
3721
304k
  if (ocState) {
3722
304k
    out->beginStringOp(state);
3723
304k
    doShowText(args[0].getString());
3724
304k
    out->endStringOp(state);
3725
304k
  } else {
3726
11
    doIncCharCount(args[0].getString());
3727
11
  }
3728
304k
}
3729
3730
2.52k
void Gfx::opMoveShowText(Object args[], int numArgs) {
3731
2.52k
  double tx, ty;
3732
3733
2.52k
  if (!state->getFont()) {
3734
616
    error(errSyntaxError, getPos(), "No font in move/show");
3735
616
    return;
3736
616
  }
3737
1.90k
  if (fontChanged) {
3738
1.46k
    out->updateFont(state);
3739
1.46k
    fontChanged = gFalse;
3740
1.46k
  }
3741
1.90k
  tx = state->getLineX();
3742
1.90k
  ty = state->getLineY() - state->getLeading();
3743
1.90k
  state->textMoveTo(tx, ty);
3744
1.90k
  out->updateTextPos(state);
3745
1.90k
  if (ocState) {
3746
1.89k
    out->beginStringOp(state);
3747
1.89k
    doShowText(args[0].getString());
3748
1.89k
    out->endStringOp(state);
3749
1.89k
  } else {
3750
11
    doIncCharCount(args[0].getString());
3751
11
  }
3752
1.90k
}
3753
3754
379
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
3755
379
  double tx, ty;
3756
3757
379
  if (!state->getFont()) {
3758
44
    error(errSyntaxError, getPos(), "No font in move/set/show");
3759
44
    return;
3760
44
  }
3761
335
  if (fontChanged) {
3762
170
    out->updateFont(state);
3763
170
    fontChanged = gFalse;
3764
170
  }
3765
335
  state->setWordSpace(args[0].getNum());
3766
335
  state->setCharSpace(args[1].getNum());
3767
335
  tx = state->getLineX();
3768
335
  ty = state->getLineY() - state->getLeading();
3769
335
  state->textMoveTo(tx, ty);
3770
335
  out->updateWordSpace(state);
3771
335
  out->updateCharSpace(state);
3772
335
  out->updateTextPos(state);
3773
335
  if (ocState) {
3774
335
    out->beginStringOp(state);
3775
335
    doShowText(args[2].getString());
3776
335
    out->endStringOp(state);
3777
335
  } else {
3778
0
    doIncCharCount(args[2].getString());
3779
0
  }
3780
335
}
3781
3782
42.0k
void Gfx::opShowSpaceText(Object args[], int numArgs) {
3783
42.0k
  Array *a;
3784
42.0k
  Object obj;
3785
42.0k
  int wMode;
3786
42.0k
  int i;
3787
3788
42.0k
  if (!state->getFont()) {
3789
4.80k
    error(errSyntaxError, getPos(), "No font in show/space");
3790
4.80k
    return;
3791
4.80k
  }
3792
37.2k
  if (fontChanged) {
3793
28.5k
    out->updateFont(state);
3794
28.5k
    fontChanged = gFalse;
3795
28.5k
  }
3796
37.2k
  if (ocState) {
3797
37.2k
    out->beginStringOp(state);
3798
37.2k
    wMode = state->getFont()->getWMode();
3799
37.2k
    a = args[0].getArray();
3800
681k
    for (i = 0; i < a->getLength(); ++i) {
3801
644k
      a->get(i, &obj);
3802
644k
      if (obj.isNum()) {
3803
312k
  if (wMode) {
3804
558
    state->textShift(0, -obj.getNum() * 0.001 *
3805
558
         state->getFontSize());
3806
311k
  } else {
3807
311k
    state->textShift(-obj.getNum() * 0.001 *
3808
311k
         state->getFontSize() *
3809
311k
         state->getHorizScaling(), 0);
3810
311k
  }
3811
312k
  out->updateTextShift(state, obj.getNum());
3812
332k
      } else if (obj.isString()) {
3813
219k
  doShowText(obj.getString());
3814
219k
      } else {
3815
113k
  error(errSyntaxError, getPos(),
3816
113k
        "Element of show/space array must be number or string");
3817
113k
      }
3818
644k
      obj.free();
3819
644k
    }
3820
37.2k
    out->endStringOp(state);
3821
37.2k
  } else {
3822
0
    a = args[0].getArray();
3823
0
    for (i = 0; i < a->getLength(); ++i) {
3824
0
      a->get(i, &obj);
3825
0
      if (obj.isString()) {
3826
0
  doIncCharCount(obj.getString());
3827
0
      }
3828
0
      obj.free();
3829
0
    }
3830
0
  }
3831
37.2k
}
3832
3833
525k
void Gfx::doShowText(GString *s) {
3834
525k
  GfxFont *font = state->getFont();
3835
525k
  int wMode = font->getWMode();
3836
3837
525k
  if (globalParams->isDroppedFont(font->getName()
3838
525k
            ? font->getName()->getCString() : "")) {
3839
0
    doIncCharCount(s);
3840
0
    return;
3841
0
  }
3842
3843
525k
  if (out->useDrawChar()) {
3844
525k
    out->beginString(state, s);
3845
525k
  }
3846
3847
  // figure out the drawing mode
3848
  // note: clipping, pattern fill, and pattern stroke are all handled
3849
  //       by saving the path and then performing the appropriate
3850
  //       actions in opEndText()
3851
525k
  GBool doFill = gFalse;
3852
525k
  GBool doStroke = gFalse;
3853
525k
  GBool doMakePath = gFalse;
3854
525k
  int render = state->getRender() & 7;
3855
525k
  switch (render & 7) {
3856
492k
  case 0:     // fill
3857
492k
    if (state->getFillColorSpace()->getMode() == csPattern) {
3858
30.3k
      doMakePath = gTrue;
3859
461k
    } else {
3860
461k
      doFill = gTrue;
3861
461k
    }
3862
492k
    break;
3863
600
  case 1:     // stroke
3864
600
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
3865
0
      doMakePath = gTrue;
3866
600
    } else {
3867
600
      doStroke = gTrue;
3868
600
    }
3869
600
    break;
3870
28.3k
  case 2:     // fill + stroke
3871
28.3k
    if (state->getFillColorSpace()->getMode() == csPattern ||
3872
21.4k
  state->getStrokeColorSpace()->getMode() == csPattern) {
3873
6.97k
      doMakePath = gTrue;
3874
21.4k
    } else {
3875
21.4k
      doFill = gTrue;
3876
21.4k
      doStroke = gTrue;
3877
21.4k
    }
3878
28.3k
    break;
3879
100
  case 3:     // invisible
3880
    // nothing
3881
100
    break;
3882
97
  case 4:     // fill + clip
3883
1.60k
  case 5:     // stroke + clip
3884
1.95k
  case 6:     // fill + stroke + clip
3885
4.04k
  case 7:     // clip
3886
4.04k
    doMakePath = gTrue;
3887
4.04k
    break;
3888
525k
  }
3889
3890
525k
  double riseX, riseY;
3891
525k
  state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3892
525k
  double xMin = state->getCurX() + riseX;
3893
525k
  double yMin = state->getCurY() + riseY;
3894
3895
  // handle a Type 3 char
3896
525k
  if (font->getType() == fontType3 && out->interpretType3Chars()) {
3897
38.1k
    double *mat = state->getCTM();
3898
38.1k
    double oldCTM[6];
3899
267k
    for (int i = 0; i < 6; ++i) {
3900
229k
      oldCTM[i] = mat[i];
3901
229k
    }
3902
38.1k
    mat = state->getTextMat();
3903
38.1k
    double newCTM[6];
3904
38.1k
    newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
3905
38.1k
    newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
3906
38.1k
    newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
3907
38.1k
    newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
3908
38.1k
    mat = font->getFontMatrix();
3909
38.1k
    newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
3910
38.1k
    newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
3911
38.1k
    newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
3912
38.1k
    newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
3913
38.1k
    newCTM[0] *= state->getFontSize();
3914
38.1k
    newCTM[1] *= state->getFontSize();
3915
38.1k
    newCTM[2] *= state->getFontSize();
3916
38.1k
    newCTM[3] *= state->getFontSize();
3917
38.1k
    newCTM[0] *= state->getHorizScaling();
3918
38.1k
    newCTM[1] *= state->getHorizScaling();
3919
38.1k
    double curX = state->getCurX();
3920
38.1k
    double curY = state->getCurY();
3921
38.1k
    Parser *oldParser = parser;
3922
38.1k
    char *p = s->getCString();
3923
38.1k
    int len = s->getLength();
3924
2.09M
    while (len > 0) {
3925
2.05M
      CharCode code;
3926
2.05M
      Unicode u[8];
3927
2.05M
      int uLen;
3928
2.05M
      double dx, dy, originX, originY;
3929
2.05M
      int n = font->getNextChar(p, len, &code,
3930
2.05M
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3931
2.05M
        &dx, &dy, &originX, &originY);
3932
2.05M
      dx = dx * state->getFontSize() + state->getCharSpace();
3933
2.05M
      if (n == 1 && *p == ' ') {
3934
119k
  dx += state->getWordSpace();
3935
119k
      }
3936
2.05M
      dx *= state->getHorizScaling();
3937
2.05M
      dy *= state->getFontSize();
3938
2.05M
      double tdx, tdy, ddx, ddy, x, y;
3939
2.05M
      state->textTransformDelta(dx, dy, &tdx, &tdy);
3940
2.05M
      state->transform(curX + riseX, curY + riseY, &x, &y);
3941
2.05M
      GfxState *savedState = saveStateStack();
3942
2.05M
      state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
3943
      //~ the CTM concat values here are wrong (but never used)
3944
2.05M
      out->updateCTM(state, 1, 0, 0, 1, 0, 0);
3945
2.05M
      state->transformDelta(dx, dy, &ddx, &ddy);
3946
#if 0
3947
      // The PDF spec says: "the graphics state shall be inherited
3948
      // from the environment of the text-showing operator that caused
3949
      // the [Type 3] glyph description to be invoked".  However,
3950
      // Acrobat apparently copies the fill color to the stroke color.
3951
      // It looks like Ghostscript does the same.  (This is only
3952
      // relevant for uncached Type 3 glyphs.)  Uncomment this block
3953
      // to make Xpdf mimic Adobe (in violation of the PDF spec).
3954
      state->setStrokeColorSpace(state->getFillColorSpace()->copy());
3955
      out->updateStrokeColorSpace(state);
3956
      state->setStrokeColor(state->getFillColor());
3957
      out->updateStrokeColor(state);
3958
#endif
3959
2.05M
      if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
3960
2.05M
             code, u, uLen)) {
3961
2.05M
  Object charProcRef, charProc;
3962
2.05M
  ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef);
3963
2.05M
  charProcRef.fetch(xref, &charProc);
3964
2.05M
  Dict *resDict = ((Gfx8BitFont *)font)->getResources();
3965
2.05M
  if (resDict) {
3966
1.72M
    pushResources(resDict);
3967
1.72M
  }
3968
2.05M
  if (charProc.isStream()) {
3969
64.1k
    display(&charProcRef, gFalse);
3970
1.98M
  } else {
3971
1.98M
    error(errSyntaxError, getPos(),
3972
1.98M
    "Missing or bad Type3 CharProc entry");
3973
1.98M
  }
3974
2.05M
  out->endType3Char(state);
3975
2.05M
  if (resDict) {
3976
1.72M
    popResources();
3977
1.72M
  }
3978
2.05M
  charProc.free();
3979
2.05M
  charProcRef.free();
3980
2.05M
      }
3981
2.05M
      restoreStateStack(savedState);
3982
2.05M
      curX += tdx;
3983
2.05M
      curY += tdy;
3984
2.05M
      state->moveTo(curX, curY);
3985
2.05M
      p += n;
3986
2.05M
      len -= n;
3987
2.05M
    }
3988
38.1k
    parser = oldParser;
3989
3990
487k
  } else if (out->useDrawChar()) {
3991
487k
    char *p = s->getCString();
3992
487k
    int len = s->getLength();
3993
4.80M
    while (len > 0) {
3994
4.31M
      CharCode code;
3995
4.31M
      Unicode u[8];
3996
4.31M
      int uLen;
3997
4.31M
      double dx, dy, originX, originY;
3998
4.31M
      int n = font->getNextChar(p, len, &code,
3999
4.31M
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
4000
4.31M
        &dx, &dy, &originX, &originY);
4001
4.31M
      if (wMode) {
4002
2.44k
  dx *= state->getFontSize();
4003
2.44k
  dy = dy * state->getFontSize() + state->getCharSpace();
4004
2.44k
  if (n == 1 && *p == ' ') {
4005
156
    dy += state->getWordSpace();
4006
156
  }
4007
4.31M
      } else {
4008
4.31M
  dx = dx * state->getFontSize() + state->getCharSpace();
4009
4.31M
  if (n == 1 && *p == ' ') {
4010
379k
    dx += state->getWordSpace();
4011
379k
  }
4012
4.31M
  dx *= state->getHorizScaling();
4013
4.31M
  dy *= state->getFontSize();
4014
4.31M
      }
4015
4.31M
      double tdx, tdy, tOriginX, tOriginY;
4016
4.31M
      state->textTransformDelta(dx, dy, &tdx, &tdy);
4017
4.31M
      originX *= state->getFontSize();
4018
4.31M
      originY *= state->getFontSize();
4019
4.31M
      state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
4020
4.31M
      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
4021
4.31M
        tdx, tdy, tOriginX, tOriginY, code, n, u, uLen,
4022
4.31M
        doFill, doStroke, doMakePath);
4023
4.31M
      state->shift(tdx, tdy);
4024
4.31M
      p += n;
4025
4.31M
      len -= n;
4026
4.31M
    }
4027
4028
487k
  } else {
4029
0
    double dx = 0, dy = 0;
4030
0
    char *p = s->getCString();
4031
0
    int len = s->getLength();
4032
0
    int nChars = 0, nSpaces = 0;
4033
0
    while (len > 0) {
4034
0
      CharCode code;
4035
0
      Unicode u[8];
4036
0
      int uLen;
4037
0
      double dx2, dy2, originX, originY;
4038
0
      int n = font->getNextChar(p, len, &code,
4039
0
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
4040
0
        &dx2, &dy2, &originX, &originY);
4041
0
      dx += dx2;
4042
0
      dy += dy2;
4043
0
      if (n == 1 && *p == ' ') {
4044
0
  ++nSpaces;
4045
0
      }
4046
0
      ++nChars;
4047
0
      p += n;
4048
0
      len -= n;
4049
0
    }
4050
0
    if (wMode) {
4051
0
      dx *= state->getFontSize();
4052
0
      dy = dy * state->getFontSize()
4053
0
     + nChars * state->getCharSpace()
4054
0
     + nSpaces * state->getWordSpace();
4055
0
    } else {
4056
0
      dx = dx * state->getFontSize()
4057
0
     + nChars * state->getCharSpace()
4058
0
     + nSpaces * state->getWordSpace();
4059
0
      dx *= state->getHorizScaling();
4060
0
      dy *= state->getFontSize();
4061
0
    }
4062
0
    double tdx, tdy;
4063
0
    state->textTransformDelta(dx, dy, &tdx, &tdy);
4064
0
    out->drawString(state, s, doFill, doStroke, doMakePath);
4065
0
    state->shift(tdx, tdy);
4066
0
  }
4067
4068
525k
  if (out->useDrawChar()) {
4069
525k
    out->endString(state);
4070
525k
  }
4071
4072
525k
  if (doMakePath) {
4073
    // compute the clipping bbox for the saved text path -- assume
4074
    // that the text bounding box does not extend past the baseline in
4075
    // any direction by more than twice the font size
4076
41.3k
    double xMax = state->getCurX() + riseX;
4077
41.3k
    double yMax = state->getCurY() + riseY;
4078
41.3k
    double t;
4079
41.3k
    if (xMin > xMax) {
4080
2.31k
      t = xMin; xMin = xMax; xMax = t;
4081
2.31k
    }
4082
41.3k
    if (yMin > yMax) {
4083
136
      t = yMin; yMin = yMax; yMax = t;
4084
136
    }
4085
41.3k
    double dx1, dy1, dx2, dy2;
4086
41.3k
    state->textTransformDelta(0, state->getFontSize(), &dx1, &dy1);
4087
41.3k
    state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2);
4088
41.3k
    dx1 = fabs(dx1);
4089
41.3k
    dx2 = fabs(dx2);
4090
41.3k
    if (dx2 > dx1) {
4091
23.3k
      dx1 = dx2;
4092
23.3k
    }
4093
41.3k
    dy1 = fabs(dy1);
4094
41.3k
    dy2 = fabs(dy2);
4095
41.3k
    if (dy2 > dy1) {
4096
1.53k
      dy1 = dy2;
4097
1.53k
    }
4098
41.3k
    xMin -= 2 * dx1;
4099
41.3k
    yMin -= 2 * dy1;
4100
41.3k
    xMax += 2 * dx1;
4101
41.3k
    yMax += 2 * dy1;
4102
4103
    //--- fill
4104
41.3k
    if ((render & 3) == 0 || (render & 3) == 2) {
4105
37.8k
      if (state->getFillColorSpace()->getMode() == csPattern) {
4106
37.3k
  saveState();
4107
37.3k
  state->clipToRect(xMin, yMin, xMax, yMax);
4108
37.3k
  out->clipToTextPath(state);
4109
37.3k
  doPatternText(gFalse);
4110
37.3k
  restoreState();
4111
37.3k
      } else {
4112
420
  out->fillTextPath(state);
4113
420
      }
4114
37.8k
    }
4115
4116
    //--- stroke
4117
41.3k
    if ((render & 3) == 1 || (render & 3) == 2) {
4118
8.83k
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
4119
0
  saveState();
4120
0
  state->clipToRect(xMin, yMin, xMax, yMax);
4121
0
  out->clipToTextStrokePath(state);
4122
0
  doPatternText(gTrue);
4123
0
  restoreState();
4124
8.83k
      } else {
4125
8.83k
  out->strokeTextPath(state);
4126
8.83k
      }
4127
8.83k
    }
4128
4129
    //--- clip
4130
41.3k
    if (render & 4) {
4131
4.04k
      out->addTextPathToSavedClipPath(state);
4132
4.04k
      haveSavedClipPath = gTrue;
4133
37.3k
    } else {
4134
37.3k
      out->clearTextPath(state);
4135
37.3k
    }
4136
41.3k
  }
4137
4138
525k
  opCounter += 10 * s->getLength();
4139
525k
}
4140
4141
// NB: this is only called when ocState is false.
4142
22
void Gfx::doIncCharCount(GString *s) {
4143
22
  if (out->needCharCount()) {
4144
0
    out->incCharCount(s->getLength());
4145
0
  }
4146
22
}
4147
4148
//------------------------------------------------------------------------
4149
// XObject operators
4150
//------------------------------------------------------------------------
4151
4152
48.9k
void Gfx::opXObject(Object args[], int numArgs) {
4153
48.9k
  char *name;
4154
48.9k
  Object xObj, refObj, obj2, obj3;
4155
48.9k
  GBool ocSaved, oc;
4156
48.9k
#if OPI_SUPPORT
4157
48.9k
  Object opiDict;
4158
48.9k
#endif
4159
4160
48.9k
  if (!ocState && !out->needCharCount()) {
4161
843
    return;
4162
843
  }
4163
48.1k
  name = args[0].getName();
4164
  // NB: we get both the reference and the object here, to make sure
4165
  // they refer to the same object.  There's a problematic corner
4166
  // case: if the resource dict contains an entry for [name] with a
4167
  // reference to a nonexistent object ("/name 99999 0 R", where
4168
  // object 99999 doesn't exist), and a parent resource dict contains
4169
  // a valid entry with the same name, then lookupXObjectNF() will
4170
  // return "99999 0 R", but lookupXObject() will return the valid
4171
  // entry.  This causes problems for doImage() and doForm().
4172
48.1k
  if (!res->lookupXObjectNF(name, &refObj)) {
4173
10.4k
    return;
4174
10.4k
  }
4175
37.6k
  if (!refObj.fetch(xref, &xObj)) {
4176
0
    refObj.free();
4177
0
    return;
4178
0
  }
4179
37.6k
  if (!xObj.isStream()) {
4180
6.45k
    error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name);
4181
6.45k
    xObj.free();
4182
6.45k
    refObj.free();
4183
6.45k
    return;
4184
6.45k
  }
4185
4186
  // check for optional content key
4187
31.2k
  ocSaved = ocState;
4188
31.2k
  xObj.streamGetDict()->lookupNF("OC", &obj2);
4189
31.2k
  if (doc->getOptionalContent()->evalOCObject(&obj2, &oc)) {
4190
0
    ocState &= oc;
4191
0
  }
4192
31.2k
  obj2.free();
4193
4194
31.2k
#if USE_EXCEPTIONS
4195
31.2k
  try {
4196
31.2k
#endif
4197
31.2k
#if OPI_SUPPORT
4198
31.2k
    xObj.streamGetDict()->lookup("OPI", &opiDict);
4199
31.2k
    if (opiDict.isDict()) {
4200
0
      out->opiBegin(state, opiDict.getDict());
4201
0
    }
4202
31.2k
#endif
4203
31.2k
    xObj.streamGetDict()->lookup("Subtype", &obj2);
4204
31.2k
    if (obj2.isName("Image")) {
4205
7.40k
      if (out->needNonText()) {
4206
7.40k
  doImage(&refObj, xObj.getStream(), gFalse);
4207
7.40k
      }
4208
23.8k
    } else if (obj2.isName("Form")) {
4209
23.1k
      if (out->useDrawForm() && refObj.isRef()) {
4210
0
  if (ocState) {
4211
0
    out->drawForm(refObj.getRef());
4212
0
  }
4213
23.1k
      } else {
4214
23.1k
  doForm(&refObj, &xObj);
4215
23.1k
      }
4216
23.1k
    } else if (obj2.isName("PS")) {
4217
0
      if (ocState) {
4218
0
  xObj.streamGetDict()->lookup("Level1", &obj3);
4219
0
  out->psXObject(xObj.getStream(),
4220
0
           obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
4221
0
  obj3.free();
4222
0
      }
4223
686
    } else if (obj2.isName()) {
4224
250
      error(errSyntaxError, getPos(),
4225
250
      "Unknown XObject subtype '{0:s}'", obj2.getName());
4226
436
    } else {
4227
436
      error(errSyntaxError, getPos(),
4228
436
      "XObject subtype is missing or wrong type");
4229
436
    }
4230
31.2k
    obj2.free();
4231
31.2k
#if OPI_SUPPORT
4232
31.2k
    if (opiDict.isDict()) {
4233
0
      out->opiEnd(state, opiDict.getDict());
4234
0
    }
4235
31.2k
    opiDict.free();
4236
31.2k
#endif
4237
31.2k
#if USE_EXCEPTIONS
4238
31.2k
  } catch (GMemException e) {
4239
32
    xObj.free();
4240
32
    refObj.free();
4241
32
    throw;
4242
32
  }
4243
0
#endif
4244
4245
31.1k
  ocState = ocSaved;
4246
4247
31.1k
  xObj.free();
4248
31.1k
  refObj.free();
4249
31.1k
}
4250
4251
41.6k
GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
4252
41.6k
  Dict *dict, *maskDict;
4253
41.6k
  int width, height;
4254
41.6k
  int bits, maskBits;
4255
41.6k
  StreamColorSpaceMode csMode;
4256
41.6k
  GBool mask, invert;
4257
41.6k
  GfxColorSpace *colorSpace, *maskColorSpace;
4258
41.6k
  GfxImageColorMap *colorMap, *maskColorMap;
4259
41.6k
  Object maskObj, smaskObj, maskRef;
4260
41.6k
  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte;
4261
41.6k
  int maskColors[2*gfxColorMaxComps];
4262
41.6k
  int maskWidth, maskHeight;
4263
41.6k
  GBool maskInvert;
4264
41.6k
  Stream *maskStr;
4265
41.6k
  double matte[gfxColorMaxComps];
4266
41.6k
  GBool interpolate;
4267
41.6k
  GfxRenderingIntent riSaved;
4268
41.6k
  Object obj1, obj2;
4269
41.6k
  int i, n;
4270
4271
  // check for optional content
4272
41.6k
  if (!ocState && !inlineImg) {
4273
0
    return gTrue;
4274
0
  }
4275
4276
  // images can have arbitrarily high compression ratios, but the
4277
  // data size is inherently limited
4278
41.6k
  str->disableDecompressionBombChecking();
4279
4280
  // get info from the stream
4281
41.6k
  bits = 0;
4282
41.6k
  csMode = streamCSNone;
4283
41.6k
  str->getImageParams(&bits, &csMode);
4284
4285
  // get stream dict
4286
41.6k
  dict = str->getDict();
4287
4288
  // save the current rendering intent
4289
41.6k
  riSaved = state->getRenderingIntent();
4290
4291
  // get size
4292
41.6k
  dict->lookup("Width", &obj1);
4293
41.6k
  if (obj1.isNull()) {
4294
34.1k
    obj1.free();
4295
34.1k
    dict->lookup("W", &obj1);
4296
34.1k
  }
4297
41.6k
  if (obj1.isInt()) {
4298
40.7k
    width = obj1.getInt();
4299
40.7k
  } else if (obj1.isReal()) {
4300
160
    error(errSyntaxWarning, getPos(), "Non-integer image width");
4301
160
    width = (int)obj1.getReal();
4302
708
  } else {
4303
708
    goto err2;
4304
708
  }
4305
40.9k
  obj1.free();
4306
40.9k
  if (width <= 0) {
4307
57
    goto err1;
4308
57
  }
4309
40.8k
  dict->lookup("Height", &obj1);
4310
40.8k
  if (obj1.isNull()) {
4311
33.4k
    obj1.free();
4312
33.4k
    dict->lookup("H", &obj1);
4313
33.4k
  }
4314
40.8k
  if (obj1.isInt()) {
4315
39.2k
    height = obj1.getInt();
4316
39.2k
  } else if (obj1.isReal()) {
4317
121
    error(errSyntaxWarning, getPos(), "Non-integer image height");
4318
121
    height = (int)obj1.getReal();
4319
1.48k
  } else {
4320
1.48k
    goto err2;
4321
1.48k
  }
4322
39.3k
  obj1.free();
4323
39.3k
  if (height <= 0) {
4324
216
    goto err1;
4325
216
  }
4326
4327
  // image or mask?
4328
39.1k
  dict->lookup("ImageMask", &obj1);
4329
39.1k
  if (obj1.isNull()) {
4330
39.0k
    obj1.free();
4331
39.0k
    dict->lookup("IM", &obj1);
4332
39.0k
  }
4333
39.1k
  mask = gFalse;
4334
39.1k
  if (obj1.isBool())
4335
15.5k
    mask = obj1.getBool();
4336
23.5k
  else if (!obj1.isNull())
4337
799
    goto err2;
4338
38.3k
  obj1.free();
4339
4340
  // bit depth
4341
38.3k
  if (bits == 0) {
4342
38.1k
    dict->lookup("BitsPerComponent", &obj1);
4343
38.1k
    if (obj1.isNull()) {
4344
31.1k
      obj1.free();
4345
31.1k
      dict->lookup("BPC", &obj1);
4346
31.1k
    }
4347
38.1k
    if (obj1.isNum()) {
4348
34.7k
      if (obj1.isInt()) {
4349
34.6k
  bits = obj1.getInt();
4350
34.6k
      } else if (obj1.isReal()) {
4351
77
  error(errSyntaxWarning, getPos(),
4352
77
        "Non-integer image bits per component");
4353
77
  bits = (int)obj1.getReal();
4354
77
      }
4355
34.7k
      if (bits < 1 || bits > 16) {
4356
307
  goto err2;
4357
307
      }
4358
34.7k
    } else if (mask) {
4359
1.65k
      bits = 1;
4360
1.68k
    } else {
4361
1.68k
      goto err2;
4362
1.68k
    }
4363
36.1k
    obj1.free();
4364
36.1k
  }
4365
4366
  // interpolate flag
4367
36.3k
  dict->lookup("Interpolate", &obj1);
4368
36.3k
  if (obj1.isNull()) {
4369
36.3k
    obj1.free();
4370
36.3k
    dict->lookup("I", &obj1);
4371
36.3k
  }
4372
36.3k
  interpolate = obj1.isBool() && obj1.getBool();
4373
36.3k
  obj1.free();
4374
4375
  // display a mask
4376
36.3k
  if (mask) {
4377
4378
    // check for inverted mask
4379
15.4k
    if (bits != 1)
4380
1.24k
      goto err1;
4381
14.1k
    invert = gFalse;
4382
14.1k
    dict->lookup("Decode", &obj1);
4383
14.1k
    if (obj1.isNull()) {
4384
14.1k
      obj1.free();
4385
14.1k
      dict->lookup("D", &obj1);
4386
14.1k
    }
4387
14.1k
    if (obj1.isArray()) {
4388
3.95k
      obj1.arrayGet(0, &obj2);
4389
3.95k
      invert = obj2.isNum() && obj2.getNum() == 1;
4390
3.95k
      obj2.free();
4391
10.2k
    } else if (!obj1.isNull()) {
4392
1.18k
      goto err2;
4393
1.18k
    }
4394
13.0k
    obj1.free();
4395
4396
    // if drawing is disabled, skip over inline image data
4397
13.0k
    if (!ocState) {
4398
0
      str->disableDecompressionBombChecking();
4399
0
      str->reset();
4400
0
      n = height * ((width + 7) / 8);
4401
0
      for (i = 0; i < n; ++i) {
4402
0
  str->getChar();
4403
0
      }
4404
0
      str->close();
4405
4406
    // draw it
4407
13.0k
    } else {
4408
13.0k
      if (state->getFillColorSpace()->getMode() == csPattern) {
4409
11
  doPatternImageMask(ref, str, width, height, invert, inlineImg,
4410
11
         interpolate);
4411
12.9k
      } else {
4412
12.9k
  out->drawImageMask(state, ref, str, width, height, invert, inlineImg,
4413
12.9k
         interpolate);
4414
12.9k
      }
4415
13.0k
    }
4416
4417
20.9k
  } else {
4418
4419
    // rendering intent
4420
20.9k
    if (dict->lookup("Intent", &obj1)->isName()) {
4421
1.12k
      opSetRenderingIntent(&obj1, 1);
4422
1.12k
    }
4423
20.9k
    obj1.free();
4424
4425
    // get color space and color map
4426
20.9k
    dict->lookup("ColorSpace", &obj1);
4427
20.9k
    if (obj1.isNull()) {
4428
14.2k
      obj1.free();
4429
14.2k
      dict->lookup("CS", &obj1);
4430
14.2k
    }
4431
20.9k
    if (obj1.isName()) {
4432
16.4k
      res->lookupColorSpace(obj1.getName(), &obj2);
4433
16.4k
      if (!obj2.isNull()) {
4434
18
  obj1.free();
4435
18
  obj1 = obj2;
4436
16.4k
      } else {
4437
16.4k
  obj2.free();
4438
16.4k
      }
4439
16.4k
    }
4440
20.9k
    if (!obj1.isNull()) {
4441
19.3k
      colorSpace = GfxColorSpace::parse(&obj1
4442
19.3k
          );
4443
19.3k
    } else if (csMode == streamCSDeviceGray) {
4444
20
      colorSpace = GfxColorSpace::create(csDeviceGray);
4445
1.60k
    } else if (csMode == streamCSDeviceRGB) {
4446
26
      colorSpace = GfxColorSpace::create(csDeviceRGB);
4447
1.58k
    } else if (csMode == streamCSDeviceCMYK) {
4448
0
      colorSpace = GfxColorSpace::create(csDeviceCMYK);
4449
1.58k
    } else {
4450
1.58k
      colorSpace = NULL;
4451
1.58k
    }
4452
20.9k
    obj1.free();
4453
20.9k
    if (!colorSpace) {
4454
3.20k
      goto err1;
4455
3.20k
    }
4456
17.7k
    if (colorSpace->getMode() == csPattern) {
4457
36
      error(errSyntaxError, getPos(), "Image with a Pattern color space");
4458
36
      delete colorSpace;
4459
36
      goto err1;
4460
36
    }
4461
17.6k
    dict->lookup("Decode", &obj1);
4462
17.6k
    if (obj1.isNull()) {
4463
17.2k
      obj1.free();
4464
17.2k
      dict->lookup("D", &obj1);
4465
17.2k
    }
4466
17.6k
    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
4467
17.6k
    obj1.free();
4468
17.6k
    if (!colorMap->isOk()) {
4469
1.10k
      delete colorMap;
4470
1.10k
      goto err1;
4471
1.10k
    }
4472
4473
    // get the mask
4474
16.5k
    haveColorKeyMask = haveExplicitMask = haveSoftMask = haveMatte = gFalse;
4475
16.5k
    maskStr = NULL; // make gcc happy
4476
16.5k
    maskWidth = maskHeight = 0; // make gcc happy
4477
16.5k
    maskInvert = gFalse; // make gcc happy
4478
16.5k
    maskColorMap = NULL; // make gcc happy
4479
16.5k
    dict->lookup("Mask", &maskObj);
4480
16.5k
    dict->lookup("SMask", &smaskObj);
4481
16.5k
    if (smaskObj.isStream()) {
4482
      // soft mask
4483
2.55k
      if (inlineImg) {
4484
0
  delete colorMap;
4485
0
  maskObj.free();
4486
0
  smaskObj.free();
4487
0
  goto err1;
4488
0
      }
4489
2.55k
      maskStr = smaskObj.getStream();
4490
2.55k
      maskStr->disableDecompressionBombChecking();
4491
2.55k
      maskDict = smaskObj.streamGetDict();
4492
2.55k
      maskDict->lookup("Width", &obj1);
4493
2.55k
      if (obj1.isNull()) {
4494
88
  obj1.free();
4495
88
  maskDict->lookup("W", &obj1);
4496
88
      }
4497
2.55k
      if (obj1.isInt()) {
4498
2.39k
  maskWidth = obj1.getInt();
4499
2.39k
      } else if (obj1.isReal()) {
4500
73
  error(errSyntaxWarning, getPos(), "Non-integer image mask width");
4501
73
  maskWidth = (int)obj1.getReal();
4502
88
      } else {
4503
88
  delete colorMap;
4504
88
  maskObj.free();
4505
88
  smaskObj.free();
4506
88
  goto err2;
4507
88
      }
4508
2.47k
      obj1.free();
4509
2.47k
      maskDict->lookup("Height", &obj1);
4510
2.47k
      if (obj1.isNull()) {
4511
142
  obj1.free();
4512
142
  maskDict->lookup("H", &obj1);
4513
142
      }
4514
2.47k
      if (obj1.isInt()) {
4515
2.21k
  maskHeight = obj1.getInt();
4516
2.21k
      } else if (obj1.isReal()) {
4517
116
  error(errSyntaxWarning, getPos(), "Non-integer image mask height");
4518
116
  maskHeight = (int)obj1.getReal();
4519
142
      } else {
4520
142
  delete colorMap;
4521
142
  maskObj.free();
4522
142
  smaskObj.free();
4523
142
  goto err2;
4524
142
      }
4525
2.32k
      obj1.free();
4526
2.32k
      if (maskWidth <= 0 || maskHeight <= 0) {
4527
154
  delete colorMap;
4528
154
  maskObj.free();
4529
154
  smaskObj.free();
4530
154
  goto err1;
4531
154
      }
4532
2.17k
      maskDict->lookup("BitsPerComponent", &obj1);
4533
2.17k
      if (obj1.isNull()) {
4534
78
  obj1.free();
4535
78
  maskDict->lookup("BPC", &obj1);
4536
78
      }
4537
2.17k
      if (obj1.isInt()) {
4538
1.96k
  maskBits = obj1.getInt();
4539
1.96k
      } else if (obj1.isReal()) {
4540
129
  error(errSyntaxWarning, getPos(),
4541
129
        "Non-integer image mask bits per component");
4542
129
  maskBits = (int)obj1.getReal();
4543
129
      } else {
4544
78
  delete colorMap;
4545
78
  maskObj.free();
4546
78
  smaskObj.free();
4547
78
  goto err2;
4548
78
      }
4549
2.09k
      obj1.free();
4550
2.09k
      if (maskBits < 1 || maskBits > 16) {
4551
129
  delete colorMap;
4552
129
  maskObj.free();
4553
129
  smaskObj.free();
4554
129
  goto err1;
4555
129
      }
4556
1.96k
      maskDict->lookup("ColorSpace", &obj1);
4557
1.96k
      if (obj1.isNull()) {
4558
113
  obj1.free();
4559
113
  maskDict->lookup("CS", &obj1);
4560
113
      }
4561
1.96k
      if (obj1.isName()) {
4562
1.85k
  res->lookupColorSpace(obj1.getName(), &obj2);
4563
1.85k
  if (!obj2.isNull()) {
4564
0
    obj1.free();
4565
0
    obj1 = obj2;
4566
1.85k
  } else {
4567
1.85k
    obj2.free();
4568
1.85k
  }
4569
1.85k
      }
4570
1.96k
      if (!obj1.isName("DeviceGray")) {
4571
200
  delete colorMap;
4572
200
  maskObj.free();
4573
200
  smaskObj.free();
4574
200
  goto err2;
4575
200
      }
4576
1.76k
      maskColorSpace = new GfxDeviceGrayColorSpace();
4577
1.76k
      obj1.free();
4578
1.76k
      maskDict->lookup("Decode", &obj1);
4579
1.76k
      if (obj1.isNull()) {
4580
1.70k
  obj1.free();
4581
1.70k
  maskDict->lookup("D", &obj1);
4582
1.70k
      }
4583
1.76k
      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
4584
1.76k
      obj1.free();
4585
1.76k
      if (!maskColorMap->isOk()) {
4586
88
  delete maskColorMap;
4587
88
  delete colorMap;
4588
88
  maskObj.free();
4589
88
  smaskObj.free();
4590
88
  goto err1;
4591
88
      }
4592
1.67k
      if (maskDict->lookup("Matte", &obj1)->isArray()) {
4593
0
  if (obj1.arrayGetLength() == colorSpace->getNComps()) {
4594
0
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
4595
0
      if (obj1.arrayGet(i, &obj2)->isNum()) {
4596
0
        matte[i] = obj2.getNum();
4597
0
      } else {
4598
0
        error(errSyntaxError, getPos(),
4599
0
        "Invalid Matte entry in soft mask");
4600
0
        matte[i] = 0;
4601
0
      }
4602
0
      obj2.free();
4603
0
    }
4604
0
    haveMatte = gTrue;
4605
0
  } else {
4606
0
    error(errSyntaxError, getPos(), "Invalid Matte entry in soft mask");
4607
0
  }
4608
0
      }
4609
1.67k
      obj1.free();
4610
1.67k
      haveSoftMask = gTrue;
4611
14.0k
    } else if (maskObj.isArray()) {
4612
      // color key mask
4613
301
      haveColorKeyMask = gTrue;
4614
301
      for (i = 0;
4615
308
     i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps;
4616
301
     i += 2) {
4617
244
  maskObj.arrayGet(i, &obj1);
4618
244
  if (!obj1.isInt()) {
4619
40
    obj1.free();
4620
40
    haveColorKeyMask = gFalse;
4621
40
    break;
4622
40
  }
4623
204
  maskColors[i] = obj1.getInt();
4624
204
  obj1.free();
4625
204
  if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) {
4626
41
    haveColorKeyMask = gFalse;
4627
41
    break;
4628
41
  }
4629
163
  maskObj.arrayGet(i+1, &obj1);
4630
163
  if (!obj1.isInt()) {
4631
37
    obj1.free();
4632
37
    haveColorKeyMask = gFalse;
4633
37
    break;
4634
37
  }
4635
126
  maskColors[i+1] = obj1.getInt();
4636
126
  obj1.free();
4637
126
  if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) ||
4638
119
      maskColors[i] > maskColors[i+1]) {
4639
119
    haveColorKeyMask = gFalse;
4640
119
    break;
4641
119
  }
4642
126
      }
4643
13.7k
    } else if (maskObj.isStream()) {
4644
      // explicit mask
4645
491
      haveExplicitMask = gTrue;
4646
491
      if (inlineImg) {
4647
0
  delete colorMap;
4648
0
  maskObj.free();
4649
0
  smaskObj.free();
4650
0
  goto err1;
4651
0
      }
4652
491
      maskStr = maskObj.getStream();
4653
491
      maskStr->disableDecompressionBombChecking();
4654
491
      maskDict = maskObj.streamGetDict();
4655
491
      maskDict->lookup("Width", &obj1);
4656
491
      if (obj1.isNull()) {
4657
23
  obj1.free();
4658
23
  maskDict->lookup("W", &obj1);
4659
23
      }
4660
491
      if (obj1.isInt()) {
4661
417
  maskWidth = obj1.getInt();
4662
417
      } else if (obj1.isReal()) {
4663
53
  error(errSyntaxWarning, getPos(), "Non-integer image mask width");
4664
53
  maskWidth = (int)obj1.getReal();
4665
53
      } else {
4666
21
  haveExplicitMask = gFalse;
4667
21
      }
4668
491
      obj1.free();
4669
491
      maskDict->lookup("Height", &obj1);
4670
491
      if (obj1.isNull()) {
4671
60
  obj1.free();
4672
60
  maskDict->lookup("H", &obj1);
4673
60
      }
4674
491
      if (obj1.isInt()) {
4675
124
  maskHeight = obj1.getInt();
4676
367
      } else if (obj1.isReal()) {
4677
307
  error(errSyntaxWarning, getPos(), "Non-integer image mask height");
4678
307
  maskHeight = (int)obj1.getReal();
4679
307
      } else {
4680
60
  haveExplicitMask = gFalse;
4681
60
      }
4682
491
      obj1.free();
4683
491
      if (maskWidth <= 0 || maskHeight <= 0) {
4684
71
  haveExplicitMask = gFalse;
4685
71
      }
4686
491
      maskDict->lookup("ImageMask", &obj1);
4687
491
      if (obj1.isNull()) {
4688
53
  obj1.free();
4689
53
  maskDict->lookup("IM", &obj1);
4690
53
      }
4691
491
      if (!obj1.isBool() || !obj1.getBool()) {
4692
58
  haveExplicitMask = gFalse;
4693
58
      }
4694
491
      obj1.free();
4695
491
      maskInvert = gFalse;
4696
491
      maskDict->lookup("Decode", &obj1);
4697
491
      if (obj1.isNull()) {
4698
177
  obj1.free();
4699
177
  maskDict->lookup("D", &obj1);
4700
177
      }
4701
491
      if (obj1.isArray()) {
4702
295
  obj1.arrayGet(0, &obj2);
4703
295
  maskInvert = obj2.isNum() && obj2.getNum() == 1;
4704
295
  obj2.free();
4705
295
      } else if (!obj1.isNull()) {
4706
40
  haveExplicitMask = gFalse;
4707
40
      }
4708
491
      obj1.free();
4709
491
      if (!haveExplicitMask) {
4710
139
  error(errSyntaxError, getPos(), "Bad image parameters");
4711
139
      }
4712
491
    }
4713
4714
    // if drawing is disabled, skip over inline image data
4715
15.7k
    if (state->getIgnoreColorOps() || !ocState) {
4716
486
      if (state->getIgnoreColorOps()) {
4717
486
  error(errSyntaxWarning, getPos(), "Ignoring image"
4718
486
        " in uncolored Type 3 char or tiling pattern");
4719
486
      }
4720
486
      if (inlineImg) {
4721
446
  str->disableDecompressionBombChecking();
4722
446
  str->reset();
4723
446
  n = height * ((width * colorMap->getNumPixelComps() *
4724
446
           colorMap->getBits() + 7) / 8);
4725
446
  str->discardChars(n);
4726
446
  str->close();
4727
446
      }
4728
4729
    // draw it
4730
15.2k
    } else {
4731
15.2k
      if (haveSoftMask) {
4732
1.67k
  dict->lookupNF("SMask", &maskRef);
4733
1.67k
  out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
4734
1.67k
         &maskRef, maskStr, maskWidth, maskHeight,
4735
1.67k
         maskColorMap,
4736
1.67k
         haveMatte ? matte : (double *)NULL,
4737
1.67k
         interpolate);
4738
1.67k
  maskRef.free();
4739
1.67k
  delete maskColorMap;
4740
13.5k
      } else if (haveExplicitMask) {
4741
352
  dict->lookupNF("Mask", &maskRef);
4742
352
  out->drawMaskedImage(state, ref, str, width, height, colorMap,
4743
352
           &maskRef, maskStr, maskWidth, maskHeight,
4744
352
           maskInvert, interpolate);
4745
352
  maskRef.free();
4746
13.1k
      } else {
4747
13.1k
  out->drawImage(state, ref, str, width, height, colorMap,
4748
13.1k
           haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
4749
13.1k
           interpolate);
4750
13.1k
      }
4751
15.2k
    }
4752
4753
15.7k
    delete colorMap;
4754
15.7k
    maskObj.free();
4755
15.7k
    smaskObj.free();
4756
15.7k
  }
4757
4758
  // restore rendering intent
4759
28.7k
  if (state->getRenderingIntent() != riSaved) {
4760
109
    state->setRenderingIntent(riSaved);
4761
109
    out->updateRenderingIntent(state);
4762
109
  }
4763
4764
28.7k
  if ((i = width * height) > 1000) {
4765
11.5k
    i = 1000;
4766
11.5k
  }
4767
28.7k
  opCounter += i;
4768
4769
28.7k
  return gTrue;
4770
4771
6.67k
 err2:
4772
6.67k
  obj1.free();
4773
12.9k
 err1:
4774
12.9k
  error(errSyntaxError, getPos(), "Bad image parameters");
4775
4776
  // restore rendering intent
4777
12.9k
  if (state->getRenderingIntent() != riSaved) {
4778
428
    state->setRenderingIntent(riSaved);
4779
428
    out->updateRenderingIntent(state);
4780
428
  }
4781
4782
12.9k
  return gFalse;
4783
6.67k
}
4784
4785
23.1k
void Gfx::doForm(Object *strRef, Object *str) {
4786
23.1k
  Dict *dict;
4787
23.1k
  GBool transpGroup, isolated, knockout;
4788
23.1k
  Object matrixObj, bboxObj;
4789
23.1k
  double m[6], bbox[4];
4790
23.1k
  Object resObj;
4791
23.1k
  Dict *resDict;
4792
23.1k
  Object obj1, obj2, obj3;
4793
23.1k
  int i;
4794
4795
  // check for optional content
4796
23.1k
  if (!ocState && !out->needCharCount()) {
4797
0
    return;
4798
0
  }
4799
4800
  // get stream dict
4801
23.1k
  dict = str->streamGetDict();
4802
4803
  // check form type
4804
23.1k
  dict->lookup("FormType", &obj1);
4805
23.1k
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
4806
335
    error(errSyntaxError, getPos(), "Unknown form type");
4807
335
  }
4808
23.1k
  obj1.free();
4809
4810
  // get bounding box
4811
23.1k
  dict->lookup("BBox", &bboxObj);
4812
23.1k
  if (!(bboxObj.isArray() && bboxObj.arrayGetLength() == 4)) {
4813
314
    bboxObj.free();
4814
314
    error(errSyntaxError, getPos(), "Bad form bounding box");
4815
314
    return;
4816
314
  }
4817
113k
  for (i = 0; i < 4; ++i) {
4818
90.8k
    bboxObj.arrayGet(i, &obj1);
4819
90.8k
    if (!obj1.isNum()) {
4820
245
      bboxObj.free();
4821
245
      error(errSyntaxError, getPos(), "Bad form bounding box");
4822
245
      return;
4823
245
    }
4824
90.6k
    bbox[i] = obj1.getNum();
4825
90.6k
    obj1.free();
4826
90.6k
  }
4827
22.5k
  bboxObj.free();
4828
4829
  // get matrix
4830
22.5k
  dict->lookup("Matrix", &matrixObj);
4831
22.5k
  if (matrixObj.isArray() && matrixObj.arrayGetLength() == 6) {
4832
43.1k
    for (i = 0; i < 6; ++i) {
4833
36.9k
      matrixObj.arrayGet(i, &obj1);
4834
36.9k
      if (obj1.isNum()) {
4835
36.7k
  m[i] = obj1.getNum();
4836
36.7k
      } else {
4837
242
  m[i] = 0;
4838
242
      }
4839
36.9k
      obj1.free();
4840
36.9k
    }
4841
16.4k
  } else {
4842
16.4k
    m[0] = 1; m[1] = 0;
4843
16.4k
    m[2] = 0; m[3] = 1;
4844
16.4k
    m[4] = 0; m[5] = 0;
4845
16.4k
  }
4846
22.5k
  matrixObj.free();
4847
4848
  // get resources
4849
22.5k
  dict->lookup("Resources", &resObj);
4850
22.5k
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4851
4852
  // check for a transparency group
4853
22.5k
  transpGroup = isolated = knockout = gFalse;
4854
22.5k
  if (dict->lookup("Group", &obj1)->isDict()) {
4855
18.8k
    if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
4856
5.09k
      transpGroup = gTrue;
4857
5.09k
      if (obj1.dictLookup("I", &obj3)->isBool()) {
4858
479
  isolated = obj3.getBool();
4859
479
      }
4860
5.09k
      obj3.free();
4861
5.09k
      if (obj1.dictLookup("K", &obj3)->isBool()) {
4862
834
  knockout = obj3.getBool();
4863
834
      }
4864
5.09k
      obj3.free();
4865
5.09k
    }
4866
18.8k
    obj2.free();
4867
18.8k
  }
4868
22.5k
  obj1.free();
4869
4870
  // draw it
4871
22.5k
  drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout);
4872
4873
22.5k
  resObj.free();
4874
22.5k
}
4875
4876
void Gfx::drawForm(Object *strRef, Dict *resDict,
4877
       double *matrix, double *bbox,
4878
       GBool transpGroup, GBool softMask,
4879
       GBool isolated, GBool knockout,
4880
       GBool alpha, Function *transferFunc,
4881
84.9k
       Object *backdropColorObj) {
4882
84.9k
  Parser *oldParser;
4883
84.9k
  GfxState *savedState;
4884
84.9k
  GfxColorSpace *blendingColorSpace;
4885
84.9k
  GfxColor backdropColor;
4886
84.9k
  Object strObj, groupAttrsObj, csObj, obj1;
4887
84.9k
  double oldBaseMatrix[6];
4888
84.9k
  int i;
4889
4890
84.9k
  if (formDepth > 100) {
4891
0
    error(errSyntaxError, getPos(), "Excessive recursion in Form XObjects");
4892
0
    return;
4893
0
  }
4894
84.9k
  ++formDepth;
4895
4896
84.9k
  out->startStream(strRef->getRef(), state);
4897
4898
  // push new resources on stack
4899
84.9k
  pushResources(resDict);
4900
4901
  // save current graphics state
4902
84.9k
  saveState();
4903
4904
  // kill any pre-existing path
4905
84.9k
  state->clearPath();
4906
4907
  // save current parser
4908
84.9k
  oldParser = parser;
4909
4910
  // set form transformation matrix
4911
84.9k
  state->concatCTM(matrix[0], matrix[1], matrix[2],
4912
84.9k
       matrix[3], matrix[4], matrix[5]);
4913
84.9k
  out->updateCTM(state, matrix[0], matrix[1], matrix[2],
4914
84.9k
     matrix[3], matrix[4], matrix[5]);
4915
4916
  // set form bounding box
4917
84.9k
  state->moveTo(bbox[0], bbox[1]);
4918
84.9k
  state->lineTo(bbox[2], bbox[1]);
4919
84.9k
  state->lineTo(bbox[2], bbox[3]);
4920
84.9k
  state->lineTo(bbox[0], bbox[3]);
4921
84.9k
  state->closePath();
4922
84.9k
  state->clip();
4923
84.9k
  out->clip(state);
4924
84.9k
  state->clearPath();
4925
4926
84.9k
  blendingColorSpace = NULL;
4927
84.9k
  if (softMask || transpGroup) {
4928
    // get the blending color space
4929
    // NB: this must be done AFTER pushing the resource dictionary,
4930
    //     so that any Default*** color spaces are available
4931
6.19k
    strRef->fetch(xref, &strObj);
4932
6.19k
    if (strObj.streamGetDict()->lookup("Group", &groupAttrsObj)->isDict()) {
4933
6.19k
      if (!groupAttrsObj.dictLookup("CS", &csObj)->isNull()) {
4934
1.36k
  blendingColorSpace = GfxColorSpace::parse(&csObj
4935
1.36k
              );
4936
1.36k
      }
4937
6.19k
      csObj.free();
4938
6.19k
    }
4939
6.19k
    groupAttrsObj.free();
4940
6.19k
    strObj.free();
4941
4942
6.19k
    if (!out->beginTransparencyGroup(state, bbox, blendingColorSpace,
4943
6.19k
             isolated, knockout, softMask)) {
4944
1.64k
      transpGroup = gFalse;
4945
1.64k
    }
4946
4947
6.19k
    if (softMask || transpGroup) {
4948
4.54k
      traceBegin(oldBaseMatrix, softMask ? "begin soft mask" : "begin t-group");
4949
4.54k
      if (state->getBlendMode() != gfxBlendNormal) {
4950
169
  state->setBlendMode(gfxBlendNormal);
4951
169
  out->updateBlendMode(state);
4952
169
      }
4953
4.54k
      if (state->getFillOpacity() != 1) {
4954
130
  state->setFillOpacity(1);
4955
130
  out->updateFillOpacity(state);
4956
130
      }
4957
4.54k
      if (state->getStrokeOpacity() != 1) {
4958
124
  state->setStrokeOpacity(1);
4959
124
  out->updateStrokeOpacity(state);
4960
124
      }
4961
4.54k
      out->clearSoftMask(state);
4962
4.54k
    }
4963
6.19k
  }
4964
4965
  // set new base matrix
4966
594k
  for (i = 0; i < 6; ++i) {
4967
509k
    oldBaseMatrix[i] = baseMatrix[i];
4968
509k
    baseMatrix[i] = state->getCTM()[i];
4969
509k
  }
4970
4971
  // save the state stack -- this handles the case where the form
4972
  // contents have unbalanced q/Q operators
4973
84.9k
  savedState = saveStateStack();
4974
4975
  // draw the form
4976
84.9k
  display(strRef, gFalse);
4977
4978
84.9k
  restoreStateStack(savedState);
4979
4980
84.9k
  if (softMask || transpGroup) {
4981
4.54k
    out->endTransparencyGroup(state);
4982
4.54k
  }
4983
4984
  // restore base matrix
4985
594k
  for (i = 0; i < 6; ++i) {
4986
509k
    baseMatrix[i] = oldBaseMatrix[i];
4987
509k
  }
4988
4989
  // restore parser
4990
84.9k
  parser = oldParser;
4991
4992
  // restore graphics state
4993
84.9k
  restoreState();
4994
4995
  // pop resource stack
4996
84.9k
  popResources();
4997
4998
84.9k
  if (softMask) {
4999
36.4k
    for (i = 0; i < gfxColorMaxComps; ++i) {
5000
35.3k
      backdropColor.c[i] = 0;
5001
35.3k
    }
5002
1.10k
    if (backdropColorObj->isArray()) {
5003
116
      for (i = 0;
5004
1.05k
     i < backdropColorObj->arrayGetLength() && i < gfxColorMaxComps;
5005
942
     ++i) {
5006
942
  backdropColorObj->arrayGet(i, &obj1);
5007
942
  if (obj1.isNum()) {
5008
581
    backdropColor.c[i] = dblToCol(obj1.getNum());
5009
581
  }
5010
942
  obj1.free();
5011
942
      }
5012
988
    } else if (blendingColorSpace) {
5013
874
      blendingColorSpace->getDefaultColor(&backdropColor);
5014
874
    }
5015
    //~ else: need to get the parent or default color space (?)
5016
1.10k
    out->setSoftMask(state, bbox, alpha, transferFunc, &backdropColor);
5017
1.10k
    traceEnd(oldBaseMatrix, "end soft mask");
5018
83.8k
  } else if (transpGroup) {
5019
3.44k
    out->paintTransparencyGroup(state, bbox);
5020
3.44k
    traceEnd(oldBaseMatrix, "end t-group");
5021
3.44k
  }
5022
5023
84.9k
  if (blendingColorSpace) {
5024
1.22k
    delete blendingColorSpace;
5025
1.22k
  }
5026
5027
84.9k
  out->endStream(strRef->getRef());
5028
5029
84.9k
  --formDepth;
5030
84.9k
}
5031
5032
0
void Gfx::takeContentStreamStack(Gfx *oldGfx) {
5033
0
  contentStreamStack->append(oldGfx->contentStreamStack);
5034
0
}
5035
5036
96.0k
void Gfx::endOfPage() {
5037
257k
  while (state->hasSaves()) {
5038
161k
    restoreState();
5039
161k
  }
5040
99.6k
  while (markedContentStack->getLength() > 0) {
5041
3.61k
    opEndMarkedContent(NULL, 0);
5042
3.61k
  }
5043
96.0k
}
5044
5045
//------------------------------------------------------------------------
5046
// in-line image operators
5047
//------------------------------------------------------------------------
5048
5049
35.5k
void Gfx::opBeginImage(Object args[], int numArgs) {
5050
35.5k
  Stream *str;
5051
35.5k
  GBool haveLength;
5052
35.5k
  int c1, c2, c3;
5053
5054
  // NB: this function is run even if ocState is false -- doImage() is
5055
  // responsible for skipping over the inline image data
5056
5057
  // build dict/stream
5058
35.5k
  str = buildImageStream(&haveLength);
5059
5060
  // display the image
5061
35.5k
  if (str) {
5062
34.2k
    if (!doImage(NULL, str, gTrue)) {
5063
10.4k
      delete str;
5064
  
5065
    // if we have the stream length, skip to end-of-stream and then
5066
    // skip 'EI' in the original stream
5067
23.7k
    } else if (haveLength) {
5068
      // NB: getUndecodedStream() returns the EmbedStream created by
5069
      // buildImageStream()
5070
36.2k
      while ((c1 = str->getUndecodedStream()->getChar()) != EOF) ;
5071
386
      delete str;
5072
386
      str = parser->getStream();
5073
386
      c1 = str->getChar();
5074
386
      c2 = str->getChar();
5075
386
      c3 = str->lookChar();
5076
124k
      while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
5077
124k
  c1 = c2;
5078
124k
  c2 = str->getChar();
5079
124k
  c3 = str->lookChar();
5080
124k
      }
5081
5082
    // else, look for the 'EI' tag and skip it
5083
23.3k
    } else {
5084
23.3k
      c1 = str->getUndecodedStream()->getChar();
5085
23.3k
      c2 = str->getUndecodedStream()->getChar();
5086
23.3k
      c3 = str->getUndecodedStream()->lookChar();
5087
1.33M
      while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
5088
1.31M
  c1 = c2;
5089
1.31M
  c2 = str->getUndecodedStream()->getChar();
5090
1.31M
  c3 = str->getUndecodedStream()->lookChar();
5091
1.31M
      }
5092
23.3k
      delete str;
5093
23.3k
    }
5094
34.2k
  }
5095
35.5k
}
5096
5097
35.5k
Stream *Gfx::buildImageStream(GBool *haveLength) {
5098
35.5k
  Object dict;
5099
35.5k
  Object obj, lengthObj;
5100
35.5k
  char *key;
5101
35.5k
  int length;
5102
35.5k
  Stream *str;
5103
5104
  // build dictionary
5105
35.5k
  dict.initDict(xref);
5106
35.5k
  getContentObj(&obj);
5107
948k
  while (!obj.isCmd("ID") && !obj.isEOF()) {
5108
913k
    if (!obj.isName()) {
5109
664k
      error(errSyntaxError, getPos(),
5110
664k
      "Inline image dictionary key must be a name object");
5111
664k
      obj.free();
5112
664k
    } else {
5113
249k
      key = copyString(obj.getName());
5114
249k
      obj.free();
5115
249k
      getContentObj(&obj);
5116
249k
      if (obj.isEOF()) {
5117
135
  gfree(key);
5118
135
  break;
5119
135
      }
5120
248k
      if (obj.isError()) {
5121
4.84k
  gfree(key);
5122
4.84k
  obj.free();
5123
244k
      } else {
5124
244k
  dict.dictAdd(key, &obj);
5125
244k
      }
5126
248k
    }
5127
913k
    getContentObj(&obj);
5128
913k
  }
5129
35.5k
  if (obj.isEOF()) {
5130
1.24k
    error(errSyntaxError, getPos(), "End of file in inline image");
5131
1.24k
    obj.free();
5132
1.24k
    dict.free();
5133
1.24k
    return NULL;
5134
1.24k
  }
5135
34.3k
  obj.free();
5136
5137
  // check for length field
5138
34.3k
  length = 0;
5139
34.3k
  *haveLength = gFalse;
5140
34.3k
  if (!dict.dictLookup("Length", &lengthObj)->isInt()) {
5141
33.9k
    lengthObj.free();
5142
33.9k
    dict.dictLookup("L", &lengthObj);
5143
33.9k
  }
5144
34.3k
  if (lengthObj.isInt()) {
5145
444
    length = lengthObj.getInt();
5146
444
    *haveLength = gTrue;
5147
444
  }
5148
34.3k
  lengthObj.free();
5149
5150
  // make stream
5151
34.3k
  if (!(str = parser->getStream())) {
5152
101
    error(errSyntaxError, getPos(), "Invalid inline image data");
5153
101
    dict.free();
5154
101
    return NULL;
5155
101
  }
5156
34.2k
  str = new EmbedStream(str, &dict, *haveLength, (GFileOffset)length);
5157
34.2k
  str = str->addFilters(&dict);
5158
5159
34.2k
  return str;
5160
34.3k
}
5161
5162
11.6k
void Gfx::opImageData(Object args[], int numArgs) {
5163
11.6k
  error(errInternal, getPos(), "Got 'ID' operator");
5164
11.6k
}
5165
5166
18.6k
void Gfx::opEndImage(Object args[], int numArgs) {
5167
18.6k
  error(errInternal, getPos(), "Got 'EI' operator");
5168
18.6k
}
5169
5170
//------------------------------------------------------------------------
5171
// type 3 font operators
5172
//------------------------------------------------------------------------
5173
5174
14.4k
void Gfx::opSetCharWidth(Object args[], int numArgs) {
5175
14.4k
  out->type3D0(state, args[0].getNum(), args[1].getNum());
5176
14.4k
}
5177
5178
8.86k
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
5179
8.86k
  state->setIgnoreColorOps(gTrue);
5180
8.86k
  out->type3D1(state, args[0].getNum(), args[1].getNum(),
5181
8.86k
         args[2].getNum(), args[3].getNum(),
5182
8.86k
         args[4].getNum(), args[5].getNum());
5183
8.86k
}
5184
5185
//------------------------------------------------------------------------
5186
// compatibility operators
5187
//------------------------------------------------------------------------
5188
5189
2.38k
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
5190
2.38k
  ++ignoreUndef;
5191
2.38k
}
5192
5193
2.05k
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
5194
2.05k
  if (ignoreUndef > 0)
5195
1.62k
    --ignoreUndef;
5196
2.05k
}
5197
5198
//------------------------------------------------------------------------
5199
// marked content operators
5200
//------------------------------------------------------------------------
5201
5202
20.4k
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
5203
20.4k
  GfxMarkedContent *mc;
5204
20.4k
  Object obj;
5205
20.4k
  GBool ocStateNew;
5206
20.4k
  TextString *s;
5207
20.4k
  GfxMarkedContentKind mcKind;
5208
5209
20.4k
  if (printCommands) {
5210
0
    printf("  marked content: %s ", args[0].getName());
5211
0
    if (numArgs == 2) {
5212
0
      args[1].print(stdout);
5213
0
    }
5214
0
    printf("\n");
5215
0
    fflush(stdout);
5216
0
  }
5217
20.4k
  mcKind = gfxMCOther;
5218
20.4k
  if (args[0].isName("OC") && numArgs == 2 && args[1].isName() &&
5219
14.4k
      res->lookupPropertiesNF(args[1].getName(), &obj)) {
5220
8.38k
    if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) {
5221
1.66k
      ocState &= ocStateNew;
5222
1.66k
    }
5223
8.38k
    obj.free();
5224
8.38k
    mcKind = gfxMCOptionalContent;
5225
12.1k
  } else if (numArgs == 2 && args[1].isDict()) {
5226
1.93k
    if (args[0].isName("Span")) {
5227
280
      if (args[1].dictLookup("ActualText", &obj)->isString()) {
5228
63
  s = new TextString(obj.getString());
5229
63
  out->beginActualText(state, s->getUnicode(), s->getLength());
5230
63
  delete s;
5231
63
  mcKind = gfxMCActualText;
5232
63
      }
5233
280
      obj.free();
5234
280
    }
5235
1.93k
    if (args[1].dictLookup("MCID", &obj)->isInt()) {
5236
719
      out->beginStructureItem(args[0].getName(), obj.getInt(),
5237
719
            args[1].getDict());
5238
719
      if (mcKind == gfxMCActualText) {
5239
0
  mcKind = gfxMCStructureItemAndActualText;
5240
719
      } else {
5241
719
  mcKind = gfxMCStructureItem;
5242
719
      }
5243
719
    }
5244
1.93k
    obj.free();
5245
1.93k
  }
5246
5247
20.4k
  mc = new GfxMarkedContent(mcKind, ocState);
5248
20.4k
  markedContentStack->append(mc);
5249
20.4k
}
5250
5251
26.5k
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
5252
26.5k
  GfxMarkedContent *mc;
5253
26.5k
  GfxMarkedContentKind mcKind;
5254
5255
26.5k
  if (markedContentStack->getLength() > 0) {
5256
20.3k
    mc = (GfxMarkedContent *)
5257
20.3k
             markedContentStack->del(markedContentStack->getLength() - 1);
5258
20.3k
    mcKind = mc->kind;
5259
20.3k
    delete mc;
5260
20.3k
    if (mcKind == gfxMCOptionalContent) {
5261
8.38k
      if (markedContentStack->getLength() > 0) {
5262
2.50k
  mc = (GfxMarkedContent *)
5263
2.50k
           markedContentStack->get(markedContentStack->getLength() - 1);
5264
2.50k
  ocState = mc->ocState;
5265
5.87k
      } else {
5266
5.87k
  ocState = gTrue;
5267
5.87k
      }
5268
11.9k
    } else if (mcKind == gfxMCActualText) {
5269
63
      out->endActualText(state);
5270
11.8k
    } else if (mcKind == gfxMCStructureItem) {
5271
718
      out->endStructureItem();
5272
11.1k
    } else if (mcKind == gfxMCStructureItemAndActualText) {
5273
0
      out->endStructureItem();
5274
0
      out->endActualText(state);
5275
0
    }
5276
20.3k
  } else {
5277
6.25k
    error(errSyntaxWarning, getPos(), "Mismatched EMC operator");
5278
6.25k
  }
5279
26.5k
}
5280
5281
61
void Gfx::opMarkPoint(Object args[], int numArgs) {
5282
61
  if (printCommands) {
5283
0
    printf("  mark point: %s ", args[0].getName());
5284
0
    if (numArgs == 2)
5285
0
      args[1].print(stdout);
5286
0
    printf("\n");
5287
0
    fflush(stdout);
5288
0
  }
5289
61
}
5290
5291
//------------------------------------------------------------------------
5292
// misc
5293
//------------------------------------------------------------------------
5294
5295
void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
5296
12.3k
        double xMin, double yMin, double xMax, double yMax) {
5297
12.3k
  Dict *dict, *resDict;
5298
12.3k
  Object str, matrixObj, bboxObj, resObj, obj1;
5299
12.3k
  double formXMin, formYMin, formXMax, formYMax;
5300
12.3k
  double x, y, sx, sy, tx, ty;
5301
12.3k
  double m[6], bbox[4];
5302
12.3k
  double *borderColor;
5303
12.3k
  GfxColor color;
5304
12.3k
  double *dash, *dash2;
5305
12.3k
  int dashLength;
5306
12.3k
  int i;
5307
5308
  // this function assumes that we are in the default user space,
5309
  // i.e., baseMatrix = ctm
5310
5311
  // if the bounding box has zero width or height, don't draw anything
5312
  // at all
5313
12.3k
  if (xMin == xMax || yMin == yMax) {
5314
340
    return;
5315
340
  }
5316
5317
  // draw the appearance stream (if there is one)
5318
12.0k
  strRef->fetch(xref, &str);
5319
12.0k
  if (str.isStream()) {
5320
5321
    // get stream dict
5322
3.26k
    dict = str.streamGetDict();
5323
5324
    // get the form bounding box
5325
3.26k
    dict->lookup("BBox", &bboxObj);
5326
3.26k
    if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) {
5327
63
      error(errSyntaxError, getPos(), "Bad form bounding box");
5328
63
      bboxObj.free();
5329
63
      str.free();
5330
63
      return;
5331
63
    }
5332
16.0k
    for (i = 0; i < 4; ++i) {
5333
12.8k
      bboxObj.arrayGet(i, &obj1);
5334
12.8k
      if (obj1.isNum()) {
5335
12.5k
  bbox[i] = obj1.getNum();
5336
12.5k
      } else {
5337
288
  bbox[i] = 0;
5338
288
      }
5339
12.8k
      obj1.free();
5340
12.8k
    }
5341
3.20k
    bboxObj.free();
5342
5343
    // get the form matrix
5344
3.20k
    dict->lookup("Matrix", &matrixObj);
5345
3.20k
    if (matrixObj.isArray() && matrixObj.arrayGetLength() == 6) {
5346
1.75k
      for (i = 0; i < 6; ++i) {
5347
1.50k
  matrixObj.arrayGet(i, &obj1);
5348
1.50k
  if (obj1.isNum()) {
5349
1.49k
    m[i] = obj1.getNum();
5350
1.49k
  } else {
5351
8
    m[i] = 0;
5352
8
  }
5353
1.50k
  obj1.free();
5354
1.50k
      }
5355
2.95k
    } else {
5356
2.95k
      m[0] = 1; m[1] = 0;
5357
2.95k
      m[2] = 0; m[3] = 1;
5358
2.95k
      m[4] = 0; m[5] = 0;
5359
2.95k
    }
5360
3.20k
    matrixObj.free();
5361
5362
    // transform the four corners of the form bbox to default user
5363
    // space, and construct the transformed bbox
5364
3.20k
    x = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
5365
3.20k
    y = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
5366
3.20k
    formXMin = formXMax = x;
5367
3.20k
    formYMin = formYMax = y;
5368
3.20k
    x = bbox[0] * m[0] + bbox[3] * m[2] + m[4];
5369
3.20k
    y = bbox[0] * m[1] + bbox[3] * m[3] + m[5];
5370
3.20k
    if (x < formXMin) {
5371
42
      formXMin = x;
5372
3.16k
    } else if (x > formXMax) {
5373
8
      formXMax = x;
5374
8
    }
5375
3.20k
    if (y < formYMin) {
5376
21
      formYMin = y;
5377
3.18k
    } else if (y > formYMax) {
5378
3.05k
      formYMax = y;
5379
3.05k
    }
5380
3.20k
    x = bbox[2] * m[0] + bbox[1] * m[2] + m[4];
5381
3.20k
    y = bbox[2] * m[1] + bbox[1] * m[3] + m[5];
5382
3.20k
    if (x < formXMin) {
5383
6
      formXMin = x;
5384
3.20k
    } else if (x > formXMax) {
5385
3.06k
      formXMax = x;
5386
3.06k
    }
5387
3.20k
    if (y < formYMin) {
5388
12
      formYMin = y;
5389
3.19k
    } else if (y > formYMax) {
5390
44
      formYMax = y;
5391
44
    }
5392
3.20k
    x = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
5393
3.20k
    y = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
5394
3.20k
    if (x < formXMin) {
5395
0
      formXMin = x;
5396
3.20k
    } else if (x > formXMax) {
5397
8
      formXMax = x;
5398
8
    }
5399
3.20k
    if (y < formYMin) {
5400
11
      formYMin = y;
5401
3.19k
    } else if (y > formYMax) {
5402
4
      formYMax = y;
5403
4
    }
5404
5405
    // construct a mapping matrix, [sx 0  0], which maps the transformed
5406
    //                             [0  sy 0]
5407
    //                             [tx ty 1]
5408
    // bbox to the annotation rectangle
5409
3.20k
    if (formXMax - formXMin < 1e-8) {
5410
      // this shouldn't happen
5411
90
      sx = 1;
5412
3.11k
    } else {
5413
3.11k
      sx = (xMax - xMin) / (formXMax - formXMin);
5414
3.11k
    }
5415
3.20k
    if (formYMax - formYMin < 1e-8) {
5416
      // this shouldn't happen
5417
84
      sy = 1;
5418
3.12k
    } else {
5419
3.12k
      sy = (yMax - yMin) / (formYMax - formYMin);
5420
3.12k
    }
5421
3.20k
    tx = -formXMin * sx + xMin;
5422
3.20k
    ty = -formYMin * sy + yMin;
5423
5424
    // the final transform matrix is (form matrix) * (mapping matrix)
5425
3.20k
    m[0] *= sx;
5426
3.20k
    m[1] *= sy;
5427
3.20k
    m[2] *= sx;
5428
3.20k
    m[3] *= sy;
5429
3.20k
    m[4] = m[4] * sx + tx;
5430
3.20k
    m[5] = m[5] * sy + ty;
5431
5432
    // get the resources
5433
3.20k
    dict->lookup("Resources", &resObj);
5434
3.20k
    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
5435
5436
    // draw it
5437
3.20k
    drawForm(strRef, resDict, m, bbox);
5438
5439
3.20k
    resObj.free();
5440
3.20k
  }
5441
11.9k
  str.free();
5442
5443
  // draw the border
5444
11.9k
  if (borderStyle && borderStyle->getWidth() > 0 &&
5445
366
      borderStyle->getNumColorComps() > 0) {
5446
366
    borderColor = borderStyle->getColor();
5447
366
    switch (borderStyle->getNumColorComps()) {
5448
0
    case 1:
5449
0
      if (state->getStrokeColorSpace()->getMode() != csDeviceGray) {
5450
0
  state->setStrokePattern(NULL);
5451
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
5452
0
  out->updateStrokeColorSpace(state);
5453
0
      }
5454
0
      break;
5455
307
    case 3:
5456
307
      if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
5457
148
  state->setStrokePattern(NULL);
5458
148
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
5459
148
  out->updateStrokeColorSpace(state);
5460
148
      }
5461
307
      break;
5462
59
    case 4:
5463
59
      if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) {
5464
21
  state->setStrokePattern(NULL);
5465
21
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
5466
21
  out->updateStrokeColorSpace(state);
5467
21
      }
5468
59
      break;
5469
366
    }
5470
366
    color.c[0] = dblToCol(borderColor[0]);
5471
366
    color.c[1] = dblToCol(borderColor[1]);
5472
366
    color.c[2] = dblToCol(borderColor[2]);
5473
366
    color.c[3] = dblToCol(borderColor[3]);
5474
366
    state->setStrokeColor(&color);
5475
366
    out->updateStrokeColor(state);
5476
366
    state->setLineWidth(borderStyle->getWidth());
5477
366
    out->updateLineWidth(state);
5478
366
    borderStyle->getDash(&dash, &dashLength);
5479
366
    if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
5480
27
      dash2 = (double *)gmallocn(dashLength, sizeof(double));
5481
27
      memcpy(dash2, dash, dashLength * sizeof(double));
5482
27
      state->setLineDash(dash2, dashLength, 0);
5483
27
      out->updateLineDash(state);
5484
27
    }
5485
    //~ this doesn't currently handle the beveled and engraved styles
5486
366
    state->clearPath();
5487
366
    state->moveTo(xMin, yMin);
5488
366
    state->lineTo(xMax, yMin);
5489
366
    if (borderStyle->getType() != annotBorderUnderlined) {
5490
366
      state->lineTo(xMax, yMax);
5491
366
      state->lineTo(xMin, yMax);
5492
366
      state->closePath();
5493
366
    }
5494
366
    out->stroke(state);
5495
366
  }
5496
11.9k
}
5497
5498
466k
void Gfx::saveState() {
5499
466k
  out->saveState(state);
5500
466k
  state = state->save();
5501
466k
}
5502
5503
596k
void Gfx::restoreState() {
5504
596k
  state = state->restore();
5505
596k
  out->restoreState(state);
5506
596k
}
5507
5508
// Create a new state stack, and initialize it with a copy of the
5509
// current state.
5510
2.17M
GfxState *Gfx::saveStateStack() {
5511
2.17M
  GfxState *oldState;
5512
5513
2.17M
  out->saveState(state);
5514
2.17M
  oldState = state;
5515
2.17M
  state = state->copy(gTrue);
5516
2.17M
  return oldState;
5517
2.17M
}
5518
5519
// Switch back to the previous state stack.
5520
2.17M
void Gfx::restoreStateStack(GfxState *oldState) {
5521
2.22M
  while (state->hasSaves()) {
5522
45.4k
    restoreState();
5523
45.4k
  }
5524
2.17M
  delete state;
5525
2.17M
  state = oldState;
5526
2.17M
  out->restoreState(state);
5527
2.17M
}
5528
5529
1.80M
void Gfx::pushResources(Dict *resDict) {
5530
1.80M
  res = new GfxResources(xref, resDict, res);
5531
1.80M
}
5532
5533
2.03M
void Gfx::popResources() {
5534
2.03M
  GfxResources *resPtr;
5535
5536
2.03M
  resPtr = res->getNext();
5537
2.03M
  delete res;
5538
2.03M
  res = resPtr;
5539
2.03M
}