Coverage Report

Created: 2026-09-03 07:21

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