Coverage Report

Created: 2026-06-22 07:14

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