Coverage Report

Created: 2026-07-16 06:52

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