Coverage Report

Created: 2026-03-15 06:38

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