Coverage Report

Created: 2026-08-13 06:56

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