Coverage Report

Created: 2025-07-11 07:47

/src/xpdf-4.05/xpdf/Gfx.cc
Line
Count
Source (jump to first uncovered line)
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
0
#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
0
#define numOps (sizeof(opTab) / sizeof(Operator))
283
284
//------------------------------------------------------------------------
285
// GfxResources
286
//------------------------------------------------------------------------
287
288
0
GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
289
0
  Object obj1, obj2;
290
0
  Ref r;
291
292
0
  if (resDict) {
293
0
    valid = gTrue;
294
295
    // build font dictionary
296
0
    fonts = NULL;
297
0
    resDict->lookupNF("Font", &obj1);
298
0
    if (obj1.isRef()) {
299
0
      obj1.fetch(xref, &obj2);
300
0
      if (obj2.isDict()) {
301
0
  r = obj1.getRef();
302
0
  fonts = new GfxFontDict(xref, &r, obj2.getDict());
303
0
      }
304
0
      obj2.free();
305
0
    } else if (obj1.isDict()) {
306
0
      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
307
0
    }
308
0
    obj1.free();
309
310
    // get XObject dictionary
311
0
    resDict->lookup("XObject", &xObjDict);
312
313
    // get color space dictionary
314
0
    resDict->lookup("ColorSpace", &colorSpaceDict);
315
316
    // get pattern dictionary
317
0
    resDict->lookup("Pattern", &patternDict);
318
319
    // get shading dictionary
320
0
    resDict->lookup("Shading", &shadingDict);
321
322
    // get graphics state parameter dictionary
323
0
    resDict->lookup("ExtGState", &gStateDict);
324
325
    // get properties dictionary
326
0
    resDict->lookup("Properties", &propsDict);
327
328
0
  } else {
329
0
    valid = gFalse;
330
0
    fonts = NULL;
331
0
    xObjDict.initNull();
332
0
    colorSpaceDict.initNull();
333
0
    patternDict.initNull();
334
0
    shadingDict.initNull();
335
0
    gStateDict.initNull();
336
0
    propsDict.initNull();
337
0
  }
338
339
0
  next = nextA;
340
0
}
341
342
0
GfxResources::~GfxResources() {
343
0
  if (fonts) {
344
0
    delete fonts;
345
0
  }
346
0
  xObjDict.free();
347
0
  colorSpaceDict.free();
348
0
  patternDict.free();
349
0
  shadingDict.free();
350
0
  gStateDict.free();
351
0
  propsDict.free();
352
0
}
353
354
0
GfxFont *GfxResources::lookupFont(char *name) {
355
0
  GfxFont *font;
356
0
  GfxResources *resPtr;
357
358
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
359
0
    if (resPtr->fonts) {
360
0
      if ((font = resPtr->fonts->lookup(name))) {
361
0
  return font;
362
0
      }
363
0
    }
364
0
  }
365
0
  error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name);
366
0
  return NULL;
367
0
}
368
369
0
GfxFont *GfxResources::lookupFontByRef(Ref ref) {
370
0
  GfxFont *font;
371
0
  GfxResources *resPtr;
372
373
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
374
0
    if (resPtr->fonts) {
375
0
      if ((font = resPtr->fonts->lookupByRef(ref))) {
376
0
  return font;
377
0
      }
378
0
    }
379
0
  }
380
0
  error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen);
381
0
  return NULL;
382
0
}
383
384
0
GBool GfxResources::lookupXObject(const char *name, Object *obj) {
385
0
  GfxResources *resPtr;
386
387
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
388
0
    if (resPtr->xObjDict.isDict()) {
389
0
      if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
390
0
  return gTrue;
391
0
      obj->free();
392
0
    }
393
0
  }
394
0
  error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
395
0
  return gFalse;
396
0
}
397
398
0
GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
399
0
  GfxResources *resPtr;
400
401
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
402
0
    if (resPtr->xObjDict.isDict()) {
403
0
      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
404
0
  return gTrue;
405
0
      obj->free();
406
0
    }
407
0
  }
408
0
  error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
409
0
  return gFalse;
410
0
}
411
412
void GfxResources::lookupColorSpace(const char *name, Object *obj,
413
0
            GBool inherit) {
414
0
  GfxResources *resPtr;
415
416
  //~ should also test for G, RGB, and CMYK - but only in inline images (?)
417
0
  if (!strcmp(name, "DeviceGray") ||
418
0
      !strcmp(name, "DeviceRGB") ||
419
0
      !strcmp(name, "DeviceCMYK")) {
420
0
    obj->initNull();
421
0
    return;
422
0
  }
423
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
424
0
    if (resPtr->colorSpaceDict.isDict()) {
425
0
      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
426
0
  return;
427
0
      }
428
0
      obj->free();
429
0
    }
430
0
    if (!inherit && valid) {
431
0
      break;
432
0
    }
433
0
  }
434
0
  obj->initNull();
435
0
}
436
437
GfxPattern *GfxResources::lookupPattern(const char *name
438
0
          ) {
439
0
  GfxResources *resPtr;
440
0
  GfxPattern *pattern;
441
0
  Object objRef, obj;
442
443
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
444
0
    if (resPtr->patternDict.isDict()) {
445
0
      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
446
0
  resPtr->patternDict.dictLookupNF(name, &objRef);
447
0
  pattern = GfxPattern::parse(&objRef, &obj
448
0
            );
449
0
  objRef.free();
450
0
  obj.free();
451
0
  return pattern;
452
0
      }
453
0
      obj.free();
454
0
    }
455
0
  }
456
0
  error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name);
457
0
  return NULL;
458
0
}
459
460
GfxShading *GfxResources::lookupShading(const char *name
461
0
          ) {
462
0
  GfxResources *resPtr;
463
0
  GfxShading *shading;
464
0
  Object obj;
465
466
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
467
0
    if (resPtr->shadingDict.isDict()) {
468
0
      if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
469
0
  shading = GfxShading::parse(&obj
470
0
            );
471
0
  obj.free();
472
0
  return shading;
473
0
      }
474
0
      obj.free();
475
0
    }
476
0
  }
477
0
  error(errSyntaxError, -1, "Unknown shading '{0:s}'", name);
478
0
  return NULL;
479
0
}
480
481
0
GBool GfxResources::lookupGState(const char *name, Object *obj) {
482
0
  GfxResources *resPtr;
483
484
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
485
0
    if (resPtr->gStateDict.isDict()) {
486
0
      if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
487
0
  return gTrue;
488
0
      }
489
0
      obj->free();
490
0
    }
491
0
  }
492
0
  error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name);
493
0
  return gFalse;
494
0
}
495
496
0
GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) {
497
0
  GfxResources *resPtr;
498
499
0
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
500
0
    if (resPtr->propsDict.isDict()) {
501
0
      if (!resPtr->propsDict.dictLookupNF(name, obj)->isNull()) {
502
0
  return gTrue;
503
0
      }
504
0
      obj->free();
505
0
    }
506
0
  }
507
0
  error(errSyntaxError, -1, "Properties '{0:s}' is unknown", name);
508
0
  return gFalse;
509
0
}
510
511
//------------------------------------------------------------------------
512
// Gfx
513
//------------------------------------------------------------------------
514
515
Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
516
   double hDPI, double vDPI, PDFRectangle *box,
517
   PDFRectangle *cropBox, int rotate,
518
   GBool (*abortCheckCbkA)(void *data),
519
0
   void *abortCheckCbkDataA) {
520
0
  int i;
521
522
0
  doc = docA;
523
0
  xref = doc->getXRef();
524
0
  subPage = gFalse;
525
0
  printCommands = globalParams->getPrintCommands();
526
0
  defaultFont = NULL;
527
528
  // start the resource stack
529
0
  res = new GfxResources(xref, resDict, NULL);
530
531
  // initialize
532
0
  out = outA;
533
0
  state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
534
0
  fontChanged = gFalse;
535
0
  haveSavedClipPath = gFalse;
536
0
  clip = clipNone;
537
0
  ignoreUndef = 0;
538
0
  out->startPage(pageNum, state);
539
0
  out->setDefaultCTM(state->getCTM());
540
0
  out->updateAll(state);
541
0
  for (i = 0; i < 6; ++i) {
542
0
    baseMatrix[i] = state->getCTM()[i];
543
0
  }
544
0
  formDepth = 0;
545
0
  markedContentStack = new GList();
546
0
  ocState = gTrue;
547
0
  parser = NULL;
548
0
  contentStreamStack = new GList();
549
0
  abortCheckCbk = abortCheckCbkA;
550
0
  abortCheckCbkData = abortCheckCbkDataA;
551
552
  // set crop box
553
0
  if (cropBox) {
554
0
    state->moveTo(cropBox->x1, cropBox->y1);
555
0
    state->lineTo(cropBox->x2, cropBox->y1);
556
0
    state->lineTo(cropBox->x2, cropBox->y2);
557
0
    state->lineTo(cropBox->x1, cropBox->y2);
558
0
    state->closePath();
559
0
    state->clip();
560
0
    out->clip(state);
561
0
    state->clearPath();
562
0
  }
563
0
}
564
565
Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
566
   PDFRectangle *box, PDFRectangle *cropBox,
567
   GBool (*abortCheckCbkA)(void *data),
568
0
   void *abortCheckCbkDataA) {
569
0
  int i;
570
571
0
  doc = docA;
572
0
  xref = doc->getXRef();
573
0
  subPage = gTrue;
574
0
  printCommands = globalParams->getPrintCommands();
575
0
  defaultFont = NULL;
576
577
  // start the resource stack
578
0
  res = new GfxResources(xref, resDict, NULL);
579
580
  // initialize
581
0
  out = outA;
582
0
  state = new GfxState(72, 72, box, 0, gFalse);
583
0
  fontChanged = gFalse;
584
0
  haveSavedClipPath = gFalse;
585
0
  clip = clipNone;
586
0
  ignoreUndef = 0;
587
0
  for (i = 0; i < 6; ++i) {
588
0
    baseMatrix[i] = state->getCTM()[i];
589
0
  }
590
0
  formDepth = 0;
591
0
  markedContentStack = new GList();
592
0
  ocState = gTrue;
593
0
  parser = NULL;
594
0
  contentStreamStack = new GList();
595
0
  abortCheckCbk = abortCheckCbkA;
596
0
  abortCheckCbkData = abortCheckCbkDataA;
597
598
  // set crop box
599
0
  if (cropBox) {
600
0
    state->moveTo(cropBox->x1, cropBox->y1);
601
0
    state->lineTo(cropBox->x2, cropBox->y1);
602
0
    state->lineTo(cropBox->x2, cropBox->y2);
603
0
    state->lineTo(cropBox->x1, cropBox->y2);
604
0
    state->closePath();
605
0
    state->clip();
606
0
    out->clip(state);
607
0
    state->clearPath();
608
0
  }
609
0
}
610
611
0
Gfx::~Gfx() {
612
0
  if (defaultFont) {
613
0
    delete defaultFont;
614
0
  }
615
0
  if (!subPage) {
616
0
    out->endPage();
617
0
  }
618
0
  while (state->hasSaves()) {
619
0
    restoreState();
620
0
  }
621
0
  delete state;
622
0
  while (res) {
623
0
    popResources();
624
0
  }
625
0
  deleteGList(markedContentStack, GfxMarkedContent);
626
0
  delete contentStreamStack;
627
0
}
628
629
0
void Gfx::display(Object *objRef, GBool topLevel) {
630
0
  Object obj1, obj2;
631
0
  int i;
632
633
0
  objRef->fetch(xref, &obj1);
634
0
  if (obj1.isArray()) {
635
0
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
636
0
      obj1.arrayGetNF(i, &obj2);
637
0
      if (checkForContentStreamLoop(&obj2)) {
638
0
  obj2.free();
639
0
  obj1.free();
640
0
  return;
641
0
      }
642
0
      obj2.free();
643
0
    }
644
0
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
645
0
      obj1.arrayGet(i, &obj2);
646
0
      if (!obj2.isStream()) {
647
0
  error(errSyntaxError, -1, "Invalid object type for content stream");
648
0
  obj2.free();
649
0
  obj1.free();
650
0
  return;
651
0
      }
652
0
      obj2.free();
653
0
    }
654
0
    contentStreamStack->append(&obj1);
655
0
  } else if (obj1.isStream()) {
656
0
    if (checkForContentStreamLoop(objRef)) {
657
0
      obj1.free();
658
0
      return;
659
0
    }
660
0
    contentStreamStack->append(objRef);
661
0
  } else {
662
0
    error(errSyntaxError, -1, "Invalid object type for content stream");
663
0
    obj1.free();
664
0
    return;
665
0
  }
666
0
  parser = new Parser(xref, new Lexer(xref, &obj1), gFalse);
667
0
  go(topLevel);
668
0
  delete parser;
669
0
  parser = NULL;
670
0
  contentStreamStack->del(contentStreamStack->getLength() - 1);
671
0
  obj1.free();
672
0
}
673
674
// If <ref> is already on contentStreamStack, i.e., if there is a loop
675
// in the content streams, report an error, and return true.
676
0
GBool Gfx::checkForContentStreamLoop(Object *ref) {
677
0
  Object *objPtr;
678
0
  Object obj1;
679
0
  int i, j;
680
681
0
  if (ref->isRef()) {
682
0
    for (i = 0; i < contentStreamStack->getLength(); ++i) {
683
0
      objPtr = (Object *)contentStreamStack->get(i);
684
0
      if (objPtr->isRef()) {
685
0
  if (ref->getRefNum() == objPtr->getRefNum() &&
686
0
      ref->getRefGen() == objPtr->getRefGen()) {
687
0
    error(errSyntaxError, -1, "Loop in content streams");
688
0
    return gTrue;
689
0
  }
690
0
      } else if (objPtr->isArray()) {
691
0
  for (j = 0; j < objPtr->arrayGetLength(); ++j) {
692
0
    objPtr->arrayGetNF(j, &obj1);
693
0
    if (obj1.isRef()) {
694
0
      if (ref->getRefNum() == obj1.getRefNum() &&
695
0
    ref->getRefGen() == obj1.getRefGen()) {
696
0
        error(errSyntaxError, -1, "Loop in content streams");
697
0
        obj1.free();
698
0
        return gTrue;
699
0
      }
700
0
    }
701
0
    obj1.free();
702
0
  }
703
0
      }
704
0
    }
705
0
  }
706
0
  return gFalse;
707
0
}
708
709
0
void Gfx::go(GBool topLevel) {
710
0
  Object obj;
711
0
  Object args[maxArgs];
712
0
  GBool aborted;
713
0
  int numArgs, i;
714
0
  int errCount;
715
716
  // scan a sequence of objects
717
0
  opCounter = 0;
718
0
  aborted = gFalse;
719
0
  errCount = 0;
720
0
  numArgs = 0;
721
0
  getContentObj(&obj);
722
0
  while (!obj.isEOF()) {
723
724
    // check for an abort
725
0
    ++opCounter;
726
0
    if (abortCheckCbk && opCounter > 100) {
727
0
      if ((*abortCheckCbk)(abortCheckCbkData)) {
728
0
  aborted = gTrue;
729
0
  break;
730
0
      }
731
0
      opCounter = 0;
732
0
    }
733
734
    // got a command - execute it
735
0
    if (obj.isCmd()) {
736
0
      if (printCommands) {
737
0
  obj.print(stdout);
738
0
  for (i = 0; i < numArgs; ++i) {
739
0
    printf(" ");
740
0
    args[i].print(stdout);
741
0
  }
742
0
  printf("\n");
743
0
  fflush(stdout);
744
0
      }
745
0
      if (!execOp(&obj, args, numArgs)) {
746
0
  ++errCount;
747
0
      }
748
0
      obj.free();
749
0
      for (i = 0; i < numArgs; ++i)
750
0
  args[i].free();
751
0
      numArgs = 0;
752
753
      // check for too many errors
754
0
      if (errCount > contentStreamErrorLimit) {
755
0
  error(errSyntaxError, -1,
756
0
        "Too many errors - giving up on this content stream");
757
0
  break;
758
0
      }
759
760
    // got an argument - save it
761
0
    } else if (numArgs < maxArgs) {
762
0
      args[numArgs++] = obj;
763
764
    // too many arguments - something is wrong
765
0
    } else {
766
0
      error(errSyntaxError, getPos(), "Too many args in content stream");
767
0
      if (printCommands) {
768
0
  printf("throwing away arg: ");
769
0
  obj.print(stdout);
770
0
  printf("\n");
771
0
  fflush(stdout);
772
0
      }
773
0
      obj.free();
774
0
    }
775
776
    // grab the next object
777
0
    getContentObj(&obj);
778
0
  }
779
0
  obj.free();
780
781
  // args at end with no command
782
0
  if (numArgs > 0) {
783
0
    if (!aborted) {
784
0
      error(errSyntaxError, getPos(), "Leftover args in content stream");
785
0
      if (printCommands) {
786
0
  printf("%d leftovers:", numArgs);
787
0
  for (i = 0; i < numArgs; ++i) {
788
0
    printf(" ");
789
0
    args[i].print(stdout);
790
0
  }
791
0
  printf("\n");
792
0
  fflush(stdout);
793
0
      }
794
0
    }
795
0
    for (i = 0; i < numArgs; ++i) {
796
0
      args[i].free();
797
0
    }
798
0
  }
799
0
}
800
801
0
void Gfx::getContentObj(Object *obj) {
802
0
  parser->getObj(obj);
803
0
  if (obj->isRef()) {
804
0
    error(errSyntaxError, getPos(), "Indirect reference in content stream");
805
0
    obj->free();
806
0
    obj->initError();
807
0
  }
808
0
}
809
810
// Returns true if successful, false on error.
811
0
GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
812
0
  Operator *op;
813
0
  char *name;
814
0
  Object *argPtr;
815
0
  int i;
816
817
  // find operator
818
0
  name = cmd->getCmd();
819
0
  if (!(op = findOp(name))) {
820
0
    if (ignoreUndef > 0) {
821
0
      return gTrue;
822
0
    }
823
0
    error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
824
0
    return gFalse;
825
0
  }
826
827
  // type check args
828
0
  argPtr = args;
829
0
  if (op->numArgs >= 0) {
830
0
    if (numArgs < op->numArgs) {
831
0
      error(errSyntaxError, getPos(),
832
0
      "Too few ({0:d}) args to '{1:s}' operator", numArgs, name);
833
0
      return gFalse;
834
0
    }
835
0
    if (numArgs > op->numArgs) {
836
#if 0
837
      error(errSyntaxWarning, getPos(),
838
      "Too many ({0:d}) args to '{1:s}' operator", numArgs, name);
839
#endif
840
0
      argPtr += numArgs - op->numArgs;
841
0
      numArgs = op->numArgs;
842
0
    }
843
0
  } else {
844
0
    if (numArgs > -op->numArgs) {
845
0
      error(errSyntaxWarning, getPos(),
846
0
      "Too many ({0:d}) args to '{1:s}' operator",
847
0
      numArgs, name);
848
0
    }
849
0
  }
850
0
  for (i = 0; i < numArgs; ++i) {
851
0
    if (!checkArg(&argPtr[i], op->tchk[i])) {
852
0
      error(errSyntaxError, getPos(),
853
0
      "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
854
0
      i, name, argPtr[i].getTypeName());
855
0
      return gFalse;
856
0
    }
857
0
  }
858
859
  // do it
860
0
  (this->*op->func)(argPtr, numArgs);
861
862
0
  return gTrue;
863
0
}
864
865
0
Operator *Gfx::findOp(char *name) {
866
0
  int a, b, m, cmp;
867
868
0
  a = -1;
869
0
  b = numOps;
870
0
  cmp = 0; // make gcc happy
871
  // invariant: opTab[a] < name < opTab[b]
872
0
  while (b - a > 1) {
873
0
    m = (a + b) / 2;
874
0
    cmp = strcmp(opTab[m].name, name);
875
0
    if (cmp < 0)
876
0
      a = m;
877
0
    else if (cmp > 0)
878
0
      b = m;
879
0
    else
880
0
      a = b = m;
881
0
  }
882
0
  if (cmp != 0)
883
0
    return NULL;
884
0
  return &opTab[a];
885
0
}
886
887
0
GBool Gfx::checkArg(Object *arg, TchkType type) {
888
0
  switch (type) {
889
0
  case tchkBool:   return arg->isBool();
890
0
  case tchkInt:    return arg->isInt();
891
0
  case tchkNum:    return arg->isNum();
892
0
  case tchkString: return arg->isString();
893
0
  case tchkName:   return arg->isName();
894
0
  case tchkArray:  return arg->isArray();
895
0
  case tchkProps:  return arg->isDict() || arg->isName();
896
0
  case tchkSCN:    return arg->isNum() || arg->isName();
897
0
  case tchkNone:   return gFalse;
898
0
  }
899
0
  return gFalse;
900
0
}
901
902
0
GFileOffset Gfx::getPos() {
903
0
  return parser ? parser->getPos() : -1;
904
0
}
905
906
//------------------------------------------------------------------------
907
// graphics state operators
908
//------------------------------------------------------------------------
909
910
0
void Gfx::opSave(Object args[], int numArgs) {
911
0
  saveState();
912
0
}
913
914
0
void Gfx::opRestore(Object args[], int numArgs) {
915
0
  restoreState();
916
0
}
917
918
0
void Gfx::opConcat(Object args[], int numArgs) {
919
0
  state->concatCTM(args[0].getNum(), args[1].getNum(),
920
0
       args[2].getNum(), args[3].getNum(),
921
0
       args[4].getNum(), args[5].getNum());
922
0
  out->updateCTM(state, args[0].getNum(), args[1].getNum(),
923
0
     args[2].getNum(), args[3].getNum(),
924
0
     args[4].getNum(), args[5].getNum());
925
0
  fontChanged = gTrue;
926
0
}
927
928
0
void Gfx::opSetDash(Object args[], int numArgs) {
929
0
  Array *a;
930
0
  int length;
931
0
  Object obj;
932
0
  double *dash;
933
0
  int i;
934
935
0
  a = args[0].getArray();
936
0
  length = a->getLength();
937
0
  if (length == 0) {
938
0
    dash = NULL;
939
0
  } else {
940
0
    dash = (double *)gmallocn(length, sizeof(double));
941
0
    for (i = 0; i < length; ++i) {
942
0
      dash[i] = a->get(i, &obj)->getNum();
943
0
      obj.free();
944
0
    }
945
0
  }
946
0
  state->setLineDash(dash, length, args[1].getNum());
947
0
  out->updateLineDash(state);
948
0
}
949
950
0
void Gfx::opSetFlat(Object args[], int numArgs) {
951
0
  state->setFlatness((int)args[0].getNum());
952
0
  out->updateFlatness(state);
953
0
}
954
955
0
void Gfx::opSetLineJoin(Object args[], int numArgs) {
956
0
  int lineJoin;
957
958
0
  lineJoin = args[0].getInt();
959
0
  if (lineJoin < 0 || lineJoin > 2) {
960
0
    lineJoin = 0;
961
0
  }
962
0
  state->setLineJoin(lineJoin);
963
0
  out->updateLineJoin(state);
964
0
}
965
966
0
void Gfx::opSetLineCap(Object args[], int numArgs) {
967
0
  int lineCap;
968
969
0
  lineCap = args[0].getInt();
970
0
  if (lineCap < 0 || lineCap > 2) {
971
0
    lineCap = 0;
972
0
  }
973
0
  state->setLineCap(lineCap);
974
0
  out->updateLineCap(state);
975
0
}
976
977
0
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
978
0
  state->setMiterLimit(args[0].getNum());
979
0
  out->updateMiterLimit(state);
980
0
}
981
982
0
void Gfx::opSetLineWidth(Object args[], int numArgs) {
983
0
  state->setLineWidth(args[0].getNum());
984
0
  out->updateLineWidth(state);
985
0
}
986
987
0
void Gfx::opSetExtGState(Object args[], int numArgs) {
988
0
  Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj;
989
0
  Object args2[2];
990
0
  GfxBlendMode mode;
991
0
  GBool haveFillOP;
992
0
  Function *funcs[4];
993
0
  GBool alpha, knockout;
994
0
  double opac;
995
0
  int i;
996
997
0
  if (!res->lookupGState(args[0].getName(), &obj1)) {
998
0
    return;
999
0
  }
1000
0
  if (!obj1.isDict()) {
1001
0
    error(errSyntaxError, getPos(),
1002
0
    "ExtGState '{0:s}' is wrong type", args[0].getName());
1003
0
    obj1.free();
1004
0
    return;
1005
0
  }
1006
0
  if (printCommands) {
1007
0
    printf("  gfx state dict: ");
1008
0
    obj1.print();
1009
0
    printf("\n");
1010
0
  }
1011
1012
  // parameters that are also set by individual PDF operators
1013
0
  if (obj1.dictLookup("LW", &obj2)->isNum()) {
1014
0
    opSetLineWidth(&obj2, 1);
1015
0
  }
1016
0
  obj2.free();
1017
0
  if (obj1.dictLookup("LC", &obj2)->isInt()) {
1018
0
    opSetLineCap(&obj2, 1);
1019
0
  }
1020
0
  obj2.free();
1021
0
  if (obj1.dictLookup("LJ", &obj2)->isInt()) {
1022
0
    opSetLineJoin(&obj2, 1);
1023
0
  }
1024
0
  obj2.free();
1025
0
  if (obj1.dictLookup("ML", &obj2)->isNum()) {
1026
0
    opSetMiterLimit(&obj2, 1);
1027
0
  }
1028
0
  obj2.free();
1029
0
  if (obj1.dictLookup("D", &obj2)->isArray() &&
1030
0
      obj2.arrayGetLength() == 2) {
1031
0
    obj2.arrayGet(0, &args2[0]);
1032
0
    obj2.arrayGet(1, &args2[1]);
1033
0
    if (args2[0].isArray() && args2[1].isNum()) {
1034
0
      opSetDash(args2, 2);
1035
0
    }
1036
0
    args2[0].free();
1037
0
    args2[1].free();
1038
0
  }
1039
0
  obj2.free();
1040
0
  if (obj1.dictLookup("FL", &obj2)->isNum()) {
1041
0
    opSetFlat(&obj2, 1);
1042
0
  }
1043
0
  obj2.free();
1044
0
  if (obj1.dictLookup("RI", &obj2)->isName()) {
1045
0
    opSetRenderingIntent(&obj2, 1);
1046
0
  }
1047
0
  obj2.free();
1048
1049
  // font
1050
0
  if (obj1.dictLookup("Font", &obj2)->isArray() &&
1051
0
      obj2.arrayGetLength() == 2) {
1052
0
    obj2.arrayGetNF(0, &obj3);
1053
0
    obj2.arrayGetNF(1, &obj4);
1054
0
    if (obj3.isRef() && obj4.isNum()) {
1055
0
      doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum());
1056
0
    }
1057
0
    obj3.free();
1058
0
    obj4.free();
1059
0
  }
1060
0
  obj2.free();
1061
1062
  // transparency support: blend mode, fill/stroke opacity
1063
0
  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
1064
0
    if (state->parseBlendMode(&obj2, &mode)) {
1065
0
      state->setBlendMode(mode);
1066
0
      out->updateBlendMode(state);
1067
0
    } else {
1068
0
      error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState");
1069
0
    }
1070
0
  }
1071
0
  obj2.free();
1072
0
  if (obj1.dictLookup("ca", &obj2)->isNum()) {
1073
0
    opac = obj2.getNum();
1074
0
    state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
1075
0
    out->updateFillOpacity(state);
1076
0
  }
1077
0
  obj2.free();
1078
0
  if (obj1.dictLookup("CA", &obj2)->isNum()) {
1079
0
    opac = obj2.getNum();
1080
0
    state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
1081
0
    out->updateStrokeOpacity(state);
1082
0
  }
1083
0
  obj2.free();
1084
1085
  // fill/stroke overprint, overprint mode
1086
0
  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
1087
0
    if (!state->getIgnoreColorOps()) {
1088
0
      state->setFillOverprint(obj2.getBool());
1089
0
      out->updateFillOverprint(state);
1090
0
    } else {
1091
0
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1092
0
      " in uncolored Type 3 char or tiling pattern");
1093
0
    }
1094
0
  }
1095
0
  obj2.free();
1096
0
  if (obj1.dictLookup("OP", &obj2)->isBool()) {
1097
0
    if (!state->getIgnoreColorOps()) {
1098
0
      state->setStrokeOverprint(obj2.getBool());
1099
0
      out->updateStrokeOverprint(state);
1100
0
      if (!haveFillOP) {
1101
0
  state->setFillOverprint(obj2.getBool());
1102
0
  out->updateFillOverprint(state);
1103
0
      }
1104
0
    } else {
1105
0
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1106
0
      " in uncolored Type 3 char or tiling pattern");
1107
0
    }
1108
0
  }
1109
0
  obj2.free();
1110
0
  if (obj1.dictLookup("OPM", &obj2)->isInt()) {
1111
0
    if (!state->getIgnoreColorOps()) {
1112
0
      state->setOverprintMode(obj2.getInt());
1113
0
      out->updateOverprintMode(state);
1114
0
    } else {
1115
0
      error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
1116
0
      " in uncolored Type 3 char or tiling pattern");
1117
0
    }
1118
0
  }
1119
0
  obj2.free();
1120
1121
  // stroke adjust
1122
0
  if (obj1.dictLookup("SA", &obj2)->isBool()) {
1123
0
    state->setStrokeAdjust(obj2.getBool());
1124
0
    out->updateStrokeAdjust(state);
1125
0
  }
1126
0
  obj2.free();
1127
1128
  // transfer function
1129
0
  if (obj1.dictLookup("TR2", &obj2)->isNull()) {
1130
0
    obj2.free();
1131
0
    obj1.dictLookup("TR", &obj2);
1132
0
  }
1133
0
  if (!obj2.isNull()) {
1134
0
    if (!state->getIgnoreColorOps()) {
1135
0
      if (obj2.isName("Default") ||
1136
0
    obj2.isName("Identity")) {
1137
0
  funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
1138
0
  state->setTransfer(funcs);
1139
0
  out->updateTransfer(state);
1140
0
      } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
1141
0
  for (i = 0; i < 4; ++i) {
1142
0
    obj2.arrayGet(i, &obj3);
1143
0
    funcs[i] = Function::parse(&obj3, 1, 1);
1144
0
    obj3.free();
1145
0
    if (!funcs[i]) {
1146
0
      break;
1147
0
    }
1148
0
  }
1149
0
  if (i == 4) {
1150
0
    state->setTransfer(funcs);
1151
0
    out->updateTransfer(state);
1152
0
  }
1153
0
      } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
1154
0
  if ((funcs[0] = Function::parse(&obj2, 1, 1))) {
1155
0
    funcs[1] = funcs[2] = funcs[3] = NULL;
1156
0
    state->setTransfer(funcs);
1157
0
    out->updateTransfer(state);
1158
0
  }
1159
0
      } else {
1160
0
  error(errSyntaxError, getPos(),
1161
0
        "Invalid transfer function in ExtGState");
1162
0
      }
1163
0
    } else {
1164
0
      error(errSyntaxWarning, getPos(), "Ignoring transfer function setting"
1165
0
      " in uncolored Type 3 char or tiling pattern");
1166
0
    }
1167
0
  }
1168
0
  obj2.free();
1169
1170
  // soft mask
1171
0
  if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
1172
0
    if (obj2.isName("None")) {
1173
0
      out->clearSoftMask(state);
1174
0
    } else if (obj2.isDict()) {
1175
0
      obj2.dictLookup("S", &obj3);
1176
0
      if (obj3.isName("Alpha")) {
1177
0
  alpha = gTrue;
1178
0
      } else if (obj3.isName("Luminosity")) {
1179
0
  alpha = gFalse;
1180
0
      } else {
1181
0
  error(errSyntaxError, getPos(),
1182
0
        "Missing S (subtype) entry in soft mask");
1183
0
  alpha = gFalse;
1184
0
      }
1185
0
      obj3.free();
1186
0
      funcs[0] = NULL;
1187
0
      if (!obj2.dictLookup("TR", &obj3)->isNull()) {
1188
0
  if (obj3.isName("Default") ||
1189
0
      obj3.isName("Identity")) {
1190
0
    funcs[0] = NULL;
1191
0
  } else {
1192
0
    if (!(funcs[0] = Function::parse(&obj3, 1, 1))) {
1193
0
      error(errSyntaxError, getPos(),
1194
0
      "Invalid transfer function in soft mask in ExtGState");
1195
0
      delete funcs[0];
1196
0
      funcs[0] = NULL;
1197
0
    }
1198
0
  }
1199
0
      }
1200
0
      obj3.free();
1201
0
      obj2.dictLookup("BC", &backdropColorObj);
1202
0
      if (obj2.dictLookup("G", &obj3)->isStream()) {
1203
0
  if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
1204
0
    knockout = gFalse;
1205
0
    if (obj4.dictLookup("K", &obj5)->isBool()) {
1206
0
      knockout = obj5.getBool();
1207
0
    }
1208
0
    obj5.free();
1209
0
    obj2.dictLookupNF("G", &objRef3);
1210
    // it doesn't make sense for softmasks to be non-isolated,
1211
    // because they're blended with a backdrop color, rather
1212
    // than the original backdrop
1213
0
    doSoftMask(&obj3, &objRef3, alpha, gTrue, knockout, funcs[0],
1214
0
         &backdropColorObj);
1215
0
    objRef3.free();
1216
0
    if (funcs[0]) {
1217
0
      delete funcs[0];
1218
0
    }
1219
0
  } else {
1220
0
    error(errSyntaxError, getPos(),
1221
0
    "Invalid soft mask in ExtGState - missing group");
1222
0
  }
1223
0
  obj4.free();
1224
0
      } else {
1225
0
  error(errSyntaxError, getPos(),
1226
0
        "Invalid soft mask in ExtGState - missing group");
1227
0
      }
1228
0
      obj3.free();
1229
0
      backdropColorObj.free();
1230
0
    } else if (!obj2.isNull()) {
1231
0
      error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState");
1232
0
    }
1233
0
  }
1234
0
  obj2.free();
1235
1236
0
  obj1.free();
1237
0
}
1238
1239
void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
1240
         GBool isolated, GBool knockout,
1241
0
         Function *transferFunc, Object *backdropColorObj) {
1242
0
  Dict *dict, *resDict;
1243
0
  double m[6], bbox[4];
1244
0
  Object obj1, obj2;
1245
0
  int i;
1246
1247
  // check for excessive recursion
1248
0
  if (formDepth > 20) {
1249
0
    return;
1250
0
  }
1251
1252
  // get stream dict
1253
0
  dict = str->streamGetDict();
1254
1255
  // check form type
1256
0
  dict->lookup("FormType", &obj1);
1257
0
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
1258
0
    error(errSyntaxError, getPos(), "Unknown form type");
1259
0
  }
1260
0
  obj1.free();
1261
1262
  // get bounding box
1263
0
  dict->lookup("BBox", &obj1);
1264
0
  if (!obj1.isArray()) {
1265
0
    obj1.free();
1266
0
    error(errSyntaxError, getPos(), "Bad form bounding box");
1267
0
    return;
1268
0
  }
1269
0
  for (i = 0; i < 4; ++i) {
1270
0
    obj1.arrayGet(i, &obj2);
1271
0
    bbox[i] = obj2.getNum();
1272
0
    obj2.free();
1273
0
  }
1274
0
  obj1.free();
1275
1276
  // get matrix
1277
0
  dict->lookup("Matrix", &obj1);
1278
0
  if (obj1.isArray()) {
1279
0
    for (i = 0; i < 6; ++i) {
1280
0
      obj1.arrayGet(i, &obj2);
1281
0
      m[i] = obj2.getNum();
1282
0
      obj2.free();
1283
0
    }
1284
0
  } else {
1285
0
    m[0] = 1; m[1] = 0;
1286
0
    m[2] = 0; m[3] = 1;
1287
0
    m[4] = 0; m[5] = 0;
1288
0
  }
1289
0
  obj1.free();
1290
1291
  // get resources
1292
0
  dict->lookup("Resources", &obj1);
1293
0
  resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
1294
1295
  // draw it
1296
0
  ++formDepth;
1297
0
  drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout,
1298
0
     alpha, transferFunc, backdropColorObj);
1299
0
  --formDepth;
1300
1301
0
  obj1.free();
1302
0
}
1303
1304
0
void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
1305
0
  GfxRenderingIntent ri;
1306
1307
0
  if (state->getIgnoreColorOps()) {
1308
0
    error(errSyntaxWarning, getPos(), "Ignoring rendering intent setting"
1309
0
    " in uncolored Type 3 char or tiling pattern");
1310
0
    return;
1311
0
  }
1312
0
  ri = parseRenderingIntent(args[0].getName());
1313
0
  state->setRenderingIntent(ri);
1314
0
  out->updateRenderingIntent(state);
1315
0
}
1316
1317
0
GfxRenderingIntent Gfx::parseRenderingIntent(const char *name) {
1318
0
  if (!strcmp(name, "AbsoluteColorimetric")) {
1319
0
    return gfxRenderingIntentAbsoluteColorimetric;
1320
0
  }
1321
0
  if (!strcmp(name, "Saturation")) {
1322
0
    return gfxRenderingIntentSaturation;
1323
0
  }
1324
0
  if (!strcmp(name, "Perceptual")) {
1325
0
    return gfxRenderingIntentPerceptual;
1326
0
  }
1327
0
  return gfxRenderingIntentRelativeColorimetric;
1328
0
}
1329
1330
//------------------------------------------------------------------------
1331
// color operators
1332
//------------------------------------------------------------------------
1333
1334
0
void Gfx::opSetFillGray(Object args[], int numArgs) {
1335
0
  GfxColor color;
1336
1337
0
  if (state->getIgnoreColorOps()) {
1338
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1339
0
    " in uncolored Type 3 char or tiling pattern");
1340
0
    return;
1341
0
  }
1342
0
  state->setFillPattern(NULL);
1343
0
  state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
1344
0
  out->updateFillColorSpace(state);
1345
0
  color.c[0] = dblToCol(args[0].getNum());
1346
0
  state->setFillColor(&color);
1347
0
  out->updateFillColor(state);
1348
0
}
1349
1350
0
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
1351
0
  GfxColor color;
1352
1353
0
  if (state->getIgnoreColorOps()) {
1354
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1355
0
    " in uncolored Type 3 char or tiling pattern");
1356
0
    return;
1357
0
  }
1358
0
  state->setStrokePattern(NULL);
1359
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
1360
0
  out->updateStrokeColorSpace(state);
1361
0
  color.c[0] = dblToCol(args[0].getNum());
1362
0
  state->setStrokeColor(&color);
1363
0
  out->updateStrokeColor(state);
1364
0
}
1365
1366
0
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
1367
0
  GfxColor color;
1368
0
  int i;
1369
1370
0
  if (state->getIgnoreColorOps()) {
1371
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1372
0
    " in uncolored Type 3 char or tiling pattern");
1373
0
    return;
1374
0
  }
1375
0
  state->setFillPattern(NULL);
1376
0
  state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK));
1377
0
  out->updateFillColorSpace(state);
1378
0
  for (i = 0; i < 4; ++i) {
1379
0
    color.c[i] = dblToCol(args[i].getNum());
1380
0
  }
1381
0
  state->setFillColor(&color);
1382
0
  out->updateFillColor(state);
1383
0
}
1384
1385
0
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
1386
0
  GfxColor color;
1387
0
  int i;
1388
1389
0
  if (state->getIgnoreColorOps()) {
1390
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1391
0
    " in uncolored Type 3 char or tiling pattern");
1392
0
    return;
1393
0
  }
1394
0
  state->setStrokePattern(NULL);
1395
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
1396
0
  out->updateStrokeColorSpace(state);
1397
0
  for (i = 0; i < 4; ++i) {
1398
0
    color.c[i] = dblToCol(args[i].getNum());
1399
0
  }
1400
0
  state->setStrokeColor(&color);
1401
0
  out->updateStrokeColor(state);
1402
0
}
1403
1404
0
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
1405
0
  GfxColor color;
1406
0
  int i;
1407
1408
0
  if (state->getIgnoreColorOps()) {
1409
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1410
0
    " in uncolored Type 3 char or tiling pattern");
1411
0
    return;
1412
0
  }
1413
0
  state->setFillPattern(NULL);
1414
0
  state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB));
1415
0
  out->updateFillColorSpace(state);
1416
0
  for (i = 0; i < 3; ++i) {
1417
0
    color.c[i] = dblToCol(args[i].getNum());
1418
0
  }
1419
0
  state->setFillColor(&color);
1420
0
  out->updateFillColor(state);
1421
0
}
1422
1423
0
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
1424
0
  GfxColor color;
1425
0
  int i;
1426
1427
0
  if (state->getIgnoreColorOps()) {
1428
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1429
0
    " in uncolored Type 3 char or tiling pattern");
1430
0
    return;
1431
0
  }
1432
0
  state->setStrokePattern(NULL);
1433
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
1434
0
  out->updateStrokeColorSpace(state);
1435
0
  for (i = 0; i < 3; ++i) {
1436
0
    color.c[i] = dblToCol(args[i].getNum());
1437
0
  }
1438
0
  state->setStrokeColor(&color);
1439
0
  out->updateStrokeColor(state);
1440
0
}
1441
1442
0
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
1443
0
  Object obj;
1444
0
  GfxColorSpace *colorSpace;
1445
0
  GfxColor color;
1446
1447
0
  if (state->getIgnoreColorOps()) {
1448
0
    error(errSyntaxWarning, getPos(), "Ignoring color space setting"
1449
0
    " in uncolored Type 3 char or tiling pattern");
1450
0
    return;
1451
0
  }
1452
0
  state->setFillPattern(NULL);
1453
0
  res->lookupColorSpace(args[0].getName(), &obj);
1454
0
  if (obj.isNull()) {
1455
0
    colorSpace = GfxColorSpace::parse(&args[0]
1456
0
              );
1457
0
  } else {
1458
0
    colorSpace = GfxColorSpace::parse(&obj
1459
0
              );
1460
0
  }
1461
0
  obj.free();
1462
0
  if (colorSpace) {
1463
0
    state->setFillColorSpace(colorSpace);
1464
0
    out->updateFillColorSpace(state);
1465
0
    colorSpace->getDefaultColor(&color);
1466
0
    state->setFillColor(&color);
1467
0
    out->updateFillColor(state);
1468
0
  } else {
1469
0
    error(errSyntaxError, getPos(), "Bad color space (fill)");
1470
0
  }
1471
0
}
1472
1473
0
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
1474
0
  Object obj;
1475
0
  GfxColorSpace *colorSpace;
1476
0
  GfxColor color;
1477
1478
0
  if (state->getIgnoreColorOps()) {
1479
0
    error(errSyntaxWarning, getPos(), "Ignoring color space setting"
1480
0
    " in uncolored Type 3 char or tiling pattern");
1481
0
    return;
1482
0
  }
1483
0
  state->setStrokePattern(NULL);
1484
0
  res->lookupColorSpace(args[0].getName(), &obj);
1485
0
  if (obj.isNull()) {
1486
0
    colorSpace = GfxColorSpace::parse(&args[0]
1487
0
              );
1488
0
  } else {
1489
0
    colorSpace = GfxColorSpace::parse(&obj
1490
0
              );
1491
0
  }
1492
0
  obj.free();
1493
0
  if (colorSpace) {
1494
0
    state->setStrokeColorSpace(colorSpace);
1495
0
    out->updateStrokeColorSpace(state);
1496
0
    colorSpace->getDefaultColor(&color);
1497
0
    state->setStrokeColor(&color);
1498
0
    out->updateStrokeColor(state);
1499
0
  } else {
1500
0
    error(errSyntaxError, getPos(), "Bad color space (stroke)");
1501
0
  }
1502
0
}
1503
1504
// Technically, per the PDF spec, the 'sc' operator can only be used
1505
// with device, CIE, and Indexed color spaces.  Some buggy PDF
1506
// generators use it for DeviceN, so we also allow that (same as
1507
// 'scn', minus the optional pattern name argument).
1508
0
void Gfx::opSetFillColor(Object args[], int numArgs) {
1509
0
  GfxColor color;
1510
0
  int i;
1511
1512
0
  if (state->getIgnoreColorOps()) {
1513
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1514
0
    " in uncolored Type 3 char or tiling pattern");
1515
0
    return;
1516
0
  }
1517
0
  if (numArgs != state->getFillColorSpace()->getNComps()) {
1518
0
    error(errSyntaxError, getPos(),
1519
0
    "Incorrect number of arguments in 'sc' command");
1520
0
    return;
1521
0
  }
1522
0
  state->setFillPattern(NULL);
1523
0
  for (i = 0; i < numArgs; ++i) {
1524
0
    color.c[i] = dblToCol(args[i].getNum());
1525
0
  }
1526
0
  state->setFillColor(&color);
1527
0
  out->updateFillColor(state);
1528
0
}
1529
1530
// Technically, per the PDF spec, the 'SC' operator can only be used
1531
// with device, CIE, and Indexed color spaces.  Some buggy PDF
1532
// generators use it for DeviceN, so we also allow that (same as
1533
// 'SCN', minus the optional pattern name argument).
1534
0
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
1535
0
  GfxColor color;
1536
0
  int i;
1537
1538
0
  if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1539
0
    error(errSyntaxError, getPos(),
1540
0
    "Incorrect number of arguments in 'SC' command");
1541
0
    return;
1542
0
  }
1543
0
  state->setStrokePattern(NULL);
1544
0
  for (i = 0; i < numArgs; ++i) {
1545
0
    color.c[i] = dblToCol(args[i].getNum());
1546
0
  }
1547
0
  state->setStrokeColor(&color);
1548
0
  out->updateStrokeColor(state);
1549
0
}
1550
1551
0
void Gfx::opSetFillColorN(Object args[], int numArgs) {
1552
0
  GfxColor color;
1553
0
  GfxPattern *pattern;
1554
0
  int i;
1555
1556
0
  if (state->getIgnoreColorOps()) {
1557
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1558
0
    " in uncolored Type 3 char or tiling pattern");
1559
0
    return;
1560
0
  }
1561
0
  if (state->getFillColorSpace()->getMode() == csPattern) {
1562
0
    if (numArgs == 0 || !args[numArgs-1].isName()) {
1563
0
      error(errSyntaxError, getPos(),
1564
0
      "Invalid arguments in 'scn' command");
1565
0
      return;
1566
0
    }
1567
0
    if (numArgs > 1) {
1568
0
      if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1569
0
    numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1570
0
                       ->getUnder()->getNComps()) {
1571
0
  error(errSyntaxError, getPos(),
1572
0
        "Incorrect number of arguments in 'scn' command");
1573
0
  return;
1574
0
      }
1575
0
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1576
0
  if (args[i].isNum()) {
1577
0
    color.c[i] = dblToCol(args[i].getNum());
1578
0
  }
1579
0
      }
1580
0
      state->setFillColor(&color);
1581
0
      out->updateFillColor(state);
1582
0
    }
1583
0
    if ((pattern = res->lookupPattern(args[numArgs-1].getName()
1584
0
              ))) {
1585
0
      state->setFillPattern(pattern);
1586
0
    }
1587
1588
0
  } else {
1589
0
    if (numArgs != state->getFillColorSpace()->getNComps()) {
1590
0
      error(errSyntaxError, getPos(),
1591
0
      "Incorrect number of arguments in 'scn' command");
1592
0
      return;
1593
0
    }
1594
0
    state->setFillPattern(NULL);
1595
0
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1596
0
      if (args[i].isNum()) {
1597
0
  color.c[i] = dblToCol(args[i].getNum());
1598
0
      }
1599
0
    }
1600
0
    state->setFillColor(&color);
1601
0
    out->updateFillColor(state);
1602
0
  }
1603
0
}
1604
1605
0
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
1606
0
  GfxColor color;
1607
0
  GfxPattern *pattern;
1608
0
  int i;
1609
1610
0
  if (state->getIgnoreColorOps()) {
1611
0
    error(errSyntaxWarning, getPos(), "Ignoring color setting"
1612
0
    " in uncolored Type 3 char or tiling pattern");
1613
0
    return;
1614
0
  }
1615
0
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1616
0
    if (numArgs == 0 || !args[numArgs-1].isName()) {
1617
0
      error(errSyntaxError, getPos(),
1618
0
      "Invalid arguments in 'SCN' command");
1619
0
      return;
1620
0
    }
1621
0
    if (numArgs > 1) {
1622
0
      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1623
0
         ->getUnder() ||
1624
0
    numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1625
0
                       ->getUnder()->getNComps()) {
1626
0
  error(errSyntaxError, getPos(),
1627
0
        "Incorrect number of arguments in 'SCN' command");
1628
0
  return;
1629
0
      }
1630
0
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1631
0
  if (args[i].isNum()) {
1632
0
    color.c[i] = dblToCol(args[i].getNum());
1633
0
  }
1634
0
      }
1635
0
      state->setStrokeColor(&color);
1636
0
      out->updateStrokeColor(state);
1637
0
    }
1638
0
    if ((pattern = res->lookupPattern(args[numArgs-1].getName()
1639
0
              ))) {
1640
0
      state->setStrokePattern(pattern);
1641
0
    }
1642
1643
0
  } else {
1644
0
    if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1645
0
      error(errSyntaxError, getPos(),
1646
0
      "Incorrect number of arguments in 'SCN' command");
1647
0
      return;
1648
0
    }
1649
0
    state->setStrokePattern(NULL);
1650
0
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1651
0
      if (args[i].isNum()) {
1652
0
  color.c[i] = dblToCol(args[i].getNum());
1653
0
      }
1654
0
    }
1655
0
    state->setStrokeColor(&color);
1656
0
    out->updateStrokeColor(state);
1657
0
  }
1658
0
}
1659
1660
//------------------------------------------------------------------------
1661
// path segment operators
1662
//------------------------------------------------------------------------
1663
1664
0
void Gfx::opMoveTo(Object args[], int numArgs) {
1665
0
  state->moveTo(args[0].getNum(), args[1].getNum());
1666
0
}
1667
1668
0
void Gfx::opLineTo(Object args[], int numArgs) {
1669
0
  if (!state->isCurPt()) {
1670
0
    error(errSyntaxError, getPos(), "No current point in lineto");
1671
0
    return;
1672
0
  }
1673
0
  state->lineTo(args[0].getNum(), args[1].getNum());
1674
0
}
1675
1676
0
void Gfx::opCurveTo(Object args[], int numArgs) {
1677
0
  double x1, y1, x2, y2, x3, y3;
1678
1679
0
  if (!state->isCurPt()) {
1680
0
    error(errSyntaxError, getPos(), "No current point in curveto");
1681
0
    return;
1682
0
  }
1683
0
  x1 = args[0].getNum();
1684
0
  y1 = args[1].getNum();
1685
0
  x2 = args[2].getNum();
1686
0
  y2 = args[3].getNum();
1687
0
  x3 = args[4].getNum();
1688
0
  y3 = args[5].getNum();
1689
0
  state->curveTo(x1, y1, x2, y2, x3, y3);
1690
0
}
1691
1692
0
void Gfx::opCurveTo1(Object args[], int numArgs) {
1693
0
  double x1, y1, x2, y2, x3, y3;
1694
1695
0
  if (!state->isCurPt()) {
1696
0
    error(errSyntaxError, getPos(), "No current point in curveto1");
1697
0
    return;
1698
0
  }
1699
0
  x1 = state->getCurX();
1700
0
  y1 = state->getCurY();
1701
0
  x2 = args[0].getNum();
1702
0
  y2 = args[1].getNum();
1703
0
  x3 = args[2].getNum();
1704
0
  y3 = args[3].getNum();
1705
0
  state->curveTo(x1, y1, x2, y2, x3, y3);
1706
0
}
1707
1708
0
void Gfx::opCurveTo2(Object args[], int numArgs) {
1709
0
  double x1, y1, x2, y2, x3, y3;
1710
1711
0
  if (!state->isCurPt()) {
1712
0
    error(errSyntaxError, getPos(), "No current point in curveto2");
1713
0
    return;
1714
0
  }
1715
0
  x1 = args[0].getNum();
1716
0
  y1 = args[1].getNum();
1717
0
  x2 = args[2].getNum();
1718
0
  y2 = args[3].getNum();
1719
0
  x3 = x2;
1720
0
  y3 = y2;
1721
0
  state->curveTo(x1, y1, x2, y2, x3, y3);
1722
0
}
1723
1724
0
void Gfx::opRectangle(Object args[], int numArgs) {
1725
0
  double x, y, w, h;
1726
1727
0
  x = args[0].getNum();
1728
0
  y = args[1].getNum();
1729
0
  w = args[2].getNum();
1730
0
  h = args[3].getNum();
1731
0
  state->moveTo(x, y);
1732
0
  state->lineTo(x + w, y);
1733
0
  state->lineTo(x + w, y + h);
1734
0
  state->lineTo(x, y + h);
1735
0
  state->closePath();
1736
0
}
1737
1738
0
void Gfx::opClosePath(Object args[], int numArgs) {
1739
0
  if (!state->isCurPt()) {
1740
0
    error(errSyntaxError, getPos(), "No current point in closepath");
1741
0
    return;
1742
0
  }
1743
0
  state->closePath();
1744
0
}
1745
1746
//------------------------------------------------------------------------
1747
// path painting operators
1748
//------------------------------------------------------------------------
1749
1750
0
void Gfx::opEndPath(Object args[], int numArgs) {
1751
0
  doEndPath();
1752
0
}
1753
1754
0
void Gfx::opStroke(Object args[], int numArgs) {
1755
0
  if (!state->isCurPt()) {
1756
    //error(errSyntaxError, getPos(), "No path in stroke");
1757
0
    return;
1758
0
  }
1759
0
  if (state->isPath()) {
1760
0
    if (ocState) {
1761
0
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
1762
0
  doPatternStroke();
1763
0
      } else {
1764
0
  out->stroke(state);
1765
0
      }
1766
0
    }
1767
0
  }
1768
0
  doEndPath();
1769
0
}
1770
1771
0
void Gfx::opCloseStroke(Object args[], int numArgs) {
1772
0
  if (!state->isCurPt()) {
1773
    //error(errSyntaxError, getPos(), "No path in closepath/stroke");
1774
0
    return;
1775
0
  }
1776
0
  if (state->isPath()) {
1777
0
    state->closePath();
1778
0
    if (ocState) {
1779
0
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
1780
0
  doPatternStroke();
1781
0
      } else {
1782
0
  out->stroke(state);
1783
0
      }
1784
0
    }
1785
0
  }
1786
0
  doEndPath();
1787
0
}
1788
1789
0
void Gfx::opFill(Object args[], int numArgs) {
1790
0
  if (!state->isCurPt()) {
1791
    //error(errSyntaxError, getPos(), "No path in fill");
1792
0
    return;
1793
0
  }
1794
0
  if (state->isPath()) {
1795
0
    if (ocState) {
1796
0
      if (state->getFillColorSpace()->getMode() == csPattern) {
1797
0
  doPatternFill(gFalse);
1798
0
      } else {
1799
0
  out->fill(state);
1800
0
      }
1801
0
    }
1802
0
  }
1803
0
  doEndPath();
1804
0
}
1805
1806
0
void Gfx::opEOFill(Object args[], int numArgs) {
1807
0
  if (!state->isCurPt()) {
1808
    //error(errSyntaxError, getPos(), "No path in eofill");
1809
0
    return;
1810
0
  }
1811
0
  if (state->isPath()) {
1812
0
    if (ocState) {
1813
0
      if (state->getFillColorSpace()->getMode() == csPattern) {
1814
0
  doPatternFill(gTrue);
1815
0
      } else {
1816
0
  out->eoFill(state);
1817
0
      }
1818
0
    }
1819
0
  }
1820
0
  doEndPath();
1821
0
}
1822
1823
0
void Gfx::opFillStroke(Object args[], int numArgs) {
1824
0
  if (!state->isCurPt()) {
1825
    //error(errSyntaxError, getPos(), "No path in fill/stroke");
1826
0
    return;
1827
0
  }
1828
0
  if (state->isPath()) {
1829
0
    if (ocState) {
1830
0
      if (state->getFillColorSpace()->getMode() == csPattern ||
1831
0
    state->getStrokeColorSpace()->getMode() == csPattern) {
1832
0
  if (state->getFillColorSpace()->getMode() == csPattern) {
1833
0
    doPatternFill(gFalse);
1834
0
  } else {
1835
0
    out->fill(state);
1836
0
  }
1837
0
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1838
0
    doPatternStroke();
1839
0
  } else {
1840
0
    out->stroke(state);
1841
0
  }
1842
0
      } else {
1843
0
  out->fillStroke(state, gFalse);
1844
0
      }
1845
0
    }
1846
0
  }
1847
0
  doEndPath();
1848
0
}
1849
1850
0
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1851
0
  if (!state->isCurPt()) {
1852
    //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke");
1853
0
    return;
1854
0
  }
1855
0
  if (state->isPath()) {
1856
0
    state->closePath();
1857
0
    if (ocState) {
1858
0
      if (state->getFillColorSpace()->getMode() == csPattern ||
1859
0
    state->getStrokeColorSpace()->getMode() == csPattern) {
1860
0
  if (state->getFillColorSpace()->getMode() == csPattern) {
1861
0
    doPatternFill(gFalse);
1862
0
  } else {
1863
0
    out->fill(state);
1864
0
  }
1865
0
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1866
0
    doPatternStroke();
1867
0
  } else {
1868
0
    out->stroke(state);
1869
0
  }
1870
0
      } else {
1871
0
  out->fillStroke(state, gFalse);
1872
0
      }
1873
0
    }
1874
0
  }
1875
0
  doEndPath();
1876
0
}
1877
1878
0
void Gfx::opEOFillStroke(Object args[], int numArgs) {
1879
0
  if (!state->isCurPt()) {
1880
    //error(errSyntaxError, getPos(), "No path in eofill/stroke");
1881
0
    return;
1882
0
  }
1883
0
  if (state->isPath()) {
1884
0
    if (ocState) {
1885
0
      if (state->getFillColorSpace()->getMode() == csPattern ||
1886
0
    state->getStrokeColorSpace()->getMode() == csPattern) {
1887
0
  if (state->getFillColorSpace()->getMode() == csPattern) {
1888
0
    doPatternFill(gTrue);
1889
0
  } else {
1890
0
    out->eoFill(state);
1891
0
  }
1892
0
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1893
0
    doPatternStroke();
1894
0
  } else {
1895
0
    out->stroke(state);
1896
0
  }
1897
0
      } else {
1898
0
  out->fillStroke(state, gTrue);
1899
0
      }
1900
0
    }
1901
0
  }
1902
0
  doEndPath();
1903
0
}
1904
1905
0
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1906
0
  if (!state->isCurPt()) {
1907
    //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke");
1908
0
    return;
1909
0
  }
1910
0
  if (state->isPath()) {
1911
0
    state->closePath();
1912
0
    if (ocState) {
1913
0
      if (state->getFillColorSpace()->getMode() == csPattern ||
1914
0
    state->getStrokeColorSpace()->getMode() == csPattern) {
1915
0
  if (state->getFillColorSpace()->getMode() == csPattern) {
1916
0
    doPatternFill(gTrue);
1917
0
  } else {
1918
0
    out->eoFill(state);
1919
0
  }
1920
0
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
1921
0
    doPatternStroke();
1922
0
  } else {
1923
0
    out->stroke(state);
1924
0
  }
1925
0
      } else {
1926
0
  out->fillStroke(state, gTrue);
1927
0
      }
1928
0
    }
1929
0
  }
1930
0
  doEndPath();
1931
0
}
1932
1933
0
void Gfx::doPatternFill(GBool eoFill) {
1934
0
  GfxPattern *pattern;
1935
1936
  // this is a bit of a kludge -- patterns can be really slow, so we
1937
  // skip them if we're only doing text extraction, since they almost
1938
  // certainly don't contain any text
1939
0
  if (!out->needNonText()) {
1940
0
    return;
1941
0
  }
1942
1943
0
  if (!(pattern = state->getFillPattern())) {
1944
0
    return;
1945
0
  }
1946
0
  switch (pattern->getType()) {
1947
0
  case 1:
1948
0
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse);
1949
0
    break;
1950
0
  case 2:
1951
0
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse);
1952
0
    break;
1953
0
  default:
1954
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
1955
0
    pattern->getType());
1956
0
    break;
1957
0
  }
1958
0
}
1959
1960
0
void Gfx::doPatternStroke() {
1961
0
  GfxPattern *pattern;
1962
1963
  // this is a bit of a kludge -- patterns can be really slow, so we
1964
  // skip them if we're only doing text extraction, since they almost
1965
  // certainly don't contain any text
1966
0
  if (!out->needNonText()) {
1967
0
    return;
1968
0
  }
1969
1970
0
  if (!(pattern = state->getStrokePattern())) {
1971
0
    return;
1972
0
  }
1973
0
  switch (pattern->getType()) {
1974
0
  case 1:
1975
0
    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse);
1976
0
    break;
1977
0
  case 2:
1978
0
    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse);
1979
0
    break;
1980
0
  default:
1981
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke",
1982
0
    pattern->getType());
1983
0
    break;
1984
0
  }
1985
0
}
1986
1987
0
void Gfx::doPatternText(GBool stroke) {
1988
0
  GfxPattern *pattern;
1989
1990
  // this is a bit of a kludge -- patterns can be really slow, so we
1991
  // skip them if we're only doing text extraction, since they almost
1992
  // certainly don't contain any text
1993
0
  if (!out->needNonText()) {
1994
0
    return;
1995
0
  }
1996
1997
0
  pattern = stroke ? state->getStrokePattern() : state->getFillPattern();
1998
0
  if (!pattern) {
1999
0
    return;
2000
0
  }
2001
0
  switch (pattern->getType()) {
2002
0
  case 1:
2003
0
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue);
2004
0
    break;
2005
0
  case 2:
2006
0
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue);
2007
0
    break;
2008
0
  default:
2009
0
    error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
2010
0
    pattern->getType());
2011
0
    break;
2012
0
  }
2013
0
}
2014
2015
void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
2016
0
           GBool invert, GBool inlineImg, GBool interpolate) {
2017
0
  saveState();
2018
2019
0
  out->setSoftMaskFromImageMask(state, ref, str,
2020
0
        width, height, invert, inlineImg, interpolate);
2021
2022
0
  state->clearPath();
2023
0
  state->moveTo(0, 0);
2024
0
  state->lineTo(1, 0);
2025
0
  state->lineTo(1, 1);
2026
0
  state->lineTo(0, 1);
2027
0
  state->closePath();
2028
0
  doPatternFill(gTrue);
2029
0
  state->clearPath();
2030
2031
0
  restoreState();
2032
0
}
2033
2034
void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
2035
0
            GBool stroke, GBool eoFill, GBool text) {
2036
0
  GfxPatternColorSpace *patCS;
2037
0
  GfxColorSpace *cs;
2038
0
  GfxColor color;
2039
0
  GfxState *savedState;
2040
0
  double xMin, yMin, xMax, yMax, x, y, x1, y1, t;
2041
0
  double cxMin, cyMin, cxMax, cyMax;
2042
0
  int xi0, yi0, xi1, yi1, xi, yi;
2043
0
  double *ctm, *btm, *ptm;
2044
0
  double bbox[4], m[6], ictm[6], m1[6], imb[6];
2045
0
  double det;
2046
0
  double xstep, ystep;
2047
0
  int abortCheckCounter, i;
2048
2049
  // get color space
2050
0
  patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
2051
0
                  : state->getFillColorSpace());
2052
2053
  // construct a (pattern space) -> (current space) transform matrix
2054
0
  ctm = state->getCTM();
2055
0
  btm = baseMatrix;
2056
0
  ptm = tPat->getMatrix();
2057
  // iCTM = invert CTM
2058
0
  det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2059
0
  if (fabs(det) <= 1e-10) {
2060
0
    error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2061
0
    return;
2062
0
  }
2063
0
  det = 1 / det;
2064
0
  ictm[0] = ctm[3] * det;
2065
0
  ictm[1] = -ctm[1] * det;
2066
0
  ictm[2] = -ctm[2] * det;
2067
0
  ictm[3] = ctm[0] * det;
2068
0
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2069
0
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2070
  // m1 = PTM * BTM = PTM * base transform matrix
2071
0
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2072
0
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2073
0
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2074
0
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2075
0
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2076
0
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2077
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2078
0
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2079
0
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2080
0
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2081
0
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2082
0
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2083
0
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2084
2085
  // construct a (device space) -> (pattern space) transform matrix
2086
0
  det = m1[0] * m1[3] - m1[1] * m1[2];
2087
0
  if (fabs(det) <= 1e-10) {
2088
0
    error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
2089
0
    return;
2090
0
  }
2091
0
  det = 1 / det;
2092
0
  imb[0] = m1[3] * det;
2093
0
  imb[1] = -m1[1] * det;
2094
0
  imb[2] = -m1[2] * det;
2095
0
  imb[3] = m1[0] * det;
2096
0
  imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
2097
0
  imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
2098
2099
  // save current graphics state
2100
0
  savedState = saveStateStack();
2101
2102
  // set underlying color space (for uncolored tiling patterns); set
2103
  // various other parameters (stroke color, line width) to match
2104
  // Adobe's behavior
2105
0
  state->setFillPattern(NULL);
2106
0
  state->setStrokePattern(NULL);
2107
0
  state->setFillOverprint(gFalse);
2108
0
  state->setStrokeOverprint(gFalse);
2109
0
  state->setOverprintMode(0);
2110
0
  if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
2111
0
    state->setFillColorSpace(cs->copy());
2112
0
    out->updateFillColorSpace(state);
2113
0
    state->setStrokeColorSpace(cs->copy());
2114
0
    out->updateStrokeColorSpace(state);
2115
0
    if (stroke) {
2116
0
      state->setFillColor(state->getStrokeColor());
2117
0
    } else {
2118
0
      state->setStrokeColor(state->getFillColor());
2119
0
    }
2120
0
    out->updateFillColor(state);
2121
0
    out->updateStrokeColor(state);
2122
0
    state->setIgnoreColorOps(gTrue);
2123
0
  } else {
2124
0
    state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
2125
0
    out->updateFillColorSpace(state);
2126
0
    state->getFillColorSpace()->getDefaultColor(&color);
2127
0
    state->setFillColor(&color);
2128
0
    out->updateFillColor(state);
2129
0
    state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
2130
0
    out->updateStrokeColorSpace(state);
2131
0
    state->getStrokeColorSpace()->getDefaultColor(&color);
2132
0
    state->setStrokeColor(&color);
2133
0
    out->updateStrokeColor(state);
2134
0
  }
2135
0
  if (!stroke) {
2136
0
    state->setLineWidth(0);
2137
0
    out->updateLineWidth(state);
2138
0
    state->setLineDash(NULL, 0, 0);
2139
0
    out->updateLineDash(state);
2140
0
  }
2141
2142
  // clip to current path
2143
0
  if (stroke) {
2144
0
    state->clipToStrokePath();
2145
0
    out->clipToStrokePath(state);
2146
0
  } else if (!text) {
2147
0
    state->clip();
2148
0
    if (eoFill) {
2149
0
      out->eoClip(state);
2150
0
    } else {
2151
0
      out->clip(state);
2152
0
    }
2153
0
  }
2154
0
  state->clearPath();
2155
2156
  // get the clip region, check for empty
2157
0
  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
2158
0
  if (cxMin > cxMax || cyMin > cyMax) {
2159
0
    goto err;
2160
0
  }
2161
2162
  // transform clip region bbox to pattern space
2163
0
  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
2164
0
  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
2165
0
  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
2166
0
  y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
2167
0
  if (x1 < xMin) {
2168
0
    xMin = x1;
2169
0
  } else if (x1 > xMax) {
2170
0
    xMax = x1;
2171
0
  }
2172
0
  if (y1 < yMin) {
2173
0
    yMin = y1;
2174
0
  } else if (y1 > yMax) {
2175
0
    yMax = y1;
2176
0
  }
2177
0
  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
2178
0
  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
2179
0
  if (x1 < xMin) {
2180
0
    xMin = x1;
2181
0
  } else if (x1 > xMax) {
2182
0
    xMax = x1;
2183
0
  }
2184
0
  if (y1 < yMin) {
2185
0
    yMin = y1;
2186
0
  } else if (y1 > yMax) {
2187
0
    yMax = y1;
2188
0
  }
2189
0
  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
2190
0
  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
2191
0
  if (x1 < xMin) {
2192
0
    xMin = x1;
2193
0
  } else if (x1 > xMax) {
2194
0
    xMax = x1;
2195
0
  }
2196
0
  if (y1 < yMin) {
2197
0
    yMin = y1;
2198
0
  } else if (y1 > yMax) {
2199
0
    yMax = y1;
2200
0
  }
2201
2202
  // draw the pattern
2203
  //~ this should treat negative steps differently -- start at right/top
2204
  //~ edge instead of left/bottom (?)
2205
0
  bbox[0] = tPat->getBBox()[0];
2206
0
  bbox[1] = tPat->getBBox()[1];
2207
0
  bbox[2] = tPat->getBBox()[2];
2208
0
  bbox[3] = tPat->getBBox()[3];
2209
0
  if (bbox[0] > bbox[2]) {
2210
0
    t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t;
2211
0
  }
2212
0
  if (bbox[1] > bbox[3]) {
2213
0
    t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t;
2214
0
  }
2215
0
  xstep = tPat->getXStep();
2216
0
  ystep = tPat->getYStep();
2217
0
  if (xstep == 0 || ystep == 0) {
2218
0
    error(errSyntaxError, getPos(), "Zero x or y step in tiling pattern fill");
2219
0
    goto err;
2220
0
  }
2221
0
  if (xstep > 0) {
2222
0
    xi0 = (int)ceil((xMin - bbox[2]) / xstep);
2223
0
    xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1;
2224
0
  } else {
2225
0
    xi0 = (int)ceil((xMax - bbox[0]) / xstep);
2226
0
    xi1 = (int)floor((xMin - bbox[2]) / xstep) + 1;
2227
0
  }
2228
0
  if (ystep > 0) {
2229
0
    yi0 = (int)ceil((yMin - bbox[3]) / ystep);
2230
0
    yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1;
2231
0
  } else {
2232
0
    yi0 = (int)ceil((yMax - bbox[1]) / ystep);
2233
0
    yi1 = (int)floor((yMin - bbox[3]) / ystep) + 1;
2234
0
  }
2235
0
  for (i = 0; i < 4; ++i) {
2236
0
    m1[i] = m[i];
2237
0
  }
2238
0
  if (out->useTilingPatternFill() && !tPat->usesBlendMode(xref)) {
2239
0
    m1[4] = m[4];
2240
0
    m1[5] = m[5];
2241
0
    out->tilingPatternFill(state, this, tPat->getContentStreamRef(),
2242
0
         tPat->getPaintType(), tPat->getTilingType(),
2243
0
         tPat->getResDict(),
2244
0
         m1, bbox,
2245
0
         xi0, yi0, xi1, yi1, xstep, ystep);
2246
0
  } else {
2247
0
    abortCheckCounter = 0;
2248
0
    for (yi = yi0; yi < yi1; ++yi) {
2249
0
      for (xi = xi0; xi < xi1; ++xi) {
2250
0
  if (abortCheckCbk) {
2251
0
    ++abortCheckCounter;
2252
0
    if (abortCheckCounter > 100) {
2253
0
      if ((*abortCheckCbk)(abortCheckCbkData)) {
2254
0
        goto err;
2255
0
      }
2256
0
      abortCheckCounter = 0;
2257
0
    }
2258
0
  }
2259
0
  x = xi * xstep;
2260
0
  y = yi * ystep;
2261
0
  m1[4] = x * m[0] + y * m[2] + m[4];
2262
0
  m1[5] = x * m[1] + y * m[3] + m[5];
2263
0
  drawForm(tPat->getContentStreamRef(), tPat->getResDict(),
2264
0
     m1, bbox);
2265
0
      }
2266
0
    }
2267
0
  }
2268
2269
  // restore graphics state
2270
0
 err:
2271
0
  restoreStateStack(savedState);
2272
0
}
2273
2274
void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
2275
0
             GBool stroke, GBool eoFill, GBool text) {
2276
0
  GfxShading *shading;
2277
0
  GfxState *savedState;
2278
0
  double *ctm, *btm, *ptm;
2279
0
  double m[6], ictm[6], m1[6];
2280
0
  double xMin, yMin, xMax, yMax;
2281
0
  double det;
2282
2283
0
  shading = sPat->getShading();
2284
2285
  // save current graphics state
2286
0
  savedState = saveStateStack();
2287
2288
  // clip to current path
2289
0
  if (stroke) {
2290
0
    state->clipToStrokePath();
2291
0
    out->clipToStrokePath(state);
2292
0
    state->setFillOverprint(state->getStrokeOverprint());
2293
0
  } else if (!text) {
2294
0
    state->clip();
2295
0
    if (eoFill) {
2296
0
      out->eoClip(state);
2297
0
    } else {
2298
0
      out->clip(state);
2299
0
    }
2300
0
  }
2301
0
  state->clearPath();
2302
2303
  // construct a (pattern space) -> (current space) transform matrix
2304
0
  ctm = state->getCTM();
2305
0
  btm = baseMatrix;
2306
0
  ptm = sPat->getMatrix();
2307
  // iCTM = invert CTM
2308
0
  det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
2309
0
  if (fabs(det) <= 1e-10) {
2310
0
    error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
2311
0
    restoreStateStack(savedState);
2312
0
    return;
2313
0
  }
2314
0
  det = 1 / det;
2315
0
  ictm[0] = ctm[3] * det;
2316
0
  ictm[1] = -ctm[1] * det;
2317
0
  ictm[2] = -ctm[2] * det;
2318
0
  ictm[3] = ctm[0] * det;
2319
0
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2320
0
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2321
  // m1 = PTM * BTM = PTM * base transform matrix
2322
0
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
2323
0
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
2324
0
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
2325
0
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
2326
0
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
2327
0
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
2328
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
2329
0
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
2330
0
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
2331
0
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
2332
0
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
2333
0
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
2334
0
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
2335
2336
  // set the new matrix
2337
0
  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
2338
0
  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
2339
2340
  // clip to bbox
2341
0
  if (shading->getHasBBox()) {
2342
0
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2343
0
    state->moveTo(xMin, yMin);
2344
0
    state->lineTo(xMax, yMin);
2345
0
    state->lineTo(xMax, yMax);
2346
0
    state->lineTo(xMin, yMax);
2347
0
    state->closePath();
2348
0
    state->clip();
2349
0
    out->clip(state);
2350
0
    state->clearPath();
2351
0
  }
2352
2353
  // set the color space
2354
0
  state->setFillColorSpace(shading->getColorSpace()->copy());
2355
0
  out->updateFillColorSpace(state);
2356
2357
  // background color fill
2358
0
  if (shading->getHasBackground()) {
2359
0
    state->setFillColor(shading->getBackground());
2360
0
    out->updateFillColor(state);
2361
0
    state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2362
0
    state->moveTo(xMin, yMin);
2363
0
    state->lineTo(xMax, yMin);
2364
0
    state->lineTo(xMax, yMax);
2365
0
    state->lineTo(xMin, yMax);
2366
0
    state->closePath();
2367
0
    out->fill(state);
2368
0
    state->clearPath();
2369
0
  }
2370
2371
  // perform the fill
2372
0
  doShFill(shading);
2373
2374
  // restore graphics state
2375
0
  restoreStateStack(savedState);
2376
0
}
2377
2378
0
void Gfx::opShFill(Object args[], int numArgs) {
2379
0
  GfxShading *shading;
2380
0
  GfxState *savedState;
2381
0
  double xMin, yMin, xMax, yMax;
2382
2383
0
  if (state->getIgnoreColorOps()) {
2384
0
    error(errSyntaxWarning, getPos(), "Ignoring shaded fill"
2385
0
    " in uncolored Type 3 char or tiling pattern");
2386
0
    return;
2387
0
  }
2388
2389
0
  if (!out->needNonText()) {
2390
0
    return;
2391
0
  }
2392
2393
0
  if (!ocState) {
2394
0
    return;
2395
0
  }
2396
2397
0
  if (!(shading = res->lookupShading(args[0].getName()
2398
0
             ))) {
2399
0
    return;
2400
0
  }
2401
2402
  // save current graphics state
2403
0
  savedState = saveStateStack();
2404
2405
  // clip to bbox
2406
0
  if (shading->getHasBBox()) {
2407
0
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
2408
0
    state->moveTo(xMin, yMin);
2409
0
    state->lineTo(xMax, yMin);
2410
0
    state->lineTo(xMax, yMax);
2411
0
    state->lineTo(xMin, yMax);
2412
0
    state->closePath();
2413
0
    state->clip();
2414
0
    out->clip(state);
2415
0
    state->clearPath();
2416
0
  }
2417
2418
  // set the color space
2419
0
  state->setFillColorSpace(shading->getColorSpace()->copy());
2420
0
  out->updateFillColorSpace(state);
2421
2422
  // perform the fill
2423
0
  doShFill(shading);
2424
2425
  // restore graphics state
2426
0
  restoreStateStack(savedState);
2427
2428
0
  delete shading;
2429
0
}
2430
2431
0
void Gfx::doShFill(GfxShading *shading) {
2432
0
  if (out->shadedFill(state, shading)) {
2433
0
    return;
2434
0
  }
2435
2436
  // do shading type-specific operations
2437
0
  switch (shading->getType()) {
2438
0
  case 1:
2439
0
    doFunctionShFill((GfxFunctionShading *)shading);
2440
0
    break;
2441
0
  case 2:
2442
0
    doAxialShFill((GfxAxialShading *)shading);
2443
0
    break;
2444
0
  case 3:
2445
0
    doRadialShFill((GfxRadialShading *)shading);
2446
0
    break;
2447
0
  case 4:
2448
0
  case 5:
2449
0
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
2450
0
    break;
2451
0
  case 6:
2452
0
  case 7:
2453
0
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
2454
0
    break;
2455
0
  }
2456
0
}
2457
2458
0
void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
2459
0
  double x0, y0, x1, y1;
2460
0
  GfxColor colors[4];
2461
2462
0
  shading->getDomain(&x0, &y0, &x1, &y1);
2463
0
  shading->getColor(x0, y0, &colors[0]);
2464
0
  shading->getColor(x0, y1, &colors[1]);
2465
0
  shading->getColor(x1, y0, &colors[2]);
2466
0
  shading->getColor(x1, y1, &colors[3]);
2467
0
  doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
2468
0
}
2469
2470
void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
2471
          double x0, double y0,
2472
          double x1, double y1,
2473
0
          GfxColor *colors, int depth) {
2474
0
  GfxColor fillColor;
2475
0
  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
2476
0
  GfxColor colors2[4];
2477
0
  double *matrix;
2478
0
  double xM, yM;
2479
0
  int nComps, i, j;
2480
2481
0
  nComps = shading->getColorSpace()->getNComps();
2482
0
  matrix = shading->getMatrix();
2483
2484
  // compare the four corner colors
2485
0
  for (i = 0; i < 4; ++i) {
2486
0
    for (j = 0; j < nComps; ++j) {
2487
0
      if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
2488
0
  break;
2489
0
      }
2490
0
    }
2491
0
    if (j < nComps) {
2492
0
      break;
2493
0
    }
2494
0
  }
2495
2496
  // center of the rectangle
2497
0
  xM = 0.5 * (x0 + x1);
2498
0
  yM = 0.5 * (y0 + y1);
2499
2500
  // the four corner colors are close (or we hit the recursive limit)
2501
  // -- fill the rectangle; but require at least one subdivision
2502
  // (depth==0) to avoid problems when the four outer corners of the
2503
  // shaded region are the same color
2504
0
  if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
2505
2506
    // use the center color
2507
0
    shading->getColor(xM, yM, &fillColor);
2508
0
    state->setFillColor(&fillColor);
2509
0
    out->updateFillColor(state);
2510
2511
    // fill the rectangle
2512
0
    state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
2513
0
      x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
2514
0
    state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
2515
0
      x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
2516
0
    state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
2517
0
      x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
2518
0
    state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
2519
0
      x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
2520
0
    state->closePath();
2521
0
    out->fill(state);
2522
0
    state->clearPath();
2523
2524
  // the four corner colors are not close enough -- subdivide the
2525
  // rectangle
2526
0
  } else {
2527
2528
    // colors[0]       colorM0       colors[2]
2529
    //   (x0,y0)       (xM,y0)       (x1,y0)
2530
    //         +----------+----------+
2531
    //         |          |          |
2532
    //         |    UL    |    UR    |
2533
    // color0M |       colorMM       | color1M
2534
    // (x0,yM) +----------+----------+ (x1,yM)
2535
    //         |       (xM,yM)       |
2536
    //         |    LL    |    LR    |
2537
    //         |          |          |
2538
    //         +----------+----------+
2539
    // colors[1]       colorM1       colors[3]
2540
    //   (x0,y1)       (xM,y1)       (x1,y1)
2541
2542
0
    shading->getColor(x0, yM, &color0M);
2543
0
    shading->getColor(x1, yM, &color1M);
2544
0
    shading->getColor(xM, y0, &colorM0);
2545
0
    shading->getColor(xM, y1, &colorM1);
2546
0
    shading->getColor(xM, yM, &colorMM);
2547
2548
    // upper-left sub-rectangle
2549
0
    colors2[0] = colors[0];
2550
0
    colors2[1] = color0M;
2551
0
    colors2[2] = colorM0;
2552
0
    colors2[3] = colorMM;
2553
0
    doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
2554
    
2555
    // lower-left sub-rectangle
2556
0
    colors2[0] = color0M;
2557
0
    colors2[1] = colors[1];
2558
0
    colors2[2] = colorMM;
2559
0
    colors2[3] = colorM1;
2560
0
    doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
2561
    
2562
    // upper-right sub-rectangle
2563
0
    colors2[0] = colorM0;
2564
0
    colors2[1] = colorMM;
2565
0
    colors2[2] = colors[2];
2566
0
    colors2[3] = color1M;
2567
0
    doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
2568
2569
    // lower-right sub-rectangle
2570
0
    colors2[0] = colorMM;
2571
0
    colors2[1] = colorM1;
2572
0
    colors2[2] = color1M;
2573
0
    colors2[3] = colors[3];
2574
0
    doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
2575
0
  }
2576
0
}
2577
2578
0
void Gfx::doAxialShFill(GfxAxialShading *shading) {
2579
0
  double xMin, yMin, xMax, yMax;
2580
0
  double x0, y0, x1, y1;
2581
0
  double dx, dy, mul;
2582
0
  GBool dxdyZero, horiz;
2583
0
  double tMin, tMax, tMinExt, tMaxExt, t, tx, ty;
2584
0
  double sMin, sMax, tmp;
2585
0
  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
2586
0
  double t0, t1, tt;
2587
0
  GfxColor colors[axialSplits];
2588
0
  int abortCheckCounter, nComps, i, j, k;
2589
2590
  // get the clip region bbox
2591
0
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2592
2593
  // compute min and max t values, based on the four corners of the
2594
  // clip region bbox
2595
0
  shading->getCoords(&x0, &y0, &x1, &y1);
2596
0
  dx = x1 - x0;
2597
0
  dy = y1 - y0;
2598
0
  dxdyZero = fabs(dx) < 0.0001 && fabs(dy) < 0.0001;
2599
0
  horiz = fabs(dy) < fabs(dx);
2600
0
  if (dxdyZero) {
2601
0
    tMinExt = tMaxExt = 0;
2602
0
    tMin = tMax = 0;
2603
0
  } else {
2604
0
    mul = 1 / (dx * dx + dy * dy);
2605
0
    tMinExt = tMaxExt = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
2606
0
    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
2607
0
    if (t < tMinExt) {
2608
0
      tMinExt = t;
2609
0
    } else if (t > tMaxExt) {
2610
0
      tMaxExt = t;
2611
0
    }
2612
0
    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
2613
0
    if (t < tMinExt) {
2614
0
      tMinExt = t;
2615
0
    } else if (t > tMaxExt) {
2616
0
      tMaxExt = t;
2617
0
    }
2618
0
    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
2619
0
    if (t < tMinExt) {
2620
0
      tMinExt = t;
2621
0
    } else if (t > tMaxExt) {
2622
0
      tMaxExt = t;
2623
0
    }
2624
0
    if ((tMin = tMinExt) < 0) {
2625
0
      tMin = 0;
2626
0
    }
2627
0
    if (!shading->getExtend0()) {
2628
0
      tMinExt = tMin;
2629
0
    }
2630
0
    if ((tMax = tMaxExt) > 1) {
2631
0
      tMax = 1;
2632
0
    }
2633
0
    if (!shading->getExtend1()) {
2634
0
      tMaxExt = tMax;
2635
0
    }
2636
0
  }
2637
2638
  // get the function domain
2639
0
  t0 = shading->getDomain0();
2640
0
  t1 = shading->getDomain1();
2641
2642
  // Traverse the t axis and do the shading.
2643
  //
2644
  // For each point (tx, ty) on the t axis, consider a line through
2645
  // that point perpendicular to the t axis:
2646
  //
2647
  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
2648
  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
2649
  //
2650
  // Then look at the intersection of this line with the bounding box
2651
  // (xMin, yMin, xMax, yMax).  For -1 < |dy/dx| < 1, look at the
2652
  // intersection with yMin, yMax:
2653
  //
2654
  //     s0 = (yMin - ty) / dx
2655
  //     s1 = (yMax - ty) / dx
2656
  //
2657
  // else look at the intersection with xMin, xMax:
2658
  //
2659
  //     s0 = (xMin - tx) / -dy
2660
  //     s1 = (xMax - tx) / -dy
2661
  //
2662
  // Each filled polygon is bounded by two of these line segments
2663
  // perpdendicular to the t axis.
2664
  //
2665
  // The t axis is bisected into smaller regions until the color
2666
  // difference across a region is small enough, and then the region
2667
  // is painted with a single color.
2668
2669
  // compute the coordinates of the point on the t axis at t = tMin;
2670
  // then compute the intersection of the perpendicular line with the
2671
  // bounding box
2672
0
  tx = x0 + tMin * dx;
2673
0
  ty = y0 + tMin * dy;
2674
0
  if (dxdyZero) {
2675
0
    sMin = sMax = 0;
2676
0
  } else {
2677
0
    if (horiz) {
2678
0
      sMin = (yMin - ty) / dx;
2679
0
      sMax = (yMax - ty) / dx;
2680
0
    } else {
2681
0
      sMin = (xMin - tx) / -dy;
2682
0
      sMax = (xMax - tx) / -dy;
2683
0
    }
2684
0
    if (sMin > sMax) {
2685
0
      tmp = sMin; sMin = sMax; sMax = tmp;
2686
0
    }
2687
0
  }
2688
0
  ux0 = tx - sMin * dy;
2689
0
  uy0 = ty + sMin * dx;
2690
0
  vx0 = tx - sMax * dy;
2691
0
  vy0 = ty + sMax * dx;
2692
2693
  // fill the extension at t0
2694
0
  if (shading->getExtend0() && tMinExt < tMin) {
2695
2696
    // compute the color at t0
2697
0
    shading->getColor(t0, &colors[0]);
2698
2699
    // compute the coordinates of the point on the t axis at t =
2700
    // tMinExt; then compute the intersection of the perpendicular
2701
    // line with the bounding box
2702
0
    tx = x0 + tMinExt * dx;
2703
0
    ty = y0 + tMinExt * dy;
2704
0
    if (dxdyZero) {
2705
0
      sMin = sMax = 0;
2706
0
    } else {
2707
0
      if (horiz) {
2708
0
  sMin = (yMin - ty) / dx;
2709
0
  sMax = (yMax - ty) / dx;
2710
0
      } else {
2711
0
  sMin = (xMin - tx) / -dy;
2712
0
  sMax = (xMax - tx) / -dy;
2713
0
      }
2714
0
      if (sMin > sMax) {
2715
0
  tmp = sMin; sMin = sMax; sMax = tmp;
2716
0
      }
2717
0
    }
2718
0
    ux1 = tx - sMin * dy;
2719
0
    uy1 = ty + sMin * dx;
2720
0
    vx1 = tx - sMax * dy;
2721
0
    vy1 = ty + sMax * dx;
2722
2723
    // set the color
2724
0
    state->setFillColor(&colors[0]);
2725
0
    out->updateFillColor(state);
2726
2727
    // fill the region
2728
0
    state->moveTo(ux1, uy1);
2729
0
    state->lineTo(vx1, vy1);
2730
0
    state->lineTo(vx0, vy0);
2731
0
    state->lineTo(ux0, uy0);
2732
0
    state->closePath();
2733
0
    out->fill(state);
2734
0
    state->clearPath();
2735
0
  }
2736
2737
  // traverse the t axis, splitting [tMin, tMax] into axialSplits regions
2738
2739
  // compute the color in the center of each region
2740
0
  for (i = 0; i < axialSplits; ++i) {
2741
0
    t = tMin + (tMax - tMin) * (i + 0.5) / axialSplits;
2742
0
    tt = t0 + (t1 - t0) * t;
2743
0
    shading->getColor(tt, &colors[i]);
2744
0
  }
2745
2746
  // each iteration draws one or more regions, starting at i --
2747
  // if the colors are similar, it will combine regions i, i+1, ...
2748
0
  nComps = shading->getColorSpace()->getNComps();
2749
0
  abortCheckCounter = 0;
2750
0
  i = 0;
2751
0
  while (i < axialSplits) {
2752
2753
0
    if (abortCheckCbk) {
2754
0
      ++abortCheckCounter;
2755
0
      if (abortCheckCounter > 100) {
2756
0
        if ((*abortCheckCbk)(abortCheckCbkData)) {
2757
0
          break;
2758
0
        }
2759
0
        abortCheckCounter = 0;
2760
0
      }
2761
0
    }
2762
2763
    // check for similar colors
2764
0
    for (j = i + 1; j < axialSplits; ++j) {
2765
0
      for (k = 0; k < nComps; ++k) {
2766
0
  if (abs(colors[j].c[k] - colors[i].c[k]) > axialColorDelta) {
2767
0
    break;
2768
0
  }
2769
0
      }
2770
0
      if (k < nComps) {
2771
0
  break;
2772
0
      }
2773
0
    }
2774
2775
    // compute the coordinates of the point on the t axis; then
2776
    // compute the intersection of the perpendicular line with the
2777
    // bounding box
2778
0
    t = tMin + (tMax - tMin) * (double)j / axialSplits;
2779
0
    tx = x0 + t * dx;
2780
0
    ty = y0 + t * dy;
2781
0
    if (dxdyZero) {
2782
0
      sMin = sMax = 0;
2783
0
    } else {
2784
0
      if (horiz) {
2785
0
  sMin = (yMin - ty) / dx;
2786
0
  sMax = (yMax - ty) / dx;
2787
0
      } else {
2788
0
  sMin = (xMin - tx) / -dy;
2789
0
  sMax = (xMax - tx) / -dy;
2790
0
      }
2791
0
      if (sMin > sMax) {
2792
0
  tmp = sMin; sMin = sMax; sMax = tmp;
2793
0
      }
2794
0
    }
2795
0
    ux1 = tx - sMin * dy;
2796
0
    uy1 = ty + sMin * dx;
2797
0
    vx1 = tx - sMax * dy;
2798
0
    vy1 = ty + sMax * dx;
2799
2800
    // set the color
2801
0
    state->setFillColor(&colors[i]);
2802
0
    out->updateFillColor(state);
2803
2804
    // fill the region
2805
0
    state->moveTo(ux0, uy0);
2806
0
    state->lineTo(vx0, vy0);
2807
0
    state->lineTo(vx1, vy1);
2808
0
    state->lineTo(ux1, uy1);
2809
0
    state->closePath();
2810
0
    out->fill(state);
2811
0
    state->clearPath();
2812
2813
    // set up for next region
2814
0
    ux0 = ux1;
2815
0
    uy0 = uy1;
2816
0
    vx0 = vx1;
2817
0
    vy0 = vy1;
2818
2819
0
    i = j;
2820
0
  }
2821
2822
  // fill the extension at t1
2823
0
  if (shading->getExtend1() && tMaxExt > tMax) {
2824
2825
    // compute the color at t1
2826
0
    shading->getColor(t1, &colors[0]);
2827
2828
    // compute the coordinates of the point on the t axis at t =
2829
    // tMaxExt; then compute the intersection of the perpendicular
2830
    // line with the bounding box
2831
0
    tx = x0 + tMaxExt * dx;
2832
0
    ty = y0 + tMaxExt * dy;
2833
0
    if (dxdyZero) {
2834
0
      sMin = sMax = 0;
2835
0
    } else {
2836
0
      if (horiz) {
2837
0
  sMin = (yMin - ty) / dx;
2838
0
  sMax = (yMax - ty) / dx;
2839
0
      } else {
2840
0
  sMin = (xMin - tx) / -dy;
2841
0
  sMax = (xMax - tx) / -dy;
2842
0
      }
2843
0
      if (sMin > sMax) {
2844
0
  tmp = sMin; sMin = sMax; sMax = tmp;
2845
0
      }
2846
0
    }
2847
0
    ux1 = tx - sMin * dy;
2848
0
    uy1 = ty + sMin * dx;
2849
0
    vx1 = tx - sMax * dy;
2850
0
    vy1 = ty + sMax * dx;
2851
2852
    // set the color
2853
0
    state->setFillColor(&colors[0]);
2854
0
    out->updateFillColor(state);
2855
2856
    // fill the region
2857
0
    state->moveTo(ux0, uy0);
2858
0
    state->lineTo(vx0, vy0);
2859
0
    state->lineTo(vx1, vy1);
2860
0
    state->lineTo(ux1, uy1);
2861
0
    state->closePath();
2862
0
    out->fill(state);
2863
0
    state->clearPath();
2864
0
  }
2865
0
}
2866
2867
#if defined(__GNUC__) && !defined(__clang__)
2868
// this function makes a lot of sin()/cos() calls, which are slow
2869
// with glibc 2.16 and newer on x86; accuracy isn't terribly
2870
// important here, so tell gcc to use the fast version
2871
#pragma GCC optimize ("fast-math")
2872
#endif
2873
2874
0
void Gfx::doRadialShFill(GfxRadialShading *shading) {
2875
0
  double xMin, yMin, xMax, yMax;
2876
0
  double x0, y0, r0, x1, y1, r1, t0, t1;
2877
0
  int nComps;
2878
0
  GfxColor colorA, colorB;
2879
0
  double xa, ya, xb, yb, ra, rb;
2880
0
  double ta, tb, sa, sb;
2881
0
  double sMin, sMax, h;
2882
0
  double sLeft, sRight, sTop, sBottom, sZero, sDiag;
2883
0
  GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
2884
0
  GBool haveSMin, haveSMax;
2885
0
  GBool enclosed;
2886
0
  double *ctm;
2887
0
  double theta, alpha, angle, t;
2888
0
  int abortCheckCounter, ia, ib, k, n;
2889
2890
  // get the shading info
2891
0
  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2892
0
  t0 = shading->getDomain0();
2893
0
  t1 = shading->getDomain1();
2894
0
  nComps = shading->getColorSpace()->getNComps();
2895
2896
  // Compute the point at which r(s) = 0; check for the enclosed
2897
  // circles case; and compute the angles for the tangent lines.
2898
0
  h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
2899
0
  if (h == 0) {
2900
0
    enclosed = gTrue;
2901
0
    theta = 0; // make gcc happy
2902
0
  } else if (r1 - r0 == 0) {
2903
0
    enclosed = gFalse;
2904
0
    theta = 0;
2905
0
  } else if (fabs(r1 - r0) >= h - 0.0001) {
2906
0
    enclosed = gTrue;
2907
0
    theta = 0; // make gcc happy
2908
0
  } else {
2909
0
    enclosed = gFalse;
2910
0
    theta = asin((r1 - r0) / h);
2911
0
  }
2912
0
  if (enclosed) {
2913
0
    alpha = 0;
2914
0
  } else {
2915
0
    alpha = atan2(y1 - y0, x1 - x0);
2916
0
  }
2917
2918
  // compute the (possibly extended) s range
2919
0
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2920
0
  if (enclosed) {
2921
0
    sMin = 0;
2922
0
    sMax = 1;
2923
0
  } else {
2924
    // solve x(sLeft) + r(sLeft) = xMin
2925
0
    if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
2926
0
      sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
2927
0
    } else {
2928
0
      sLeft = 0; // make gcc happy
2929
0
    }
2930
    // solve x(sRight) - r(sRight) = xMax
2931
0
    if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
2932
0
      sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
2933
0
    } else {
2934
0
      sRight = 0; // make gcc happy
2935
0
    }
2936
    // solve y(sBottom) + r(sBottom) = yMin
2937
0
    if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
2938
0
      sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
2939
0
    } else {
2940
0
      sBottom = 0; // make gcc happy
2941
0
    }
2942
    // solve y(sTop) - r(sTop) = yMax
2943
0
    if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
2944
0
      sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
2945
0
    } else {
2946
0
      sTop = 0; // make gcc happy
2947
0
    }
2948
    // solve r(sZero) = 0
2949
0
    if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
2950
0
      sZero = -r0 / (r1 - r0);
2951
0
    } else {
2952
0
      sZero = 0; // make gcc happy
2953
0
    }
2954
    // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
2955
0
    if (haveSZero) {
2956
0
      sDiag = (sqrt((xMax - xMin) * (xMax - xMin) +
2957
0
        (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
2958
0
    } else {
2959
0
      sDiag = 0; // make gcc happy
2960
0
    }
2961
    // compute sMin
2962
0
    if (shading->getExtend0()) {
2963
0
      sMin = 0;
2964
0
      haveSMin = gFalse;
2965
0
      if (x0 < x1 && haveSLeft && sLeft < 0) {
2966
0
  sMin = sLeft;
2967
0
  haveSMin = gTrue;
2968
0
      } else if (x0 > x1 && haveSRight && sRight < 0) {
2969
0
  sMin = sRight;
2970
0
  haveSMin = gTrue;
2971
0
      }
2972
0
      if (y0 < y1 && haveSBottom && sBottom < 0) {
2973
0
  if (!haveSMin || sBottom > sMin) {
2974
0
    sMin = sBottom;
2975
0
    haveSMin = gTrue;
2976
0
  }
2977
0
      } else if (y0 > y1 && haveSTop && sTop < 0) {
2978
0
  if (!haveSMin || sTop > sMin) {
2979
0
    sMin = sTop;
2980
0
    haveSMin = gTrue;
2981
0
  }
2982
0
      }
2983
0
      if (haveSZero && sZero <= 0) {
2984
0
  if (!haveSMin || sZero > sMin) {
2985
0
    sMin = sZero;
2986
0
  }
2987
0
      }
2988
0
    } else {
2989
0
      sMin = 0;
2990
0
    }
2991
    // compute sMax
2992
0
    if (shading->getExtend1()) {
2993
0
      sMax = 1;
2994
0
      haveSMax = gFalse;
2995
0
      if (x1 < x0 && haveSLeft && sLeft > 1) {
2996
0
  sMax = sLeft;
2997
0
  haveSMax = gTrue;
2998
0
      } else if (x1 > x0 && haveSRight && sRight > 1) {
2999
0
  sMax = sRight;
3000
0
  haveSMax = gTrue;
3001
0
      }
3002
0
      if (y1 < y0 && haveSBottom && sBottom > 1) {
3003
0
  if (!haveSMax || sBottom < sMax) {
3004
0
    sMax = sBottom;
3005
0
    haveSMax = gTrue;
3006
0
  }
3007
0
      } else if (y1 > y0 && haveSTop && sTop > 1) {
3008
0
  if (!haveSMax || sTop < sMax) {
3009
0
    sMax = sTop;
3010
0
    haveSMax = gTrue;
3011
0
  }
3012
0
      }
3013
0
      if (haveSZero && sDiag > 1) {
3014
0
  if (!haveSMax || sDiag < sMax) {
3015
0
    sMax = sDiag;
3016
0
  }
3017
0
      }
3018
0
    } else {
3019
0
      sMax = 1;
3020
0
    }
3021
0
  }
3022
3023
  // compute the number of steps into which circles must be divided to
3024
  // achieve a curve flatness of 0.1 pixel in device space for the
3025
  // largest circle (note that "device space" is 72 dpi when generating
3026
  // PostScript, hence the relatively small 0.1 pixel accuracy)
3027
0
  ctm = state->getCTM();
3028
0
  t = fabs(ctm[0]);
3029
0
  if (fabs(ctm[1]) > t) {
3030
0
    t = fabs(ctm[1]);
3031
0
  }
3032
0
  if (fabs(ctm[2]) > t) {
3033
0
    t = fabs(ctm[2]);
3034
0
  }
3035
0
  if (fabs(ctm[3]) > t) {
3036
0
    t = fabs(ctm[3]);
3037
0
  }
3038
0
  if (r0 > r1) {
3039
0
    t *= r0;
3040
0
  } else {
3041
0
    t *= r1;
3042
0
  }
3043
0
  if (t < 1) {
3044
0
    n = 3;
3045
0
  } else {
3046
0
    n = (int)(M_PI / acos(1 - 0.1 / t));
3047
0
    if (n < 3) {
3048
0
      n = 3;
3049
0
    } else if (n > 200) {
3050
0
      n = 200;
3051
0
    }
3052
0
  }
3053
3054
  // setup for the start circle
3055
0
  ia = 0;
3056
0
  sa = sMin;
3057
0
  ta = t0 + sa * (t1 - t0);
3058
0
  xa = x0 + sa * (x1 - x0);
3059
0
  ya = y0 + sa * (y1 - y0);
3060
0
  ra = r0 + sa * (r1 - r0);
3061
0
  if (ta < t0) {
3062
0
    shading->getColor(t0, &colorA);
3063
0
  } else if (ta > t1) {
3064
0
    shading->getColor(t1, &colorA);
3065
0
  } else {
3066
0
    shading->getColor(ta, &colorA);
3067
0
  }
3068
3069
  // fill the circles
3070
0
  abortCheckCounter = 0;
3071
0
  while (ia < radialMaxSplits) {
3072
3073
0
    if (abortCheckCbk) {
3074
0
      ++abortCheckCounter;
3075
0
      if (abortCheckCounter > 100) {
3076
0
  if ((*abortCheckCbk)(abortCheckCbkData)) {
3077
0
    break;
3078
0
  }
3079
0
  abortCheckCounter = 0;
3080
0
      }
3081
0
    }
3082
3083
    // go as far along the t axis (toward t1) as we can, such that the
3084
    // color difference is within the tolerance (radialColorDelta) --
3085
    // this uses bisection (between the current value, t, and t1),
3086
    // limited to radialMaxSplits points along the t axis; require at
3087
    // least one split to avoid problems when the innermost and
3088
    // outermost colors are the same
3089
0
    ib = radialMaxSplits;
3090
0
    sb = sMax;
3091
0
    tb = t0 + sb * (t1 - t0);
3092
0
    if (tb < t0) {
3093
0
      shading->getColor(t0, &colorB);
3094
0
    } else if (tb > t1) {
3095
0
      shading->getColor(t1, &colorB);
3096
0
    } else {
3097
0
      shading->getColor(tb, &colorB);
3098
0
    }
3099
0
    while (ib - ia > 1) {
3100
0
      for (k = 0; k < nComps; ++k) {
3101
0
  if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
3102
0
    break;
3103
0
  }
3104
0
      }
3105
0
      if (k == nComps && ib < radialMaxSplits) {
3106
0
  break;
3107
0
      }
3108
0
      ib = (ia + ib) / 2;
3109
0
      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
3110
0
      tb = t0 + sb * (t1 - t0);
3111
0
      if (tb < t0) {
3112
0
  shading->getColor(t0, &colorB);
3113
0
      } else if (tb > t1) {
3114
0
  shading->getColor(t1, &colorB);
3115
0
      } else {
3116
0
  shading->getColor(tb, &colorB);
3117
0
      }
3118
0
    }
3119
3120
    // compute center and radius of the circle
3121
0
    xb = x0 + sb * (x1 - x0);
3122
0
    yb = y0 + sb * (y1 - y0);
3123
0
    rb = r0 + sb * (r1 - r0);
3124
3125
    // use the average of the colors at the two circles
3126
0
    for (k = 0; k < nComps; ++k) {
3127
0
      colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
3128
0
    }
3129
0
    state->setFillColor(&colorA);
3130
0
    out->updateFillColor(state);
3131
3132
0
    if (enclosed) {
3133
3134
      // construct path for first circle (counterclockwise)
3135
0
      state->moveTo(xa + ra, ya);
3136
0
      for (k = 1; k < n; ++k) {
3137
0
  angle = ((double)k / (double)n) * 2 * M_PI;
3138
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3139
0
      }
3140
0
      state->closePath();
3141
3142
      // construct and append path for second circle (clockwise)
3143
0
      state->moveTo(xb + rb, yb);
3144
0
      for (k = 1; k < n; ++k) {
3145
0
  angle = -((double)k / (double)n) * 2 * M_PI;
3146
0
  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3147
0
      }
3148
0
      state->closePath();
3149
3150
0
    } else {
3151
3152
      // construct the first subpath (clockwise)
3153
0
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3154
0
        ya + ra * sin(alpha + theta + 0.5 * M_PI));
3155
0
      for (k = 0; k < n; ++k) {
3156
0
  angle = alpha + theta + 0.5 * M_PI
3157
0
    - ((double)k / (double)n) * (2 * theta + M_PI);
3158
0
  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3159
0
      }
3160
0
      for (k = 0; k < n; ++k) {
3161
0
  angle = alpha - theta - 0.5 * M_PI
3162
0
    + ((double)k / (double)n) * (2 * theta - M_PI);
3163
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3164
0
      }
3165
0
      state->closePath();
3166
3167
      // construct the second subpath (counterclockwise)
3168
0
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
3169
0
        ya + ra * sin(alpha + theta + 0.5 * M_PI));
3170
0
      for (k = 0; k < n; ++k) {
3171
0
  angle = alpha + theta + 0.5 * M_PI
3172
0
          + ((double)k / (double)n) * (-2 * theta + M_PI);
3173
0
  state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
3174
0
      }
3175
0
      for (k = 0; k < n; ++k) {
3176
0
  angle = alpha - theta - 0.5 * M_PI
3177
0
          + ((double)k / (double)n) * (2 * theta + M_PI);
3178
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3179
0
      }
3180
0
      state->closePath();
3181
0
    }
3182
3183
    // fill the path
3184
0
    out->fill(state);
3185
0
    state->clearPath();
3186
3187
    // step to the next value of t
3188
0
    ia = ib;
3189
0
    sa = sb;
3190
0
    ta = tb;
3191
0
    xa = xb;
3192
0
    ya = yb;
3193
0
    ra = rb;
3194
0
    colorA = colorB;
3195
0
  }
3196
3197
0
  if (enclosed) {
3198
    // extend the smaller circle
3199
0
    if ((shading->getExtend0() && r0 <= r1) ||
3200
0
  (shading->getExtend1() && r1 < r0)) {
3201
0
      if (r0 <= r1) {
3202
0
  ta = t0;
3203
0
  ra = r0;
3204
0
  xa = x0;
3205
0
  ya = y0;
3206
0
      } else {
3207
0
  ta = t1;
3208
0
  ra = r1;
3209
0
  xa = x1;
3210
0
  ya = y1;
3211
0
      }
3212
0
      shading->getColor(ta, &colorA);
3213
0
      state->setFillColor(&colorA);
3214
0
      out->updateFillColor(state);
3215
0
      state->moveTo(xa + ra, ya);
3216
0
      for (k = 1; k < n; ++k) {
3217
0
  angle = ((double)k / (double)n) * 2 * M_PI;
3218
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3219
0
      }
3220
0
      state->closePath();
3221
0
      out->fill(state);
3222
0
      state->clearPath();
3223
0
    }
3224
3225
    // extend the larger circle
3226
0
    if ((shading->getExtend0() && r0 > r1) ||
3227
0
  (shading->getExtend1() && r1 >= r0)) {
3228
0
      if (r0 > r1) {
3229
0
  ta = t0;
3230
0
  ra = r0;
3231
0
  xa = x0;
3232
0
  ya = y0;
3233
0
      } else {
3234
0
  ta = t1;
3235
0
  ra = r1;
3236
0
  xa = x1;
3237
0
  ya = y1;
3238
0
      }
3239
0
      shading->getColor(ta, &colorA);
3240
0
      state->setFillColor(&colorA);
3241
0
      out->updateFillColor(state);
3242
0
      state->moveTo(xMin, yMin);
3243
0
      state->lineTo(xMin, yMax);
3244
0
      state->lineTo(xMax, yMax);
3245
0
      state->lineTo(xMax, yMin);
3246
0
      state->closePath();
3247
0
      state->moveTo(xa + ra, ya);
3248
0
      for (k = 1; k < n; ++k) {
3249
0
  angle = ((double)k / (double)n) * 2 * M_PI;
3250
0
  state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
3251
0
      }
3252
0
      state->closePath();
3253
0
      out->fill(state);
3254
0
      state->clearPath();
3255
0
    }
3256
0
  }
3257
0
}
3258
3259
#if defined(__GNUC__) && !defined(__clang__)
3260
#pragma GCC reset_options
3261
#endif
3262
3263
0
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
3264
0
  double x0, y0, x1, y1, x2, y2;
3265
0
  double color0[gfxColorMaxComps];
3266
0
  double color1[gfxColorMaxComps];
3267
0
  double color2[gfxColorMaxComps];
3268
0
  int abortCheckCounter, i;
3269
3270
0
  abortCheckCounter = 0;
3271
0
  for (i = 0; i < shading->getNTriangles(); ++i) {
3272
0
    if (abortCheckCbk) {
3273
0
      ++abortCheckCounter;
3274
0
      if (abortCheckCounter > 25) {
3275
0
  if ((*abortCheckCbk)(abortCheckCbkData)) {
3276
0
    break;
3277
0
  }
3278
0
  abortCheckCounter = 0;
3279
0
      }
3280
0
    }
3281
0
    shading->getTriangle(i, &x0, &y0, color0,
3282
0
       &x1, &y1, color1,
3283
0
       &x2, &y2, color2);
3284
0
    gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2,
3285
0
      shading, 0);
3286
0
  }
3287
0
}
3288
3289
void Gfx::gouraudFillTriangle(double x0, double y0, double *color0,
3290
            double x1, double y1, double *color1,
3291
            double x2, double y2, double *color2,
3292
0
            GfxGouraudTriangleShading *shading, int depth) {
3293
0
  double dx0, dy0, dx1, dy1, dx2, dy2;
3294
0
  double x01, y01, x12, y12, x20, y20;
3295
0
  double color01[gfxColorMaxComps];
3296
0
  double color12[gfxColorMaxComps];
3297
0
  double color20[gfxColorMaxComps];
3298
0
  GfxColor c0, c1, c2;
3299
0
  int nComps, i;
3300
3301
  // recursion ends when:
3302
  // (1) color difference is smaller than gouraudColorDelta; or
3303
  // (2) triangles are smaller than 0.5 pixel (note that "device
3304
  //     space" is 72dpi when generating PostScript); or
3305
  // (3) max recursion depth (gouraudMaxDepth) is hit.
3306
0
  nComps = shading->getColorSpace()->getNComps();
3307
0
  shading->getColor(color0, &c0);
3308
0
  shading->getColor(color1, &c1);
3309
0
  shading->getColor(color2, &c2);
3310
0
  for (i = 0; i < nComps; ++i) {
3311
0
    if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta ||
3312
0
  abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) {
3313
0
      break;
3314
0
    }
3315
0
  }
3316
0
  state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0);
3317
0
  state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1);
3318
0
  state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2);
3319
0
  if (i == nComps ||
3320
0
      depth == gouraudMaxDepth ||
3321
0
      (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 &&
3322
0
       fabs(dx1) < 0.5 && fabs(dy1) < 0.5 &&
3323
0
       fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) {
3324
0
    state->setFillColor(&c0);
3325
0
    out->updateFillColor(state);
3326
0
    state->moveTo(x0, y0);
3327
0
    state->lineTo(x1, y1);
3328
0
    state->lineTo(x2, y2);
3329
0
    state->closePath();
3330
0
    out->fill(state);
3331
0
    state->clearPath();
3332
0
  } else {
3333
0
    x01 = 0.5 * (x0 + x1);
3334
0
    y01 = 0.5 * (y0 + y1);
3335
0
    x12 = 0.5 * (x1 + x2);
3336
0
    y12 = 0.5 * (y1 + y2);
3337
0
    x20 = 0.5 * (x2 + x0);
3338
0
    y20 = 0.5 * (y2 + y0);
3339
0
    for (i = 0; i < shading->getNComps(); ++i) {
3340
0
      color01[i] = 0.5 * (color0[i] + color1[i]);
3341
0
      color12[i] = 0.5 * (color1[i] + color2[i]);
3342
0
      color20[i] = 0.5 * (color2[i] + color0[i]);
3343
0
    }
3344
0
    gouraudFillTriangle(x0, y0, color0, x01, y01, color01,
3345
0
      x20, y20, color20, shading, depth + 1);
3346
0
    gouraudFillTriangle(x01, y01, color01, x1, y1, color1,
3347
0
      x12, y12, color12, shading, depth + 1);
3348
0
    gouraudFillTriangle(x01, y01, color01, x12, y12, color12,
3349
0
      x20, y20, color20, shading, depth + 1);
3350
0
    gouraudFillTriangle(x20, y20, color20, x12, y12, color12,
3351
0
      x2, y2, color2, shading, depth + 1);
3352
0
  }
3353
0
}
3354
3355
0
void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
3356
0
  int start, abortCheckCounter, i;
3357
3358
0
  if (shading->getNPatches() > 128) {
3359
0
    start = 3;
3360
0
  } else if (shading->getNPatches() > 64) {
3361
0
    start = 2;
3362
0
  } else if (shading->getNPatches() > 16) {
3363
0
    start = 1;
3364
0
  } else {
3365
0
    start = 0;
3366
0
  }
3367
0
  abortCheckCounter = 0;
3368
0
  for (i = 0; i < shading->getNPatches(); ++i) {
3369
0
    if (abortCheckCbk) {
3370
0
      ++abortCheckCounter;
3371
0
      if (abortCheckCounter > 25) {
3372
0
  if ((*abortCheckCbk)(abortCheckCbkData)) {
3373
0
    break;
3374
0
  }
3375
0
  abortCheckCounter = 0;
3376
0
      }
3377
0
    }
3378
0
    fillPatch(shading->getPatch(i), shading, start);
3379
0
  }
3380
0
}
3381
3382
0
void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) {
3383
0
  GfxPatch patch00, patch01, patch10, patch11;
3384
0
  GfxColor c00, c01, c10, c11;
3385
0
  double xx[4][8], yy[4][8];
3386
0
  double x, y, xMin, yMin, xMax, yMax, xxm, yym;
3387
0
  int nComps, i, j;
3388
0
  GBool stop;
3389
3390
0
  shading->getColor(patch->color[0][0], &c00);
3391
0
  stop = gFalse;
3392
3393
  // stop subdivision at max depth
3394
0
  if (depth == patchMaxDepth) {
3395
0
    stop = gTrue;
3396
0
  }
3397
3398
  // stop subdivision if colors are close enough
3399
0
  if (!stop) {
3400
0
    nComps = shading->getColorSpace()->getNComps();
3401
0
    shading->getColor(patch->color[0][1], &c01);
3402
0
    shading->getColor(patch->color[1][0], &c10);
3403
0
    shading->getColor(patch->color[1][1], &c11);
3404
0
    for (i = 0; i < nComps; ++i) {
3405
0
      if (abs(c00.c[i] - c01.c[i]) > patchColorDelta ||
3406
0
    abs(c01.c[i] - c11.c[i]) > patchColorDelta ||
3407
0
    abs(c11.c[i] - c10.c[i]) > patchColorDelta ||
3408
0
    abs(c10.c[i] - c00.c[i]) > patchColorDelta) {
3409
0
  break;
3410
0
      }
3411
0
    }
3412
0
    if (i == nComps) {
3413
0
      stop = gTrue;
3414
0
    }
3415
0
  }
3416
3417
  // stop subdivision if patch is small enough
3418
0
  if (!stop) {
3419
0
    xMin = yMin = xMax = yMax = 0;
3420
0
    for (j = 0; j < 4; ++j) {
3421
0
      for (i = 0; i < 4; ++i) {
3422
0
  state->transformDelta(patch->x[i][j], patch->y[i][j], &x, &y);
3423
0
  if (i == 0 && j == 0) {
3424
0
    xMin = xMax = x;
3425
0
    yMin = yMax = y;
3426
0
  } else {
3427
0
    if (x < xMin) {
3428
0
      xMin = x;
3429
0
    } else if (x > xMax) {
3430
0
      xMax = x;
3431
0
    }
3432
0
    if (y < yMin) {
3433
0
      yMin = y;
3434
0
    } else if (y > yMax) {
3435
0
      yMax = y;
3436
0
    }
3437
0
  }
3438
0
      }
3439
0
    }
3440
0
    if (xMax - xMin < 1 && yMax - yMin < 1) {
3441
0
      stop = gTrue;
3442
0
    }
3443
0
  }
3444
3445
  // draw the patch
3446
0
  if (stop) {
3447
0
    state->setFillColor(&c00);
3448
0
    out->updateFillColor(state);
3449
0
    state->moveTo(patch->x[0][0], patch->y[0][0]);
3450
0
    state->curveTo(patch->x[0][1], patch->y[0][1],
3451
0
       patch->x[0][2], patch->y[0][2],
3452
0
       patch->x[0][3], patch->y[0][3]);
3453
0
    state->curveTo(patch->x[1][3], patch->y[1][3],
3454
0
       patch->x[2][3], patch->y[2][3],
3455
0
       patch->x[3][3], patch->y[3][3]);
3456
0
    state->curveTo(patch->x[3][2], patch->y[3][2],
3457
0
       patch->x[3][1], patch->y[3][1],
3458
0
       patch->x[3][0], patch->y[3][0]);
3459
0
    state->curveTo(patch->x[2][0], patch->y[2][0],
3460
0
       patch->x[1][0], patch->y[1][0],
3461
0
       patch->x[0][0], patch->y[0][0]);
3462
0
    state->closePath();
3463
0
    out->fill(state);
3464
0
    state->clearPath();
3465
3466
  // subdivide the patch
3467
0
  } else {
3468
0
    for (i = 0; i < 4; ++i) {
3469
0
      xx[i][0] = patch->x[i][0];
3470
0
      yy[i][0] = patch->y[i][0];
3471
0
      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
3472
0
      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
3473
0
      xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
3474
0
      yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
3475
0
      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
3476
0
      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
3477
0
      xx[i][2] = 0.5 * (xx[i][1] + xxm);
3478
0
      yy[i][2] = 0.5 * (yy[i][1] + yym);
3479
0
      xx[i][5] = 0.5 * (xxm + xx[i][6]);
3480
0
      yy[i][5] = 0.5 * (yym + yy[i][6]);
3481
0
      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
3482
0
      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
3483
0
      xx[i][7] = patch->x[i][3];
3484
0
      yy[i][7] = patch->y[i][3];
3485
0
    }
3486
0
    for (i = 0; i < 4; ++i) {
3487
0
      patch00.x[0][i] = xx[0][i];
3488
0
      patch00.y[0][i] = yy[0][i];
3489
0
      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
3490
0
      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
3491
0
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
3492
0
      yym = 0.5 * (yy[1][i] + yy[2][i]);
3493
0
      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
3494
0
      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
3495
0
      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
3496
0
      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
3497
0
      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
3498
0
      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
3499
0
      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
3500
0
      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
3501
0
      patch10.x[0][i] = patch00.x[3][i];
3502
0
      patch10.y[0][i] = patch00.y[3][i];
3503
0
      patch10.x[3][i] = xx[3][i];
3504
0
      patch10.y[3][i] = yy[3][i];
3505
0
    }
3506
0
    for (i = 4; i < 8; ++i) {
3507
0
      patch01.x[0][i-4] = xx[0][i];
3508
0
      patch01.y[0][i-4] = yy[0][i];
3509
0
      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
3510
0
      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
3511
0
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
3512
0
      yym = 0.5 * (yy[1][i] + yy[2][i]);
3513
0
      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
3514
0
      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
3515
0
      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
3516
0
      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
3517
0
      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
3518
0
      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
3519
0
      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
3520
0
      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
3521
0
      patch11.x[0][i-4] = patch01.x[3][i-4];
3522
0
      patch11.y[0][i-4] = patch01.y[3][i-4];
3523
0
      patch11.x[3][i-4] = xx[3][i];
3524
0
      patch11.y[3][i-4] = yy[3][i];
3525
0
    }
3526
0
    for (i = 0; i < shading->getNComps(); ++i) {
3527
0
      patch00.color[0][0][i] = patch->color[0][0][i];
3528
0
      patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
3529
0
              patch->color[0][1][i]);
3530
0
      patch01.color[0][0][i] = patch00.color[0][1][i];
3531
0
      patch01.color[0][1][i] = patch->color[0][1][i];
3532
0
      patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] +
3533
0
              patch->color[1][1][i]);
3534
0
      patch11.color[0][1][i] = patch01.color[1][1][i];
3535
0
      patch11.color[1][1][i] = patch->color[1][1][i];
3536
0
      patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] +
3537
0
              patch->color[1][0][i]);
3538
0
      patch10.color[1][1][i] = patch11.color[1][0][i];
3539
0
      patch10.color[1][0][i] = patch->color[1][0][i];
3540
0
      patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] +
3541
0
              patch->color[0][0][i]);
3542
0
      patch00.color[1][0][i] = patch10.color[0][0][i];
3543
0
      patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] +
3544
0
              patch01.color[1][1][i]);
3545
0
      patch01.color[1][0][i] = patch00.color[1][1][i];
3546
0
      patch11.color[0][0][i] = patch00.color[1][1][i];
3547
0
      patch10.color[0][1][i] = patch00.color[1][1][i];
3548
0
    }
3549
0
    fillPatch(&patch00, shading, depth + 1);
3550
0
    fillPatch(&patch10, shading, depth + 1);
3551
0
    fillPatch(&patch01, shading, depth + 1);
3552
0
    fillPatch(&patch11, shading, depth + 1);
3553
0
  }
3554
0
}
3555
3556
0
void Gfx::doEndPath() {
3557
0
  if (clip != clipNone) {
3558
0
    if (state->isCurPt()) {
3559
0
      state->clip();
3560
0
      if (clip == clipNormal) {
3561
0
  out->clip(state);
3562
0
      } else {
3563
0
  out->eoClip(state);
3564
0
      }
3565
0
    } else {
3566
0
      error(errSyntaxError, getPos(), "Empty path in clip");
3567
0
    }
3568
0
  }
3569
0
  clip = clipNone;
3570
0
  state->clearPath();
3571
0
}
3572
3573
//------------------------------------------------------------------------
3574
// path clipping operators
3575
//------------------------------------------------------------------------
3576
3577
0
void Gfx::opClip(Object args[], int numArgs) {
3578
0
  clip = clipNormal;
3579
0
}
3580
3581
0
void Gfx::opEOClip(Object args[], int numArgs) {
3582
0
  clip = clipEO;
3583
0
}
3584
3585
//------------------------------------------------------------------------
3586
// text object operators
3587
//------------------------------------------------------------------------
3588
3589
0
void Gfx::opBeginText(Object args[], int numArgs) {
3590
0
  state->setTextMat(1, 0, 0, 1, 0, 0);
3591
0
  state->textMoveTo(0, 0);
3592
0
  out->updateTextMat(state);
3593
0
  out->updateTextPos(state);
3594
0
  fontChanged = gTrue;
3595
0
  haveSavedClipPath = gFalse;
3596
0
}
3597
3598
0
void Gfx::opEndText(Object args[], int numArgs) {
3599
0
  if (haveSavedClipPath) {
3600
0
    out->clipToSavedClipPath(state);
3601
0
    haveSavedClipPath = gFalse;
3602
0
  }
3603
3604
0
  out->endTextObject(state);
3605
0
}
3606
3607
//------------------------------------------------------------------------
3608
// text state operators
3609
//------------------------------------------------------------------------
3610
3611
0
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
3612
0
  state->setCharSpace(args[0].getNum());
3613
0
  out->updateCharSpace(state);
3614
0
}
3615
3616
0
void Gfx::opSetFont(Object args[], int numArgs) {
3617
0
  doSetFont(res->lookupFont(args[0].getName()), args[1].getNum());
3618
0
}
3619
3620
0
void Gfx::doSetFont(GfxFont *font, double size) {
3621
0
  if (!font) {
3622
0
    if (!defaultFont) {
3623
0
      defaultFont = GfxFont::makeDefaultFont(xref);
3624
0
    }
3625
0
    font = defaultFont;
3626
0
  }
3627
0
  if (printCommands) {
3628
0
    printf("  font: tag=%s name='%s' %g\n",
3629
0
     font->getTag()->getCString(),
3630
0
     font->getName() ? font->getName()->getCString() : "???",
3631
0
     size);
3632
0
    fflush(stdout);
3633
0
  }
3634
0
  state->setFont(font, size);
3635
0
  fontChanged = gTrue;
3636
0
}
3637
3638
0
void Gfx::opSetTextLeading(Object args[], int numArgs) {
3639
0
  state->setLeading(args[0].getNum());
3640
0
}
3641
3642
0
void Gfx::opSetTextRender(Object args[], int numArgs) {
3643
0
  state->setRender(args[0].getInt());
3644
0
  out->updateRender(state);
3645
0
}
3646
3647
0
void Gfx::opSetTextRise(Object args[], int numArgs) {
3648
0
  state->setRise(args[0].getNum());
3649
0
  out->updateRise(state);
3650
0
}
3651
3652
0
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
3653
0
  state->setWordSpace(args[0].getNum());
3654
0
  out->updateWordSpace(state);
3655
0
}
3656
3657
0
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
3658
0
  state->setHorizScaling(args[0].getNum());
3659
0
  out->updateHorizScaling(state);
3660
0
  fontChanged = gTrue;
3661
0
}
3662
3663
//------------------------------------------------------------------------
3664
// text positioning operators
3665
//------------------------------------------------------------------------
3666
3667
0
void Gfx::opTextMove(Object args[], int numArgs) {
3668
0
  double tx, ty;
3669
3670
0
  tx = state->getLineX() + args[0].getNum();
3671
0
  ty = state->getLineY() + args[1].getNum();
3672
0
  state->textMoveTo(tx, ty);
3673
0
  out->updateTextPos(state);
3674
0
}
3675
3676
0
void Gfx::opTextMoveSet(Object args[], int numArgs) {
3677
0
  double tx, ty;
3678
3679
0
  tx = state->getLineX() + args[0].getNum();
3680
0
  ty = args[1].getNum();
3681
0
  state->setLeading(-ty);
3682
0
  ty += state->getLineY();
3683
0
  state->textMoveTo(tx, ty);
3684
0
  out->updateTextPos(state);
3685
0
}
3686
3687
0
void Gfx::opSetTextMatrix(Object args[], int numArgs) {
3688
0
  state->setTextMat(args[0].getNum(), args[1].getNum(),
3689
0
        args[2].getNum(), args[3].getNum(),
3690
0
        args[4].getNum(), args[5].getNum());
3691
0
  state->textMoveTo(0, 0);
3692
0
  out->updateTextMat(state);
3693
0
  out->updateTextPos(state);
3694
0
  fontChanged = gTrue;
3695
0
}
3696
3697
0
void Gfx::opTextNextLine(Object args[], int numArgs) {
3698
0
  double tx, ty;
3699
3700
0
  tx = state->getLineX();
3701
0
  ty = state->getLineY() - state->getLeading();
3702
0
  state->textMoveTo(tx, ty);
3703
0
  out->updateTextPos(state);
3704
0
}
3705
3706
//------------------------------------------------------------------------
3707
// text string operators
3708
//------------------------------------------------------------------------
3709
3710
0
void Gfx::opShowText(Object args[], int numArgs) {
3711
0
  if (!state->getFont()) {
3712
0
    error(errSyntaxError, getPos(), "No font in show");
3713
0
    return;
3714
0
  }
3715
0
  if (fontChanged) {
3716
0
    out->updateFont(state);
3717
0
    fontChanged = gFalse;
3718
0
  }
3719
0
  if (ocState) {
3720
0
    out->beginStringOp(state);
3721
0
    doShowText(args[0].getString());
3722
0
    out->endStringOp(state);
3723
0
  } else {
3724
0
    doIncCharCount(args[0].getString());
3725
0
  }
3726
0
}
3727
3728
0
void Gfx::opMoveShowText(Object args[], int numArgs) {
3729
0
  double tx, ty;
3730
3731
0
  if (!state->getFont()) {
3732
0
    error(errSyntaxError, getPos(), "No font in move/show");
3733
0
    return;
3734
0
  }
3735
0
  if (fontChanged) {
3736
0
    out->updateFont(state);
3737
0
    fontChanged = gFalse;
3738
0
  }
3739
0
  tx = state->getLineX();
3740
0
  ty = state->getLineY() - state->getLeading();
3741
0
  state->textMoveTo(tx, ty);
3742
0
  out->updateTextPos(state);
3743
0
  if (ocState) {
3744
0
    out->beginStringOp(state);
3745
0
    doShowText(args[0].getString());
3746
0
    out->endStringOp(state);
3747
0
  } else {
3748
0
    doIncCharCount(args[0].getString());
3749
0
  }
3750
0
}
3751
3752
0
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
3753
0
  double tx, ty;
3754
3755
0
  if (!state->getFont()) {
3756
0
    error(errSyntaxError, getPos(), "No font in move/set/show");
3757
0
    return;
3758
0
  }
3759
0
  if (fontChanged) {
3760
0
    out->updateFont(state);
3761
0
    fontChanged = gFalse;
3762
0
  }
3763
0
  state->setWordSpace(args[0].getNum());
3764
0
  state->setCharSpace(args[1].getNum());
3765
0
  tx = state->getLineX();
3766
0
  ty = state->getLineY() - state->getLeading();
3767
0
  state->textMoveTo(tx, ty);
3768
0
  out->updateWordSpace(state);
3769
0
  out->updateCharSpace(state);
3770
0
  out->updateTextPos(state);
3771
0
  if (ocState) {
3772
0
    out->beginStringOp(state);
3773
0
    doShowText(args[2].getString());
3774
0
    out->endStringOp(state);
3775
0
  } else {
3776
0
    doIncCharCount(args[2].getString());
3777
0
  }
3778
0
}
3779
3780
0
void Gfx::opShowSpaceText(Object args[], int numArgs) {
3781
0
  Array *a;
3782
0
  Object obj;
3783
0
  int wMode;
3784
0
  int i;
3785
3786
0
  if (!state->getFont()) {
3787
0
    error(errSyntaxError, getPos(), "No font in show/space");
3788
0
    return;
3789
0
  }
3790
0
  if (fontChanged) {
3791
0
    out->updateFont(state);
3792
0
    fontChanged = gFalse;
3793
0
  }
3794
0
  if (ocState) {
3795
0
    out->beginStringOp(state);
3796
0
    wMode = state->getFont()->getWMode();
3797
0
    a = args[0].getArray();
3798
0
    for (i = 0; i < a->getLength(); ++i) {
3799
0
      a->get(i, &obj);
3800
0
      if (obj.isNum()) {
3801
0
  if (wMode) {
3802
0
    state->textShift(0, -obj.getNum() * 0.001 *
3803
0
         state->getFontSize());
3804
0
  } else {
3805
0
    state->textShift(-obj.getNum() * 0.001 *
3806
0
         state->getFontSize() *
3807
0
         state->getHorizScaling(), 0);
3808
0
  }
3809
0
  out->updateTextShift(state, obj.getNum());
3810
0
      } else if (obj.isString()) {
3811
0
  doShowText(obj.getString());
3812
0
      } else {
3813
0
  error(errSyntaxError, getPos(),
3814
0
        "Element of show/space array must be number or string");
3815
0
      }
3816
0
      obj.free();
3817
0
    }
3818
0
    out->endStringOp(state);
3819
0
  } else {
3820
0
    a = args[0].getArray();
3821
0
    for (i = 0; i < a->getLength(); ++i) {
3822
0
      a->get(i, &obj);
3823
0
      if (obj.isString()) {
3824
0
  doIncCharCount(obj.getString());
3825
0
      }
3826
0
      obj.free();
3827
0
    }
3828
0
  }
3829
0
}
3830
3831
0
void Gfx::doShowText(GString *s) {
3832
0
  GfxFont *font = state->getFont();
3833
0
  int wMode = font->getWMode();
3834
3835
0
  if (globalParams->isDroppedFont(font->getName()
3836
0
            ? font->getName()->getCString() : "")) {
3837
0
    doIncCharCount(s);
3838
0
    return;
3839
0
  }
3840
3841
0
  if (out->useDrawChar()) {
3842
0
    out->beginString(state, s);
3843
0
  }
3844
3845
  // figure out the drawing mode
3846
  // note: clipping, pattern fill, and pattern stroke are all handled
3847
  //       by saving the path and then performing the appropriate
3848
  //       actions in opEndText()
3849
0
  GBool doFill = gFalse;
3850
0
  GBool doStroke = gFalse;
3851
0
  GBool doMakePath = gFalse;
3852
0
  int render = state->getRender() & 7;
3853
0
  switch (render & 7) {
3854
0
  case 0:     // fill
3855
0
    if (state->getFillColorSpace()->getMode() == csPattern) {
3856
0
      doMakePath = gTrue;
3857
0
    } else {
3858
0
      doFill = gTrue;
3859
0
    }
3860
0
    break;
3861
0
  case 1:     // stroke
3862
0
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
3863
0
      doMakePath = gTrue;
3864
0
    } else {
3865
0
      doStroke = gTrue;
3866
0
    }
3867
0
    break;
3868
0
  case 2:     // fill + stroke
3869
0
    if (state->getFillColorSpace()->getMode() == csPattern ||
3870
0
  state->getStrokeColorSpace()->getMode() == csPattern) {
3871
0
      doMakePath = gTrue;
3872
0
    } else {
3873
0
      doFill = gTrue;
3874
0
      doStroke = gTrue;
3875
0
    }
3876
0
    break;
3877
0
  case 3:     // invisible
3878
    // nothing
3879
0
    break;
3880
0
  case 4:     // fill + clip
3881
0
  case 5:     // stroke + clip
3882
0
  case 6:     // fill + stroke + clip
3883
0
  case 7:     // clip
3884
0
    doMakePath = gTrue;
3885
0
    break;
3886
0
  }
3887
3888
0
  double riseX, riseY;
3889
0
  state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
3890
0
  double xMin = state->getCurX() + riseX;
3891
0
  double yMin = state->getCurY() + riseY;
3892
3893
  // handle a Type 3 char
3894
0
  if (font->getType() == fontType3 && out->interpretType3Chars()) {
3895
0
    double *mat = state->getCTM();
3896
0
    double oldCTM[6];
3897
0
    for (int i = 0; i < 6; ++i) {
3898
0
      oldCTM[i] = mat[i];
3899
0
    }
3900
0
    mat = state->getTextMat();
3901
0
    double newCTM[6];
3902
0
    newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
3903
0
    newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
3904
0
    newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
3905
0
    newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
3906
0
    mat = font->getFontMatrix();
3907
0
    newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
3908
0
    newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
3909
0
    newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
3910
0
    newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
3911
0
    newCTM[0] *= state->getFontSize();
3912
0
    newCTM[1] *= state->getFontSize();
3913
0
    newCTM[2] *= state->getFontSize();
3914
0
    newCTM[3] *= state->getFontSize();
3915
0
    newCTM[0] *= state->getHorizScaling();
3916
0
    newCTM[1] *= state->getHorizScaling();
3917
0
    double curX = state->getCurX();
3918
0
    double curY = state->getCurY();
3919
0
    Parser *oldParser = parser;
3920
0
    char *p = s->getCString();
3921
0
    int len = s->getLength();
3922
0
    while (len > 0) {
3923
0
      CharCode code;
3924
0
      Unicode u[8];
3925
0
      int uLen;
3926
0
      double dx, dy, originX, originY;
3927
0
      int n = font->getNextChar(p, len, &code,
3928
0
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3929
0
        &dx, &dy, &originX, &originY);
3930
0
      dx = dx * state->getFontSize() + state->getCharSpace();
3931
0
      if (n == 1 && *p == ' ') {
3932
0
  dx += state->getWordSpace();
3933
0
      }
3934
0
      dx *= state->getHorizScaling();
3935
0
      dy *= state->getFontSize();
3936
0
      double tdx, tdy, ddx, ddy, x, y;
3937
0
      state->textTransformDelta(dx, dy, &tdx, &tdy);
3938
0
      state->transform(curX + riseX, curY + riseY, &x, &y);
3939
0
      GfxState *savedState = saveStateStack();
3940
0
      state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
3941
      //~ the CTM concat values here are wrong (but never used)
3942
0
      out->updateCTM(state, 1, 0, 0, 1, 0, 0);
3943
0
      state->transformDelta(dx, dy, &ddx, &ddy);
3944
#if 0
3945
      // The PDF spec says: "the graphics state shall be inherited
3946
      // from the environment of the text-showing operator that caused
3947
      // the [Type 3] glyph description to be invoked".  However,
3948
      // Acrobat apparently copies the fill color to the stroke color.
3949
      // It looks like Ghostscript does the same.  (This is only
3950
      // relevant for uncached Type 3 glyphs.)  Uncomment this block
3951
      // to make Xpdf mimic Adobe (in violation of the PDF spec).
3952
      state->setStrokeColorSpace(state->getFillColorSpace()->copy());
3953
      out->updateStrokeColorSpace(state);
3954
      state->setStrokeColor(state->getFillColor());
3955
      out->updateStrokeColor(state);
3956
#endif
3957
0
      if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
3958
0
             code, u, uLen)) {
3959
0
  Object charProcRef, charProc;
3960
0
  ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef);
3961
0
  charProcRef.fetch(xref, &charProc);
3962
0
  Dict *resDict = ((Gfx8BitFont *)font)->getResources();
3963
0
  if (resDict) {
3964
0
    pushResources(resDict);
3965
0
  }
3966
0
  if (charProc.isStream()) {
3967
0
    display(&charProcRef, gFalse);
3968
0
  } else {
3969
0
    error(errSyntaxError, getPos(),
3970
0
    "Missing or bad Type3 CharProc entry");
3971
0
  }
3972
0
  out->endType3Char(state);
3973
0
  if (resDict) {
3974
0
    popResources();
3975
0
  }
3976
0
  charProc.free();
3977
0
  charProcRef.free();
3978
0
      }
3979
0
      restoreStateStack(savedState);
3980
0
      curX += tdx;
3981
0
      curY += tdy;
3982
0
      state->moveTo(curX, curY);
3983
0
      p += n;
3984
0
      len -= n;
3985
0
    }
3986
0
    parser = oldParser;
3987
3988
0
  } else if (out->useDrawChar()) {
3989
0
    char *p = s->getCString();
3990
0
    int len = s->getLength();
3991
0
    while (len > 0) {
3992
0
      CharCode code;
3993
0
      Unicode u[8];
3994
0
      int uLen;
3995
0
      double dx, dy, originX, originY;
3996
0
      int n = font->getNextChar(p, len, &code,
3997
0
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3998
0
        &dx, &dy, &originX, &originY);
3999
0
      if (wMode) {
4000
0
  dx *= state->getFontSize();
4001
0
  dy = dy * state->getFontSize() + state->getCharSpace();
4002
0
  if (n == 1 && *p == ' ') {
4003
0
    dy += state->getWordSpace();
4004
0
  }
4005
0
      } else {
4006
0
  dx = dx * state->getFontSize() + state->getCharSpace();
4007
0
  if (n == 1 && *p == ' ') {
4008
0
    dx += state->getWordSpace();
4009
0
  }
4010
0
  dx *= state->getHorizScaling();
4011
0
  dy *= state->getFontSize();
4012
0
      }
4013
0
      double tdx, tdy, tOriginX, tOriginY;
4014
0
      state->textTransformDelta(dx, dy, &tdx, &tdy);
4015
0
      originX *= state->getFontSize();
4016
0
      originY *= state->getFontSize();
4017
0
      state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
4018
0
      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
4019
0
        tdx, tdy, tOriginX, tOriginY, code, n, u, uLen,
4020
0
        doFill, doStroke, doMakePath);
4021
0
      state->shift(tdx, tdy);
4022
0
      p += n;
4023
0
      len -= n;
4024
0
    }
4025
4026
0
  } else {
4027
0
    double dx = 0, dy = 0;
4028
0
    char *p = s->getCString();
4029
0
    int len = s->getLength();
4030
0
    int nChars = 0, nSpaces = 0;
4031
0
    while (len > 0) {
4032
0
      CharCode code;
4033
0
      Unicode u[8];
4034
0
      int uLen;
4035
0
      double dx2, dy2, originX, originY;
4036
0
      int n = font->getNextChar(p, len, &code,
4037
0
        u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
4038
0
        &dx2, &dy2, &originX, &originY);
4039
0
      dx += dx2;
4040
0
      dy += dy2;
4041
0
      if (n == 1 && *p == ' ') {
4042
0
  ++nSpaces;
4043
0
      }
4044
0
      ++nChars;
4045
0
      p += n;
4046
0
      len -= n;
4047
0
    }
4048
0
    if (wMode) {
4049
0
      dx *= state->getFontSize();
4050
0
      dy = dy * state->getFontSize()
4051
0
     + nChars * state->getCharSpace()
4052
0
     + nSpaces * state->getWordSpace();
4053
0
    } else {
4054
0
      dx = dx * state->getFontSize()
4055
0
     + nChars * state->getCharSpace()
4056
0
     + nSpaces * state->getWordSpace();
4057
0
      dx *= state->getHorizScaling();
4058
0
      dy *= state->getFontSize();
4059
0
    }
4060
0
    double tdx, tdy;
4061
0
    state->textTransformDelta(dx, dy, &tdx, &tdy);
4062
0
    out->drawString(state, s, doFill, doStroke, doMakePath);
4063
0
    state->shift(tdx, tdy);
4064
0
  }
4065
4066
0
  if (out->useDrawChar()) {
4067
0
    out->endString(state);
4068
0
  }
4069
4070
0
  if (doMakePath) {
4071
    // compute the clipping bbox for the saved text path -- assume
4072
    // that the text bounding box does not extend past the baseline in
4073
    // any direction by more than twice the font size
4074
0
    double xMax = state->getCurX() + riseX;
4075
0
    double yMax = state->getCurY() + riseY;
4076
0
    double t;
4077
0
    if (xMin > xMax) {
4078
0
      t = xMin; xMin = xMax; xMax = t;
4079
0
    }
4080
0
    if (yMin > yMax) {
4081
0
      t = yMin; yMin = yMax; yMax = t;
4082
0
    }
4083
0
    double dx1, dy1, dx2, dy2;
4084
0
    state->textTransformDelta(0, state->getFontSize(), &dx1, &dy1);
4085
0
    state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2);
4086
0
    dx1 = fabs(dx1);
4087
0
    dx2 = fabs(dx2);
4088
0
    if (dx2 > dx1) {
4089
0
      dx1 = dx2;
4090
0
    }
4091
0
    dy1 = fabs(dy1);
4092
0
    dy2 = fabs(dy2);
4093
0
    if (dy2 > dy1) {
4094
0
      dy1 = dy2;
4095
0
    }
4096
0
    xMin -= 2 * dx1;
4097
0
    yMin -= 2 * dy1;
4098
0
    xMax += 2 * dx1;
4099
0
    yMax += 2 * dy1;
4100
4101
    //--- fill
4102
0
    if ((render & 3) == 0 || (render & 3) == 2) {
4103
0
      if (state->getFillColorSpace()->getMode() == csPattern) {
4104
0
  saveState();
4105
0
  state->clipToRect(xMin, yMin, xMax, yMax);
4106
0
  out->clipToTextPath(state);
4107
0
  doPatternText(gFalse);
4108
0
  restoreState();
4109
0
      } else {
4110
0
  out->fillTextPath(state);
4111
0
      }
4112
0
    }
4113
4114
    //--- stroke
4115
0
    if ((render & 3) == 1 || (render & 3) == 2) {
4116
0
      if (state->getStrokeColorSpace()->getMode() == csPattern) {
4117
0
  saveState();
4118
0
  state->clipToRect(xMin, yMin, xMax, yMax);
4119
0
  out->clipToTextStrokePath(state);
4120
0
  doPatternText(gTrue);
4121
0
  restoreState();
4122
0
      } else {
4123
0
  out->strokeTextPath(state);
4124
0
      }
4125
0
    }
4126
4127
    //--- clip
4128
0
    if (render & 4) {
4129
0
      out->addTextPathToSavedClipPath(state);
4130
0
      haveSavedClipPath = gTrue;
4131
0
    } else {
4132
0
      out->clearTextPath(state);
4133
0
    }
4134
0
  }
4135
4136
0
  opCounter += 10 * s->getLength();
4137
0
}
4138
4139
// NB: this is only called when ocState is false.
4140
0
void Gfx::doIncCharCount(GString *s) {
4141
0
  if (out->needCharCount()) {
4142
0
    out->incCharCount(s->getLength());
4143
0
  }
4144
0
}
4145
4146
//------------------------------------------------------------------------
4147
// XObject operators
4148
//------------------------------------------------------------------------
4149
4150
0
void Gfx::opXObject(Object args[], int numArgs) {
4151
0
  char *name;
4152
0
  Object xObj, refObj, obj2, obj3;
4153
0
  GBool ocSaved, oc;
4154
0
#if OPI_SUPPORT
4155
0
  Object opiDict;
4156
0
#endif
4157
4158
0
  if (!ocState && !out->needCharCount()) {
4159
0
    return;
4160
0
  }
4161
0
  name = args[0].getName();
4162
  // NB: we get both the reference and the object here, to make sure
4163
  // they refer to the same object.  There's a problematic corner
4164
  // case: if the resource dict contains an entry for [name] with a
4165
  // reference to a nonexistent object ("/name 99999 0 R", where
4166
  // object 99999 doesn't exist), and a parent resource dict contains
4167
  // a valid entry with the same name, then lookupXObjectNF() will
4168
  // return "99999 0 R", but lookupXObject() will return the valid
4169
  // entry.  This causes problems for doImage() and doForm().
4170
0
  if (!res->lookupXObjectNF(name, &refObj)) {
4171
0
    return;
4172
0
  }
4173
0
  if (!refObj.fetch(xref, &xObj)) {
4174
0
    refObj.free();
4175
0
    return;
4176
0
  }
4177
0
  if (!xObj.isStream()) {
4178
0
    error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name);
4179
0
    xObj.free();
4180
0
    refObj.free();
4181
0
    return;
4182
0
  }
4183
4184
  // check for optional content key
4185
0
  ocSaved = ocState;
4186
0
  xObj.streamGetDict()->lookupNF("OC", &obj2);
4187
0
  if (doc->getOptionalContent()->evalOCObject(&obj2, &oc)) {
4188
0
    ocState &= oc;
4189
0
  }
4190
0
  obj2.free();
4191
4192
0
#if USE_EXCEPTIONS
4193
0
  try {
4194
0
#endif
4195
0
#if OPI_SUPPORT
4196
0
    xObj.streamGetDict()->lookup("OPI", &opiDict);
4197
0
    if (opiDict.isDict()) {
4198
0
      out->opiBegin(state, opiDict.getDict());
4199
0
    }
4200
0
#endif
4201
0
    xObj.streamGetDict()->lookup("Subtype", &obj2);
4202
0
    if (obj2.isName("Image")) {
4203
0
      if (out->needNonText()) {
4204
0
  doImage(&refObj, xObj.getStream(), gFalse);
4205
0
      }
4206
0
    } else if (obj2.isName("Form")) {
4207
0
      if (out->useDrawForm() && refObj.isRef()) {
4208
0
  if (ocState) {
4209
0
    out->drawForm(refObj.getRef());
4210
0
  }
4211
0
      } else {
4212
0
  doForm(&refObj, &xObj);
4213
0
      }
4214
0
    } else if (obj2.isName("PS")) {
4215
0
      if (ocState) {
4216
0
  xObj.streamGetDict()->lookup("Level1", &obj3);
4217
0
  out->psXObject(xObj.getStream(),
4218
0
           obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
4219
0
  obj3.free();
4220
0
      }
4221
0
    } else if (obj2.isName()) {
4222
0
      error(errSyntaxError, getPos(),
4223
0
      "Unknown XObject subtype '{0:s}'", obj2.getName());
4224
0
    } else {
4225
0
      error(errSyntaxError, getPos(),
4226
0
      "XObject subtype is missing or wrong type");
4227
0
    }
4228
0
    obj2.free();
4229
0
#if OPI_SUPPORT
4230
0
    if (opiDict.isDict()) {
4231
0
      out->opiEnd(state, opiDict.getDict());
4232
0
    }
4233
0
    opiDict.free();
4234
0
#endif
4235
0
#if USE_EXCEPTIONS
4236
0
  } catch (GMemException e) {
4237
0
    xObj.free();
4238
0
    refObj.free();
4239
0
    throw;
4240
0
  }
4241
0
#endif
4242
4243
0
  ocState = ocSaved;
4244
4245
0
  xObj.free();
4246
0
  refObj.free();
4247
0
}
4248
4249
0
GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
4250
0
  Dict *dict, *maskDict;
4251
0
  int width, height;
4252
0
  int bits, maskBits;
4253
0
  StreamColorSpaceMode csMode;
4254
0
  GBool mask, invert;
4255
0
  GfxColorSpace *colorSpace, *maskColorSpace;
4256
0
  GfxImageColorMap *colorMap, *maskColorMap;
4257
0
  Object maskObj, smaskObj, maskRef;
4258
0
  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte;
4259
0
  int maskColors[2*gfxColorMaxComps];
4260
0
  int maskWidth, maskHeight;
4261
0
  GBool maskInvert;
4262
0
  Stream *maskStr;
4263
0
  double matte[gfxColorMaxComps];
4264
0
  GBool interpolate;
4265
0
  GfxRenderingIntent riSaved;
4266
0
  Object obj1, obj2;
4267
0
  int i, n;
4268
4269
  // check for optional content
4270
0
  if (!ocState && !inlineImg) {
4271
0
    return gTrue;
4272
0
  }
4273
4274
  // images can have arbitrarily high compression ratios, but the
4275
  // data size is inherently limited
4276
0
  str->disableDecompressionBombChecking();
4277
4278
  // get info from the stream
4279
0
  bits = 0;
4280
0
  csMode = streamCSNone;
4281
0
  str->getImageParams(&bits, &csMode);
4282
4283
  // get stream dict
4284
0
  dict = str->getDict();
4285
4286
  // save the current rendering intent
4287
0
  riSaved = state->getRenderingIntent();
4288
4289
  // get size
4290
0
  dict->lookup("Width", &obj1);
4291
0
  if (obj1.isNull()) {
4292
0
    obj1.free();
4293
0
    dict->lookup("W", &obj1);
4294
0
  }
4295
0
  if (obj1.isInt()) {
4296
0
    width = obj1.getInt();
4297
0
  } else if (obj1.isReal()) {
4298
0
    error(errSyntaxWarning, getPos(), "Non-integer image width");
4299
0
    width = (int)obj1.getReal();
4300
0
  } else {
4301
0
    goto err2;
4302
0
  }
4303
0
  obj1.free();
4304
0
  if (width <= 0) {
4305
0
    goto err1;
4306
0
  }
4307
0
  dict->lookup("Height", &obj1);
4308
0
  if (obj1.isNull()) {
4309
0
    obj1.free();
4310
0
    dict->lookup("H", &obj1);
4311
0
  }
4312
0
  if (obj1.isInt()) {
4313
0
    height = obj1.getInt();
4314
0
  } else if (obj1.isReal()) {
4315
0
    error(errSyntaxWarning, getPos(), "Non-integer image height");
4316
0
    height = (int)obj1.getReal();
4317
0
  } else {
4318
0
    goto err2;
4319
0
  }
4320
0
  obj1.free();
4321
0
  if (height <= 0) {
4322
0
    goto err1;
4323
0
  }
4324
4325
  // image or mask?
4326
0
  dict->lookup("ImageMask", &obj1);
4327
0
  if (obj1.isNull()) {
4328
0
    obj1.free();
4329
0
    dict->lookup("IM", &obj1);
4330
0
  }
4331
0
  mask = gFalse;
4332
0
  if (obj1.isBool())
4333
0
    mask = obj1.getBool();
4334
0
  else if (!obj1.isNull())
4335
0
    goto err2;
4336
0
  obj1.free();
4337
4338
  // bit depth
4339
0
  if (bits == 0) {
4340
0
    dict->lookup("BitsPerComponent", &obj1);
4341
0
    if (obj1.isNull()) {
4342
0
      obj1.free();
4343
0
      dict->lookup("BPC", &obj1);
4344
0
    }
4345
0
    if (obj1.isNum()) {
4346
0
      if (obj1.isInt()) {
4347
0
  bits = obj1.getInt();
4348
0
      } else if (obj1.isReal()) {
4349
0
  error(errSyntaxWarning, getPos(),
4350
0
        "Non-integer image bits per component");
4351
0
  bits = (int)obj1.getReal();
4352
0
      }
4353
0
      if (bits < 1 || bits > 16) {
4354
0
  goto err2;
4355
0
      }
4356
0
    } else if (mask) {
4357
0
      bits = 1;
4358
0
    } else {
4359
0
      goto err2;
4360
0
    }
4361
0
    obj1.free();
4362
0
  }
4363
4364
  // interpolate flag
4365
0
  dict->lookup("Interpolate", &obj1);
4366
0
  if (obj1.isNull()) {
4367
0
    obj1.free();
4368
0
    dict->lookup("I", &obj1);
4369
0
  }
4370
0
  interpolate = obj1.isBool() && obj1.getBool();
4371
0
  obj1.free();
4372
4373
  // display a mask
4374
0
  if (mask) {
4375
4376
    // check for inverted mask
4377
0
    if (bits != 1)
4378
0
      goto err1;
4379
0
    invert = gFalse;
4380
0
    dict->lookup("Decode", &obj1);
4381
0
    if (obj1.isNull()) {
4382
0
      obj1.free();
4383
0
      dict->lookup("D", &obj1);
4384
0
    }
4385
0
    if (obj1.isArray()) {
4386
0
      obj1.arrayGet(0, &obj2);
4387
0
      invert = obj2.isNum() && obj2.getNum() == 1;
4388
0
      obj2.free();
4389
0
    } else if (!obj1.isNull()) {
4390
0
      goto err2;
4391
0
    }
4392
0
    obj1.free();
4393
4394
    // if drawing is disabled, skip over inline image data
4395
0
    if (!ocState) {
4396
0
      str->disableDecompressionBombChecking();
4397
0
      str->reset();
4398
0
      n = height * ((width + 7) / 8);
4399
0
      for (i = 0; i < n; ++i) {
4400
0
  str->getChar();
4401
0
      }
4402
0
      str->close();
4403
4404
    // draw it
4405
0
    } else {
4406
0
      if (state->getFillColorSpace()->getMode() == csPattern) {
4407
0
  doPatternImageMask(ref, str, width, height, invert, inlineImg,
4408
0
         interpolate);
4409
0
      } else {
4410
0
  out->drawImageMask(state, ref, str, width, height, invert, inlineImg,
4411
0
         interpolate);
4412
0
      }
4413
0
    }
4414
4415
0
  } else {
4416
4417
    // rendering intent
4418
0
    if (dict->lookup("Intent", &obj1)->isName()) {
4419
0
      opSetRenderingIntent(&obj1, 1);
4420
0
    }
4421
0
    obj1.free();
4422
4423
    // get color space and color map
4424
0
    dict->lookup("ColorSpace", &obj1);
4425
0
    if (obj1.isNull()) {
4426
0
      obj1.free();
4427
0
      dict->lookup("CS", &obj1);
4428
0
    }
4429
0
    if (obj1.isName()) {
4430
0
      res->lookupColorSpace(obj1.getName(), &obj2);
4431
0
      if (!obj2.isNull()) {
4432
0
  obj1.free();
4433
0
  obj1 = obj2;
4434
0
      } else {
4435
0
  obj2.free();
4436
0
      }
4437
0
    }
4438
0
    if (!obj1.isNull()) {
4439
0
      colorSpace = GfxColorSpace::parse(&obj1
4440
0
          );
4441
0
    } else if (csMode == streamCSDeviceGray) {
4442
0
      colorSpace = GfxColorSpace::create(csDeviceGray);
4443
0
    } else if (csMode == streamCSDeviceRGB) {
4444
0
      colorSpace = GfxColorSpace::create(csDeviceRGB);
4445
0
    } else if (csMode == streamCSDeviceCMYK) {
4446
0
      colorSpace = GfxColorSpace::create(csDeviceCMYK);
4447
0
    } else {
4448
0
      colorSpace = NULL;
4449
0
    }
4450
0
    obj1.free();
4451
0
    if (!colorSpace) {
4452
0
      goto err1;
4453
0
    }
4454
0
    if (colorSpace->getMode() == csPattern) {
4455
0
      error(errSyntaxError, getPos(), "Image with a Pattern color space");
4456
0
      delete colorSpace;
4457
0
      goto err1;
4458
0
    }
4459
0
    dict->lookup("Decode", &obj1);
4460
0
    if (obj1.isNull()) {
4461
0
      obj1.free();
4462
0
      dict->lookup("D", &obj1);
4463
0
    }
4464
0
    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
4465
0
    obj1.free();
4466
0
    if (!colorMap->isOk()) {
4467
0
      delete colorMap;
4468
0
      goto err1;
4469
0
    }
4470
4471
    // get the mask
4472
0
    haveColorKeyMask = haveExplicitMask = haveSoftMask = haveMatte = gFalse;
4473
0
    maskStr = NULL; // make gcc happy
4474
0
    maskWidth = maskHeight = 0; // make gcc happy
4475
0
    maskInvert = gFalse; // make gcc happy
4476
0
    maskColorMap = NULL; // make gcc happy
4477
0
    dict->lookup("Mask", &maskObj);
4478
0
    dict->lookup("SMask", &smaskObj);
4479
0
    if (smaskObj.isStream()) {
4480
      // soft mask
4481
0
      if (inlineImg) {
4482
0
  delete colorMap;
4483
0
  maskObj.free();
4484
0
  smaskObj.free();
4485
0
  goto err1;
4486
0
      }
4487
0
      maskStr = smaskObj.getStream();
4488
0
      maskStr->disableDecompressionBombChecking();
4489
0
      maskDict = smaskObj.streamGetDict();
4490
0
      maskDict->lookup("Width", &obj1);
4491
0
      if (obj1.isNull()) {
4492
0
  obj1.free();
4493
0
  maskDict->lookup("W", &obj1);
4494
0
      }
4495
0
      if (obj1.isInt()) {
4496
0
  maskWidth = obj1.getInt();
4497
0
      } else if (obj1.isReal()) {
4498
0
  error(errSyntaxWarning, getPos(), "Non-integer image mask width");
4499
0
  maskWidth = (int)obj1.getReal();
4500
0
      } else {
4501
0
  delete colorMap;
4502
0
  maskObj.free();
4503
0
  smaskObj.free();
4504
0
  goto err2;
4505
0
      }
4506
0
      obj1.free();
4507
0
      maskDict->lookup("Height", &obj1);
4508
0
      if (obj1.isNull()) {
4509
0
  obj1.free();
4510
0
  maskDict->lookup("H", &obj1);
4511
0
      }
4512
0
      if (obj1.isInt()) {
4513
0
  maskHeight = obj1.getInt();
4514
0
      } else if (obj1.isReal()) {
4515
0
  error(errSyntaxWarning, getPos(), "Non-integer image mask height");
4516
0
  maskHeight = (int)obj1.getReal();
4517
0
      } else {
4518
0
  delete colorMap;
4519
0
  maskObj.free();
4520
0
  smaskObj.free();
4521
0
  goto err2;
4522
0
      }
4523
0
      obj1.free();
4524
0
      if (maskWidth <= 0 || maskHeight <= 0) {
4525
0
  delete colorMap;
4526
0
  maskObj.free();
4527
0
  smaskObj.free();
4528
0
  goto err1;
4529
0
      }
4530
0
      maskDict->lookup("BitsPerComponent", &obj1);
4531
0
      if (obj1.isNull()) {
4532
0
  obj1.free();
4533
0
  maskDict->lookup("BPC", &obj1);
4534
0
      }
4535
0
      if (obj1.isInt()) {
4536
0
  maskBits = obj1.getInt();
4537
0
      } else if (obj1.isReal()) {
4538
0
  error(errSyntaxWarning, getPos(),
4539
0
        "Non-integer image mask bits per component");
4540
0
  maskBits = (int)obj1.getReal();
4541
0
      } else {
4542
0
  delete colorMap;
4543
0
  maskObj.free();
4544
0
  smaskObj.free();
4545
0
  goto err2;
4546
0
      }
4547
0
      obj1.free();
4548
0
      if (maskBits < 1 || maskBits > 16) {
4549
0
  delete colorMap;
4550
0
  maskObj.free();
4551
0
  smaskObj.free();
4552
0
  goto err1;
4553
0
      }
4554
0
      maskDict->lookup("ColorSpace", &obj1);
4555
0
      if (obj1.isNull()) {
4556
0
  obj1.free();
4557
0
  maskDict->lookup("CS", &obj1);
4558
0
      }
4559
0
      if (obj1.isName()) {
4560
0
  res->lookupColorSpace(obj1.getName(), &obj2);
4561
0
  if (!obj2.isNull()) {
4562
0
    obj1.free();
4563
0
    obj1 = obj2;
4564
0
  } else {
4565
0
    obj2.free();
4566
0
  }
4567
0
      }
4568
0
      if (!obj1.isName("DeviceGray")) {
4569
0
  delete colorMap;
4570
0
  maskObj.free();
4571
0
  smaskObj.free();
4572
0
  goto err2;
4573
0
      }
4574
0
      maskColorSpace = new GfxDeviceGrayColorSpace();
4575
0
      obj1.free();
4576
0
      maskDict->lookup("Decode", &obj1);
4577
0
      if (obj1.isNull()) {
4578
0
  obj1.free();
4579
0
  maskDict->lookup("D", &obj1);
4580
0
      }
4581
0
      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
4582
0
      obj1.free();
4583
0
      if (!maskColorMap->isOk()) {
4584
0
  delete maskColorMap;
4585
0
  delete colorMap;
4586
0
  maskObj.free();
4587
0
  smaskObj.free();
4588
0
  goto err1;
4589
0
      }
4590
0
      if (maskDict->lookup("Matte", &obj1)->isArray()) {
4591
0
  if (obj1.arrayGetLength() == colorSpace->getNComps()) {
4592
0
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
4593
0
      if (obj1.arrayGet(i, &obj2)->isNum()) {
4594
0
        matte[i] = obj2.getNum();
4595
0
      } else {
4596
0
        error(errSyntaxError, getPos(),
4597
0
        "Invalid Matte entry in soft mask");
4598
0
        matte[i] = 0;
4599
0
      }
4600
0
      obj2.free();
4601
0
    }
4602
0
    haveMatte = gTrue;
4603
0
  } else {
4604
0
    error(errSyntaxError, getPos(), "Invalid Matte entry in soft mask");
4605
0
  }
4606
0
      }
4607
0
      obj1.free();
4608
0
      haveSoftMask = gTrue;
4609
0
    } else if (maskObj.isArray()) {
4610
      // color key mask
4611
0
      haveColorKeyMask = gTrue;
4612
0
      for (i = 0;
4613
0
     i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps;
4614
0
     i += 2) {
4615
0
  maskObj.arrayGet(i, &obj1);
4616
0
  if (!obj1.isInt()) {
4617
0
    obj1.free();
4618
0
    haveColorKeyMask = gFalse;
4619
0
    break;
4620
0
  }
4621
0
  maskColors[i] = obj1.getInt();
4622
0
  obj1.free();
4623
0
  if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) {
4624
0
    haveColorKeyMask = gFalse;
4625
0
    break;
4626
0
  }
4627
0
  maskObj.arrayGet(i+1, &obj1);
4628
0
  if (!obj1.isInt()) {
4629
0
    obj1.free();
4630
0
    haveColorKeyMask = gFalse;
4631
0
    break;
4632
0
  }
4633
0
  maskColors[i+1] = obj1.getInt();
4634
0
  obj1.free();
4635
0
  if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) ||
4636
0
      maskColors[i] > maskColors[i+1]) {
4637
0
    haveColorKeyMask = gFalse;
4638
0
    break;
4639
0
  }
4640
0
      }
4641
0
    } else if (maskObj.isStream()) {
4642
      // explicit mask
4643
0
      haveExplicitMask = gTrue;
4644
0
      if (inlineImg) {
4645
0
  delete colorMap;
4646
0
  maskObj.free();
4647
0
  smaskObj.free();
4648
0
  goto err1;
4649
0
      }
4650
0
      maskStr = maskObj.getStream();
4651
0
      maskStr->disableDecompressionBombChecking();
4652
0
      maskDict = maskObj.streamGetDict();
4653
0
      maskDict->lookup("Width", &obj1);
4654
0
      if (obj1.isNull()) {
4655
0
  obj1.free();
4656
0
  maskDict->lookup("W", &obj1);
4657
0
      }
4658
0
      if (obj1.isInt()) {
4659
0
  maskWidth = obj1.getInt();
4660
0
      } else if (obj1.isReal()) {
4661
0
  error(errSyntaxWarning, getPos(), "Non-integer image mask width");
4662
0
  maskWidth = (int)obj1.getReal();
4663
0
      } else {
4664
0
  haveExplicitMask = gFalse;
4665
0
      }
4666
0
      obj1.free();
4667
0
      maskDict->lookup("Height", &obj1);
4668
0
      if (obj1.isNull()) {
4669
0
  obj1.free();
4670
0
  maskDict->lookup("H", &obj1);
4671
0
      }
4672
0
      if (obj1.isInt()) {
4673
0
  maskHeight = obj1.getInt();
4674
0
      } else if (obj1.isReal()) {
4675
0
  error(errSyntaxWarning, getPos(), "Non-integer image mask height");
4676
0
  maskHeight = (int)obj1.getReal();
4677
0
      } else {
4678
0
  haveExplicitMask = gFalse;
4679
0
      }
4680
0
      obj1.free();
4681
0
      if (maskWidth <= 0 || maskHeight <= 0) {
4682
0
  haveExplicitMask = gFalse;
4683
0
      }
4684
0
      maskDict->lookup("ImageMask", &obj1);
4685
0
      if (obj1.isNull()) {
4686
0
  obj1.free();
4687
0
  maskDict->lookup("IM", &obj1);
4688
0
      }
4689
0
      if (!obj1.isBool() || !obj1.getBool()) {
4690
0
  haveExplicitMask = gFalse;
4691
0
      }
4692
0
      obj1.free();
4693
0
      maskInvert = gFalse;
4694
0
      maskDict->lookup("Decode", &obj1);
4695
0
      if (obj1.isNull()) {
4696
0
  obj1.free();
4697
0
  maskDict->lookup("D", &obj1);
4698
0
      }
4699
0
      if (obj1.isArray()) {
4700
0
  obj1.arrayGet(0, &obj2);
4701
0
  maskInvert = obj2.isNum() && obj2.getNum() == 1;
4702
0
  obj2.free();
4703
0
      } else if (!obj1.isNull()) {
4704
0
  haveExplicitMask = gFalse;
4705
0
      }
4706
0
      obj1.free();
4707
0
      if (!haveExplicitMask) {
4708
0
  error(errSyntaxError, getPos(), "Bad image parameters");
4709
0
      }
4710
0
    }
4711
4712
    // if drawing is disabled, skip over inline image data
4713
0
    if (state->getIgnoreColorOps() || !ocState) {
4714
0
      if (state->getIgnoreColorOps()) {
4715
0
  error(errSyntaxWarning, getPos(), "Ignoring image"
4716
0
        " in uncolored Type 3 char or tiling pattern");
4717
0
      }
4718
0
      if (inlineImg) {
4719
0
  str->disableDecompressionBombChecking();
4720
0
  str->reset();
4721
0
  n = height * ((width * colorMap->getNumPixelComps() *
4722
0
           colorMap->getBits() + 7) / 8);
4723
0
  str->discardChars(n);
4724
0
  str->close();
4725
0
      }
4726
4727
    // draw it
4728
0
    } else {
4729
0
      if (haveSoftMask) {
4730
0
  dict->lookupNF("SMask", &maskRef);
4731
0
  out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
4732
0
         &maskRef, maskStr, maskWidth, maskHeight,
4733
0
         maskColorMap,
4734
0
         haveMatte ? matte : (double *)NULL,
4735
0
         interpolate);
4736
0
  maskRef.free();
4737
0
  delete maskColorMap;
4738
0
      } else if (haveExplicitMask) {
4739
0
  dict->lookupNF("Mask", &maskRef);
4740
0
  out->drawMaskedImage(state, ref, str, width, height, colorMap,
4741
0
           &maskRef, maskStr, maskWidth, maskHeight,
4742
0
           maskInvert, interpolate);
4743
0
  maskRef.free();
4744
0
      } else {
4745
0
  out->drawImage(state, ref, str, width, height, colorMap,
4746
0
           haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
4747
0
           interpolate);
4748
0
      }
4749
0
    }
4750
4751
0
    delete colorMap;
4752
0
    maskObj.free();
4753
0
    smaskObj.free();
4754
0
  }
4755
4756
  // restore rendering intent
4757
0
  if (state->getRenderingIntent() != riSaved) {
4758
0
    state->setRenderingIntent(riSaved);
4759
0
    out->updateRenderingIntent(state);
4760
0
  }
4761
4762
0
  if ((i = width * height) > 1000) {
4763
0
    i = 1000;
4764
0
  }
4765
0
  opCounter += i;
4766
4767
0
  return gTrue;
4768
4769
0
 err2:
4770
0
  obj1.free();
4771
0
 err1:
4772
0
  error(errSyntaxError, getPos(), "Bad image parameters");
4773
4774
  // restore rendering intent
4775
0
  if (state->getRenderingIntent() != riSaved) {
4776
0
    state->setRenderingIntent(riSaved);
4777
0
    out->updateRenderingIntent(state);
4778
0
  }
4779
4780
0
  return gFalse;
4781
0
}
4782
4783
0
void Gfx::doForm(Object *strRef, Object *str) {
4784
0
  Dict *dict;
4785
0
  GBool transpGroup, isolated, knockout;
4786
0
  Object matrixObj, bboxObj;
4787
0
  double m[6], bbox[4];
4788
0
  Object resObj;
4789
0
  Dict *resDict;
4790
0
  Object obj1, obj2, obj3;
4791
0
  int i;
4792
4793
  // check for excessive recursion
4794
0
  if (formDepth > 100) {
4795
0
    return;
4796
0
  }
4797
4798
  // check for optional content
4799
0
  if (!ocState && !out->needCharCount()) {
4800
0
    return;
4801
0
  }
4802
4803
  // get stream dict
4804
0
  dict = str->streamGetDict();
4805
4806
  // check form type
4807
0
  dict->lookup("FormType", &obj1);
4808
0
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
4809
0
    error(errSyntaxError, getPos(), "Unknown form type");
4810
0
  }
4811
0
  obj1.free();
4812
4813
  // get bounding box
4814
0
  dict->lookup("BBox", &bboxObj);
4815
0
  if (!(bboxObj.isArray() && bboxObj.arrayGetLength() == 4)) {
4816
0
    bboxObj.free();
4817
0
    error(errSyntaxError, getPos(), "Bad form bounding box");
4818
0
    return;
4819
0
  }
4820
0
  for (i = 0; i < 4; ++i) {
4821
0
    bboxObj.arrayGet(i, &obj1);
4822
0
    if (!obj1.isNum()) {
4823
0
      bboxObj.free();
4824
0
      error(errSyntaxError, getPos(), "Bad form bounding box");
4825
0
      return;
4826
0
    }
4827
0
    bbox[i] = obj1.getNum();
4828
0
    obj1.free();
4829
0
  }
4830
0
  bboxObj.free();
4831
4832
  // get matrix
4833
0
  dict->lookup("Matrix", &matrixObj);
4834
0
  if (matrixObj.isArray() && matrixObj.arrayGetLength() == 6) {
4835
0
    for (i = 0; i < 6; ++i) {
4836
0
      matrixObj.arrayGet(i, &obj1);
4837
0
      if (obj1.isNum()) {
4838
0
  m[i] = obj1.getNum();
4839
0
      } else {
4840
0
  m[i] = 0;
4841
0
      }
4842
0
      obj1.free();
4843
0
    }
4844
0
  } else {
4845
0
    m[0] = 1; m[1] = 0;
4846
0
    m[2] = 0; m[3] = 1;
4847
0
    m[4] = 0; m[5] = 0;
4848
0
  }
4849
0
  matrixObj.free();
4850
4851
  // get resources
4852
0
  dict->lookup("Resources", &resObj);
4853
0
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4854
4855
  // check for a transparency group
4856
0
  transpGroup = isolated = knockout = gFalse;
4857
0
  if (dict->lookup("Group", &obj1)->isDict()) {
4858
0
    if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
4859
0
      transpGroup = gTrue;
4860
0
      if (obj1.dictLookup("I", &obj3)->isBool()) {
4861
0
  isolated = obj3.getBool();
4862
0
      }
4863
0
      obj3.free();
4864
0
      if (obj1.dictLookup("K", &obj3)->isBool()) {
4865
0
  knockout = obj3.getBool();
4866
0
      }
4867
0
      obj3.free();
4868
0
    }
4869
0
    obj2.free();
4870
0
  }
4871
0
  obj1.free();
4872
4873
  // draw it
4874
0
  ++formDepth;
4875
0
  drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout);
4876
0
  --formDepth;
4877
4878
0
  resObj.free();
4879
0
}
4880
4881
void Gfx::drawForm(Object *strRef, Dict *resDict,
4882
       double *matrix, double *bbox,
4883
       GBool transpGroup, GBool softMask,
4884
       GBool isolated, GBool knockout,
4885
       GBool alpha, Function *transferFunc,
4886
0
       Object *backdropColorObj) {
4887
0
  Parser *oldParser;
4888
0
  GfxState *savedState;
4889
0
  GfxColorSpace *blendingColorSpace;
4890
0
  GfxColor backdropColor;
4891
0
  Object strObj, groupAttrsObj, csObj, obj1;
4892
0
  double oldBaseMatrix[6];
4893
0
  int i;
4894
4895
0
  out->startStream(strRef->getRef(), state);
4896
4897
  // push new resources on stack
4898
0
  pushResources(resDict);
4899
4900
  // save current graphics state
4901
0
  saveState();
4902
4903
  // kill any pre-existing path
4904
0
  state->clearPath();
4905
4906
  // save current parser
4907
0
  oldParser = parser;
4908
4909
  // set form transformation matrix
4910
0
  state->concatCTM(matrix[0], matrix[1], matrix[2],
4911
0
       matrix[3], matrix[4], matrix[5]);
4912
0
  out->updateCTM(state, matrix[0], matrix[1], matrix[2],
4913
0
     matrix[3], matrix[4], matrix[5]);
4914
4915
  // set form bounding box
4916
0
  state->moveTo(bbox[0], bbox[1]);
4917
0
  state->lineTo(bbox[2], bbox[1]);
4918
0
  state->lineTo(bbox[2], bbox[3]);
4919
0
  state->lineTo(bbox[0], bbox[3]);
4920
0
  state->closePath();
4921
0
  state->clip();
4922
0
  out->clip(state);
4923
0
  state->clearPath();
4924
4925
0
  blendingColorSpace = NULL;
4926
0
  if (softMask || transpGroup) {
4927
    // get the blending color space
4928
    // NB: this must be done AFTER pushing the resource dictionary,
4929
    //     so that any Default*** color spaces are available
4930
0
    strRef->fetch(xref, &strObj);
4931
0
    if (strObj.streamGetDict()->lookup("Group", &groupAttrsObj)->isDict()) {
4932
0
      if (!groupAttrsObj.dictLookup("CS", &csObj)->isNull()) {
4933
0
  blendingColorSpace = GfxColorSpace::parse(&csObj
4934
0
              );
4935
0
      }
4936
0
      csObj.free();
4937
0
    }
4938
0
    groupAttrsObj.free();
4939
0
    strObj.free();
4940
4941
0
    if (!out->beginTransparencyGroup(state, bbox, blendingColorSpace,
4942
0
             isolated, knockout, softMask)) {
4943
0
      transpGroup = gFalse;
4944
0
    }
4945
4946
0
    if (softMask || transpGroup) {
4947
0
      traceBegin(oldBaseMatrix, softMask ? "begin soft mask" : "begin t-group");
4948
0
      if (state->getBlendMode() != gfxBlendNormal) {
4949
0
  state->setBlendMode(gfxBlendNormal);
4950
0
  out->updateBlendMode(state);
4951
0
      }
4952
0
      if (state->getFillOpacity() != 1) {
4953
0
  state->setFillOpacity(1);
4954
0
  out->updateFillOpacity(state);
4955
0
      }
4956
0
      if (state->getStrokeOpacity() != 1) {
4957
0
  state->setStrokeOpacity(1);
4958
0
  out->updateStrokeOpacity(state);
4959
0
      }
4960
0
      out->clearSoftMask(state);
4961
0
    }
4962
0
  }
4963
4964
  // set new base matrix
4965
0
  for (i = 0; i < 6; ++i) {
4966
0
    oldBaseMatrix[i] = baseMatrix[i];
4967
0
    baseMatrix[i] = state->getCTM()[i];
4968
0
  }
4969
4970
  // save the state stack -- this handles the case where the form
4971
  // contents have unbalanced q/Q operators
4972
0
  savedState = saveStateStack();
4973
4974
  // draw the form
4975
0
  display(strRef, gFalse);
4976
4977
0
  restoreStateStack(savedState);
4978
4979
0
  if (softMask || transpGroup) {
4980
0
    out->endTransparencyGroup(state);
4981
0
  }
4982
4983
  // restore base matrix
4984
0
  for (i = 0; i < 6; ++i) {
4985
0
    baseMatrix[i] = oldBaseMatrix[i];
4986
0
  }
4987
4988
  // restore parser
4989
0
  parser = oldParser;
4990
4991
  // restore graphics state
4992
0
  restoreState();
4993
4994
  // pop resource stack
4995
0
  popResources();
4996
4997
0
  if (softMask) {
4998
0
    for (i = 0; i < gfxColorMaxComps; ++i) {
4999
0
      backdropColor.c[i] = 0;
5000
0
    }
5001
0
    if (backdropColorObj->isArray()) {
5002
0
      for (i = 0;
5003
0
     i < backdropColorObj->arrayGetLength() && i < gfxColorMaxComps;
5004
0
     ++i) {
5005
0
  backdropColorObj->arrayGet(i, &obj1);
5006
0
  if (obj1.isNum()) {
5007
0
    backdropColor.c[i] = dblToCol(obj1.getNum());
5008
0
  }
5009
0
  obj1.free();
5010
0
      }
5011
0
    } else if (blendingColorSpace) {
5012
0
      blendingColorSpace->getDefaultColor(&backdropColor);
5013
0
    }
5014
    //~ else: need to get the parent or default color space (?)
5015
0
    out->setSoftMask(state, bbox, alpha, transferFunc, &backdropColor);
5016
0
    traceEnd(oldBaseMatrix, "end soft mask");
5017
0
  } else if (transpGroup) {
5018
0
    out->paintTransparencyGroup(state, bbox);
5019
0
    traceEnd(oldBaseMatrix, "end t-group");
5020
0
  }
5021
5022
0
  if (blendingColorSpace) {
5023
0
    delete blendingColorSpace;
5024
0
  }
5025
5026
0
  out->endStream(strRef->getRef());
5027
0
}
5028
5029
0
void Gfx::takeContentStreamStack(Gfx *oldGfx) {
5030
0
  contentStreamStack->append(oldGfx->contentStreamStack);
5031
0
}
5032
5033
0
void Gfx::endOfPage() {
5034
0
  while (state->hasSaves()) {
5035
0
    restoreState();
5036
0
  }
5037
0
  while (markedContentStack->getLength() > 0) {
5038
0
    opEndMarkedContent(NULL, 0);
5039
0
  }
5040
0
}
5041
5042
//------------------------------------------------------------------------
5043
// in-line image operators
5044
//------------------------------------------------------------------------
5045
5046
0
void Gfx::opBeginImage(Object args[], int numArgs) {
5047
0
  Stream *str;
5048
0
  GBool haveLength;
5049
0
  int c1, c2, c3;
5050
5051
  // NB: this function is run even if ocState is false -- doImage() is
5052
  // responsible for skipping over the inline image data
5053
5054
  // build dict/stream
5055
0
  str = buildImageStream(&haveLength);
5056
5057
  // display the image
5058
0
  if (str) {
5059
0
    if (!doImage(NULL, str, gTrue)) {
5060
0
      delete str;
5061
  
5062
    // if we have the stream length, skip to end-of-stream and then
5063
    // skip 'EI' in the original stream
5064
0
    } else if (haveLength) {
5065
      // NB: getUndecodedStream() returns the EmbedStream created by
5066
      // buildImageStream()
5067
0
      while ((c1 = str->getUndecodedStream()->getChar()) != EOF) ;
5068
0
      delete str;
5069
0
      str = parser->getStream();
5070
0
      c1 = str->getChar();
5071
0
      c2 = str->getChar();
5072
0
      c3 = str->lookChar();
5073
0
      while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
5074
0
  c1 = c2;
5075
0
  c2 = str->getChar();
5076
0
  c3 = str->lookChar();
5077
0
      }
5078
5079
    // else, look for the 'EI' tag and skip it
5080
0
    } else {
5081
0
      c1 = str->getUndecodedStream()->getChar();
5082
0
      c2 = str->getUndecodedStream()->getChar();
5083
0
      c3 = str->getUndecodedStream()->lookChar();
5084
0
      while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
5085
0
  c1 = c2;
5086
0
  c2 = str->getUndecodedStream()->getChar();
5087
0
  c3 = str->getUndecodedStream()->lookChar();
5088
0
      }
5089
0
      delete str;
5090
0
    }
5091
0
  }
5092
0
}
5093
5094
0
Stream *Gfx::buildImageStream(GBool *haveLength) {
5095
0
  Object dict;
5096
0
  Object obj, lengthObj;
5097
0
  char *key;
5098
0
  int length;
5099
0
  Stream *str;
5100
5101
  // build dictionary
5102
0
  dict.initDict(xref);
5103
0
  getContentObj(&obj);
5104
0
  while (!obj.isCmd("ID") && !obj.isEOF()) {
5105
0
    if (!obj.isName()) {
5106
0
      error(errSyntaxError, getPos(),
5107
0
      "Inline image dictionary key must be a name object");
5108
0
      obj.free();
5109
0
    } else {
5110
0
      key = copyString(obj.getName());
5111
0
      obj.free();
5112
0
      getContentObj(&obj);
5113
0
      if (obj.isEOF()) {
5114
0
  gfree(key);
5115
0
  break;
5116
0
      }
5117
0
      if (obj.isError()) {
5118
0
  gfree(key);
5119
0
  obj.free();
5120
0
      } else {
5121
0
  dict.dictAdd(key, &obj);
5122
0
      }
5123
0
    }
5124
0
    getContentObj(&obj);
5125
0
  }
5126
0
  if (obj.isEOF()) {
5127
0
    error(errSyntaxError, getPos(), "End of file in inline image");
5128
0
    obj.free();
5129
0
    dict.free();
5130
0
    return NULL;
5131
0
  }
5132
0
  obj.free();
5133
5134
  // check for length field
5135
0
  length = 0;
5136
0
  *haveLength = gFalse;
5137
0
  if (!dict.dictLookup("Length", &lengthObj)->isInt()) {
5138
0
    lengthObj.free();
5139
0
    dict.dictLookup("L", &lengthObj);
5140
0
  }
5141
0
  if (lengthObj.isInt()) {
5142
0
    length = lengthObj.getInt();
5143
0
    *haveLength = gTrue;
5144
0
  }
5145
0
  lengthObj.free();
5146
5147
  // make stream
5148
0
  if (!(str = parser->getStream())) {
5149
0
    error(errSyntaxError, getPos(), "Invalid inline image data");
5150
0
    dict.free();
5151
0
    return NULL;
5152
0
  }
5153
0
  str = new EmbedStream(str, &dict, *haveLength, (GFileOffset)length);
5154
0
  str = str->addFilters(&dict);
5155
5156
0
  return str;
5157
0
}
5158
5159
0
void Gfx::opImageData(Object args[], int numArgs) {
5160
0
  error(errInternal, getPos(), "Got 'ID' operator");
5161
0
}
5162
5163
0
void Gfx::opEndImage(Object args[], int numArgs) {
5164
0
  error(errInternal, getPos(), "Got 'EI' operator");
5165
0
}
5166
5167
//------------------------------------------------------------------------
5168
// type 3 font operators
5169
//------------------------------------------------------------------------
5170
5171
0
void Gfx::opSetCharWidth(Object args[], int numArgs) {
5172
0
  out->type3D0(state, args[0].getNum(), args[1].getNum());
5173
0
}
5174
5175
0
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
5176
0
  state->setIgnoreColorOps(gTrue);
5177
0
  out->type3D1(state, args[0].getNum(), args[1].getNum(),
5178
0
         args[2].getNum(), args[3].getNum(),
5179
0
         args[4].getNum(), args[5].getNum());
5180
0
}
5181
5182
//------------------------------------------------------------------------
5183
// compatibility operators
5184
//------------------------------------------------------------------------
5185
5186
0
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
5187
0
  ++ignoreUndef;
5188
0
}
5189
5190
0
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
5191
0
  if (ignoreUndef > 0)
5192
0
    --ignoreUndef;
5193
0
}
5194
5195
//------------------------------------------------------------------------
5196
// marked content operators
5197
//------------------------------------------------------------------------
5198
5199
0
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
5200
0
  GfxMarkedContent *mc;
5201
0
  Object obj;
5202
0
  GBool ocStateNew;
5203
0
  TextString *s;
5204
0
  GfxMarkedContentKind mcKind;
5205
5206
0
  if (printCommands) {
5207
0
    printf("  marked content: %s ", args[0].getName());
5208
0
    if (numArgs == 2) {
5209
0
      args[1].print(stdout);
5210
0
    }
5211
0
    printf("\n");
5212
0
    fflush(stdout);
5213
0
  }
5214
0
  mcKind = gfxMCOther;
5215
0
  if (args[0].isName("OC") && numArgs == 2 && args[1].isName() &&
5216
0
      res->lookupPropertiesNF(args[1].getName(), &obj)) {
5217
0
    if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) {
5218
0
      ocState &= ocStateNew;
5219
0
    }
5220
0
    obj.free();
5221
0
    mcKind = gfxMCOptionalContent;
5222
0
  } else if (numArgs == 2 && args[1].isDict()) {
5223
0
    if (args[0].isName("Span")) {
5224
0
      if (args[1].dictLookup("ActualText", &obj)->isString()) {
5225
0
  s = new TextString(obj.getString());
5226
0
  out->beginActualText(state, s->getUnicode(), s->getLength());
5227
0
  delete s;
5228
0
  mcKind = gfxMCActualText;
5229
0
      }
5230
0
      obj.free();
5231
0
    }
5232
0
    if (args[1].dictLookup("MCID", &obj)->isInt()) {
5233
0
      out->beginStructureItem(args[0].getName(), obj.getInt(),
5234
0
            args[1].getDict());
5235
0
      if (mcKind == gfxMCActualText) {
5236
0
  mcKind = gfxMCStructureItemAndActualText;
5237
0
      } else {
5238
0
  mcKind = gfxMCStructureItem;
5239
0
      }
5240
0
    }
5241
0
    obj.free();
5242
0
  }
5243
5244
0
  mc = new GfxMarkedContent(mcKind, ocState);
5245
0
  markedContentStack->append(mc);
5246
0
}
5247
5248
0
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
5249
0
  GfxMarkedContent *mc;
5250
0
  GfxMarkedContentKind mcKind;
5251
5252
0
  if (markedContentStack->getLength() > 0) {
5253
0
    mc = (GfxMarkedContent *)
5254
0
             markedContentStack->del(markedContentStack->getLength() - 1);
5255
0
    mcKind = mc->kind;
5256
0
    delete mc;
5257
0
    if (mcKind == gfxMCOptionalContent) {
5258
0
      if (markedContentStack->getLength() > 0) {
5259
0
  mc = (GfxMarkedContent *)
5260
0
           markedContentStack->get(markedContentStack->getLength() - 1);
5261
0
  ocState = mc->ocState;
5262
0
      } else {
5263
0
  ocState = gTrue;
5264
0
      }
5265
0
    } else if (mcKind == gfxMCActualText) {
5266
0
      out->endActualText(state);
5267
0
    } else if (mcKind == gfxMCStructureItem) {
5268
0
      out->endStructureItem();
5269
0
    } else if (mcKind == gfxMCStructureItemAndActualText) {
5270
0
      out->endStructureItem();
5271
0
      out->endActualText(state);
5272
0
    }
5273
0
  } else {
5274
0
    error(errSyntaxWarning, getPos(), "Mismatched EMC operator");
5275
0
  }
5276
0
}
5277
5278
0
void Gfx::opMarkPoint(Object args[], int numArgs) {
5279
0
  if (printCommands) {
5280
0
    printf("  mark point: %s ", args[0].getName());
5281
0
    if (numArgs == 2)
5282
0
      args[1].print(stdout);
5283
0
    printf("\n");
5284
0
    fflush(stdout);
5285
0
  }
5286
0
}
5287
5288
//------------------------------------------------------------------------
5289
// misc
5290
//------------------------------------------------------------------------
5291
5292
void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
5293
0
        double xMin, double yMin, double xMax, double yMax) {
5294
0
  Dict *dict, *resDict;
5295
0
  Object str, matrixObj, bboxObj, resObj, obj1;
5296
0
  double formXMin, formYMin, formXMax, formYMax;
5297
0
  double x, y, sx, sy, tx, ty;
5298
0
  double m[6], bbox[4];
5299
0
  double *borderColor;
5300
0
  GfxColor color;
5301
0
  double *dash, *dash2;
5302
0
  int dashLength;
5303
0
  int i;
5304
5305
  // this function assumes that we are in the default user space,
5306
  // i.e., baseMatrix = ctm
5307
5308
  // if the bounding box has zero width or height, don't draw anything
5309
  // at all
5310
0
  if (xMin == xMax || yMin == yMax) {
5311
0
    return;
5312
0
  }
5313
5314
  // draw the appearance stream (if there is one)
5315
0
  strRef->fetch(xref, &str);
5316
0
  if (str.isStream()) {
5317
5318
    // get stream dict
5319
0
    dict = str.streamGetDict();
5320
5321
    // get the form bounding box
5322
0
    dict->lookup("BBox", &bboxObj);
5323
0
    if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) {
5324
0
      error(errSyntaxError, getPos(), "Bad form bounding box");
5325
0
      bboxObj.free();
5326
0
      str.free();
5327
0
      return;
5328
0
    }
5329
0
    for (i = 0; i < 4; ++i) {
5330
0
      bboxObj.arrayGet(i, &obj1);
5331
0
      if (obj1.isNum()) {
5332
0
  bbox[i] = obj1.getNum();
5333
0
      } else {
5334
0
  bbox[i] = 0;
5335
0
      }
5336
0
      obj1.free();
5337
0
    }
5338
0
    bboxObj.free();
5339
5340
    // get the form matrix
5341
0
    dict->lookup("Matrix", &matrixObj);
5342
0
    if (matrixObj.isArray()) {
5343
0
      for (i = 0; i < 6; ++i) {
5344
0
  matrixObj.arrayGet(i, &obj1);
5345
0
  m[i] = obj1.getNum();
5346
0
  obj1.free();
5347
0
      }
5348
0
    } else {
5349
0
      m[0] = 1; m[1] = 0;
5350
0
      m[2] = 0; m[3] = 1;
5351
0
      m[4] = 0; m[5] = 0;
5352
0
    }
5353
0
    matrixObj.free();
5354
5355
    // transform the four corners of the form bbox to default user
5356
    // space, and construct the transformed bbox
5357
0
    x = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
5358
0
    y = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
5359
0
    formXMin = formXMax = x;
5360
0
    formYMin = formYMax = y;
5361
0
    x = bbox[0] * m[0] + bbox[3] * m[2] + m[4];
5362
0
    y = bbox[0] * m[1] + bbox[3] * m[3] + m[5];
5363
0
    if (x < formXMin) {
5364
0
      formXMin = x;
5365
0
    } else if (x > formXMax) {
5366
0
      formXMax = x;
5367
0
    }
5368
0
    if (y < formYMin) {
5369
0
      formYMin = y;
5370
0
    } else if (y > formYMax) {
5371
0
      formYMax = y;
5372
0
    }
5373
0
    x = bbox[2] * m[0] + bbox[1] * m[2] + m[4];
5374
0
    y = bbox[2] * m[1] + bbox[1] * m[3] + m[5];
5375
0
    if (x < formXMin) {
5376
0
      formXMin = x;
5377
0
    } else if (x > formXMax) {
5378
0
      formXMax = x;
5379
0
    }
5380
0
    if (y < formYMin) {
5381
0
      formYMin = y;
5382
0
    } else if (y > formYMax) {
5383
0
      formYMax = y;
5384
0
    }
5385
0
    x = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
5386
0
    y = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
5387
0
    if (x < formXMin) {
5388
0
      formXMin = x;
5389
0
    } else if (x > formXMax) {
5390
0
      formXMax = x;
5391
0
    }
5392
0
    if (y < formYMin) {
5393
0
      formYMin = y;
5394
0
    } else if (y > formYMax) {
5395
0
      formYMax = y;
5396
0
    }
5397
5398
    // construct a mapping matrix, [sx 0  0], which maps the transformed
5399
    //                             [0  sy 0]
5400
    //                             [tx ty 1]
5401
    // bbox to the annotation rectangle
5402
0
    if (formXMin == formXMax) {
5403
      // this shouldn't happen
5404
0
      sx = 1;
5405
0
    } else {
5406
0
      sx = (xMax - xMin) / (formXMax - formXMin);
5407
0
    }
5408
0
    if (formYMin == formYMax) {
5409
      // this shouldn't happen
5410
0
      sy = 1;
5411
0
    } else {
5412
0
      sy = (yMax - yMin) / (formYMax - formYMin);
5413
0
    }
5414
0
    tx = -formXMin * sx + xMin;
5415
0
    ty = -formYMin * sy + yMin;
5416
5417
    // the final transform matrix is (form matrix) * (mapping matrix)
5418
0
    m[0] *= sx;
5419
0
    m[1] *= sy;
5420
0
    m[2] *= sx;
5421
0
    m[3] *= sy;
5422
0
    m[4] = m[4] * sx + tx;
5423
0
    m[5] = m[5] * sy + ty;
5424
5425
    // get the resources
5426
0
    dict->lookup("Resources", &resObj);
5427
0
    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
5428
5429
    // draw it
5430
0
    drawForm(strRef, resDict, m, bbox);
5431
5432
0
    resObj.free();
5433
0
  }
5434
0
  str.free();
5435
5436
  // draw the border
5437
0
  if (borderStyle && borderStyle->getWidth() > 0 &&
5438
0
      borderStyle->getNumColorComps() > 0) {
5439
0
    borderColor = borderStyle->getColor();
5440
0
    switch (borderStyle->getNumColorComps()) {
5441
0
    case 1:
5442
0
      if (state->getStrokeColorSpace()->getMode() != csDeviceGray) {
5443
0
  state->setStrokePattern(NULL);
5444
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
5445
0
  out->updateStrokeColorSpace(state);
5446
0
      }
5447
0
      break;
5448
0
    case 3:
5449
0
      if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
5450
0
  state->setStrokePattern(NULL);
5451
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
5452
0
  out->updateStrokeColorSpace(state);
5453
0
      }
5454
0
      break;
5455
0
    case 4:
5456
0
      if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) {
5457
0
  state->setStrokePattern(NULL);
5458
0
  state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
5459
0
  out->updateStrokeColorSpace(state);
5460
0
      }
5461
0
      break;
5462
0
    }
5463
0
    color.c[0] = dblToCol(borderColor[0]);
5464
0
    color.c[1] = dblToCol(borderColor[1]);
5465
0
    color.c[2] = dblToCol(borderColor[2]);
5466
0
    color.c[3] = dblToCol(borderColor[3]);
5467
0
    state->setStrokeColor(&color);
5468
0
    out->updateStrokeColor(state);
5469
0
    state->setLineWidth(borderStyle->getWidth());
5470
0
    out->updateLineWidth(state);
5471
0
    borderStyle->getDash(&dash, &dashLength);
5472
0
    if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
5473
0
      dash2 = (double *)gmallocn(dashLength, sizeof(double));
5474
0
      memcpy(dash2, dash, dashLength * sizeof(double));
5475
0
      state->setLineDash(dash2, dashLength, 0);
5476
0
      out->updateLineDash(state);
5477
0
    }
5478
    //~ this doesn't currently handle the beveled and engraved styles
5479
0
    state->clearPath();
5480
0
    state->moveTo(xMin, yMin);
5481
0
    state->lineTo(xMax, yMin);
5482
0
    if (borderStyle->getType() != annotBorderUnderlined) {
5483
0
      state->lineTo(xMax, yMax);
5484
0
      state->lineTo(xMin, yMax);
5485
0
      state->closePath();
5486
0
    }
5487
0
    out->stroke(state);
5488
0
  }
5489
0
}
5490
5491
0
void Gfx::saveState() {
5492
0
  out->saveState(state);
5493
0
  state = state->save();
5494
0
}
5495
5496
0
void Gfx::restoreState() {
5497
0
  state = state->restore();
5498
0
  out->restoreState(state);
5499
0
}
5500
5501
// Create a new state stack, and initialize it with a copy of the
5502
// current state.
5503
0
GfxState *Gfx::saveStateStack() {
5504
0
  GfxState *oldState;
5505
5506
0
  out->saveState(state);
5507
0
  oldState = state;
5508
0
  state = state->copy(gTrue);
5509
0
  return oldState;
5510
0
}
5511
5512
// Switch back to the previous state stack.
5513
0
void Gfx::restoreStateStack(GfxState *oldState) {
5514
0
  while (state->hasSaves()) {
5515
0
    restoreState();
5516
0
  }
5517
0
  delete state;
5518
0
  state = oldState;
5519
0
  out->restoreState(state);
5520
0
}
5521
5522
0
void Gfx::pushResources(Dict *resDict) {
5523
0
  res = new GfxResources(xref, resDict, res);
5524
0
}
5525
5526
0
void Gfx::popResources() {
5527
0
  GfxResources *resPtr;
5528
5529
0
  resPtr = res->getNext();
5530
0
  delete res;
5531
0
  res = resPtr;
5532
0
}