Coverage Report

Created: 2026-08-22 06:39

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
9.30M
#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
9.30M
#define numOps (sizeof(opTab) / sizeof(Operator))
283
284
//------------------------------------------------------------------------
285
// GfxResources
286
//------------------------------------------------------------------------
287
288
1.94M
GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
289
1.94M
  Object obj1, obj2;
290
1.94M
  Ref r;
291
292
1.94M
  if (resDict) {
293
1.85M
    valid = gTrue;
294
295
    // build font dictionary
296
1.85M
    fonts = NULL;
297
1.85M
    resDict->lookupNF("Font", &obj1);
298
1.85M
    if (obj1.isRef()) {
299
378
      obj1.fetch(xref, &obj2);
300
378
      if (obj2.isDict()) {
301
253
  r = obj1.getRef();
302
253
  fonts = new GfxFontDict(xref, &r, obj2.getDict());
303
253
      }
304
378
      obj2.free();
305
1.85M
    } else if (obj1.isDict()) {
306
121k
      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
307
121k
    }
308
1.85M
    obj1.free();
309
310
    // get XObject dictionary
311
1.85M
    resDict->lookup("XObject", &xObjDict);
312
313
    // get color space dictionary
314
1.85M
    resDict->lookup("ColorSpace", &colorSpaceDict);
315
316
    // get pattern dictionary
317
1.85M
    resDict->lookup("Pattern", &patternDict);
318
319
    // get shading dictionary
320
1.85M
    resDict->lookup("Shading", &shadingDict);
321
322
    // get graphics state parameter dictionary
323
1.85M
    resDict->lookup("ExtGState", &gStateDict);
324
325
    // get properties dictionary
326
1.85M
    resDict->lookup("Properties", &propsDict);
327
328
1.85M
  } else {
329
92.0k
    valid = gFalse;
330
92.0k
    fonts = NULL;
331
92.0k
    xObjDict.initNull();
332
92.0k
    colorSpaceDict.initNull();
333
92.0k
    patternDict.initNull();
334
92.0k
    shadingDict.initNull();
335
92.0k
    gStateDict.initNull();
336
92.0k
    propsDict.initNull();
337
92.0k
  }
338
339
1.94M
  next = nextA;
340
1.94M
}
341
342
1.94M
GfxResources::~GfxResources() {
343
1.94M
  if (fonts) {
344
121k
    delete fonts;
345
121k
  }
346
1.94M
  xObjDict.free();
347
1.94M
  colorSpaceDict.free();
348
1.94M
  patternDict.free();
349
1.94M
  shadingDict.free();
350
1.94M
  gStateDict.free();
351
1.94M
  propsDict.free();
352
1.94M
}
353
354
126k
GfxFont *GfxResources::lookupFont(char *name) {
355
126k
  GfxFont *font;
356
126k
  GfxResources *resPtr;
357
358
219k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
359
166k
    if (resPtr->fonts) {
360
135k
      if ((font = resPtr->fonts->lookup(name))) {
361
73.0k
  return font;
362
73.0k
      }
363
135k
    }
364
166k
  }
365
53.3k
  error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name);
366
53.3k
  return NULL;
367
126k
}
368
369
0
GfxFont *GfxResources::lookupFontByRef(Ref ref) {
370
0
  GfxFont *font;
371
0
  GfxResources *resPtr;
372
373
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
374
0
    if (resPtr->fonts) {
375
0
      if ((font = resPtr->fonts->lookupByRef(ref))) {
376
0
  return font;
377
0
      }
378
0
    }
379
0
  }
380
0
  error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen);
381
0
  return NULL;
382
0
}
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
50.3k
GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
399
50.3k
  GfxResources *resPtr;
400
401
97.2k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
402
86.0k
    if (resPtr->xObjDict.isDict()) {
403
67.1k
      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
404
39.0k
  return gTrue;
405
28.0k
      obj->free();
406
28.0k
    }
407
86.0k
  }
408
11.2k
  error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
409
11.2k
  return gFalse;
410
50.3k
}
411
412
void GfxResources::lookupColorSpace(const char *name, Object *obj,
413
73.2k
            GBool inherit) {
414
73.2k
  GfxResources *resPtr;
415
416
  //~ should also test for G, RGB, and CMYK - but only in inline images (?)
417
73.2k
  if (!strcmp(name, "DeviceGray") ||
418
66.2k
      !strcmp(name, "DeviceRGB") ||
419
60.6k
      !strcmp(name, "DeviceCMYK")) {
420
12.7k
    obj->initNull();
421
12.7k
    return;
422
12.7k
  }
423
137k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
424
90.6k
    if (resPtr->colorSpaceDict.isDict()) {
425
19.9k
      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
426
13.7k
  return;
427
13.7k
      }
428
6.23k
      obj->free();
429
6.23k
    }
430
76.8k
    if (!inherit && valid) {
431
0
      break;
432
0
    }
433
76.8k
  }
434
46.7k
  obj->initNull();
435
46.7k
}
436
437
GfxPattern *GfxResources::lookupPattern(const char *name
438
38.6k
          ) {
439
38.6k
  GfxResources *resPtr;
440
38.6k
  GfxPattern *pattern;
441
38.6k
  Object objRef, obj;
442
443
51.9k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
444
41.8k
    if (resPtr->patternDict.isDict()) {
445
32.6k
      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
446
28.6k
  resPtr->patternDict.dictLookupNF(name, &objRef);
447
28.6k
  pattern = GfxPattern::parse(&objRef, &obj
448
28.6k
            );
449
28.6k
  objRef.free();
450
28.6k
  obj.free();
451
28.6k
  return pattern;
452
28.6k
      }
453
4.08k
      obj.free();
454
4.08k
    }
455
41.8k
  }
456
10.0k
  error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name);
457
10.0k
  return NULL;
458
38.6k
}
459
460
GfxShading *GfxResources::lookupShading(const char *name
461
17.9k
          ) {
462
17.9k
  GfxResources *resPtr;
463
17.9k
  GfxShading *shading;
464
17.9k
  Object obj;
465
466
44.7k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
467
30.7k
    if (resPtr->shadingDict.isDict()) {
468
5.01k
      if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
469
3.88k
  shading = GfxShading::parse(&obj
470
3.88k
            );
471
3.88k
  obj.free();
472
3.88k
  return shading;
473
3.88k
      }
474
1.12k
      obj.free();
475
1.12k
    }
476
30.7k
  }
477
14.0k
  error(errSyntaxError, -1, "Unknown shading '{0:s}'", name);
478
14.0k
  return NULL;
479
17.9k
}
480
481
40.5k
GBool GfxResources::lookupGState(const char *name, Object *obj) {
482
40.5k
  GfxResources *resPtr;
483
484
119k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
485
92.7k
    if (resPtr->gStateDict.isDict()) {
486
23.0k
      if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
487
13.6k
  return gTrue;
488
13.6k
      }
489
9.38k
      obj->free();
490
9.38k
    }
491
92.7k
  }
492
26.8k
  error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name);
493
26.8k
  return gFalse;
494
40.5k
}
495
496
16.0k
GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) {
497
16.0k
  GfxResources *resPtr;
498
499
22.4k
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
500
16.0k
    if (resPtr->propsDict.isDict()) {
501
15.2k
      if (!resPtr->propsDict.dictLookupNF(name, obj)->isNull()) {
502
9.61k
  return gTrue;
503
9.61k
      }
504
5.68k
      obj->free();
505
5.68k
    }
506
16.0k
  }
507
6.39k
  error(errSyntaxError, -1, "Properties '{0:s}' is unknown", name);
508
6.39k
  return gFalse;
509
16.0k
}
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
171k
   void *abortCheckCbkDataA) {
521
171k
  int i;
522
523
171k
  doc = docA;
524
171k
  xref = doc->getXRef();
525
171k
  subPage = gFalse;
526
171k
  printCommands = globalParams->getPrintCommands();
527
171k
  defaultFont = NULL;
528
529
  // start the resource stack
530
171k
  res = new GfxResources(xref, resDict, NULL);
531
532
  // initialize
533
171k
  out = outA;
534
171k
  state = new GfxState(localParams, hDPI, vDPI,
535
171k
           box, rotate, out->upsideDown());
536
171k
  fontChanged = gFalse;
537
171k
  haveSavedClipPath = gFalse;
538
171k
  clip = clipNone;
539
171k
  ignoreUndef = 0;
540
171k
  out->startPage(pageNum, state);
541
171k
  out->setDefaultCTM(state->getCTM());
542
171k
  out->updateAll(state);
543
1.19M
  for (i = 0; i < 6; ++i) {
544
1.02M
    baseMatrix[i] = state->getCTM()[i];
545
1.02M
  }
546
171k
  formDepth = 0;
547
171k
  markedContentStack = new GList();
548
171k
  ocState = gTrue;
549
171k
  parser = NULL;
550
171k
  contentStreamStack = new GList();
551
171k
  abortCheckCbk = abortCheckCbkA;
552
171k
  abortCheckCbkData = abortCheckCbkDataA;
553
554
  // set crop box
555
171k
  if (cropBox) {
556
15.7k
    state->moveTo(cropBox->x1, cropBox->y1);
557
15.7k
    state->lineTo(cropBox->x2, cropBox->y1);
558
15.7k
    state->lineTo(cropBox->x2, cropBox->y2);
559
15.7k
    state->lineTo(cropBox->x1, cropBox->y2);
560
15.7k
    state->closePath();
561
15.7k
    state->clip();
562
15.7k
    out->clip(state);
563
15.7k
    state->clearPath();
564
15.7k
  }
565
171k
}
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
171k
Gfx::~Gfx() {
614
171k
  if (defaultFont) {
615
10.7k
    delete defaultFont;
616
10.7k
  }
617
171k
  if (!subPage) {
618
171k
    out->endPage();
619
171k
  }
620
171k
  while (state->hasSaves()) {
621
0
    restoreState();
622
0
  }
623
171k
  delete state;
624
342k
  while (res) {
625
171k
    popResources();
626
171k
  }
627
171k
  deleteGList(markedContentStack, GfxMarkedContent);
628
171k
  delete contentStreamStack;
629
171k
}
630
631
206k
void Gfx::display(Object *objRef, GBool topLevel) {
632
206k
  Object obj1, obj2;
633
206k
  int i;
634
635
206k
  objRef->fetch(xref, &obj1);
636
206k
  if (obj1.isArray()) {
637
4.60k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
638
3.58k
      obj1.arrayGetNF(i, &obj2);
639
3.58k
      if (checkForContentStreamLoop(&obj2)) {
640
0
  obj2.free();
641
0
  obj1.free();
642
0
  return;
643
0
      }
644
3.58k
      obj2.free();
645
3.58k
    }
646
3.02k
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
647
2.30k
      obj1.arrayGet(i, &obj2);
648
2.30k
      if (!obj2.isStream()) {
649
307
  error(errSyntaxError, -1, "Invalid object type for content stream");
650
307
  obj2.free();
651
307
  obj1.free();
652
307
  return;
653
307
      }
654
2.00k
      obj2.free();
655
2.00k
    }
656
716
    contentStreamStack->append(&obj1);
657
205k
  } else if (obj1.isStream()) {
658
205k
    if (checkForContentStreamLoop(objRef)) {
659
68.4k
      obj1.free();
660
68.4k
      return;
661
68.4k
    }
662
137k
    contentStreamStack->append(objRef);
663
137k
  } else {
664
148
    error(errSyntaxError, -1, "Invalid object type for content stream");
665
148
    obj1.free();
666
148
    return;
667
148
  }
668
137k
  parser = new Parser(xref, new Lexer(xref, &obj1), gFalse);
669
137k
  go(topLevel);
670
137k
  delete parser;
671
137k
  parser = NULL;
672
137k
  contentStreamStack->del(contentStreamStack->getLength() - 1);
673
137k
  obj1.free();
674
137k
}
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
209k
GBool Gfx::checkForContentStreamLoop(Object *ref) {
679
209k
  Object *objPtr;
680
209k
  Object obj1;
681
209k
  int i, j;
682
683
209k
  if (ref->isRef()) {
684
421k
    for (i = 0; i < contentStreamStack->getLength(); ++i) {
685
284k
      objPtr = (Object *)contentStreamStack->get(i);
686
284k
      if (objPtr->isRef()) {
687
279k
  if (ref->getRefNum() == objPtr->getRefNum() &&
688
68.4k
      ref->getRefGen() == objPtr->getRefGen()) {
689
68.4k
    error(errSyntaxError, -1, "Loop in content streams");
690
68.4k
    return gTrue;
691
68.4k
  }
692
279k
      } else if (objPtr->isArray()) {
693
9.19k
  for (j = 0; j < objPtr->arrayGetLength(); ++j) {
694
4.85k
    objPtr->arrayGetNF(j, &obj1);
695
4.85k
    if (obj1.isRef()) {
696
4.85k
      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
4.85k
    }
703
4.85k
    obj1.free();
704
4.85k
  }
705
4.33k
      }
706
284k
    }
707
205k
  }
708
140k
  return gFalse;
709
209k
}
710
711
137k
void Gfx::go(GBool topLevel) {
712
137k
  Object obj;
713
137k
  Object args[maxArgs];
714
137k
  GBool aborted;
715
137k
  int numArgs, i;
716
137k
  int errCount;
717
718
  // scan a sequence of objects
719
137k
  opCounter = 0;
720
137k
  aborted = gFalse;
721
137k
  errCount = 0;
722
137k
  numArgs = 0;
723
137k
  getContentObj(&obj);
724
33.1M
  while (!obj.isEOF()) {
725
726
    // check for an abort
727
33.0M
    ++opCounter;
728
33.0M
    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
33.0M
    if (obj.isCmd()) {
738
9.30M
      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
9.30M
      if (!execOp(&obj, args, numArgs)) {
748
4.68M
  ++errCount;
749
4.68M
      }
750
9.30M
      obj.free();
751
31.7M
      for (i = 0; i < numArgs; ++i)
752
22.4M
  args[i].free();
753
9.30M
      numArgs = 0;
754
755
      // check for too many errors
756
9.30M
      if (errCount > contentStreamErrorLimit) {
757
2.52k
  error(errSyntaxError, -1,
758
2.52k
        "Too many errors - giving up on this content stream");
759
2.52k
  break;
760
2.52k
      }
761
762
    // got an argument - save it
763
23.7M
    } else if (numArgs < maxArgs) {
764
22.5M
      args[numArgs++] = obj;
765
766
    // too many arguments - something is wrong
767
22.5M
    } else {
768
1.21M
      error(errSyntaxError, getPos(), "Too many args in content stream");
769
1.21M
      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.21M
      obj.free();
776
1.21M
    }
777
778
    // grab the next object
779
33.0M
    getContentObj(&obj);
780
33.0M
  }
781
137k
  obj.free();
782
783
  // args at end with no command
784
137k
  if (numArgs > 0) {
785
22.9k
    if (!aborted) {
786
22.9k
      error(errSyntaxError, getPos(), "Leftover args in content stream");
787
22.9k
      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
22.9k
    }
797
82.5k
    for (i = 0; i < numArgs; ++i) {
798
59.5k
      args[i].free();
799
59.5k
    }
800
22.9k
  }
801
137k
}
802
803
34.2M
void Gfx::getContentObj(Object *obj) {
804
34.2M
  parser->getObj(obj);
805
34.2M
  if (obj->isRef()) {
806
27.7k
    error(errSyntaxError, getPos(), "Indirect reference in content stream");
807
27.7k
    obj->free();
808
27.7k
    obj->initError();
809
27.7k
  }
810
34.2M
}
811
812
// Returns true if successful, false on error.
813
9.30M
GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
814
9.30M
  Operator *op;
815
9.30M
  char *name;
816
9.30M
  Object *argPtr;
817
9.30M
  int i;
818
819
  // find operator
820
9.30M
  name = cmd->getCmd();
821
9.30M
  if (!(op = findOp(name))) {
822
3.74M
    if (ignoreUndef > 0) {
823
93.1k
      return gTrue;
824
93.1k
    }
825
3.64M
    error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
826
3.64M
    return gFalse;
827
3.74M
  }
828
829
  // type check args
830
5.56M
  argPtr = args;
831
5.56M
  if (op->numArgs >= 0) {
832
5.49M
    if (numArgs < op->numArgs) {
833
890k
      error(errSyntaxError, getPos(),
834
890k
      "Too few ({0:d}) args to '{1:s}' operator", numArgs, name);
835
890k
      return gFalse;
836
890k
    }
837
4.60M
    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.30M
      argPtr += numArgs - op->numArgs;
843
1.30M
      numArgs = op->numArgs;
844
1.30M
    }
845
4.60M
  } else {
846
72.2k
    if (numArgs > -op->numArgs) {
847
3.00k
      error(errSyntaxWarning, getPos(),
848
3.00k
      "Too many ({0:d}) args to '{1:s}' operator",
849
3.00k
      numArgs, name);
850
3.00k
    }
851
72.2k
  }
852
10.3M
  for (i = 0; i < numArgs; ++i) {
853
5.83M
    if (!checkArg(&argPtr[i], op->tchk[i])) {
854
148k
      error(errSyntaxError, getPos(),
855
148k
      "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
856
148k
      i, name, argPtr[i].getTypeName());
857
148k
      return gFalse;
858
148k
    }
859
5.83M
  }
860
861
  // do it
862
4.52M
  (this->*op->func)(argPtr, numArgs);
863
864
4.52M
  return gTrue;
865
4.67M
}
866
867
9.30M
Operator *Gfx::findOp(char *name) {
868
9.30M
  int a, b, m, cmp;
869
870
9.30M
  a = -1;
871
9.30M
  b = numOps;
872
9.30M
  cmp = 0; // make gcc happy
873
  // invariant: opTab[a] < name < opTab[b]
874
62.5M
  while (b - a > 1) {
875
53.1M
    m = (a + b) / 2;
876
53.1M
    cmp = strcmp(opTab[m].name, name);
877
53.1M
    if (cmp < 0)
878
28.6M
      a = m;
879
24.5M
    else if (cmp > 0)
880
19.0M
      b = m;
881
5.56M
    else
882
5.56M
      a = b = m;
883
53.1M
  }
884
9.30M
  if (cmp != 0)
885
3.74M
    return NULL;
886
5.56M
  return &opTab[a];
887
9.30M
}
888
889
5.83M
GBool Gfx::checkArg(Object *arg, TchkType type) {
890
5.83M
  switch (type) {
891
0
  case tchkBool:   return arg->isBool();
892
86.8k
  case tchkInt:    return arg->isInt();
893
4.89M
  case tchkNum:    return arg->isNum();
894
345k
  case tchkString: return arg->isString();
895
365k
  case tchkName:   return arg->isName();
896
67.8k
  case tchkArray:  return arg->isArray();
897
18.9k
  case tchkProps:  return arg->isDict() || arg->isName();
898
53.8k
  case tchkSCN:    return arg->isNum() || arg->isName();
899
0
  case tchkNone:   return gFalse;
900
5.83M
  }
901
0
  return gFalse;
902
5.83M
}
903
904
8.97M
GFileOffset Gfx::getPos() {
905
8.97M
  return parser ? parser->getPos() : -1;
906
8.97M
}
907
908
//------------------------------------------------------------------------
909
// graphics state operators
910
//------------------------------------------------------------------------
911
912
252k
void Gfx::opSave(Object args[], int numArgs) {
913
252k
  saveState();
914
252k
}
915
916
256k
void Gfx::opRestore(Object args[], int numArgs) {
917
256k
  restoreState();
918
256k
}
919
920
98.4k
void Gfx::opConcat(Object args[], int numArgs) {
921
98.4k
  state->concatCTM(args[0].getNum(), args[1].getNum(),
922
98.4k
       args[2].getNum(), args[3].getNum(),
923
98.4k
       args[4].getNum(), args[5].getNum());
924
98.4k
  out->updateCTM(state, args[0].getNum(), args[1].getNum(),
925
98.4k
     args[2].getNum(), args[3].getNum(),
926
98.4k
     args[4].getNum(), args[5].getNum());
927
98.4k
  fontChanged = gTrue;
928
98.4k
}
929
930
6.31k
void Gfx::opSetDash(Object args[], int numArgs) {
931
6.31k
  Array *a;
932
6.31k
  int length;
933
6.31k
  Object obj;
934
6.31k
  double *dash;
935
6.31k
  int i;
936
937
6.31k
  a = args[0].getArray();
938
6.31k
  length = a->getLength();
939
6.31k
  if (length == 0) {
940
5.42k
    dash = NULL;
941
5.42k
  } else {
942
896
    dash = (double *)gmallocn(length, sizeof(double));
943
15.2k
    for (i = 0; i < length; ++i) {
944
14.3k
      dash[i] = a->get(i, &obj)->getNum();
945
14.3k
      obj.free();
946
14.3k
    }
947
896
  }
948
6.31k
  state->setLineDash(dash, length, args[1].getNum());
949
6.31k
  out->updateLineDash(state);
950
6.31k
}
951
952
121k
void Gfx::opSetFlat(Object args[], int numArgs) {
953
121k
  state->setFlatness((int)args[0].getNum());
954
121k
  out->updateFlatness(state);
955
121k
}
956
957
12.4k
void Gfx::opSetLineJoin(Object args[], int numArgs) {
958
12.4k
  int lineJoin;
959
960
12.4k
  lineJoin = args[0].getInt();
961
12.4k
  if (lineJoin < 0 || lineJoin > 2) {
962
4.88k
    lineJoin = 0;
963
4.88k
  }
964
12.4k
  state->setLineJoin(lineJoin);
965
12.4k
  out->updateLineJoin(state);
966
12.4k
}
967
968
59.3k
void Gfx::opSetLineCap(Object args[], int numArgs) {
969
59.3k
  int lineCap;
970
971
59.3k
  lineCap = args[0].getInt();
972
59.3k
  if (lineCap < 0 || lineCap > 2) {
973
20.5k
    lineCap = 0;
974
20.5k
  }
975
59.3k
  state->setLineCap(lineCap);
976
59.3k
  out->updateLineCap(state);
977
59.3k
}
978
979
23.6k
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
980
23.6k
  state->setMiterLimit(args[0].getNum());
981
23.6k
  out->updateMiterLimit(state);
982
23.6k
}
983
984
82.5k
void Gfx::opSetLineWidth(Object args[], int numArgs) {
985
82.5k
  state->setLineWidth(args[0].getNum());
986
82.5k
  out->updateLineWidth(state);
987
82.5k
}
988
989
40.5k
void Gfx::opSetExtGState(Object args[], int numArgs) {
990
40.5k
  Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj;
991
40.5k
  Object args2[2];
992
40.5k
  GfxBlendMode mode;
993
40.5k
  GBool haveFillOP;
994
40.5k
  Function *funcs[4];
995
40.5k
  GBool alpha, knockout;
996
40.5k
  double opac;
997
40.5k
  int i;
998
999
40.5k
  if (!res->lookupGState(args[0].getName(), &obj1)) {
1000
26.8k
    return;
1001
26.8k
  }
1002
13.6k
  if (!obj1.isDict()) {
1003
2.11k
    error(errSyntaxError, getPos(),
1004
2.11k
    "ExtGState '{0:s}' is wrong type", args[0].getName());
1005
2.11k
    obj1.free();
1006
2.11k
    return;
1007
2.11k
  }
1008
11.5k
  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.5k
  if (obj1.dictLookup("LW", &obj2)->isNum()) {
1016
217
    opSetLineWidth(&obj2, 1);
1017
217
  }
1018
11.5k
  obj2.free();
1019
11.5k
  if (obj1.dictLookup("LC", &obj2)->isInt()) {
1020
217
    opSetLineCap(&obj2, 1);
1021
217
  }
1022
11.5k
  obj2.free();
1023
11.5k
  if (obj1.dictLookup("LJ", &obj2)->isInt()) {
1024
217
    opSetLineJoin(&obj2, 1);
1025
217
  }
1026
11.5k
  obj2.free();
1027
11.5k
  if (obj1.dictLookup("ML", &obj2)->isNum()) {
1028
217
    opSetMiterLimit(&obj2, 1);
1029
217
  }
1030
11.5k
  obj2.free();
1031
11.5k
  if (obj1.dictLookup("D", &obj2)->isArray() &&
1032
117
      obj2.arrayGetLength() == 2) {
1033
104
    obj2.arrayGet(0, &args2[0]);
1034
104
    obj2.arrayGet(1, &args2[1]);
1035
104
    if (args2[0].isArray() && args2[1].isNum()) {
1036
0
      opSetDash(args2, 2);
1037
0
    }
1038
104
    args2[0].free();
1039
104
    args2[1].free();
1040
104
  }
1041
11.5k
  obj2.free();
1042
11.5k
  if (obj1.dictLookup("FL", &obj2)->isNum()) {
1043
0
    opSetFlat(&obj2, 1);
1044
0
  }
1045
11.5k
  obj2.free();
1046
11.5k
  if (obj1.dictLookup("RI", &obj2)->isName()) {
1047
0
    opSetRenderingIntent(&obj2, 1);
1048
0
  }
1049
11.5k
  obj2.free();
1050
1051
  // font
1052
11.5k
  if (obj1.dictLookup("Font", &obj2)->isArray() &&
1053
0
      obj2.arrayGetLength() == 2) {
1054
0
    obj2.arrayGetNF(0, &obj3);
1055
0
    obj2.arrayGetNF(1, &obj4);
1056
0
    if (obj3.isRef() && obj4.isNum()) {
1057
0
      doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum());
1058
0
    }
1059
0
    obj3.free();
1060
0
    obj4.free();
1061
0
  }
1062
11.5k
  obj2.free();
1063
1064
  // transparency support: blend mode, fill/stroke opacity
1065
11.5k
  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
1066
5.36k
    if (state->parseBlendMode(&obj2, &mode)) {
1067
4.66k
      state->setBlendMode(mode);
1068
4.66k
      out->updateBlendMode(state);
1069
4.66k
    } else {
1070
702
      error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState");
1071
702
    }
1072
5.36k
  }
1073
11.5k
  obj2.free();
1074
11.5k
  if (obj1.dictLookup("ca", &obj2)->isNum()) {
1075
6.09k
    opac = obj2.getNum();
1076
6.09k
    state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
1077
6.09k
    out->updateFillOpacity(state);
1078
6.09k
  }
1079
11.5k
  obj2.free();
1080
11.5k
  if (obj1.dictLookup("CA", &obj2)->isNum()) {
1081
6.01k
    opac = obj2.getNum();
1082
6.01k
    state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
1083
6.01k
    out->updateStrokeOpacity(state);
1084
6.01k
  }
1085
11.5k
  obj2.free();
1086
1087
  // fill/stroke overprint, overprint mode
1088
11.5k
  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
1089
2.56k
    if (!state->getIgnoreColorOps()) {
1090
2.53k
      state->setFillOverprint(obj2.getBool());
1091
2.53k
      out->updateFillOverprint(state);
1092
2.53k
    } else {
1093
26
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1094
26
      " in uncolored Type 3 char or tiling pattern");
1095
26
    }
1096
2.56k
  }
1097
11.5k
  obj2.free();
1098
11.5k
  if (obj1.dictLookup("OP", &obj2)->isBool()) {
1099
2.18k
    if (!state->getIgnoreColorOps()) {
1100
2.16k
      state->setStrokeOverprint(obj2.getBool());
1101
2.16k
      out->updateStrokeOverprint(state);
1102
2.16k
      if (!haveFillOP) {
1103
559
  state->setFillOverprint(obj2.getBool());
1104
559
  out->updateFillOverprint(state);
1105
559
      }
1106
2.16k
    } else {
1107
26
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1108
26
      " in uncolored Type 3 char or tiling pattern");
1109
26
    }
1110
2.18k
  }
1111
11.5k
  obj2.free();
1112
11.5k
  if (obj1.dictLookup("OPM", &obj2)->isInt()) {
1113
2.14k
    if (!state->getIgnoreColorOps()) {
1114
2.12k
      state->setOverprintMode(obj2.getInt());
1115
2.12k
      out->updateOverprintMode(state);
1116
2.12k
    } else {
1117
18
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1118
18
      " in uncolored Type 3 char or tiling pattern");
1119
18
    }
1120
2.14k
  }
1121
11.5k
  obj2.free();
1122
1123
  // stroke adjust
1124
11.5k
  if (obj1.dictLookup("SA", &obj2)->isBool()) {
1125
2.46k
    state->setStrokeAdjust(obj2.getBool());
1126
2.46k
    out->updateStrokeAdjust(state);
1127
2.46k
  }
1128
11.5k
  obj2.free();
1129
1130
  // transfer function
1131
11.5k
  if (obj1.dictLookup("TR2", &obj2)->isNull()) {
1132
11.4k
    obj2.free();
1133
11.4k
    obj1.dictLookup("TR", &obj2);
1134
11.4k
  }
1135
11.5k
  if (!obj2.isNull()) {
1136
1.82k
    if (!state->getIgnoreColorOps()) {
1137
1.82k
      if (obj2.isName("Default") ||
1138
1.68k
    obj2.isName("Identity")) {
1139
575
  funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
1140
575
  state->setTransfer(funcs);
1141
575
  out->updateTransfer(state);
1142
1.25k
      } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
1143
218
  for (i = 0; i < 4; ++i) {
1144
189
    obj2.arrayGet(i, &obj3);
1145
189
    funcs[i] = Function::parse(&obj3, 1, 1);
1146
189
    obj3.free();
1147
189
    if (!funcs[i]) {
1148
47
      break;
1149
47
    }
1150
189
  }
1151
76
  if (i == 4) {
1152
29
    state->setTransfer(funcs);
1153
29
    out->updateTransfer(state);
1154
29
  }
1155
1.17k
      } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
1156
772
  if ((funcs[0] = Function::parse(&obj2, 1, 1))) {
1157
185
    funcs[1] = funcs[2] = funcs[3] = NULL;
1158
185
    state->setTransfer(funcs);
1159
185
    out->updateTransfer(state);
1160
185
  }
1161
772
      } else {
1162
406
  error(errSyntaxError, getPos(),
1163
406
        "Invalid transfer function in ExtGState");
1164
406
      }
1165
1.82k
    } else {
1166
0
      error(errSyntaxWarning, getPos(), "Ignoring transfer function setting"
1167
0
      " in uncolored Type 3 char or tiling pattern");
1168
0
    }
1169
1.82k
  }
1170
11.5k
  obj2.free();
1171
1172
  // alpha is shape
1173
11.5k
  if (obj1.dictLookup("AIS", &obj2)->isBool()) {
1174
3.89k
    state->setAlphaIsShape(obj2.getBool());
1175
3.89k
    out->updateAlphaIsShape(state);
1176
3.89k
  }
1177
11.5k
  obj2.free();
1178
1179
  // soft mask
1180
11.5k
  if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
1181
4.02k
    if (obj2.isName("None")) {
1182
1.05k
      out->clearSoftMask(state);
1183
2.96k
    } else if (obj2.isDict()) {
1184
2.15k
      obj2.dictLookup("S", &obj3);
1185
2.15k
      if (obj3.isName("Alpha")) {
1186
532
  alpha = gTrue;
1187
1.61k
      } else if (obj3.isName("Luminosity")) {
1188
901
  alpha = gFalse;
1189
901
      } else {
1190
717
  error(errSyntaxError, getPos(),
1191
717
        "Missing S (subtype) entry in soft mask");
1192
717
  alpha = gFalse;
1193
717
      }
1194
2.15k
      obj3.free();
1195
2.15k
      funcs[0] = NULL;
1196
2.15k
      if (!obj2.dictLookup("TR", &obj3)->isNull()) {
1197
827
  if (obj3.isName("Default") ||
1198
827
      obj3.isName("Identity")) {
1199
0
    funcs[0] = NULL;
1200
827
  } else {
1201
827
    if (!(funcs[0] = Function::parse(&obj3, 1, 1))) {
1202
817
      error(errSyntaxError, getPos(),
1203
817
      "Invalid transfer function in soft mask in ExtGState");
1204
817
      delete funcs[0];
1205
817
      funcs[0] = NULL;
1206
817
    }
1207
827
  }
1208
827
      }
1209
2.15k
      obj3.free();
1210
2.15k
      obj2.dictLookup("BC", &backdropColorObj);
1211
2.15k
      if (obj2.dictLookup("G", &obj3)->isStream()) {
1212
1.42k
  if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
1213
1.07k
    knockout = gFalse;
1214
1.07k
    if (obj4.dictLookup("K", &obj5)->isBool()) {
1215
596
      knockout = obj5.getBool();
1216
596
    }
1217
1.07k
    obj5.free();
1218
1.07k
    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.07k
    doSoftMask(&obj3, &objRef3, alpha, gTrue, knockout, funcs[0],
1223
1.07k
         &backdropColorObj);
1224
1.07k
    objRef3.free();
1225
1.07k
    if (funcs[0]) {
1226
10
      delete funcs[0];
1227
10
    }
1228
1.07k
  } else {
1229
348
    error(errSyntaxError, getPos(),
1230
348
    "Invalid soft mask in ExtGState - missing group");
1231
348
  }
1232
1.42k
  obj4.free();
1233
1.42k
      } else {
1234
727
  error(errSyntaxError, getPos(),
1235
727
        "Invalid soft mask in ExtGState - missing group");
1236
727
      }
1237
2.15k
      obj3.free();
1238
2.15k
      backdropColorObj.free();
1239
2.15k
    } else if (!obj2.isNull()) {
1240
815
      error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState");
1241
815
    }
1242
4.02k
  }
1243
11.5k
  obj2.free();
1244
1245
11.5k
  obj1.free();
1246
11.5k
}
1247
1248
void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
1249
         GBool isolated, GBool knockout,
1250
1.07k
         Function *transferFunc, Object *backdropColorObj) {
1251
1.07k
  Dict *dict, *resDict;
1252
1.07k
  double m[6], bbox[4];
1253
1.07k
  Object obj1, obj2;
1254
1.07k
  int i;
1255
1256
  // get stream dict
1257
1.07k
  dict = str->streamGetDict();
1258
1259
  // check form type
1260
1.07k
  dict->lookup("FormType", &obj1);
1261
1.07k
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
1262
148
    error(errSyntaxError, getPos(), "Unknown form type");
1263
148
  }
1264
1.07k
  obj1.free();
1265
1266
  // get bounding box
1267
1.07k
  dict->lookup("BBox", &obj1);
1268
1.07k
  if (!obj1.isArray()) {
1269
30
    obj1.free();
1270
30
    error(errSyntaxError, getPos(), "Bad form bounding box");
1271
30
    return;
1272
30
  }
1273
5.22k
  for (i = 0; i < 4; ++i) {
1274
4.18k
    obj1.arrayGet(i, &obj2);
1275
4.18k
    bbox[i] = obj2.getNum();
1276
4.18k
    obj2.free();
1277
4.18k
  }
1278
1.04k
  obj1.free();
1279
1280
  // get matrix
1281
1.04k
  dict->lookup("Matrix", &obj1);
1282
1.04k
  if (obj1.isArray()) {
1283
2.45k
    for (i = 0; i < 6; ++i) {
1284
2.10k
      obj1.arrayGet(i, &obj2);
1285
2.10k
      m[i] = obj2.getNum();
1286
2.10k
      obj2.free();
1287
2.10k
    }
1288
694
  } else {
1289
694
    m[0] = 1; m[1] = 0;
1290
694
    m[2] = 0; m[3] = 1;
1291
694
    m[4] = 0; m[5] = 0;
1292
694
  }
1293
1.04k
  obj1.free();
1294
1295
  // get resources
1296
1.04k
  dict->lookup("Resources", &obj1);
1297
1.04k
  resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
1298
1299
  // draw it
1300
1.04k
  drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout,
1301
1.04k
     alpha, transferFunc, backdropColorObj);
1302
1303
1.04k
  obj1.free();
1304
1.04k
}
1305
1306
1.97k
void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
1307
1.97k
  GfxRenderingIntent ri;
1308
1309
1.97k
  if (state->getIgnoreColorOps()) {
1310
16
    error(errSyntaxWarning, getPos(), "Ignoring rendering intent setting"
1311
16
    " in uncolored Type 3 char or tiling pattern");
1312
16
    return;
1313
16
  }
1314
1.95k
  ri = parseRenderingIntent(args[0].getName());
1315
1.95k
  state->setRenderingIntent(ri);
1316
1.95k
  out->updateRenderingIntent(state);
1317
1.95k
}
1318
1319
1.95k
GfxRenderingIntent Gfx::parseRenderingIntent(const char *name) {
1320
1.95k
  if (!strcmp(name, "AbsoluteColorimetric")) {
1321
0
    return gfxRenderingIntentAbsoluteColorimetric;
1322
0
  }
1323
1.95k
  if (!strcmp(name, "Saturation")) {
1324
0
    return gfxRenderingIntentSaturation;
1325
0
  }
1326
1.95k
  if (!strcmp(name, "Perceptual")) {
1327
1.01k
    return gfxRenderingIntentPerceptual;
1328
1.01k
  }
1329
941
  return gfxRenderingIntentRelativeColorimetric;
1330
1.95k
}
1331
1332
//------------------------------------------------------------------------
1333
// color operators
1334
//------------------------------------------------------------------------
1335
1336
47.9k
void Gfx::opSetFillGray(Object args[], int numArgs) {
1337
47.9k
  GfxColor color;
1338
1339
47.9k
  if (state->getIgnoreColorOps()) {
1340
3.12k
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1341
3.12k
    " in uncolored Type 3 char or tiling pattern");
1342
3.12k
    return;
1343
3.12k
  }
1344
44.8k
  state->setFillPattern(NULL);
1345
44.8k
  state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
1346
44.8k
  out->updateFillColorSpace(state);
1347
44.8k
  color.c[0] = dblToCol(args[0].getNum());
1348
44.8k
  state->setFillColor(&color);
1349
44.8k
  out->updateFillColor(state);
1350
44.8k
}
1351
1352
69.6k
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
1353
69.6k
  GfxColor color;
1354
1355
69.6k
  if (state->getIgnoreColorOps()) {
1356
1.55k
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1357
1.55k
    " in uncolored Type 3 char or tiling pattern");
1358
1.55k
    return;
1359
1.55k
  }
1360
68.0k
  state->setStrokePattern(NULL);
1361
68.0k
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
1362
68.0k
  out->updateStrokeColorSpace(state);
1363
68.0k
  color.c[0] = dblToCol(args[0].getNum());
1364
68.0k
  state->setStrokeColor(&color);
1365
68.0k
  out->updateStrokeColor(state);
1366
68.0k
}
1367
1368
8.58k
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
1369
8.58k
  GfxColor color;
1370
8.58k
  int i;
1371
1372
8.58k
  if (state->getIgnoreColorOps()) {
1373
304
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1374
304
    " in uncolored Type 3 char or tiling pattern");
1375
304
    return;
1376
304
  }
1377
8.28k
  state->setFillPattern(NULL);
1378
8.28k
  state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK));
1379
8.28k
  out->updateFillColorSpace(state);
1380
41.4k
  for (i = 0; i < 4; ++i) {
1381
33.1k
    color.c[i] = dblToCol(args[i].getNum());
1382
33.1k
  }
1383
8.28k
  state->setFillColor(&color);
1384
8.28k
  out->updateFillColor(state);
1385
8.28k
}
1386
1387
3.32k
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
1388
3.32k
  GfxColor color;
1389
3.32k
  int i;
1390
1391
3.32k
  if (state->getIgnoreColorOps()) {
1392
232
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1393
232
    " in uncolored Type 3 char or tiling pattern");
1394
232
    return;
1395
232
  }
1396
3.09k
  state->setStrokePattern(NULL);
1397
3.09k
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
1398
3.09k
  out->updateStrokeColorSpace(state);
1399
15.4k
  for (i = 0; i < 4; ++i) {
1400
12.3k
    color.c[i] = dblToCol(args[i].getNum());
1401
12.3k
  }
1402
3.09k
  state->setStrokeColor(&color);
1403
3.09k
  out->updateStrokeColor(state);
1404
3.09k
}
1405
1406
82.5k
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
1407
82.5k
  GfxColor color;
1408
82.5k
  int i;
1409
1410
82.5k
  if (state->getIgnoreColorOps()) {
1411
395
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1412
395
    " in uncolored Type 3 char or tiling pattern");
1413
395
    return;
1414
395
  }
1415
82.1k
  state->setFillPattern(NULL);
1416
82.1k
  state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB));
1417
82.1k
  out->updateFillColorSpace(state);
1418
328k
  for (i = 0; i < 3; ++i) {
1419
246k
    color.c[i] = dblToCol(args[i].getNum());
1420
246k
  }
1421
82.1k
  state->setFillColor(&color);
1422
82.1k
  out->updateFillColor(state);
1423
82.1k
}
1424
1425
28.6k
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
1426
28.6k
  GfxColor color;
1427
28.6k
  int i;
1428
1429
28.6k
  if (state->getIgnoreColorOps()) {
1430
522
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1431
522
    " in uncolored Type 3 char or tiling pattern");
1432
522
    return;
1433
522
  }
1434
28.1k
  state->setStrokePattern(NULL);
1435
28.1k
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
1436
28.1k
  out->updateStrokeColorSpace(state);
1437
112k
  for (i = 0; i < 3; ++i) {
1438
84.4k
    color.c[i] = dblToCol(args[i].getNum());
1439
84.4k
  }
1440
28.1k
  state->setStrokeColor(&color);
1441
28.1k
  out->updateStrokeColor(state);
1442
28.1k
}
1443
1444
52.2k
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
1445
52.2k
  Object obj;
1446
52.2k
  GfxColorSpace *colorSpace;
1447
52.2k
  GfxColor color;
1448
1449
52.2k
  if (state->getIgnoreColorOps()) {
1450
19
    error(errSyntaxWarning, getPos(), "Ignoring color space setting"
1451
19
    " in uncolored Type 3 char or tiling pattern");
1452
19
    return;
1453
19
  }
1454
52.2k
  state->setFillPattern(NULL);
1455
52.2k
  res->lookupColorSpace(args[0].getName(), &obj);
1456
52.2k
  if (obj.isNull()) {
1457
38.6k
    colorSpace = GfxColorSpace::parse(&args[0]
1458
38.6k
              );
1459
38.6k
  } else {
1460
13.5k
    colorSpace = GfxColorSpace::parse(&obj
1461
13.5k
              );
1462
13.5k
  }
1463
52.2k
  obj.free();
1464
52.2k
  if (colorSpace) {
1465
38.9k
    state->setFillColorSpace(colorSpace);
1466
38.9k
    out->updateFillColorSpace(state);
1467
38.9k
    colorSpace->getDefaultColor(&color);
1468
38.9k
    state->setFillColor(&color);
1469
38.9k
    out->updateFillColor(state);
1470
38.9k
  } else {
1471
13.2k
    error(errSyntaxError, getPos(), "Bad color space (fill)");
1472
13.2k
  }
1473
52.2k
}
1474
1475
765
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
1476
765
  Object obj;
1477
765
  GfxColorSpace *colorSpace;
1478
765
  GfxColor color;
1479
1480
765
  if (state->getIgnoreColorOps()) {
1481
73
    error(errSyntaxWarning, getPos(), "Ignoring color space setting"
1482
73
    " in uncolored Type 3 char or tiling pattern");
1483
73
    return;
1484
73
  }
1485
692
  state->setStrokePattern(NULL);
1486
692
  res->lookupColorSpace(args[0].getName(), &obj);
1487
692
  if (obj.isNull()) {
1488
523
    colorSpace = GfxColorSpace::parse(&args[0]
1489
523
              );
1490
523
  } else {
1491
169
    colorSpace = GfxColorSpace::parse(&obj
1492
169
              );
1493
169
  }
1494
692
  obj.free();
1495
692
  if (colorSpace) {
1496
278
    state->setStrokeColorSpace(colorSpace);
1497
278
    out->updateStrokeColorSpace(state);
1498
278
    colorSpace->getDefaultColor(&color);
1499
278
    state->setStrokeColor(&color);
1500
278
    out->updateStrokeColor(state);
1501
414
  } else {
1502
414
    error(errSyntaxError, getPos(), "Bad color space (stroke)");
1503
414
  }
1504
692
}
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.5k
void Gfx::opSetFillColor(Object args[], int numArgs) {
1511
17.5k
  GfxColor color;
1512
17.5k
  int i;
1513
1514
17.5k
  if (state->getIgnoreColorOps()) {
1515
78
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1516
78
    " in uncolored Type 3 char or tiling pattern");
1517
78
    return;
1518
78
  }
1519
17.4k
  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.20k
  state->setFillPattern(NULL);
1525
8.94k
  for (i = 0; i < numArgs; ++i) {
1526
3.74k
    color.c[i] = dblToCol(args[i].getNum());
1527
3.74k
  }
1528
5.20k
  state->setFillColor(&color);
1529
5.20k
  out->updateFillColor(state);
1530
5.20k
}
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
3.07k
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
1537
3.07k
  GfxColor color;
1538
3.07k
  int i;
1539
1540
3.07k
  if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1541
2.58k
    error(errSyntaxError, getPos(),
1542
2.58k
    "Incorrect number of arguments in 'SC' command");
1543
2.58k
    return;
1544
2.58k
  }
1545
496
  state->setStrokePattern(NULL);
1546
1.48k
  for (i = 0; i < numArgs; ++i) {
1547
988
    color.c[i] = dblToCol(args[i].getNum());
1548
988
  }
1549
496
  state->setStrokeColor(&color);
1550
496
  out->updateStrokeColor(state);
1551
496
}
1552
1553
49.8k
void Gfx::opSetFillColorN(Object args[], int numArgs) {
1554
49.8k
  GfxColor color;
1555
49.8k
  GfxPattern *pattern;
1556
49.8k
  int i;
1557
1558
49.8k
  if (state->getIgnoreColorOps()) {
1559
128
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1560
128
    " in uncolored Type 3 char or tiling pattern");
1561
128
    return;
1562
128
  }
1563
49.7k
  if (state->getFillColorSpace()->getMode() == csPattern) {
1564
45.2k
    if (numArgs == 0 || !args[numArgs-1].isName()) {
1565
6.02k
      error(errSyntaxError, getPos(),
1566
6.02k
      "Invalid arguments in 'scn' command");
1567
6.02k
      return;
1568
6.02k
    }
1569
39.2k
    if (numArgs > 1) {
1570
860
      if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1571
119
    numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1572
783
                       ->getUnder()->getNComps()) {
1573
783
  error(errSyntaxError, getPos(),
1574
783
        "Incorrect number of arguments in 'scn' command");
1575
783
  return;
1576
783
      }
1577
308
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1578
231
  if (args[i].isNum()) {
1579
211
    color.c[i] = dblToCol(args[i].getNum());
1580
211
  }
1581
231
      }
1582
77
      state->setFillColor(&color);
1583
77
      out->updateFillColor(state);
1584
77
    }
1585
38.4k
    if ((pattern = res->lookupPattern(args[numArgs-1].getName()
1586
38.4k
              ))) {
1587
20.1k
      state->setFillPattern(pattern);
1588
20.1k
    }
1589
1590
38.4k
  } else {
1591
4.45k
    if (numArgs != state->getFillColorSpace()->getNComps()) {
1592
2.20k
      error(errSyntaxError, getPos(),
1593
2.20k
      "Incorrect number of arguments in 'scn' command");
1594
2.20k
      return;
1595
2.20k
    }
1596
2.25k
    state->setFillPattern(NULL);
1597
5.14k
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1598
2.89k
      if (args[i].isNum()) {
1599
2.24k
  color.c[i] = dblToCol(args[i].getNum());
1600
2.24k
      }
1601
2.89k
    }
1602
2.25k
    state->setFillColor(&color);
1603
2.25k
    out->updateFillColor(state);
1604
2.25k
  }
1605
49.7k
}
1606
1607
813
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
1608
813
  GfxColor color;
1609
813
  GfxPattern *pattern;
1610
813
  int i;
1611
1612
813
  if (state->getIgnoreColorOps()) {
1613
68
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1614
68
    " in uncolored Type 3 char or tiling pattern");
1615
68
    return;
1616
68
  }
1617
745
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1618
330
    if (numArgs == 0 || !args[numArgs-1].isName()) {
1619
119
      error(errSyntaxError, getPos(),
1620
119
      "Invalid arguments in 'SCN' command");
1621
119
      return;
1622
119
    }
1623
211
    if (numArgs > 1) {
1624
46
      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1625
46
         ->getUnder() ||
1626
0
    numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1627
46
                       ->getUnder()->getNComps()) {
1628
46
  error(errSyntaxError, getPos(),
1629
46
        "Incorrect number of arguments in 'SCN' command");
1630
46
  return;
1631
46
      }
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
165
    if ((pattern = res->lookupPattern(args[numArgs-1].getName()
1641
165
              ))) {
1642
141
      state->setStrokePattern(pattern);
1643
141
    }
1644
1645
415
  } else {
1646
415
    if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1647
264
      error(errSyntaxError, getPos(),
1648
264
      "Incorrect number of arguments in 'SCN' command");
1649
264
      return;
1650
264
    }
1651
151
    state->setStrokePattern(NULL);
1652
466
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1653
315
      if (args[i].isNum()) {
1654
303
  color.c[i] = dblToCol(args[i].getNum());
1655
303
      }
1656
315
    }
1657
151
    state->setStrokeColor(&color);
1658
151
    out->updateStrokeColor(state);
1659
151
  }
1660
745
}
1661
1662
//------------------------------------------------------------------------
1663
// path segment operators
1664
//------------------------------------------------------------------------
1665
1666
166k
void Gfx::opMoveTo(Object args[], int numArgs) {
1667
166k
  state->moveTo(args[0].getNum(), args[1].getNum());
1668
166k
}
1669
1670
240k
void Gfx::opLineTo(Object args[], int numArgs) {
1671
240k
  if (!state->isCurPt()) {
1672
115k
    error(errSyntaxError, getPos(), "No current point in lineto");
1673
115k
    return;
1674
115k
  }
1675
124k
  state->lineTo(args[0].getNum(), args[1].getNum());
1676
124k
}
1677
1678
106k
void Gfx::opCurveTo(Object args[], int numArgs) {
1679
106k
  double x1, y1, x2, y2, x3, y3;
1680
1681
106k
  if (!state->isCurPt()) {
1682
22.4k
    error(errSyntaxError, getPos(), "No current point in curveto");
1683
22.4k
    return;
1684
22.4k
  }
1685
84.3k
  x1 = args[0].getNum();
1686
84.3k
  y1 = args[1].getNum();
1687
84.3k
  x2 = args[2].getNum();
1688
84.3k
  y2 = args[3].getNum();
1689
84.3k
  x3 = args[4].getNum();
1690
84.3k
  y3 = args[5].getNum();
1691
84.3k
  state->curveTo(x1, y1, x2, y2, x3, y3);
1692
84.3k
}
1693
1694
11.1k
void Gfx::opCurveTo1(Object args[], int numArgs) {
1695
11.1k
  double x1, y1, x2, y2, x3, y3;
1696
1697
11.1k
  if (!state->isCurPt()) {
1698
9.37k
    error(errSyntaxError, getPos(), "No current point in curveto1");
1699
9.37k
    return;
1700
9.37k
  }
1701
1.78k
  x1 = state->getCurX();
1702
1.78k
  y1 = state->getCurY();
1703
1.78k
  x2 = args[0].getNum();
1704
1.78k
  y2 = args[1].getNum();
1705
1.78k
  x3 = args[2].getNum();
1706
1.78k
  y3 = args[3].getNum();
1707
1.78k
  state->curveTo(x1, y1, x2, y2, x3, y3);
1708
1.78k
}
1709
1710
4.91k
void Gfx::opCurveTo2(Object args[], int numArgs) {
1711
4.91k
  double x1, y1, x2, y2, x3, y3;
1712
1713
4.91k
  if (!state->isCurPt()) {
1714
3.63k
    error(errSyntaxError, getPos(), "No current point in curveto2");
1715
3.63k
    return;
1716
3.63k
  }
1717
1.28k
  x1 = args[0].getNum();
1718
1.28k
  y1 = args[1].getNum();
1719
1.28k
  x2 = args[2].getNum();
1720
1.28k
  y2 = args[3].getNum();
1721
1.28k
  x3 = x2;
1722
1.28k
  y3 = y2;
1723
1.28k
  state->curveTo(x1, y1, x2, y2, x3, y3);
1724
1.28k
}
1725
1726
129k
void Gfx::opRectangle(Object args[], int numArgs) {
1727
129k
  double x, y, w, h;
1728
1729
129k
  x = args[0].getNum();
1730
129k
  y = args[1].getNum();
1731
129k
  w = args[2].getNum();
1732
129k
  h = args[3].getNum();
1733
129k
  state->moveTo(x, y);
1734
129k
  state->lineTo(x + w, y);
1735
129k
  state->lineTo(x + w, y + h);
1736
129k
  state->lineTo(x, y + h);
1737
129k
  state->closePath();
1738
129k
}
1739
1740
59.8k
void Gfx::opClosePath(Object args[], int numArgs) {
1741
59.8k
  if (!state->isCurPt()) {
1742
28.4k
    error(errSyntaxError, getPos(), "No current point in closepath");
1743
28.4k
    return;
1744
28.4k
  }
1745
31.4k
  state->closePath();
1746
31.4k
}
1747
1748
//------------------------------------------------------------------------
1749
// path painting operators
1750
//------------------------------------------------------------------------
1751
1752
204k
void Gfx::opEndPath(Object args[], int numArgs) {
1753
204k
  doEndPath();
1754
204k
}
1755
1756
76.1k
void Gfx::opStroke(Object args[], int numArgs) {
1757
76.1k
  if (!state->isCurPt()) {
1758
    //error(errSyntaxError, getPos(), "No path in stroke");
1759
60.7k
    return;
1760
60.7k
  }
1761
15.4k
  if (state->isPath()) {
1762
11.7k
    if (ocState) {
1763
11.6k
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
1764
180
  doPatternStroke();
1765
11.5k
      } else {
1766
11.5k
  out->stroke(state);
1767
11.5k
      }
1768
11.6k
    }
1769
11.7k
  }
1770
15.4k
  doEndPath();
1771
15.4k
}
1772
1773
36.5k
void Gfx::opCloseStroke(Object args[], int numArgs) {
1774
36.5k
  if (!state->isCurPt()) {
1775
    //error(errSyntaxError, getPos(), "No path in closepath/stroke");
1776
26.8k
    return;
1777
26.8k
  }
1778
9.69k
  if (state->isPath()) {
1779
7.57k
    state->closePath();
1780
7.57k
    if (ocState) {
1781
7.50k
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
1782
0
  doPatternStroke();
1783
7.50k
      } else {
1784
7.50k
  out->stroke(state);
1785
7.50k
      }
1786
7.50k
    }
1787
7.57k
  }
1788
9.69k
  doEndPath();
1789
9.69k
}
1790
1791
190k
void Gfx::opFill(Object args[], int numArgs) {
1792
190k
  if (!state->isCurPt()) {
1793
    //error(errSyntaxError, getPos(), "No path in fill");
1794
89.6k
    return;
1795
89.6k
  }
1796
101k
  if (state->isPath()) {
1797
88.7k
    if (ocState) {
1798
87.2k
      if (state->getFillColorSpace()->getMode() == csPattern) {
1799
8.65k
  doPatternFill(gFalse);
1800
78.5k
      } else {
1801
78.5k
  out->fill(state);
1802
78.5k
      }
1803
87.2k
    }
1804
88.7k
  }
1805
101k
  doEndPath();
1806
101k
}
1807
1808
6.23k
void Gfx::opEOFill(Object args[], int numArgs) {
1809
6.23k
  if (!state->isCurPt()) {
1810
    //error(errSyntaxError, getPos(), "No path in eofill");
1811
1.65k
    return;
1812
1.65k
  }
1813
4.57k
  if (state->isPath()) {
1814
4.43k
    if (ocState) {
1815
4.43k
      if (state->getFillColorSpace()->getMode() == csPattern) {
1816
4.26k
  doPatternFill(gTrue);
1817
4.26k
      } else {
1818
164
  out->eoFill(state);
1819
164
      }
1820
4.43k
    }
1821
4.43k
  }
1822
4.57k
  doEndPath();
1823
4.57k
}
1824
1825
219k
void Gfx::opFillStroke(Object args[], int numArgs) {
1826
219k
  if (!state->isCurPt()) {
1827
    //error(errSyntaxError, getPos(), "No path in fill/stroke");
1828
179k
    return;
1829
179k
  }
1830
39.9k
  if (state->isPath()) {
1831
32.9k
    if (ocState) {
1832
32.9k
      if (state->getFillColorSpace()->getMode() == csPattern ||
1833
20.9k
    state->getStrokeColorSpace()->getMode() == csPattern) {
1834
20.9k
  if (state->getFillColorSpace()->getMode() == csPattern) {
1835
20.9k
    doPatternFill(gFalse);
1836
20.9k
  } else {
1837
0
    out->fill(state);
1838
0
  }
1839
20.9k
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1840
0
    doPatternStroke();
1841
20.9k
  } else {
1842
20.9k
    out->stroke(state);
1843
20.9k
  }
1844
20.9k
      } else {
1845
11.9k
  out->fillStroke(state, gFalse);
1846
11.9k
      }
1847
32.9k
    }
1848
32.9k
  }
1849
39.9k
  doEndPath();
1850
39.9k
}
1851
1852
82.8k
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1853
82.8k
  if (!state->isCurPt()) {
1854
    //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke");
1855
54.4k
    return;
1856
54.4k
  }
1857
28.4k
  if (state->isPath()) {
1858
20.7k
    state->closePath();
1859
20.7k
    if (ocState) {
1860
20.7k
      if (state->getFillColorSpace()->getMode() == csPattern ||
1861
18.4k
    state->getStrokeColorSpace()->getMode() == csPattern) {
1862
18.4k
  if (state->getFillColorSpace()->getMode() == csPattern) {
1863
18.4k
    doPatternFill(gFalse);
1864
18.4k
  } else {
1865
0
    out->fill(state);
1866
0
  }
1867
18.4k
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1868
0
    doPatternStroke();
1869
18.4k
  } else {
1870
18.4k
    out->stroke(state);
1871
18.4k
  }
1872
18.4k
      } else {
1873
2.33k
  out->fillStroke(state, gFalse);
1874
2.33k
      }
1875
20.7k
    }
1876
20.7k
  }
1877
28.4k
  doEndPath();
1878
28.4k
}
1879
1880
1.65k
void Gfx::opEOFillStroke(Object args[], int numArgs) {
1881
1.65k
  if (!state->isCurPt()) {
1882
    //error(errSyntaxError, getPos(), "No path in eofill/stroke");
1883
268
    return;
1884
268
  }
1885
1.38k
  if (state->isPath()) {
1886
145
    if (ocState) {
1887
144
      if (state->getFillColorSpace()->getMode() == csPattern ||
1888
105
    state->getStrokeColorSpace()->getMode() == csPattern) {
1889
39
  if (state->getFillColorSpace()->getMode() == csPattern) {
1890
39
    doPatternFill(gTrue);
1891
39
  } else {
1892
0
    out->eoFill(state);
1893
0
  }
1894
39
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1895
0
    doPatternStroke();
1896
39
  } else {
1897
39
    out->stroke(state);
1898
39
  }
1899
105
      } else {
1900
105
  out->fillStroke(state, gTrue);
1901
105
      }
1902
144
    }
1903
145
  }
1904
1.38k
  doEndPath();
1905
1.38k
}
1906
1907
171
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1908
171
  if (!state->isCurPt()) {
1909
    //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke");
1910
123
    return;
1911
123
  }
1912
48
  if (state->isPath()) {
1913
36
    state->closePath();
1914
36
    if (ocState) {
1915
35
      if (state->getFillColorSpace()->getMode() == csPattern ||
1916
18
    state->getStrokeColorSpace()->getMode() == csPattern) {
1917
17
  if (state->getFillColorSpace()->getMode() == csPattern) {
1918
17
    doPatternFill(gTrue);
1919
17
  } else {
1920
0
    out->eoFill(state);
1921
0
  }
1922
17
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1923
0
    doPatternStroke();
1924
17
  } else {
1925
17
    out->stroke(state);
1926
17
  }
1927
18
      } else {
1928
18
  out->fillStroke(state, gTrue);
1929
18
      }
1930
35
    }
1931
36
  }
1932
48
  doEndPath();
1933
48
}
1934
1935
52.3k
void Gfx::doPatternFill(GBool eoFill) {
1936
52.3k
  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
52.3k
  if (!out->needNonText()) {
1942
0
    return;
1943
0
  }
1944
1945
52.3k
  if (!(pattern = state->getFillPattern())) {
1946
22.2k
    return;
1947
22.2k
  }
1948
30.0k
  switch (pattern->getType()) {
1949
29.6k
  case 1:
1950
29.6k
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse);
1951
29.6k
    break;
1952
397
  case 2:
1953
397
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse);
1954
397
    break;
1955
0
  default:
1956
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
1957
0
    pattern->getType());
1958
0
    break;
1959
30.0k
  }
1960
30.0k
}
1961
1962
180
void Gfx::doPatternStroke() {
1963
180
  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
180
  if (!out->needNonText()) {
1969
0
    return;
1970
0
  }
1971
1972
180
  if (!(pattern = state->getStrokePattern())) {
1973
29
    return;
1974
29
  }
1975
151
  switch (pattern->getType()) {
1976
151
  case 1:
1977
151
    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse);
1978
151
    break;
1979
0
  case 2:
1980
0
    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse);
1981
0
    break;
1982
0
  default:
1983
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke",
1984
0
    pattern->getType());
1985
0
    break;
1986
151
  }
1987
151
}
1988
1989
40.5k
void Gfx::doPatternText(GBool stroke) {
1990
40.5k
  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
40.5k
  if (!out->needNonText()) {
1996
0
    return;
1997
0
  }
1998
1999
40.5k
  pattern = stroke ? state->getStrokePattern() : state->getFillPattern();
2000
40.5k
  if (!pattern) {
2001
37.4k
    return;
2002
37.4k
  }
2003
3.11k
  switch (pattern->getType()) {
2004
2.44k
  case 1:
2005
2.44k
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue);
2006
2.44k
    break;
2007
662
  case 2:
2008
662
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue);
2009
662
    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.11k
  }
2015
3.11k
}
2016
2017
void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
2018
12
           GBool invert, GBool inlineImg, GBool interpolate) {
2019
12
  saveState();
2020
2021
12
  out->setSoftMaskFromImageMask(state, ref, str,
2022
12
        width, height, invert, inlineImg, interpolate);
2023
2024
12
  state->clearPath();
2025
12
  state->moveTo(0, 0);
2026
12
  state->lineTo(1, 0);
2027
12
  state->lineTo(1, 1);
2028
12
  state->lineTo(0, 1);
2029
12
  state->closePath();
2030
12
  doPatternFill(gTrue);
2031
12
  state->clearPath();
2032
2033
12
  restoreState();
2034
12
}
2035
2036
void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
2037
32.2k
            GBool stroke, GBool eoFill, GBool text) {
2038
32.2k
  GfxPatternColorSpace *patCS;
2039
32.2k
  GfxColorSpace *cs;
2040
32.2k
  GfxColor color;
2041
32.2k
  GfxState *savedState;
2042
32.2k
  double xMin, yMin, xMax, yMax, x, y, x1, y1, t;
2043
32.2k
  double cxMin, cyMin, cxMax, cyMax;
2044
32.2k
  int xi0, yi0, xi1, yi1, xi, yi;
2045
32.2k
  double *ctm, *btm, *ptm;
2046
32.2k
  double bbox[4], m[6], ictm[6], m1[6], imb[6];
2047
32.2k
  double det;
2048
32.2k
  double xstep, ystep;
2049
32.2k
  int abortCheckCounter, i;
2050
2051
  // get color space
2052
32.2k
  patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
2053
32.2k
                  : state->getFillColorSpace());
2054
2055
  // construct a (pattern space) -> (current space) transform matrix
2056
32.2k
  ctm = state->getCTM();
2057
32.2k
  btm = baseMatrix;
2058
32.2k
  ptm = tPat->getMatrix();
2059
  // iCTM = invert CTM
2060
32.2k
  det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2061
32.2k
  if (fabs(det) <= 1e-10) {
2062
380
    error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2063
380
    return;
2064
380
  }
2065
31.8k
  det = 1 / det;
2066
31.8k
  ictm[0] = ctm[3] * det;
2067
31.8k
  ictm[1] = -ctm[1] * det;
2068
31.8k
  ictm[2] = -ctm[2] * det;
2069
31.8k
  ictm[3] = ctm[0] * det;
2070
31.8k
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2071
31.8k
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2072
  // m1 = PTM * BTM = PTM * base transform matrix
2073
31.8k
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2074
31.8k
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2075
31.8k
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2076
31.8k
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2077
31.8k
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2078
31.8k
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2079
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2080
31.8k
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2081
31.8k
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2082
31.8k
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2083
31.8k
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2084
31.8k
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2085
31.8k
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2086
2087
  // construct a (device space) -> (pattern space) transform matrix
2088
31.8k
  det = m1[0] * m1[3] - m1[1] * m1[2];
2089
31.8k
  if (fabs(det) <= 1e-10) {
2090
529
    error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2091
529
    return;
2092
529
  }
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
10
    state->setFillColorSpace(cs->copy());
2114
10
    out->updateFillColorSpace(state);
2115
10
    state->setStrokeColorSpace(cs->copy());
2116
10
    out->updateStrokeColorSpace(state);
2117
10
    if (stroke) {
2118
0
      state->setFillColor(state->getStrokeColor());
2119
10
    } else {
2120
10
      state->setStrokeColor(state->getFillColor());
2121
10
    }
2122
10
    out->updateFillColor(state);
2123
10
    out->updateStrokeColor(state);
2124
10
    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.1k
    state->setLineWidth(0);
2139
31.1k
    out->updateLineWidth(state);
2140
31.1k
    state->setLineDash(NULL, 0, 0);
2141
31.1k
    out->updateLineDash(state);
2142
31.1k
  }
2143
2144
  // clip to current path
2145
31.3k
  if (stroke) {
2146
147
    state->clipToStrokePath();
2147
147
    out->clipToStrokePath(state);
2148
31.1k
  } else if (!text) {
2149
29.4k
    state->clip();
2150
29.4k
    if (eoFill) {
2151
97
      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
29.0k
    goto err;
2162
29.0k
  }
2163
2164
  // transform clip region bbox to pattern space
2165
2.32k
  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
2166
2.32k
  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
2167
2.32k
  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
2168
2.32k
  y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
2169
2.32k
  if (x1 < xMin) {
2170
0
    xMin = x1;
2171
2.32k
  } else if (x1 > xMax) {
2172
0
    xMax = x1;
2173
0
  }
2174
2.32k
  if (y1 < yMin) {
2175
234
    yMin = y1;
2176
2.09k
  } else if (y1 > yMax) {
2177
0
    yMax = y1;
2178
0
  }
2179
2.32k
  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
2180
2.32k
  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
2181
2.32k
  if (x1 < xMin) {
2182
0
    xMin = x1;
2183
2.32k
  } else if (x1 > xMax) {
2184
231
    xMax = x1;
2185
231
  }
2186
2.32k
  if (y1 < yMin) {
2187
0
    yMin = y1;
2188
2.32k
  } else if (y1 > yMax) {
2189
0
    yMax = y1;
2190
0
  }
2191
2.32k
  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
2192
2.32k
  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
2193
2.32k
  if (x1 < xMin) {
2194
0
    xMin = x1;
2195
2.32k
  } else if (x1 > xMax) {
2196
0
    xMax = x1;
2197
0
  }
2198
2.32k
  if (y1 < yMin) {
2199
0
    yMin = y1;
2200
2.32k
  } 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.32k
  bbox[0] = tPat->getBBox()[0];
2208
2.32k
  bbox[1] = tPat->getBBox()[1];
2209
2.32k
  bbox[2] = tPat->getBBox()[2];
2210
2.32k
  bbox[3] = tPat->getBBox()[3];
2211
2.32k
  if (bbox[0] > bbox[2]) {
2212
6
    t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t;
2213
6
  }
2214
2.32k
  if (bbox[1] > bbox[3]) {
2215
22
    t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t;
2216
22
  }
2217
2.32k
  xstep = tPat->getXStep();
2218
2.32k
  ystep = tPat->getYStep();
2219
2.32k
  if (xstep == 0 || ystep == 0) {
2220
174
    error(errSyntaxError, getPos(), "Zero x or y step in tiling pattern fill");
2221
174
    goto err;
2222
174
  }
2223
2.15k
  if (xstep > 0) {
2224
2.12k
    xi0 = (int)ceil((xMin - bbox[2]) / xstep);
2225
2.12k
    xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1;
2226
2.12k
  } else {
2227
21
    xi0 = (int)ceil((xMax - bbox[0]) / xstep);
2228
21
    xi1 = (int)floor((xMin - bbox[2]) / xstep) + 1;
2229
21
  }
2230
2.15k
  if (ystep > 0) {
2231
1.52k
    yi0 = (int)ceil((yMin - bbox[3]) / ystep);
2232
1.52k
    yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1;
2233
1.52k
  } else {
2234
622
    yi0 = (int)ceil((yMax - bbox[1]) / ystep);
2235
622
    yi1 = (int)floor((yMin - bbox[3]) / ystep) + 1;
2236
622
  }
2237
10.7k
  for (i = 0; i < 4; ++i) {
2238
8.60k
    m1[i] = m[i];
2239
8.60k
  }
2240
2.15k
  if (out->useTilingPatternFill() && !tPat->usesBlendMode(xref)) {
2241
2.15k
    m1[4] = m[4];
2242
2.15k
    m1[5] = m[5];
2243
2.15k
    out->tilingPatternFill(state, this, tPat->getContentStreamRef(),
2244
2.15k
         tPat->getPaintType(), tPat->getTilingType(),
2245
2.15k
         tPat->getResDict(),
2246
2.15k
         m1, bbox,
2247
2.15k
         xi0, yi0, xi1, yi1, xstep, ystep);
2248
2.15k
  } 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
1.05k
             GBool stroke, GBool eoFill, GBool text) {
2278
1.05k
  GfxShading *shading;
2279
1.05k
  GfxState *savedState;
2280
1.05k
  double *ctm, *btm, *ptm;
2281
1.05k
  double m[6], ictm[6], m1[6];
2282
1.05k
  double xMin, yMin, xMax, yMax;
2283
1.05k
  double det;
2284
2285
1.05k
  shading = sPat->getShading();
2286
2287
  // save current graphics state
2288
1.05k
  savedState = saveStateStack();
2289
2290
  // clip to current path
2291
1.05k
  if (stroke) {
2292
0
    state->clipToStrokePath();
2293
0
    out->clipToStrokePath(state);
2294
0
    state->setFillOverprint(state->getStrokeOverprint());
2295
1.05k
  } else if (!text) {
2296
397
    state->clip();
2297
397
    if (eoFill) {
2298
0
      out->eoClip(state);
2299
397
    } else {
2300
397
      out->clip(state);
2301
397
    }
2302
397
  }
2303
1.05k
  state->clearPath();
2304
2305
  // construct a (pattern space) -> (current space) transform matrix
2306
1.05k
  ctm = state->getCTM();
2307
1.05k
  btm = baseMatrix;
2308
1.05k
  ptm = sPat->getMatrix();
2309
  // iCTM = invert CTM
2310
1.05k
  det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2311
1.05k
  if (fabs(det) <= 1e-10) {
2312
97
    error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
2313
97
    restoreStateStack(savedState);
2314
97
    return;
2315
97
  }
2316
962
  det = 1 / det;
2317
962
  ictm[0] = ctm[3] * det;
2318
962
  ictm[1] = -ctm[1] * det;
2319
962
  ictm[2] = -ctm[2] * det;
2320
962
  ictm[3] = ctm[0] * det;
2321
962
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2322
962
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2323
  // m1 = PTM * BTM = PTM * base transform matrix
2324
962
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2325
962
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2326
962
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2327
962
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2328
962
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2329
962
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2330
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2331
962
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2332
962
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2333
962
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2334
962
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2335
962
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2336
962
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2337
2338
  // set the new matrix
2339
962
  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
2340
962
  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
2341
2342
  // clip to bbox
2343
962
  if (shading->getHasBBox()) {
2344
6
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2345
6
    state->moveTo(xMin, yMin);
2346
6
    state->lineTo(xMax, yMin);
2347
6
    state->lineTo(xMax, yMax);
2348
6
    state->lineTo(xMin, yMax);
2349
6
    state->closePath();
2350
6
    state->clip();
2351
6
    out->clip(state);
2352
6
    state->clearPath();
2353
6
  }
2354
2355
  // set the color space
2356
962
  state->setFillColorSpace(shading->getColorSpace()->copy());
2357
962
  out->updateFillColorSpace(state);
2358
2359
  // background color fill
2360
962
  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
962
  doShFill(shading);
2375
2376
  // restore graphics state
2377
962
  restoreStateStack(savedState);
2378
962
}
2379
2380
18.0k
void Gfx::opShFill(Object args[], int numArgs) {
2381
18.0k
  GfxShading *shading;
2382
18.0k
  GfxState *savedState;
2383
18.0k
  double xMin, yMin, xMax, yMax;
2384
2385
18.0k
  if (state->getIgnoreColorOps()) {
2386
127
    error(errSyntaxWarning, getPos(), "Ignoring shaded fill"
2387
127
    " in uncolored Type 3 char or tiling pattern");
2388
127
    return;
2389
127
  }
2390
2391
17.9k
  if (!out->needNonText()) {
2392
0
    return;
2393
0
  }
2394
2395
17.9k
  if (!ocState) {
2396
0
    return;
2397
0
  }
2398
2399
17.9k
  if (!(shading = res->lookupShading(args[0].getName()
2400
17.9k
             ))) {
2401
15.8k
    return;
2402
15.8k
  }
2403
2404
  // save current graphics state
2405
2.15k
  savedState = saveStateStack();
2406
2407
  // clip to bbox
2408
2.15k
  if (shading->getHasBBox()) {
2409
0
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2410
0
    state->moveTo(xMin, yMin);
2411
0
    state->lineTo(xMax, yMin);
2412
0
    state->lineTo(xMax, yMax);
2413
0
    state->lineTo(xMin, yMax);
2414
0
    state->closePath();
2415
0
    state->clip();
2416
0
    out->clip(state);
2417
0
    state->clearPath();
2418
0
  }
2419
2420
  // set the color space
2421
2.15k
  state->setFillColorSpace(shading->getColorSpace()->copy());
2422
2.15k
  out->updateFillColorSpace(state);
2423
2424
  // perform the fill
2425
2.15k
  doShFill(shading);
2426
2427
  // restore graphics state
2428
2.15k
  restoreStateStack(savedState);
2429
2430
2.15k
  delete shading;
2431
2.15k
}
2432
2433
3.11k
void Gfx::doShFill(GfxShading *shading) {
2434
3.11k
  if (out->shadedFill(state, shading)) {
2435
3.06k
    return;
2436
3.06k
  }
2437
2438
  // do shading type-specific operations
2439
45
  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
45
  }
2458
45
}
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
404k
void Gfx::doEndPath() {
3559
404k
  if (clip != clipNone) {
3560
85.5k
    if (state->isCurPt()) {
3561
42.9k
      state->clip();
3562
42.9k
      if (clip == clipNormal) {
3563
36.8k
  out->clip(state);
3564
36.8k
      } else {
3565
6.03k
  out->eoClip(state);
3566
6.03k
      }
3567
42.9k
    } else {
3568
42.5k
      error(errSyntaxError, getPos(), "Empty path in clip");
3569
42.5k
    }
3570
85.5k
  }
3571
404k
  clip = clipNone;
3572
404k
  state->clearPath();
3573
404k
}
3574
3575
//------------------------------------------------------------------------
3576
// path clipping operators
3577
//------------------------------------------------------------------------
3578
3579
131k
void Gfx::opClip(Object args[], int numArgs) {
3580
131k
  clip = clipNormal;
3581
131k
}
3582
3583
6.67k
void Gfx::opEOClip(Object args[], int numArgs) {
3584
6.67k
  clip = clipEO;
3585
6.67k
}
3586
3587
//------------------------------------------------------------------------
3588
// text object operators
3589
//------------------------------------------------------------------------
3590
3591
250k
void Gfx::opBeginText(Object args[], int numArgs) {
3592
250k
  state->setTextMat(1, 0, 0, 1, 0, 0);
3593
250k
  state->textMoveTo(0, 0);
3594
250k
  out->updateTextMat(state);
3595
250k
  out->updateTextPos(state);
3596
250k
  fontChanged = gTrue;
3597
250k
  haveSavedClipPath = gFalse;
3598
250k
}
3599
3600
250k
void Gfx::opEndText(Object args[], int numArgs) {
3601
250k
  if (haveSavedClipPath) {
3602
1.35k
    out->clipToSavedClipPath(state);
3603
1.35k
    haveSavedClipPath = gFalse;
3604
1.35k
  }
3605
3606
250k
  out->endTextObject(state);
3607
250k
}
3608
3609
//------------------------------------------------------------------------
3610
// text state operators
3611
//------------------------------------------------------------------------
3612
3613
21.4k
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
3614
21.4k
  state->setCharSpace(args[0].getNum());
3615
21.4k
  out->updateCharSpace(state);
3616
21.4k
}
3617
3618
126k
void Gfx::opSetFont(Object args[], int numArgs) {
3619
126k
  doSetFont(res->lookupFont(args[0].getName()), args[1].getNum());
3620
126k
}
3621
3622
126k
void Gfx::doSetFont(GfxFont *font, double size) {
3623
126k
  if (!font) {
3624
53.3k
    if (!defaultFont) {
3625
10.8k
      defaultFont = GfxFont::makeDefaultFont(xref);
3626
10.8k
    }
3627
53.3k
    font = defaultFont;
3628
53.3k
  }
3629
126k
  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
126k
  state->setFont(font, size);
3637
126k
  fontChanged = gTrue;
3638
126k
}
3639
3640
3.50k
void Gfx::opSetTextLeading(Object args[], int numArgs) {
3641
3.50k
  state->setLeading(args[0].getNum());
3642
3.50k
}
3643
3644
11.4k
void Gfx::opSetTextRender(Object args[], int numArgs) {
3645
11.4k
  state->setRender(args[0].getInt());
3646
11.4k
  out->updateRender(state);
3647
11.4k
}
3648
3649
1.53k
void Gfx::opSetTextRise(Object args[], int numArgs) {
3650
1.53k
  state->setRise(args[0].getNum());
3651
1.53k
  out->updateRise(state);
3652
1.53k
}
3653
3654
15.6k
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
3655
15.6k
  state->setWordSpace(args[0].getNum());
3656
15.6k
  out->updateWordSpace(state);
3657
15.6k
}
3658
3659
14.8k
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
3660
14.8k
  state->setHorizScaling(args[0].getNum());
3661
14.8k
  out->updateHorizScaling(state);
3662
14.8k
  fontChanged = gTrue;
3663
14.8k
}
3664
3665
//------------------------------------------------------------------------
3666
// text positioning operators
3667
//------------------------------------------------------------------------
3668
3669
91.7k
void Gfx::opTextMove(Object args[], int numArgs) {
3670
91.7k
  double tx, ty;
3671
3672
91.7k
  tx = state->getLineX() + args[0].getNum();
3673
91.7k
  ty = state->getLineY() + args[1].getNum();
3674
91.7k
  state->textMoveTo(tx, ty);
3675
91.7k
  out->updateTextPos(state);
3676
91.7k
}
3677
3678
41.5k
void Gfx::opTextMoveSet(Object args[], int numArgs) {
3679
41.5k
  double tx, ty;
3680
3681
41.5k
  tx = state->getLineX() + args[0].getNum();
3682
41.5k
  ty = args[1].getNum();
3683
41.5k
  state->setLeading(-ty);
3684
41.5k
  ty += state->getLineY();
3685
41.5k
  state->textMoveTo(tx, ty);
3686
41.5k
  out->updateTextPos(state);
3687
41.5k
}
3688
3689
151k
void Gfx::opSetTextMatrix(Object args[], int numArgs) {
3690
151k
  state->setTextMat(args[0].getNum(), args[1].getNum(),
3691
151k
        args[2].getNum(), args[3].getNum(),
3692
151k
        args[4].getNum(), args[5].getNum());
3693
151k
  state->textMoveTo(0, 0);
3694
151k
  out->updateTextMat(state);
3695
151k
  out->updateTextPos(state);
3696
151k
  fontChanged = gTrue;
3697
151k
}
3698
3699
2.52k
void Gfx::opTextNextLine(Object args[], int numArgs) {
3700
2.52k
  double tx, ty;
3701
3702
2.52k
  tx = state->getLineX();
3703
2.52k
  ty = state->getLineY() - state->getLeading();
3704
2.52k
  state->textMoveTo(tx, ty);
3705
2.52k
  out->updateTextPos(state);
3706
2.52k
}
3707
3708
//------------------------------------------------------------------------
3709
// text string operators
3710
//------------------------------------------------------------------------
3711
3712
294k
void Gfx::opShowText(Object args[], int numArgs) {
3713
294k
  if (!state->getFont()) {
3714
21.6k
    error(errSyntaxError, getPos(), "No font in show");
3715
21.6k
    return;
3716
21.6k
  }
3717
273k
  if (fontChanged) {
3718
209k
    out->updateFont(state);
3719
209k
    fontChanged = gFalse;
3720
209k
  }
3721
273k
  if (ocState) {
3722
273k
    out->beginStringOp(state);
3723
273k
    doShowText(args[0].getString());
3724
273k
    out->endStringOp(state);
3725
273k
  } else {
3726
8
    doIncCharCount(args[0].getString());
3727
8
  }
3728
273k
}
3729
3730
2.23k
void Gfx::opMoveShowText(Object args[], int numArgs) {
3731
2.23k
  double tx, ty;
3732
3733
2.23k
  if (!state->getFont()) {
3734
260
    error(errSyntaxError, getPos(), "No font in move/show");
3735
260
    return;
3736
260
  }
3737
1.97k
  if (fontChanged) {
3738
1.67k
    out->updateFont(state);
3739
1.67k
    fontChanged = gFalse;
3740
1.67k
  }
3741
1.97k
  tx = state->getLineX();
3742
1.97k
  ty = state->getLineY() - state->getLeading();
3743
1.97k
  state->textMoveTo(tx, ty);
3744
1.97k
  out->updateTextPos(state);
3745
1.97k
  if (ocState) {
3746
1.95k
    out->beginStringOp(state);
3747
1.95k
    doShowText(args[0].getString());
3748
1.95k
    out->endStringOp(state);
3749
1.95k
  } else {
3750
13
    doIncCharCount(args[0].getString());
3751
13
  }
3752
1.97k
}
3753
3754
327
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
3755
327
  double tx, ty;
3756
3757
327
  if (!state->getFont()) {
3758
42
    error(errSyntaxError, getPos(), "No font in move/set/show");
3759
42
    return;
3760
42
  }
3761
285
  if (fontChanged) {
3762
125
    out->updateFont(state);
3763
125
    fontChanged = gFalse;
3764
125
  }
3765
285
  state->setWordSpace(args[0].getNum());
3766
285
  state->setCharSpace(args[1].getNum());
3767
285
  tx = state->getLineX();
3768
285
  ty = state->getLineY() - state->getLeading();
3769
285
  state->textMoveTo(tx, ty);
3770
285
  out->updateWordSpace(state);
3771
285
  out->updateCharSpace(state);
3772
285
  out->updateTextPos(state);
3773
285
  if (ocState) {
3774
285
    out->beginStringOp(state);
3775
285
    doShowText(args[2].getString());
3776
285
    out->endStringOp(state);
3777
285
  } else {
3778
0
    doIncCharCount(args[2].getString());
3779
0
  }
3780
285
}
3781
3782
44.6k
void Gfx::opShowSpaceText(Object args[], int numArgs) {
3783
44.6k
  Array *a;
3784
44.6k
  Object obj;
3785
44.6k
  int wMode;
3786
44.6k
  int i;
3787
3788
44.6k
  if (!state->getFont()) {
3789
2.99k
    error(errSyntaxError, getPos(), "No font in show/space");
3790
2.99k
    return;
3791
2.99k
  }
3792
41.6k
  if (fontChanged) {
3793
30.6k
    out->updateFont(state);
3794
30.6k
    fontChanged = gFalse;
3795
30.6k
  }
3796
41.6k
  if (ocState) {
3797
41.6k
    out->beginStringOp(state);
3798
41.6k
    wMode = state->getFont()->getWMode();
3799
41.6k
    a = args[0].getArray();
3800
724k
    for (i = 0; i < a->getLength(); ++i) {
3801
682k
      a->get(i, &obj);
3802
682k
      if (obj.isNum()) {
3803
326k
  if (wMode) {
3804
385
    state->textShift(0, -obj.getNum() * 0.001 *
3805
385
         state->getFontSize());
3806
325k
  } else {
3807
325k
    state->textShift(-obj.getNum() * 0.001 *
3808
325k
         state->getFontSize() *
3809
325k
         state->getHorizScaling(), 0);
3810
325k
  }
3811
326k
  out->updateTextShift(state, obj.getNum());
3812
356k
      } else if (obj.isString()) {
3813
236k
  doShowText(obj.getString());
3814
236k
      } else {
3815
119k
  error(errSyntaxError, getPos(),
3816
119k
        "Element of show/space array must be number or string");
3817
119k
      }
3818
682k
      obj.free();
3819
682k
    }
3820
41.6k
    out->endStringOp(state);
3821
41.6k
  } 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
41.6k
}
3832
3833
511k
void Gfx::doShowText(GString *s) {
3834
511k
  GfxFont *font = state->getFont();
3835
511k
  int wMode = font->getWMode();
3836
3837
511k
  if (globalParams->isDroppedFont(font->getName()
3838
511k
            ? font->getName()->getCString() : "")) {
3839
0
    doIncCharCount(s);
3840
0
    return;
3841
0
  }
3842
3843
511k
  if (out->useDrawChar()) {
3844
511k
    out->beginString(state, s);
3845
511k
  }
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
511k
  GBool doFill = gFalse;
3852
511k
  GBool doStroke = gFalse;
3853
511k
  GBool doMakePath = gFalse;
3854
511k
  int render = state->getRender() & 7;
3855
511k
  switch (render & 7) {
3856
488k
  case 0:     // fill
3857
488k
    if (state->getFillColorSpace()->getMode() == csPattern) {
3858
34.9k
      doMakePath = gTrue;
3859
453k
    } else {
3860
453k
      doFill = gTrue;
3861
453k
    }
3862
488k
    break;
3863
636
  case 1:     // stroke
3864
636
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
3865
0
      doMakePath = gTrue;
3866
636
    } else {
3867
636
      doStroke = gTrue;
3868
636
    }
3869
636
    break;
3870
19.4k
  case 2:     // fill + stroke
3871
19.4k
    if (state->getFillColorSpace()->getMode() == csPattern ||
3872
13.8k
  state->getStrokeColorSpace()->getMode() == csPattern) {
3873
5.55k
      doMakePath = gTrue;
3874
13.8k
    } else {
3875
13.8k
      doFill = gTrue;
3876
13.8k
      doStroke = gTrue;
3877
13.8k
    }
3878
19.4k
    break;
3879
355
  case 3:     // invisible
3880
    // nothing
3881
355
    break;
3882
101
  case 4:     // fill + clip
3883
1.04k
  case 5:     // stroke + clip
3884
1.34k
  case 6:     // fill + stroke + clip
3885
3.05k
  case 7:     // clip
3886
3.05k
    doMakePath = gTrue;
3887
3.05k
    break;
3888
511k
  }
3889
3890
511k
  double riseX, riseY;
3891
511k
  state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3892
511k
  double xMin = state->getCurX() + riseX;
3893
511k
  double yMin = state->getCurY() + riseY;
3894
3895
  // handle a Type 3 char
3896
511k
  if (font->getType() == fontType3 && out->interpretType3Chars()) {
3897
34.3k
    double *mat = state->getCTM();
3898
34.3k
    double oldCTM[6];
3899
240k
    for (int i = 0; i < 6; ++i) {
3900
206k
      oldCTM[i] = mat[i];
3901
206k
    }
3902
34.3k
    mat = state->getTextMat();
3903
34.3k
    double newCTM[6];
3904
34.3k
    newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
3905
34.3k
    newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
3906
34.3k
    newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
3907
34.3k
    newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
3908
34.3k
    mat = font->getFontMatrix();
3909
34.3k
    newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
3910
34.3k
    newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
3911
34.3k
    newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
3912
34.3k
    newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
3913
34.3k
    newCTM[0] *= state->getFontSize();
3914
34.3k
    newCTM[1] *= state->getFontSize();
3915
34.3k
    newCTM[2] *= state->getFontSize();
3916
34.3k
    newCTM[3] *= state->getFontSize();
3917
34.3k
    newCTM[0] *= state->getHorizScaling();
3918
34.3k
    newCTM[1] *= state->getHorizScaling();
3919
34.3k
    double curX = state->getCurX();
3920
34.3k
    double curY = state->getCurY();
3921
34.3k
    Parser *oldParser = parser;
3922
34.3k
    char *p = s->getCString();
3923
34.3k
    int len = s->getLength();
3924
2.13M
    while (len > 0) {
3925
2.10M
      CharCode code;
3926
2.10M
      Unicode u[8];
3927
2.10M
      int uLen;
3928
2.10M
      double dx, dy, originX, originY;
3929
2.10M
      int n = font->getNextChar(p, len, &code,
3930
2.10M
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3931
2.10M
        &dx, &dy, &originX, &originY);
3932
2.10M
      dx = dx * state->getFontSize() + state->getCharSpace();
3933
2.10M
      if (n == 1 && *p == ' ') {
3934
133k
  dx += state->getWordSpace();
3935
133k
      }
3936
2.10M
      dx *= state->getHorizScaling();
3937
2.10M
      dy *= state->getFontSize();
3938
2.10M
      double tdx, tdy, ddx, ddy, x, y;
3939
2.10M
      state->textTransformDelta(dx, dy, &tdx, &tdy);
3940
2.10M
      state->transform(curX + riseX, curY + riseY, &x, &y);
3941
2.10M
      GfxState *savedState = saveStateStack();
3942
2.10M
      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.10M
      out->updateCTM(state, 1, 0, 0, 1, 0, 0);
3945
2.10M
      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.10M
      if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
3960
2.10M
             code, u, uLen)) {
3961
2.09M
  Object charProcRef, charProc;
3962
2.09M
  ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef);
3963
2.09M
  charProcRef.fetch(xref, &charProc);
3964
2.09M
  Dict *resDict = ((Gfx8BitFont *)font)->getResources();
3965
2.09M
  if (resDict) {
3966
1.71M
    pushResources(resDict);
3967
1.71M
  }
3968
2.09M
  if (charProc.isStream()) {
3969
57.7k
    display(&charProcRef, gFalse);
3970
2.03M
  } else {
3971
2.03M
    error(errSyntaxError, getPos(),
3972
2.03M
    "Missing or bad Type3 CharProc entry");
3973
2.03M
  }
3974
2.09M
  out->endType3Char(state);
3975
2.09M
  if (resDict) {
3976
1.71M
    popResources();
3977
1.71M
  }
3978
2.09M
  charProc.free();
3979
2.09M
  charProcRef.free();
3980
2.09M
      }
3981
2.10M
      restoreStateStack(savedState);
3982
2.10M
      curX += tdx;
3983
2.10M
      curY += tdy;
3984
2.10M
      state->moveTo(curX, curY);
3985
2.10M
      p += n;
3986
2.10M
      len -= n;
3987
2.10M
    }
3988
34.3k
    parser = oldParser;
3989
3990
477k
  } else if (out->useDrawChar()) {
3991
477k
    char *p = s->getCString();
3992
477k
    int len = s->getLength();
3993
4.62M
    while (len > 0) {
3994
4.14M
      CharCode code;
3995
4.14M
      Unicode u[8];
3996
4.14M
      int uLen;
3997
4.14M
      double dx, dy, originX, originY;
3998
4.14M
      int n = font->getNextChar(p, len, &code,
3999
4.14M
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
4000
4.14M
        &dx, &dy, &originX, &originY);
4001
4.14M
      if (wMode) {
4002
2.77k
  dx *= state->getFontSize();
4003
2.77k
  dy = dy * state->getFontSize() + state->getCharSpace();
4004
2.77k
  if (n == 1 && *p == ' ') {
4005
215
    dy += state->getWordSpace();
4006
215
  }
4007
4.14M
      } else {
4008
4.14M
  dx = dx * state->getFontSize() + state->getCharSpace();
4009
4.14M
  if (n == 1 && *p == ' ') {
4010
355k
    dx += state->getWordSpace();
4011
355k
  }
4012
4.14M
  dx *= state->getHorizScaling();
4013
4.14M
  dy *= state->getFontSize();
4014
4.14M
      }
4015
4.14M
      double tdx, tdy, tOriginX, tOriginY;
4016
4.14M
      state->textTransformDelta(dx, dy, &tdx, &tdy);
4017
4.14M
      originX *= state->getFontSize();
4018
4.14M
      originY *= state->getFontSize();
4019
4.14M
      state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
4020
4.14M
      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
4021
4.14M
        tdx, tdy, tOriginX, tOriginY, code, n, u, uLen,
4022
4.14M
        doFill, doStroke, doMakePath);
4023
4.14M
      state->shift(tdx, tdy);
4024
4.14M
      p += n;
4025
4.14M
      len -= n;
4026
4.14M
    }
4027
4028
477k
  } 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
511k
  if (out->useDrawChar()) {
4069
511k
    out->endString(state);
4070
511k
  }
4071
4072
511k
  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
43.5k
    double xMax = state->getCurX() + riseX;
4077
43.5k
    double yMax = state->getCurY() + riseY;
4078
43.5k
    double t;
4079
43.5k
    if (xMin > xMax) {
4080
2.18k
      t = xMin; xMin = xMax; xMax = t;
4081
2.18k
    }
4082
43.5k
    if (yMin > yMax) {
4083
204
      t = yMin; yMin = yMax; yMax = t;
4084
204
    }
4085
43.5k
    double dx1, dy1, dx2, dy2;
4086
43.5k
    state->textTransformDelta(0, state->getFontSize(), &dx1, &dy1);
4087
43.5k
    state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2);
4088
43.5k
    dx1 = fabs(dx1);
4089
43.5k
    dx2 = fabs(dx2);
4090
43.5k
    if (dx2 > dx1) {
4091
27.7k
      dx1 = dx2;
4092
27.7k
    }
4093
43.5k
    dy1 = fabs(dy1);
4094
43.5k
    dy2 = fabs(dy2);
4095
43.5k
    if (dy2 > dy1) {
4096
168
      dy1 = dy2;
4097
168
    }
4098
43.5k
    xMin -= 2 * dx1;
4099
43.5k
    yMin -= 2 * dy1;
4100
43.5k
    xMax += 2 * dx1;
4101
43.5k
    yMax += 2 * dy1;
4102
4103
    //--- fill
4104
43.5k
    if ((render & 3) == 0 || (render & 3) == 2) {
4105
40.9k
      if (state->getFillColorSpace()->getMode() == csPattern) {
4106
40.5k
  saveState();
4107
40.5k
  state->clipToRect(xMin, yMin, xMax, yMax);
4108
40.5k
  out->clipToTextPath(state);
4109
40.5k
  doPatternText(gFalse);
4110
40.5k
  restoreState();
4111
40.5k
      } else {
4112
394
  out->fillTextPath(state);
4113
394
      }
4114
40.9k
    }
4115
4116
    //--- stroke
4117
43.5k
    if ((render & 3) == 1 || (render & 3) == 2) {
4118
6.79k
      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
6.79k
      } else {
4125
6.79k
  out->strokeTextPath(state);
4126
6.79k
      }
4127
6.79k
    }
4128
4129
    //--- clip
4130
43.5k
    if (render & 4) {
4131
3.05k
      out->addTextPathToSavedClipPath(state);
4132
3.05k
      haveSavedClipPath = gTrue;
4133
40.5k
    } else {
4134
40.5k
      out->clearTextPath(state);
4135
40.5k
    }
4136
43.5k
  }
4137
4138
511k
  opCounter += 10 * s->getLength();
4139
511k
}
4140
4141
// NB: this is only called when ocState is false.
4142
21
void Gfx::doIncCharCount(GString *s) {
4143
21
  if (out->needCharCount()) {
4144
0
    out->incCharCount(s->getLength());
4145
0
  }
4146
21
}
4147
4148
//------------------------------------------------------------------------
4149
// XObject operators
4150
//------------------------------------------------------------------------
4151
4152
51.0k
void Gfx::opXObject(Object args[], int numArgs) {
4153
51.0k
  char *name;
4154
51.0k
  Object xObj, refObj, obj2, obj3;
4155
51.0k
  GBool ocSaved, oc;
4156
51.0k
#if OPI_SUPPORT
4157
51.0k
  Object opiDict;
4158
51.0k
#endif
4159
4160
51.0k
  if (!ocState && !out->needCharCount()) {
4161
697
    return;
4162
697
  }
4163
50.3k
  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
50.3k
  if (!res->lookupXObjectNF(name, &refObj)) {
4173
11.2k
    return;
4174
11.2k
  }
4175
39.0k
  if (!refObj.fetch(xref, &xObj)) {
4176
0
    refObj.free();
4177
0
    return;
4178
0
  }
4179
39.0k
  if (!xObj.isStream()) {
4180
7.02k
    error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name);
4181
7.02k
    xObj.free();
4182
7.02k
    refObj.free();
4183
7.02k
    return;
4184
7.02k
  }
4185
4186
  // check for optional content key
4187
32.0k
  ocSaved = ocState;
4188
32.0k
  xObj.streamGetDict()->lookupNF("OC", &obj2);
4189
32.0k
  if (doc->getOptionalContent()->evalOCObject(&obj2, &oc)) {
4190
0
    ocState &= oc;
4191
0
  }
4192
32.0k
  obj2.free();
4193
4194
32.0k
#if USE_EXCEPTIONS
4195
32.0k
  try {
4196
32.0k
#endif
4197
32.0k
#if OPI_SUPPORT
4198
32.0k
    xObj.streamGetDict()->lookup("OPI", &opiDict);
4199
32.0k
    if (opiDict.isDict()) {
4200
0
      out->opiBegin(state, opiDict.getDict());
4201
0
    }
4202
32.0k
#endif
4203
32.0k
    xObj.streamGetDict()->lookup("Subtype", &obj2);
4204
32.0k
    if (obj2.isName("Image")) {
4205
8.96k
      if (out->needNonText()) {
4206
8.96k
  doImage(&refObj, xObj.getStream(), gFalse);
4207
8.96k
      }
4208
23.0k
    } else if (obj2.isName("Form")) {
4209
22.4k
      if (out->useDrawForm() && refObj.isRef()) {
4210
0
  if (ocState) {
4211
0
    out->drawForm(refObj.getRef());
4212
0
  }
4213
22.4k
      } else {
4214
22.4k
  doForm(&refObj, &xObj);
4215
22.4k
      }
4216
22.4k
    } 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
636
    } else if (obj2.isName()) {
4224
174
      error(errSyntaxError, getPos(),
4225
174
      "Unknown XObject subtype '{0:s}'", obj2.getName());
4226
462
    } else {
4227
462
      error(errSyntaxError, getPos(),
4228
462
      "XObject subtype is missing or wrong type");
4229
462
    }
4230
32.0k
    obj2.free();
4231
32.0k
#if OPI_SUPPORT
4232
32.0k
    if (opiDict.isDict()) {
4233
0
      out->opiEnd(state, opiDict.getDict());
4234
0
    }
4235
32.0k
    opiDict.free();
4236
32.0k
#endif
4237
32.0k
#if USE_EXCEPTIONS
4238
32.0k
  } catch (GMemException e) {
4239
38
    xObj.free();
4240
38
    refObj.free();
4241
38
    throw;
4242
38
  }
4243
0
#endif
4244
4245
32.0k
  ocState = ocSaved;
4246
4247
32.0k
  xObj.free();
4248
32.0k
  refObj.free();
4249
32.0k
}
4250
4251
44.1k
GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
4252
44.1k
  Dict *dict, *maskDict;
4253
44.1k
  int width, height;
4254
44.1k
  int bits, maskBits;
4255
44.1k
  StreamColorSpaceMode csMode;
4256
44.1k
  GBool mask, invert;
4257
44.1k
  GfxColorSpace *colorSpace, *maskColorSpace;
4258
44.1k
  GfxImageColorMap *colorMap, *maskColorMap;
4259
44.1k
  Object maskObj, smaskObj, maskRef;
4260
44.1k
  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte;
4261
44.1k
  int maskColors[2*gfxColorMaxComps];
4262
44.1k
  int maskWidth, maskHeight;
4263
44.1k
  GBool maskInvert;
4264
44.1k
  Stream *maskStr;
4265
44.1k
  double matte[gfxColorMaxComps];
4266
44.1k
  GBool interpolate;
4267
44.1k
  GfxRenderingIntent riSaved;
4268
44.1k
  Object obj1, obj2;
4269
44.1k
  int i, n;
4270
4271
  // check for optional content
4272
44.1k
  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
44.1k
  str->disableDecompressionBombChecking();
4279
4280
  // get info from the stream
4281
44.1k
  bits = 0;
4282
44.1k
  csMode = streamCSNone;
4283
44.1k
  str->getImageParams(&bits, &csMode);
4284
4285
  // get stream dict
4286
44.1k
  dict = str->getDict();
4287
4288
  // save the current rendering intent
4289
44.1k
  riSaved = state->getRenderingIntent();
4290
4291
  // get size
4292
44.1k
  dict->lookup("Width", &obj1);
4293
44.1k
  if (obj1.isNull()) {
4294
35.2k
    obj1.free();
4295
35.2k
    dict->lookup("W", &obj1);
4296
35.2k
  }
4297
44.1k
  if (obj1.isInt()) {
4298
43.4k
    width = obj1.getInt();
4299
43.4k
  } else if (obj1.isReal()) {
4300
211
    error(errSyntaxWarning, getPos(), "Non-integer image width");
4301
211
    width = (int)obj1.getReal();
4302
575
  } else {
4303
575
    goto err2;
4304
575
  }
4305
43.6k
  obj1.free();
4306
43.6k
  if (width <= 0) {
4307
56
    goto err1;
4308
56
  }
4309
43.5k
  dict->lookup("Height", &obj1);
4310
43.5k
  if (obj1.isNull()) {
4311
34.6k
    obj1.free();
4312
34.6k
    dict->lookup("H", &obj1);
4313
34.6k
  }
4314
43.5k
  if (obj1.isInt()) {
4315
41.9k
    height = obj1.getInt();
4316
41.9k
  } else if (obj1.isReal()) {
4317
77
    error(errSyntaxWarning, getPos(), "Non-integer image height");
4318
77
    height = (int)obj1.getReal();
4319
1.51k
  } else {
4320
1.51k
    goto err2;
4321
1.51k
  }
4322
42.0k
  obj1.free();
4323
42.0k
  if (height <= 0) {
4324
233
    goto err1;
4325
233
  }
4326
4327
  // image or mask?
4328
41.8k
  dict->lookup("ImageMask", &obj1);
4329
41.8k
  if (obj1.isNull()) {
4330
41.7k
    obj1.free();
4331
41.7k
    dict->lookup("IM", &obj1);
4332
41.7k
  }
4333
41.8k
  mask = gFalse;
4334
41.8k
  if (obj1.isBool())
4335
16.4k
    mask = obj1.getBool();
4336
25.3k
  else if (!obj1.isNull())
4337
950
    goto err2;
4338
40.8k
  obj1.free();
4339
4340
  // bit depth
4341
40.8k
  if (bits == 0) {
4342
40.5k
    dict->lookup("BitsPerComponent", &obj1);
4343
40.5k
    if (obj1.isNull()) {
4344
32.1k
      obj1.free();
4345
32.1k
      dict->lookup("BPC", &obj1);
4346
32.1k
    }
4347
40.5k
    if (obj1.isNum()) {
4348
37.4k
      if (obj1.isInt()) {
4349
37.4k
  bits = obj1.getInt();
4350
37.4k
      } else if (obj1.isReal()) {
4351
70
  error(errSyntaxWarning, getPos(),
4352
70
        "Non-integer image bits per component");
4353
70
  bits = (int)obj1.getReal();
4354
70
      }
4355
37.4k
      if (bits < 1 || bits > 16) {
4356
205
  goto err2;
4357
205
      }
4358
37.4k
    } else if (mask) {
4359
1.29k
      bits = 1;
4360
1.82k
    } else {
4361
1.82k
      goto err2;
4362
1.82k
    }
4363
38.5k
    obj1.free();
4364
38.5k
  }
4365
4366
  // interpolate flag
4367
38.8k
  dict->lookup("Interpolate", &obj1);
4368
38.8k
  if (obj1.isNull()) {
4369
38.7k
    obj1.free();
4370
38.7k
    dict->lookup("I", &obj1);
4371
38.7k
  }
4372
38.8k
  interpolate = obj1.isBool() && obj1.getBool();
4373
38.8k
  obj1.free();
4374
4375
  // display a mask
4376
38.8k
  if (mask) {
4377
4378
    // check for inverted mask
4379
16.3k
    if (bits != 1)
4380
1.31k
      goto err1;
4381
15.0k
    invert = gFalse;
4382
15.0k
    dict->lookup("Decode", &obj1);
4383
15.0k
    if (obj1.isNull()) {
4384
14.9k
      obj1.free();
4385
14.9k
      dict->lookup("D", &obj1);
4386
14.9k
    }
4387
15.0k
    if (obj1.isArray()) {
4388
3.88k
      obj1.arrayGet(0, &obj2);
4389
3.88k
      invert = obj2.isNum() && obj2.getNum() == 1;
4390
3.88k
      obj2.free();
4391
11.1k
    } else if (!obj1.isNull()) {
4392
1.29k
      goto err2;
4393
1.29k
    }
4394
13.7k
    obj1.free();
4395
4396
    // if drawing is disabled, skip over inline image data
4397
13.7k
    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.7k
    } else {
4408
13.7k
      if (state->getFillColorSpace()->getMode() == csPattern) {
4409
12
  doPatternImageMask(ref, str, width, height, invert, inlineImg,
4410
12
         interpolate);
4411
13.7k
      } else {
4412
13.7k
  out->drawImageMask(state, ref, str, width, height, invert, inlineImg,
4413
13.7k
         interpolate);
4414
13.7k
      }
4415
13.7k
    }
4416
4417
22.5k
  } else {
4418
4419
    // rendering intent
4420
22.5k
    if (dict->lookup("Intent", &obj1)->isName()) {
4421
1.16k
      opSetRenderingIntent(&obj1, 1);
4422
1.16k
    }
4423
22.5k
    obj1.free();
4424
4425
    // get color space and color map
4426
22.5k
    dict->lookup("ColorSpace", &obj1);
4427
22.5k
    if (obj1.isNull()) {
4428
14.6k
      obj1.free();
4429
14.6k
      dict->lookup("CS", &obj1);
4430
14.6k
    }
4431
22.5k
    if (obj1.isName()) {
4432
18.4k
      res->lookupColorSpace(obj1.getName(), &obj2);
4433
18.4k
      if (!obj2.isNull()) {
4434
14
  obj1.free();
4435
14
  obj1 = obj2;
4436
18.3k
      } else {
4437
18.3k
  obj2.free();
4438
18.3k
      }
4439
18.4k
    }
4440
22.5k
    if (!obj1.isNull()) {
4441
20.3k
      colorSpace = GfxColorSpace::parse(&obj1
4442
20.3k
          );
4443
20.3k
    } else if (csMode == streamCSDeviceGray) {
4444
39
      colorSpace = GfxColorSpace::create(csDeviceGray);
4445
2.08k
    } else if (csMode == streamCSDeviceRGB) {
4446
22
      colorSpace = GfxColorSpace::create(csDeviceRGB);
4447
2.06k
    } else if (csMode == streamCSDeviceCMYK) {
4448
0
      colorSpace = GfxColorSpace::create(csDeviceCMYK);
4449
2.06k
    } else {
4450
2.06k
      colorSpace = NULL;
4451
2.06k
    }
4452
22.5k
    obj1.free();
4453
22.5k
    if (!colorSpace) {
4454
3.17k
      goto err1;
4455
3.17k
    }
4456
19.3k
    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
19.2k
    dict->lookup("Decode", &obj1);
4462
19.2k
    if (obj1.isNull()) {
4463
18.7k
      obj1.free();
4464
18.7k
      dict->lookup("D", &obj1);
4465
18.7k
    }
4466
19.2k
    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
4467
19.2k
    obj1.free();
4468
19.2k
    if (!colorMap->isOk()) {
4469
1.32k
      delete colorMap;
4470
1.32k
      goto err1;
4471
1.32k
    }
4472
4473
    // get the mask
4474
17.9k
    haveColorKeyMask = haveExplicitMask = haveSoftMask = haveMatte = gFalse;
4475
17.9k
    maskStr = NULL; // make gcc happy
4476
17.9k
    maskWidth = maskHeight = 0; // make gcc happy
4477
17.9k
    maskInvert = gFalse; // make gcc happy
4478
17.9k
    maskColorMap = NULL; // make gcc happy
4479
17.9k
    dict->lookup("Mask", &maskObj);
4480
17.9k
    dict->lookup("SMask", &smaskObj);
4481
17.9k
    if (smaskObj.isStream()) {
4482
      // soft mask
4483
2.56k
      if (inlineImg) {
4484
0
  delete colorMap;
4485
0
  maskObj.free();
4486
0
  smaskObj.free();
4487
0
  goto err1;
4488
0
      }
4489
2.56k
      maskStr = smaskObj.getStream();
4490
2.56k
      maskStr->disableDecompressionBombChecking();
4491
2.56k
      maskDict = smaskObj.streamGetDict();
4492
2.56k
      maskDict->lookup("Width", &obj1);
4493
2.56k
      if (obj1.isNull()) {
4494
77
  obj1.free();
4495
77
  maskDict->lookup("W", &obj1);
4496
77
      }
4497
2.56k
      if (obj1.isInt()) {
4498
2.32k
  maskWidth = obj1.getInt();
4499
2.32k
      } else if (obj1.isReal()) {
4500
124
  error(errSyntaxWarning, getPos(), "Non-integer image mask width");
4501
124
  maskWidth = (int)obj1.getReal();
4502
124
      } else {
4503
110
  delete colorMap;
4504
110
  maskObj.free();
4505
110
  smaskObj.free();
4506
110
  goto err2;
4507
110
      }
4508
2.45k
      obj1.free();
4509
2.45k
      maskDict->lookup("Height", &obj1);
4510
2.45k
      if (obj1.isNull()) {
4511
139
  obj1.free();
4512
139
  maskDict->lookup("H", &obj1);
4513
139
      }
4514
2.45k
      if (obj1.isInt()) {
4515
2.23k
  maskHeight = obj1.getInt();
4516
2.23k
      } else if (obj1.isReal()) {
4517
83
  error(errSyntaxWarning, getPos(), "Non-integer image mask height");
4518
83
  maskHeight = (int)obj1.getReal();
4519
139
      } else {
4520
139
  delete colorMap;
4521
139
  maskObj.free();
4522
139
  smaskObj.free();
4523
139
  goto err2;
4524
139
      }
4525
2.31k
      obj1.free();
4526
2.31k
      if (maskWidth <= 0 || maskHeight <= 0) {
4527
91
  delete colorMap;
4528
91
  maskObj.free();
4529
91
  smaskObj.free();
4530
91
  goto err1;
4531
91
      }
4532
2.22k
      maskDict->lookup("BitsPerComponent", &obj1);
4533
2.22k
      if (obj1.isNull()) {
4534
78
  obj1.free();
4535
78
  maskDict->lookup("BPC", &obj1);
4536
78
      }
4537
2.22k
      if (obj1.isInt()) {
4538
2.09k
  maskBits = obj1.getInt();
4539
2.09k
      } else if (obj1.isReal()) {
4540
47
  error(errSyntaxWarning, getPos(),
4541
47
        "Non-integer image mask bits per component");
4542
47
  maskBits = (int)obj1.getReal();
4543
78
      } else {
4544
78
  delete colorMap;
4545
78
  maskObj.free();
4546
78
  smaskObj.free();
4547
78
  goto err2;
4548
78
      }
4549
2.14k
      obj1.free();
4550
2.14k
      if (maskBits < 1 || maskBits > 16) {
4551
46
  delete colorMap;
4552
46
  maskObj.free();
4553
46
  smaskObj.free();
4554
46
  goto err1;
4555
46
      }
4556
2.09k
      maskDict->lookup("ColorSpace", &obj1);
4557
2.09k
      if (obj1.isNull()) {
4558
118
  obj1.free();
4559
118
  maskDict->lookup("CS", &obj1);
4560
118
      }
4561
2.09k
      if (obj1.isName()) {
4562
1.98k
  res->lookupColorSpace(obj1.getName(), &obj2);
4563
1.98k
  if (!obj2.isNull()) {
4564
0
    obj1.free();
4565
0
    obj1 = obj2;
4566
1.98k
  } else {
4567
1.98k
    obj2.free();
4568
1.98k
  }
4569
1.98k
      }
4570
2.09k
      if (!obj1.isName("DeviceGray")) {
4571
200
  delete colorMap;
4572
200
  maskObj.free();
4573
200
  smaskObj.free();
4574
200
  goto err2;
4575
200
      }
4576
1.89k
      maskColorSpace = new GfxDeviceGrayColorSpace();
4577
1.89k
      obj1.free();
4578
1.89k
      maskDict->lookup("Decode", &obj1);
4579
1.89k
      if (obj1.isNull()) {
4580
1.84k
  obj1.free();
4581
1.84k
  maskDict->lookup("D", &obj1);
4582
1.84k
      }
4583
1.89k
      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
4584
1.89k
      obj1.free();
4585
1.89k
      if (!maskColorMap->isOk()) {
4586
73
  delete maskColorMap;
4587
73
  delete colorMap;
4588
73
  maskObj.free();
4589
73
  smaskObj.free();
4590
73
  goto err1;
4591
73
      }
4592
1.82k
      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.82k
      obj1.free();
4610
1.82k
      haveSoftMask = gTrue;
4611
15.4k
    } else if (maskObj.isArray()) {
4612
      // color key mask
4613
398
      haveColorKeyMask = gTrue;
4614
398
      for (i = 0;
4615
417
     i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps;
4616
398
     i += 2) {
4617
324
  maskObj.arrayGet(i, &obj1);
4618
324
  if (!obj1.isInt()) {
4619
36
    obj1.free();
4620
36
    haveColorKeyMask = gFalse;
4621
36
    break;
4622
36
  }
4623
288
  maskColors[i] = obj1.getInt();
4624
288
  obj1.free();
4625
288
  if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) {
4626
72
    haveColorKeyMask = gFalse;
4627
72
    break;
4628
72
  }
4629
216
  maskObj.arrayGet(i+1, &obj1);
4630
216
  if (!obj1.isInt()) {
4631
83
    obj1.free();
4632
83
    haveColorKeyMask = gFalse;
4633
83
    break;
4634
83
  }
4635
133
  maskColors[i+1] = obj1.getInt();
4636
133
  obj1.free();
4637
133
  if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) ||
4638
114
      maskColors[i] > maskColors[i+1]) {
4639
114
    haveColorKeyMask = gFalse;
4640
114
    break;
4641
114
  }
4642
133
      }
4643
15.0k
    } else if (maskObj.isStream()) {
4644
      // explicit mask
4645
324
      haveExplicitMask = gTrue;
4646
324
      if (inlineImg) {
4647
0
  delete colorMap;
4648
0
  maskObj.free();
4649
0
  smaskObj.free();
4650
0
  goto err1;
4651
0
      }
4652
324
      maskStr = maskObj.getStream();
4653
324
      maskStr->disableDecompressionBombChecking();
4654
324
      maskDict = maskObj.streamGetDict();
4655
324
      maskDict->lookup("Width", &obj1);
4656
324
      if (obj1.isNull()) {
4657
18
  obj1.free();
4658
18
  maskDict->lookup("W", &obj1);
4659
18
      }
4660
324
      if (obj1.isInt()) {
4661
306
  maskWidth = obj1.getInt();
4662
306
      } else if (obj1.isReal()) {
4663
0
  error(errSyntaxWarning, getPos(), "Non-integer image mask width");
4664
0
  maskWidth = (int)obj1.getReal();
4665
18
      } else {
4666
18
  haveExplicitMask = gFalse;
4667
18
      }
4668
324
      obj1.free();
4669
324
      maskDict->lookup("Height", &obj1);
4670
324
      if (obj1.isNull()) {
4671
39
  obj1.free();
4672
39
  maskDict->lookup("H", &obj1);
4673
39
      }
4674
324
      if (obj1.isInt()) {
4675
36
  maskHeight = obj1.getInt();
4676
288
      } else if (obj1.isReal()) {
4677
249
  error(errSyntaxWarning, getPos(), "Non-integer image mask height");
4678
249
  maskHeight = (int)obj1.getReal();
4679
249
      } else {
4680
39
  haveExplicitMask = gFalse;
4681
39
      }
4682
324
      obj1.free();
4683
324
      if (maskWidth <= 0 || maskHeight <= 0) {
4684
41
  haveExplicitMask = gFalse;
4685
41
      }
4686
324
      maskDict->lookup("ImageMask", &obj1);
4687
324
      if (obj1.isNull()) {
4688
55
  obj1.free();
4689
55
  maskDict->lookup("IM", &obj1);
4690
55
      }
4691
324
      if (!obj1.isBool() || !obj1.getBool()) {
4692
55
  haveExplicitMask = gFalse;
4693
55
      }
4694
324
      obj1.free();
4695
324
      maskInvert = gFalse;
4696
324
      maskDict->lookup("Decode", &obj1);
4697
324
      if (obj1.isNull()) {
4698
53
  obj1.free();
4699
53
  maskDict->lookup("D", &obj1);
4700
53
      }
4701
324
      if (obj1.isArray()) {
4702
252
  obj1.arrayGet(0, &obj2);
4703
252
  maskInvert = obj2.isNum() && obj2.getNum() == 1;
4704
252
  obj2.free();
4705
252
      } else if (!obj1.isNull()) {
4706
19
  haveExplicitMask = gFalse;
4707
19
      }
4708
324
      obj1.free();
4709
324
      if (!haveExplicitMask) {
4710
82
  error(errSyntaxError, getPos(), "Bad image parameters");
4711
82
      }
4712
324
    }
4713
4714
    // if drawing is disabled, skip over inline image data
4715
17.2k
    if (state->getIgnoreColorOps() || !ocState) {
4716
525
      if (state->getIgnoreColorOps()) {
4717
525
  error(errSyntaxWarning, getPos(), "Ignoring image"
4718
525
        " in uncolored Type 3 char or tiling pattern");
4719
525
      }
4720
525
      if (inlineImg) {
4721
493
  str->disableDecompressionBombChecking();
4722
493
  str->reset();
4723
493
  n = height * ((width * colorMap->getNumPixelComps() *
4724
493
           colorMap->getBits() + 7) / 8);
4725
493
  str->discardChars(n);
4726
493
  str->close();
4727
493
      }
4728
4729
    // draw it
4730
16.7k
    } else {
4731
16.7k
      if (haveSoftMask) {
4732
1.82k
  dict->lookupNF("SMask", &maskRef);
4733
1.82k
  out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
4734
1.82k
         &maskRef, maskStr, maskWidth, maskHeight,
4735
1.82k
         maskColorMap,
4736
1.82k
         haveMatte ? matte : (double *)NULL,
4737
1.82k
         interpolate);
4738
1.82k
  maskRef.free();
4739
1.82k
  delete maskColorMap;
4740
14.8k
      } else if (haveExplicitMask) {
4741
242
  dict->lookupNF("Mask", &maskRef);
4742
242
  out->drawMaskedImage(state, ref, str, width, height, colorMap,
4743
242
           &maskRef, maskStr, maskWidth, maskHeight,
4744
242
           maskInvert, interpolate);
4745
242
  maskRef.free();
4746
14.6k
      } else {
4747
14.6k
  out->drawImage(state, ref, str, width, height, colorMap,
4748
14.6k
           haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
4749
14.6k
           interpolate);
4750
14.6k
      }
4751
16.7k
    }
4752
4753
17.2k
    delete colorMap;
4754
17.2k
    maskObj.free();
4755
17.2k
    smaskObj.free();
4756
17.2k
  }
4757
4758
  // restore rendering intent
4759
30.9k
  if (state->getRenderingIntent() != riSaved) {
4760
32
    state->setRenderingIntent(riSaved);
4761
32
    out->updateRenderingIntent(state);
4762
32
  }
4763
4764
30.9k
  if ((i = width * height) > 1000) {
4765
13.7k
    i = 1000;
4766
13.7k
  }
4767
30.9k
  opCounter += i;
4768
4769
30.9k
  return gTrue;
4770
4771
6.88k
 err2:
4772
6.88k
  obj1.free();
4773
13.2k
 err1:
4774
13.2k
  error(errSyntaxError, getPos(), "Bad image parameters");
4775
4776
  // restore rendering intent
4777
13.2k
  if (state->getRenderingIntent() != riSaved) {
4778
440
    state->setRenderingIntent(riSaved);
4779
440
    out->updateRenderingIntent(state);
4780
440
  }
4781
4782
13.2k
  return gFalse;
4783
6.88k
}
4784
4785
22.4k
void Gfx::doForm(Object *strRef, Object *str) {
4786
22.4k
  Dict *dict;
4787
22.4k
  GBool transpGroup, isolated, knockout;
4788
22.4k
  Object matrixObj, bboxObj;
4789
22.4k
  double m[6], bbox[4];
4790
22.4k
  Object resObj;
4791
22.4k
  Dict *resDict;
4792
22.4k
  Object obj1, obj2, obj3;
4793
22.4k
  int i;
4794
4795
  // check for optional content
4796
22.4k
  if (!ocState && !out->needCharCount()) {
4797
0
    return;
4798
0
  }
4799
4800
  // get stream dict
4801
22.4k
  dict = str->streamGetDict();
4802
4803
  // check form type
4804
22.4k
  dict->lookup("FormType", &obj1);
4805
22.4k
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
4806
686
    error(errSyntaxError, getPos(), "Unknown form type");
4807
686
  }
4808
22.4k
  obj1.free();
4809
4810
  // get bounding box
4811
22.4k
  dict->lookup("BBox", &bboxObj);
4812
22.4k
  if (!(bboxObj.isArray() && bboxObj.arrayGetLength() == 4)) {
4813
225
    bboxObj.free();
4814
225
    error(errSyntaxError, getPos(), "Bad form bounding box");
4815
225
    return;
4816
225
  }
4817
110k
  for (i = 0; i < 4; ++i) {
4818
88.6k
    bboxObj.arrayGet(i, &obj1);
4819
88.6k
    if (!obj1.isNum()) {
4820
217
      bboxObj.free();
4821
217
      error(errSyntaxError, getPos(), "Bad form bounding box");
4822
217
      return;
4823
217
    }
4824
88.4k
    bbox[i] = obj1.getNum();
4825
88.4k
    obj1.free();
4826
88.4k
  }
4827
22.0k
  bboxObj.free();
4828
4829
  // get matrix
4830
22.0k
  dict->lookup("Matrix", &matrixObj);
4831
22.0k
  if (matrixObj.isArray() && matrixObj.arrayGetLength() == 6) {
4832
38.0k
    for (i = 0; i < 6; ++i) {
4833
32.6k
      matrixObj.arrayGet(i, &obj1);
4834
32.6k
      if (obj1.isNum()) {
4835
32.4k
  m[i] = obj1.getNum();
4836
32.4k
      } else {
4837
228
  m[i] = 0;
4838
228
      }
4839
32.6k
      obj1.free();
4840
32.6k
    }
4841
16.5k
  } else {
4842
16.5k
    m[0] = 1; m[1] = 0;
4843
16.5k
    m[2] = 0; m[3] = 1;
4844
16.5k
    m[4] = 0; m[5] = 0;
4845
16.5k
  }
4846
22.0k
  matrixObj.free();
4847
4848
  // get resources
4849
22.0k
  dict->lookup("Resources", &resObj);
4850
22.0k
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4851
4852
  // check for a transparency group
4853
22.0k
  transpGroup = isolated = knockout = gFalse;
4854
22.0k
  if (dict->lookup("Group", &obj1)->isDict()) {
4855
18.2k
    if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
4856
4.67k
      transpGroup = gTrue;
4857
4.67k
      if (obj1.dictLookup("I", &obj3)->isBool()) {
4858
344
  isolated = obj3.getBool();
4859
344
      }
4860
4.67k
      obj3.free();
4861
4.67k
      if (obj1.dictLookup("K", &obj3)->isBool()) {
4862
510
  knockout = obj3.getBool();
4863
510
      }
4864
4.67k
      obj3.free();
4865
4.67k
    }
4866
18.2k
    obj2.free();
4867
18.2k
  }
4868
22.0k
  obj1.free();
4869
4870
  // draw it
4871
22.0k
  drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout);
4872
4873
22.0k
  resObj.free();
4874
22.0k
}
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
64.2k
       Object *backdropColorObj) {
4882
64.2k
  Parser *oldParser;
4883
64.2k
  GfxState *savedState;
4884
64.2k
  GfxColorSpace *blendingColorSpace;
4885
64.2k
  GfxColor backdropColor;
4886
64.2k
  Object strObj, groupAttrsObj, csObj, obj1;
4887
64.2k
  double oldBaseMatrix[6];
4888
64.2k
  int i;
4889
4890
64.2k
  if (formDepth > 100) {
4891
0
    error(errSyntaxError, getPos(), "Excessive recursion in Form XObjects");
4892
0
    return;
4893
0
  }
4894
64.2k
  ++formDepth;
4895
4896
64.2k
  out->startStream(strRef->getRef(), state);
4897
4898
  // push new resources on stack
4899
64.2k
  pushResources(resDict);
4900
4901
  // save current graphics state
4902
64.2k
  saveState();
4903
4904
  // kill any pre-existing path
4905
64.2k
  state->clearPath();
4906
4907
  // save current parser
4908
64.2k
  oldParser = parser;
4909
4910
  // set form transformation matrix
4911
64.2k
  state->concatCTM(matrix[0], matrix[1], matrix[2],
4912
64.2k
       matrix[3], matrix[4], matrix[5]);
4913
64.2k
  out->updateCTM(state, matrix[0], matrix[1], matrix[2],
4914
64.2k
     matrix[3], matrix[4], matrix[5]);
4915
4916
  // set form bounding box
4917
64.2k
  state->moveTo(bbox[0], bbox[1]);
4918
64.2k
  state->lineTo(bbox[2], bbox[1]);
4919
64.2k
  state->lineTo(bbox[2], bbox[3]);
4920
64.2k
  state->lineTo(bbox[0], bbox[3]);
4921
64.2k
  state->closePath();
4922
64.2k
  state->clip();
4923
64.2k
  out->clip(state);
4924
64.2k
  state->clearPath();
4925
4926
64.2k
  blendingColorSpace = NULL;
4927
64.2k
  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
5.71k
    strRef->fetch(xref, &strObj);
4932
5.71k
    if (strObj.streamGetDict()->lookup("Group", &groupAttrsObj)->isDict()) {
4933
5.71k
      if (!groupAttrsObj.dictLookup("CS", &csObj)->isNull()) {
4934
1.17k
  blendingColorSpace = GfxColorSpace::parse(&csObj
4935
1.17k
              );
4936
1.17k
      }
4937
5.71k
      csObj.free();
4938
5.71k
    }
4939
5.71k
    groupAttrsObj.free();
4940
5.71k
    strObj.free();
4941
4942
5.71k
    if (!out->beginTransparencyGroup(state, bbox, blendingColorSpace,
4943
5.71k
             isolated, knockout, softMask)) {
4944
1.19k
      transpGroup = gFalse;
4945
1.19k
    }
4946
4947
5.71k
    if (softMask || transpGroup) {
4948
4.52k
      traceBegin(oldBaseMatrix, softMask ? "begin soft mask" : "begin t-group");
4949
4.52k
      if (state->getBlendMode() != gfxBlendNormal) {
4950
262
  state->setBlendMode(gfxBlendNormal);
4951
262
  out->updateBlendMode(state);
4952
262
      }
4953
4.52k
      if (state->getFillOpacity() != 1) {
4954
323
  state->setFillOpacity(1);
4955
323
  out->updateFillOpacity(state);
4956
323
      }
4957
4.52k
      if (state->getStrokeOpacity() != 1) {
4958
238
  state->setStrokeOpacity(1);
4959
238
  out->updateStrokeOpacity(state);
4960
238
      }
4961
4.52k
      out->clearSoftMask(state);
4962
4.52k
    }
4963
5.71k
  }
4964
4965
  // set new base matrix
4966
449k
  for (i = 0; i < 6; ++i) {
4967
385k
    oldBaseMatrix[i] = baseMatrix[i];
4968
385k
    baseMatrix[i] = state->getCTM()[i];
4969
385k
  }
4970
4971
  // save the state stack -- this handles the case where the form
4972
  // contents have unbalanced q/Q operators
4973
64.2k
  savedState = saveStateStack();
4974
4975
  // draw the form
4976
64.2k
  display(strRef, gFalse);
4977
4978
64.2k
  restoreStateStack(savedState);
4979
4980
64.2k
  if (softMask || transpGroup) {
4981
4.52k
    out->endTransparencyGroup(state);
4982
4.52k
  }
4983
4984
  // restore base matrix
4985
449k
  for (i = 0; i < 6; ++i) {
4986
385k
    baseMatrix[i] = oldBaseMatrix[i];
4987
385k
  }
4988
4989
  // restore parser
4990
64.2k
  parser = oldParser;
4991
4992
  // restore graphics state
4993
64.2k
  restoreState();
4994
4995
  // pop resource stack
4996
64.2k
  popResources();
4997
4998
64.2k
  if (softMask) {
4999
34.4k
    for (i = 0; i < gfxColorMaxComps; ++i) {
5000
33.3k
      backdropColor.c[i] = 0;
5001
33.3k
    }
5002
1.04k
    if (backdropColorObj->isArray()) {
5003
305
      for (i = 0;
5004
2.13k
     i < backdropColorObj->arrayGetLength() && i < gfxColorMaxComps;
5005
1.82k
     ++i) {
5006
1.82k
  backdropColorObj->arrayGet(i, &obj1);
5007
1.82k
  if (obj1.isNum()) {
5008
1.30k
    backdropColor.c[i] = dblToCol(obj1.getNum());
5009
1.30k
  }
5010
1.82k
  obj1.free();
5011
1.82k
      }
5012
738
    } else if (blendingColorSpace) {
5013
587
      blendingColorSpace->getDefaultColor(&backdropColor);
5014
587
    }
5015
    //~ else: need to get the parent or default color space (?)
5016
1.04k
    out->setSoftMask(state, bbox, alpha, transferFunc, &backdropColor);
5017
1.04k
    traceEnd(oldBaseMatrix, "end soft mask");
5018
63.1k
  } else if (transpGroup) {
5019
3.47k
    out->paintTransparencyGroup(state, bbox);
5020
3.47k
    traceEnd(oldBaseMatrix, "end t-group");
5021
3.47k
  }
5022
5023
64.2k
  if (blendingColorSpace) {
5024
988
    delete blendingColorSpace;
5025
988
  }
5026
5027
64.2k
  out->endStream(strRef->getRef());
5028
5029
64.2k
  --formDepth;
5030
64.2k
}
5031
5032
0
void Gfx::takeContentStreamStack(Gfx *oldGfx) {
5033
0
  contentStreamStack->append(oldGfx->contentStreamStack);
5034
0
}
5035
5036
84.6k
void Gfx::endOfPage() {
5037
228k
  while (state->hasSaves()) {
5038
143k
    restoreState();
5039
143k
  }
5040
88.4k
  while (markedContentStack->getLength() > 0) {
5041
3.80k
    opEndMarkedContent(NULL, 0);
5042
3.80k
  }
5043
84.6k
}
5044
5045
//------------------------------------------------------------------------
5046
// in-line image operators
5047
//------------------------------------------------------------------------
5048
5049
36.9k
void Gfx::opBeginImage(Object args[], int numArgs) {
5050
36.9k
  Stream *str;
5051
36.9k
  GBool haveLength;
5052
36.9k
  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
36.9k
  str = buildImageStream(&haveLength);
5059
5060
  // display the image
5061
36.9k
  if (str) {
5062
35.2k
    if (!doImage(NULL, str, gTrue)) {
5063
10.7k
      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
24.5k
    } else if (haveLength) {
5068
      // NB: getUndecodedStream() returns the EmbedStream created by
5069
      // buildImageStream()
5070
13.6k
      while ((c1 = str->getUndecodedStream()->getChar()) != EOF) ;
5071
278
      delete str;
5072
278
      str = parser->getStream();
5073
278
      c1 = str->getChar();
5074
278
      c2 = str->getChar();
5075
278
      c3 = str->lookChar();
5076
90.7k
      while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
5077
90.4k
  c1 = c2;
5078
90.4k
  c2 = str->getChar();
5079
90.4k
  c3 = str->lookChar();
5080
90.4k
      }
5081
5082
    // else, look for the 'EI' tag and skip it
5083
24.2k
    } else {
5084
24.2k
      c1 = str->getUndecodedStream()->getChar();
5085
24.2k
      c2 = str->getUndecodedStream()->getChar();
5086
24.2k
      c3 = str->getUndecodedStream()->lookChar();
5087
1.24M
      while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
5088
1.22M
  c1 = c2;
5089
1.22M
  c2 = str->getUndecodedStream()->getChar();
5090
1.22M
  c3 = str->getUndecodedStream()->lookChar();
5091
1.22M
      }
5092
24.2k
      delete str;
5093
24.2k
    }
5094
35.2k
  }
5095
36.9k
}
5096
5097
36.9k
Stream *Gfx::buildImageStream(GBool *haveLength) {
5098
36.9k
  Object dict;
5099
36.9k
  Object obj, lengthObj;
5100
36.9k
  char *key;
5101
36.9k
  int length;
5102
36.9k
  Stream *str;
5103
5104
  // build dictionary
5105
36.9k
  dict.initDict(xref);
5106
36.9k
  getContentObj(&obj);
5107
777k
  while (!obj.isCmd("ID") && !obj.isEOF()) {
5108
740k
    if (!obj.isName()) {
5109
495k
      error(errSyntaxError, getPos(),
5110
495k
      "Inline image dictionary key must be a name object");
5111
495k
      obj.free();
5112
495k
    } else {
5113
244k
      key = copyString(obj.getName());
5114
244k
      obj.free();
5115
244k
      getContentObj(&obj);
5116
244k
      if (obj.isEOF()) {
5117
122
  gfree(key);
5118
122
  break;
5119
122
      }
5120
244k
      if (obj.isError()) {
5121
3.08k
  gfree(key);
5122
3.08k
  obj.free();
5123
241k
      } else {
5124
241k
  dict.dictAdd(key, &obj);
5125
241k
      }
5126
244k
    }
5127
740k
    getContentObj(&obj);
5128
740k
  }
5129
36.9k
  if (obj.isEOF()) {
5130
1.58k
    error(errSyntaxError, getPos(), "End of file in inline image");
5131
1.58k
    obj.free();
5132
1.58k
    dict.free();
5133
1.58k
    return NULL;
5134
1.58k
  }
5135
35.3k
  obj.free();
5136
5137
  // check for length field
5138
35.3k
  length = 0;
5139
35.3k
  *haveLength = gFalse;
5140
35.3k
  if (!dict.dictLookup("Length", &lengthObj)->isInt()) {
5141
35.0k
    lengthObj.free();
5142
35.0k
    dict.dictLookup("L", &lengthObj);
5143
35.0k
  }
5144
35.3k
  if (lengthObj.isInt()) {
5145
323
    length = lengthObj.getInt();
5146
323
    *haveLength = gTrue;
5147
323
  }
5148
35.3k
  lengthObj.free();
5149
5150
  // make stream
5151
35.3k
  if (!(str = parser->getStream())) {
5152
107
    error(errSyntaxError, getPos(), "Invalid inline image data");
5153
107
    dict.free();
5154
107
    return NULL;
5155
107
  }
5156
35.2k
  str = new EmbedStream(str, &dict, *haveLength, (GFileOffset)length);
5157
35.2k
  str = str->addFilters(&dict);
5158
5159
35.2k
  return str;
5160
35.3k
}
5161
5162
11.5k
void Gfx::opImageData(Object args[], int numArgs) {
5163
11.5k
  error(errInternal, getPos(), "Got 'ID' operator");
5164
11.5k
}
5165
5166
18.1k
void Gfx::opEndImage(Object args[], int numArgs) {
5167
18.1k
  error(errInternal, getPos(), "Got 'EI' operator");
5168
18.1k
}
5169
5170
//------------------------------------------------------------------------
5171
// type 3 font operators
5172
//------------------------------------------------------------------------
5173
5174
12.1k
void Gfx::opSetCharWidth(Object args[], int numArgs) {
5175
12.1k
  out->type3D0(state, args[0].getNum(), args[1].getNum());
5176
12.1k
}
5177
5178
9.70k
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
5179
9.70k
  state->setIgnoreColorOps(gTrue);
5180
9.70k
  out->type3D1(state, args[0].getNum(), args[1].getNum(),
5181
9.70k
         args[2].getNum(), args[3].getNum(),
5182
9.70k
         args[4].getNum(), args[5].getNum());
5183
9.70k
}
5184
5185
//------------------------------------------------------------------------
5186
// compatibility operators
5187
//------------------------------------------------------------------------
5188
5189
2.02k
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
5190
2.02k
  ++ignoreUndef;
5191
2.02k
}
5192
5193
1.51k
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
5194
1.51k
  if (ignoreUndef > 0)
5195
1.24k
    --ignoreUndef;
5196
1.51k
}
5197
5198
//------------------------------------------------------------------------
5199
// marked content operators
5200
//------------------------------------------------------------------------
5201
5202
22.2k
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
5203
22.2k
  GfxMarkedContent *mc;
5204
22.2k
  Object obj;
5205
22.2k
  GBool ocStateNew;
5206
22.2k
  TextString *s;
5207
22.2k
  GfxMarkedContentKind mcKind;
5208
5209
22.2k
  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
22.2k
  mcKind = gfxMCOther;
5218
22.2k
  if (args[0].isName("OC") && numArgs == 2 && args[1].isName() &&
5219
16.0k
      res->lookupPropertiesNF(args[1].getName(), &obj)) {
5220
9.61k
    if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) {
5221
1.80k
      ocState &= ocStateNew;
5222
1.80k
    }
5223
9.61k
    obj.free();
5224
9.61k
    mcKind = gfxMCOptionalContent;
5225
12.6k
  } else if (numArgs == 2 && args[1].isDict()) {
5226
1.87k
    if (args[0].isName("Span")) {
5227
162
      if (args[1].dictLookup("ActualText", &obj)->isString()) {
5228
41
  s = new TextString(obj.getString());
5229
41
  out->beginActualText(state, s->getUnicode(), s->getLength());
5230
41
  delete s;
5231
41
  mcKind = gfxMCActualText;
5232
41
      }
5233
162
      obj.free();
5234
162
    }
5235
1.87k
    if (args[1].dictLookup("MCID", &obj)->isInt()) {
5236
566
      out->beginStructureItem(args[0].getName(), obj.getInt(),
5237
566
            args[1].getDict());
5238
566
      if (mcKind == gfxMCActualText) {
5239
0
  mcKind = gfxMCStructureItemAndActualText;
5240
566
      } else {
5241
566
  mcKind = gfxMCStructureItem;
5242
566
      }
5243
566
    }
5244
1.87k
    obj.free();
5245
1.87k
  }
5246
5247
22.2k
  mc = new GfxMarkedContent(mcKind, ocState);
5248
22.2k
  markedContentStack->append(mc);
5249
22.2k
}
5250
5251
29.2k
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
5252
29.2k
  GfxMarkedContent *mc;
5253
29.2k
  GfxMarkedContentKind mcKind;
5254
5255
29.2k
  if (markedContentStack->getLength() > 0) {
5256
22.0k
    mc = (GfxMarkedContent *)
5257
22.0k
             markedContentStack->del(markedContentStack->getLength() - 1);
5258
22.0k
    mcKind = mc->kind;
5259
22.0k
    delete mc;
5260
22.0k
    if (mcKind == gfxMCOptionalContent) {
5261
9.60k
      if (markedContentStack->getLength() > 0) {
5262
2.47k
  mc = (GfxMarkedContent *)
5263
2.47k
           markedContentStack->get(markedContentStack->getLength() - 1);
5264
2.47k
  ocState = mc->ocState;
5265
7.13k
      } else {
5266
7.13k
  ocState = gTrue;
5267
7.13k
      }
5268
12.4k
    } else if (mcKind == gfxMCActualText) {
5269
41
      out->endActualText(state);
5270
12.4k
    } else if (mcKind == gfxMCStructureItem) {
5271
565
      out->endStructureItem();
5272
11.8k
    } else if (mcKind == gfxMCStructureItemAndActualText) {
5273
0
      out->endStructureItem();
5274
0
      out->endActualText(state);
5275
0
    }
5276
22.0k
  } else {
5277
7.12k
    error(errSyntaxWarning, getPos(), "Mismatched EMC operator");
5278
7.12k
  }
5279
29.2k
}
5280
5281
76
void Gfx::opMarkPoint(Object args[], int numArgs) {
5282
76
  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
76
}
5290
5291
//------------------------------------------------------------------------
5292
// misc
5293
//------------------------------------------------------------------------
5294
5295
void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
5296
13.0k
        double xMin, double yMin, double xMax, double yMax) {
5297
13.0k
  Dict *dict, *resDict;
5298
13.0k
  Object str, matrixObj, bboxObj, resObj, obj1;
5299
13.0k
  double formXMin, formYMin, formXMax, formYMax;
5300
13.0k
  double x, y, sx, sy, tx, ty;
5301
13.0k
  double m[6], bbox[4];
5302
13.0k
  double *borderColor;
5303
13.0k
  GfxColor color;
5304
13.0k
  double *dash, *dash2;
5305
13.0k
  int dashLength;
5306
13.0k
  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
13.0k
  if (xMin == xMax || yMin == yMax) {
5314
385
    return;
5315
385
  }
5316
5317
  // draw the appearance stream (if there is one)
5318
12.6k
  strRef->fetch(xref, &str);
5319
12.6k
  if (str.isStream()) {
5320
5321
    // get stream dict
5322
3.59k
    dict = str.streamGetDict();
5323
5324
    // get the form bounding box
5325
3.59k
    dict->lookup("BBox", &bboxObj);
5326
3.59k
    if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) {
5327
90
      error(errSyntaxError, getPos(), "Bad form bounding box");
5328
90
      bboxObj.free();
5329
90
      str.free();
5330
90
      return;
5331
90
    }
5332
17.5k
    for (i = 0; i < 4; ++i) {
5333
14.0k
      bboxObj.arrayGet(i, &obj1);
5334
14.0k
      if (obj1.isNum()) {
5335
13.5k
  bbox[i] = obj1.getNum();
5336
13.5k
      } else {
5337
489
  bbox[i] = 0;
5338
489
      }
5339
14.0k
      obj1.free();
5340
14.0k
    }
5341
3.50k
    bboxObj.free();
5342
5343
    // get the form matrix
5344
3.50k
    dict->lookup("Matrix", &matrixObj);
5345
3.50k
    if (matrixObj.isArray() && matrixObj.arrayGetLength() == 6) {
5346
2.07k
      for (i = 0; i < 6; ++i) {
5347
1.77k
  matrixObj.arrayGet(i, &obj1);
5348
1.77k
  if (obj1.isNum()) {
5349
1.73k
    m[i] = obj1.getNum();
5350
1.73k
  } else {
5351
40
    m[i] = 0;
5352
40
  }
5353
1.77k
  obj1.free();
5354
1.77k
      }
5355
3.20k
    } else {
5356
3.20k
      m[0] = 1; m[1] = 0;
5357
3.20k
      m[2] = 0; m[3] = 1;
5358
3.20k
      m[4] = 0; m[5] = 0;
5359
3.20k
    }
5360
3.50k
    matrixObj.free();
5361
5362
    // transform the four corners of the form bbox to default user
5363
    // space, and construct the transformed bbox
5364
3.50k
    x = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
5365
3.50k
    y = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
5366
3.50k
    formXMin = formXMax = x;
5367
3.50k
    formYMin = formYMax = y;
5368
3.50k
    x = bbox[0] * m[0] + bbox[3] * m[2] + m[4];
5369
3.50k
    y = bbox[0] * m[1] + bbox[3] * m[3] + m[5];
5370
3.50k
    if (x < formXMin) {
5371
50
      formXMin = x;
5372
3.45k
    } else if (x > formXMax) {
5373
28
      formXMax = x;
5374
28
    }
5375
3.50k
    if (y < formYMin) {
5376
11
      formYMin = y;
5377
3.49k
    } else if (y > formYMax) {
5378
3.26k
      formYMax = y;
5379
3.26k
    }
5380
3.50k
    x = bbox[2] * m[0] + bbox[1] * m[2] + m[4];
5381
3.50k
    y = bbox[2] * m[1] + bbox[1] * m[3] + m[5];
5382
3.50k
    if (x < formXMin) {
5383
6
      formXMin = x;
5384
3.49k
    } else if (x > formXMax) {
5385
3.26k
      formXMax = x;
5386
3.26k
    }
5387
3.50k
    if (y < formYMin) {
5388
4
      formYMin = y;
5389
3.49k
    } else if (y > formYMax) {
5390
71
      formYMax = y;
5391
71
    }
5392
3.50k
    x = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
5393
3.50k
    y = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
5394
3.50k
    if (x < formXMin) {
5395
0
      formXMin = x;
5396
3.50k
    } else if (x > formXMax) {
5397
16
      formXMax = x;
5398
16
    }
5399
3.50k
    if (y < formYMin) {
5400
3
      formYMin = y;
5401
3.49k
    } 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.50k
    if (formXMax - formXMin < 1e-8) {
5410
      // this shouldn't happen
5411
168
      sx = 1;
5412
3.33k
    } else {
5413
3.33k
      sx = (xMax - xMin) / (formXMax - formXMin);
5414
3.33k
    }
5415
3.50k
    if (formYMax - formYMin < 1e-8) {
5416
      // this shouldn't happen
5417
162
      sy = 1;
5418
3.34k
    } else {
5419
3.34k
      sy = (yMax - yMin) / (formYMax - formYMin);
5420
3.34k
    }
5421
3.50k
    tx = -formXMin * sx + xMin;
5422
3.50k
    ty = -formYMin * sy + yMin;
5423
5424
    // the final transform matrix is (form matrix) * (mapping matrix)
5425
3.50k
    m[0] *= sx;
5426
3.50k
    m[1] *= sy;
5427
3.50k
    m[2] *= sx;
5428
3.50k
    m[3] *= sy;
5429
3.50k
    m[4] = m[4] * sx + tx;
5430
3.50k
    m[5] = m[5] * sy + ty;
5431
5432
    // get the resources
5433
3.50k
    dict->lookup("Resources", &resObj);
5434
3.50k
    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
5435
5436
    // draw it
5437
3.50k
    drawForm(strRef, resDict, m, bbox);
5438
5439
3.50k
    resObj.free();
5440
3.50k
  }
5441
12.5k
  str.free();
5442
5443
  // draw the border
5444
12.5k
  if (borderStyle && borderStyle->getWidth() > 0 &&
5445
536
      borderStyle->getNumColorComps() > 0) {
5446
536
    borderColor = borderStyle->getColor();
5447
536
    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
452
    case 3:
5456
452
      if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
5457
167
  state->setStrokePattern(NULL);
5458
167
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
5459
167
  out->updateStrokeColorSpace(state);
5460
167
      }
5461
452
      break;
5462
84
    case 4:
5463
84
      if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) {
5464
31
  state->setStrokePattern(NULL);
5465
31
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
5466
31
  out->updateStrokeColorSpace(state);
5467
31
      }
5468
84
      break;
5469
536
    }
5470
536
    color.c[0] = dblToCol(borderColor[0]);
5471
536
    color.c[1] = dblToCol(borderColor[1]);
5472
536
    color.c[2] = dblToCol(borderColor[2]);
5473
536
    color.c[3] = dblToCol(borderColor[3]);
5474
536
    state->setStrokeColor(&color);
5475
536
    out->updateStrokeColor(state);
5476
536
    state->setLineWidth(borderStyle->getWidth());
5477
536
    out->updateLineWidth(state);
5478
536
    borderStyle->getDash(&dash, &dashLength);
5479
536
    if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
5480
80
      dash2 = (double *)gmallocn(dashLength, sizeof(double));
5481
80
      memcpy(dash2, dash, dashLength * sizeof(double));
5482
80
      state->setLineDash(dash2, dashLength, 0);
5483
80
      out->updateLineDash(state);
5484
80
    }
5485
    //~ this doesn't currently handle the beveled and engraved styles
5486
536
    state->clearPath();
5487
536
    state->moveTo(xMin, yMin);
5488
536
    state->lineTo(xMax, yMin);
5489
536
    if (borderStyle->getType() != annotBorderUnderlined) {
5490
536
      state->lineTo(xMax, yMax);
5491
536
      state->lineTo(xMin, yMax);
5492
536
      state->closePath();
5493
536
    }
5494
536
    out->stroke(state);
5495
536
  }
5496
12.5k
}
5497
5498
442k
void Gfx::saveState() {
5499
442k
  out->saveState(state);
5500
442k
  state = state->save();
5501
442k
}
5502
5503
551k
void Gfx::restoreState() {
5504
551k
  state = state->restore();
5505
551k
  out->restoreState(state);
5506
551k
}
5507
5508
// Create a new state stack, and initialize it with a copy of the
5509
// current state.
5510
2.20M
GfxState *Gfx::saveStateStack() {
5511
2.20M
  GfxState *oldState;
5512
5513
2.20M
  out->saveState(state);
5514
2.20M
  oldState = state;
5515
2.20M
  state = state->copy(gTrue);
5516
2.20M
  return oldState;
5517
2.20M
}
5518
5519
// Switch back to the previous state stack.
5520
2.20M
void Gfx::restoreStateStack(GfxState *oldState) {
5521
2.24M
  while (state->hasSaves()) {
5522
46.4k
    restoreState();
5523
46.4k
  }
5524
2.20M
  delete state;
5525
2.20M
  state = oldState;
5526
2.20M
  out->restoreState(state);
5527
2.20M
}
5528
5529
1.77M
void Gfx::pushResources(Dict *resDict) {
5530
1.77M
  res = new GfxResources(xref, resDict, res);
5531
1.77M
}
5532
5533
1.94M
void Gfx::popResources() {
5534
1.94M
  GfxResources *resPtr;
5535
5536
1.94M
  resPtr = res->getNext();
5537
1.94M
  delete res;
5538
1.94M
  res = resPtr;
5539
1.94M
}