~ubuntu-branches/ubuntu/wily/luatex/wily

« back to all changes in this revision

Viewing changes to source/libs/xpdf/xpdf-3.02/xpdf/Gfx.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Preining
  • Date: 2010-04-29 00:47:19 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20100429004719-o42etkqe90n97b9e
Tags: 0.60.1-1
* new upstream release, adapt build-script patch
* disable patch: upstream-epstopdf_cc_no_xpdf_patching, included upstream
* disable patch: libpoppler-0.12, not needed anymore

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//========================================================================
 
2
//
 
3
// Gfx.cc
 
4
//
 
5
// Copyright 1996-2003 Glyph & Cog, LLC
 
6
//
 
7
//========================================================================
 
8
 
 
9
#include <aconf.h>
 
10
 
 
11
#ifdef USE_GCC_PRAGMAS
 
12
#pragma implementation
 
13
#endif
 
14
 
 
15
#include <stdlib.h>
 
16
#include <stdio.h>
 
17
#include <stddef.h>
 
18
#include <string.h>
 
19
#include <math.h>
 
20
#include "gmem.h"
 
21
#include "GlobalParams.h"
 
22
#include "CharTypes.h"
 
23
#include "Object.h"
 
24
#include "Array.h"
 
25
#include "Dict.h"
 
26
#include "Stream.h"
 
27
#include "Lexer.h"
 
28
#include "Parser.h"
 
29
#include "GfxFont.h"
 
30
#include "GfxState.h"
 
31
#include "OutputDev.h"
 
32
#include "Page.h"
 
33
#include "Annot.h"
 
34
#include "Error.h"
 
35
#include "Gfx.h"
 
36
 
 
37
// the MSVC math.h doesn't define this
 
38
#ifndef M_PI
 
39
#define M_PI 3.14159265358979323846
 
40
#endif
 
41
 
 
42
//------------------------------------------------------------------------
 
43
// constants
 
44
//------------------------------------------------------------------------
 
45
 
 
46
// Max recursive depth for a function shading fill.
 
47
#define functionMaxDepth 6
 
48
 
 
49
// Max delta allowed in any color component for a function shading fill.
 
50
#define functionColorDelta (dblToCol(1 / 256.0))
 
51
 
 
52
// Max number of splits along the t axis for an axial shading fill.
 
53
#define axialMaxSplits 256
 
54
 
 
55
// Max delta allowed in any color component for an axial shading fill.
 
56
#define axialColorDelta (dblToCol(1 / 256.0))
 
57
 
 
58
// Max number of splits along the t axis for a radial shading fill.
 
59
#define radialMaxSplits 256
 
60
 
 
61
// Max delta allowed in any color component for a radial shading fill.
 
62
#define radialColorDelta (dblToCol(1 / 256.0))
 
63
 
 
64
// Max recursive depth for a Gouraud triangle shading fill.
 
65
#define gouraudMaxDepth 6
 
66
 
 
67
// Max delta allowed in any color component for a Gouraud triangle
 
68
// shading fill.
 
69
#define gouraudColorDelta (dblToCol(1 / 256.0))
 
70
 
 
71
// Max recursive depth for a patch mesh shading fill.
 
72
#define patchMaxDepth 6
 
73
 
 
74
// Max delta allowed in any color component for a patch mesh shading
 
75
// fill.
 
76
#define patchColorDelta (dblToCol(1 / 256.0))
 
77
 
 
78
//------------------------------------------------------------------------
 
79
// Operator table
 
80
//------------------------------------------------------------------------
 
81
 
 
82
#ifdef WIN32 // this works around a bug in the VC7 compiler
 
83
#  pragma optimize("",off)
 
84
#endif
 
85
 
 
86
Operator Gfx::opTab[] = {
 
87
  {"\"",  3, {tchkNum,    tchkNum,    tchkString},
 
88
          &Gfx::opMoveSetShowText},
 
89
  {"'",   1, {tchkString},
 
90
          &Gfx::opMoveShowText},
 
91
  {"B",   0, {tchkNone},
 
92
          &Gfx::opFillStroke},
 
93
  {"B*",  0, {tchkNone},
 
94
          &Gfx::opEOFillStroke},
 
95
  {"BDC", 2, {tchkName,   tchkProps},
 
96
          &Gfx::opBeginMarkedContent},
 
97
  {"BI",  0, {tchkNone},
 
98
          &Gfx::opBeginImage},
 
99
  {"BMC", 1, {tchkName},
 
100
          &Gfx::opBeginMarkedContent},
 
101
  {"BT",  0, {tchkNone},
 
102
          &Gfx::opBeginText},
 
103
  {"BX",  0, {tchkNone},
 
104
          &Gfx::opBeginIgnoreUndef},
 
105
  {"CS",  1, {tchkName},
 
106
          &Gfx::opSetStrokeColorSpace},
 
107
  {"DP",  2, {tchkName,   tchkProps},
 
108
          &Gfx::opMarkPoint},
 
109
  {"Do",  1, {tchkName},
 
110
          &Gfx::opXObject},
 
111
  {"EI",  0, {tchkNone},
 
112
          &Gfx::opEndImage},
 
113
  {"EMC", 0, {tchkNone},
 
114
          &Gfx::opEndMarkedContent},
 
115
  {"ET",  0, {tchkNone},
 
116
          &Gfx::opEndText},
 
117
  {"EX",  0, {tchkNone},
 
118
          &Gfx::opEndIgnoreUndef},
 
119
  {"F",   0, {tchkNone},
 
120
          &Gfx::opFill},
 
121
  {"G",   1, {tchkNum},
 
122
          &Gfx::opSetStrokeGray},
 
123
  {"ID",  0, {tchkNone},
 
124
          &Gfx::opImageData},
 
125
  {"J",   1, {tchkInt},
 
126
          &Gfx::opSetLineCap},
 
127
  {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
128
          &Gfx::opSetStrokeCMYKColor},
 
129
  {"M",   1, {tchkNum},
 
130
          &Gfx::opSetMiterLimit},
 
131
  {"MP",  1, {tchkName},
 
132
          &Gfx::opMarkPoint},
 
133
  {"Q",   0, {tchkNone},
 
134
          &Gfx::opRestore},
 
135
  {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
 
136
          &Gfx::opSetStrokeRGBColor},
 
137
  {"S",   0, {tchkNone},
 
138
          &Gfx::opStroke},
 
139
  {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
 
140
          &Gfx::opSetStrokeColor},
 
141
  {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
142
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
143
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
144
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
145
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
146
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
147
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
148
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
149
                tchkSCN},
 
150
          &Gfx::opSetStrokeColorN},
 
151
  {"T*",  0, {tchkNone},
 
152
          &Gfx::opTextNextLine},
 
153
  {"TD",  2, {tchkNum,    tchkNum},
 
154
          &Gfx::opTextMoveSet},
 
155
  {"TJ",  1, {tchkArray},
 
156
          &Gfx::opShowSpaceText},
 
157
  {"TL",  1, {tchkNum},
 
158
          &Gfx::opSetTextLeading},
 
159
  {"Tc",  1, {tchkNum},
 
160
          &Gfx::opSetCharSpacing},
 
161
  {"Td",  2, {tchkNum,    tchkNum},
 
162
          &Gfx::opTextMove},
 
163
  {"Tf",  2, {tchkName,   tchkNum},
 
164
          &Gfx::opSetFont},
 
165
  {"Tj",  1, {tchkString},
 
166
          &Gfx::opShowText},
 
167
  {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
168
              tchkNum,    tchkNum},
 
169
          &Gfx::opSetTextMatrix},
 
170
  {"Tr",  1, {tchkInt},
 
171
          &Gfx::opSetTextRender},
 
172
  {"Ts",  1, {tchkNum},
 
173
          &Gfx::opSetTextRise},
 
174
  {"Tw",  1, {tchkNum},
 
175
          &Gfx::opSetWordSpacing},
 
176
  {"Tz",  1, {tchkNum},
 
177
          &Gfx::opSetHorizScaling},
 
178
  {"W",   0, {tchkNone},
 
179
          &Gfx::opClip},
 
180
  {"W*",  0, {tchkNone},
 
181
          &Gfx::opEOClip},
 
182
  {"b",   0, {tchkNone},
 
183
          &Gfx::opCloseFillStroke},
 
184
  {"b*",  0, {tchkNone},
 
185
          &Gfx::opCloseEOFillStroke},
 
186
  {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
187
              tchkNum,    tchkNum},
 
188
          &Gfx::opCurveTo},
 
189
  {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
190
              tchkNum,    tchkNum},
 
191
          &Gfx::opConcat},
 
192
  {"cs",  1, {tchkName},
 
193
          &Gfx::opSetFillColorSpace},
 
194
  {"d",   2, {tchkArray,  tchkNum},
 
195
          &Gfx::opSetDash},
 
196
  {"d0",  2, {tchkNum,    tchkNum},
 
197
          &Gfx::opSetCharWidth},
 
198
  {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
 
199
              tchkNum,    tchkNum},
 
200
          &Gfx::opSetCacheDevice},
 
201
  {"f",   0, {tchkNone},
 
202
          &Gfx::opFill},
 
203
  {"f*",  0, {tchkNone},
 
204
          &Gfx::opEOFill},
 
205
  {"g",   1, {tchkNum},
 
206
          &Gfx::opSetFillGray},
 
207
  {"gs",  1, {tchkName},
 
208
          &Gfx::opSetExtGState},
 
209
  {"h",   0, {tchkNone},
 
210
          &Gfx::opClosePath},
 
211
  {"i",   1, {tchkNum},
 
212
          &Gfx::opSetFlat},
 
213
  {"j",   1, {tchkInt},
 
214
          &Gfx::opSetLineJoin},
 
215
  {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
216
          &Gfx::opSetFillCMYKColor},
 
217
  {"l",   2, {tchkNum,    tchkNum},
 
218
          &Gfx::opLineTo},
 
219
  {"m",   2, {tchkNum,    tchkNum},
 
220
          &Gfx::opMoveTo},
 
221
  {"n",   0, {tchkNone},
 
222
          &Gfx::opEndPath},
 
223
  {"q",   0, {tchkNone},
 
224
          &Gfx::opSave},
 
225
  {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
226
          &Gfx::opRectangle},
 
227
  {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
 
228
          &Gfx::opSetFillRGBColor},
 
229
  {"ri",  1, {tchkName},
 
230
          &Gfx::opSetRenderingIntent},
 
231
  {"s",   0, {tchkNone},
 
232
          &Gfx::opCloseStroke},
 
233
  {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
 
234
          &Gfx::opSetFillColor},
 
235
  {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
236
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
237
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
238
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
239
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
240
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
241
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
242
                tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
 
243
                tchkSCN},
 
244
          &Gfx::opSetFillColorN},
 
245
  {"sh",  1, {tchkName},
 
246
          &Gfx::opShFill},
 
247
  {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
248
          &Gfx::opCurveTo1},
 
249
  {"w",   1, {tchkNum},
 
250
          &Gfx::opSetLineWidth},
 
251
  {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
 
252
          &Gfx::opCurveTo2},
 
253
};
 
254
 
 
255
#ifdef WIN32 // this works around a bug in the VC7 compiler
 
256
#  pragma optimize("",on)
 
257
#endif
 
258
 
 
259
#define numOps (sizeof(opTab) / sizeof(Operator))
 
260
 
 
261
//------------------------------------------------------------------------
 
262
// GfxResources
 
263
//------------------------------------------------------------------------
 
264
 
 
265
GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
 
266
  Object obj1, obj2;
 
267
  Ref r;
 
268
 
 
269
  if (resDict) {
 
270
 
 
271
    // build font dictionary
 
272
    fonts = NULL;
 
273
    resDict->lookupNF("Font", &obj1);
 
274
    if (obj1.isRef()) {
 
275
      obj1.fetch(xref, &obj2);
 
276
      if (obj2.isDict()) {
 
277
        r = obj1.getRef();
 
278
        fonts = new GfxFontDict(xref, &r, obj2.getDict());
 
279
      }
 
280
      obj2.free();
 
281
    } else if (obj1.isDict()) {
 
282
      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
 
283
    }
 
284
    obj1.free();
 
285
 
 
286
    // get XObject dictionary
 
287
    resDict->lookup("XObject", &xObjDict);
 
288
 
 
289
    // get color space dictionary
 
290
    resDict->lookup("ColorSpace", &colorSpaceDict);
 
291
 
 
292
    // get pattern dictionary
 
293
    resDict->lookup("Pattern", &patternDict);
 
294
 
 
295
    // get shading dictionary
 
296
    resDict->lookup("Shading", &shadingDict);
 
297
 
 
298
    // get graphics state parameter dictionary
 
299
    resDict->lookup("ExtGState", &gStateDict);
 
300
 
 
301
  } else {
 
302
    fonts = NULL;
 
303
    xObjDict.initNull();
 
304
    colorSpaceDict.initNull();
 
305
    patternDict.initNull();
 
306
    shadingDict.initNull();
 
307
    gStateDict.initNull();
 
308
  }
 
309
 
 
310
  next = nextA;
 
311
}
 
312
 
 
313
GfxResources::~GfxResources() {
 
314
  if (fonts) {
 
315
    delete fonts;
 
316
  }
 
317
  xObjDict.free();
 
318
  colorSpaceDict.free();
 
319
  patternDict.free();
 
320
  shadingDict.free();
 
321
  gStateDict.free();
 
322
}
 
323
 
 
324
GfxFont *GfxResources::lookupFont(char *name) {
 
325
  GfxFont *font;
 
326
  GfxResources *resPtr;
 
327
 
 
328
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
329
    if (resPtr->fonts) {
 
330
      if ((font = resPtr->fonts->lookup(name)))
 
331
        return font;
 
332
    }
 
333
  }
 
334
  error(-1, "Unknown font tag '%s'", name);
 
335
  return NULL;
 
336
}
 
337
 
 
338
GBool GfxResources::lookupXObject(char *name, Object *obj) {
 
339
  GfxResources *resPtr;
 
340
 
 
341
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
342
    if (resPtr->xObjDict.isDict()) {
 
343
      if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
 
344
        return gTrue;
 
345
      obj->free();
 
346
    }
 
347
  }
 
348
  error(-1, "XObject '%s' is unknown", name);
 
349
  return gFalse;
 
350
}
 
351
 
 
352
GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
 
353
  GfxResources *resPtr;
 
354
 
 
355
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
356
    if (resPtr->xObjDict.isDict()) {
 
357
      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
 
358
        return gTrue;
 
359
      obj->free();
 
360
    }
 
361
  }
 
362
  error(-1, "XObject '%s' is unknown", name);
 
363
  return gFalse;
 
364
}
 
365
 
 
366
void GfxResources::lookupColorSpace(char *name, Object *obj) {
 
367
  GfxResources *resPtr;
 
368
 
 
369
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
370
    if (resPtr->colorSpaceDict.isDict()) {
 
371
      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
 
372
        return;
 
373
      }
 
374
      obj->free();
 
375
    }
 
376
  }
 
377
  obj->initNull();
 
378
}
 
379
 
 
380
GfxPattern *GfxResources::lookupPattern(char *name) {
 
381
  GfxResources *resPtr;
 
382
  GfxPattern *pattern;
 
383
  Object obj;
 
384
 
 
385
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
386
    if (resPtr->patternDict.isDict()) {
 
387
      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
 
388
        pattern = GfxPattern::parse(&obj);
 
389
        obj.free();
 
390
        return pattern;
 
391
      }
 
392
      obj.free();
 
393
    }
 
394
  }
 
395
  error(-1, "Unknown pattern '%s'", name);
 
396
  return NULL;
 
397
}
 
398
 
 
399
GfxShading *GfxResources::lookupShading(char *name) {
 
400
  GfxResources *resPtr;
 
401
  GfxShading *shading;
 
402
  Object obj;
 
403
 
 
404
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
405
    if (resPtr->shadingDict.isDict()) {
 
406
      if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
 
407
        shading = GfxShading::parse(&obj);
 
408
        obj.free();
 
409
        return shading;
 
410
      }
 
411
      obj.free();
 
412
    }
 
413
  }
 
414
  error(-1, "Unknown shading '%s'", name);
 
415
  return NULL;
 
416
}
 
417
 
 
418
GBool GfxResources::lookupGState(char *name, Object *obj) {
 
419
  GfxResources *resPtr;
 
420
 
 
421
  for (resPtr = this; resPtr; resPtr = resPtr->next) {
 
422
    if (resPtr->gStateDict.isDict()) {
 
423
      if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
 
424
        return gTrue;
 
425
      }
 
426
      obj->free();
 
427
    }
 
428
  }
 
429
  error(-1, "ExtGState '%s' is unknown", name);
 
430
  return gFalse;
 
431
}
 
432
 
 
433
//------------------------------------------------------------------------
 
434
// Gfx
 
435
//------------------------------------------------------------------------
 
436
 
 
437
Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
 
438
         double hDPI, double vDPI, PDFRectangle *box,
 
439
         PDFRectangle *cropBox, int rotate,
 
440
         GBool (*abortCheckCbkA)(void *data),
 
441
         void *abortCheckCbkDataA) {
 
442
  int i;
 
443
 
 
444
  xref = xrefA;
 
445
  subPage = gFalse;
 
446
  printCommands = globalParams->getPrintCommands();
 
447
 
 
448
  // start the resource stack
 
449
  res = new GfxResources(xref, resDict, NULL);
 
450
 
 
451
  // initialize
 
452
  out = outA;
 
453
  state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
 
454
  fontChanged = gFalse;
 
455
  clip = clipNone;
 
456
  ignoreUndef = 0;
 
457
  out->startPage(pageNum, state);
 
458
  out->setDefaultCTM(state->getCTM());
 
459
  out->updateAll(state);
 
460
  for (i = 0; i < 6; ++i) {
 
461
    baseMatrix[i] = state->getCTM()[i];
 
462
  }
 
463
  formDepth = 0;
 
464
  abortCheckCbk = abortCheckCbkA;
 
465
  abortCheckCbkData = abortCheckCbkDataA;
 
466
 
 
467
  // set crop box
 
468
  if (cropBox) {
 
469
    state->moveTo(cropBox->x1, cropBox->y1);
 
470
    state->lineTo(cropBox->x2, cropBox->y1);
 
471
    state->lineTo(cropBox->x2, cropBox->y2);
 
472
    state->lineTo(cropBox->x1, cropBox->y2);
 
473
    state->closePath();
 
474
    state->clip();
 
475
    out->clip(state);
 
476
    state->clearPath();
 
477
  }
 
478
}
 
479
 
 
480
Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
 
481
         PDFRectangle *box, PDFRectangle *cropBox,
 
482
         GBool (*abortCheckCbkA)(void *data),
 
483
         void *abortCheckCbkDataA) {
 
484
  int i;
 
485
 
 
486
  xref = xrefA;
 
487
  subPage = gTrue;
 
488
  printCommands = globalParams->getPrintCommands();
 
489
 
 
490
  // start the resource stack
 
491
  res = new GfxResources(xref, resDict, NULL);
 
492
 
 
493
  // initialize
 
494
  out = outA;
 
495
  state = new GfxState(72, 72, box, 0, gFalse);
 
496
  fontChanged = gFalse;
 
497
  clip = clipNone;
 
498
  ignoreUndef = 0;
 
499
  for (i = 0; i < 6; ++i) {
 
500
    baseMatrix[i] = state->getCTM()[i];
 
501
  }
 
502
  formDepth = 0;
 
503
  abortCheckCbk = abortCheckCbkA;
 
504
  abortCheckCbkData = abortCheckCbkDataA;
 
505
 
 
506
  // set crop box
 
507
  if (cropBox) {
 
508
    state->moveTo(cropBox->x1, cropBox->y1);
 
509
    state->lineTo(cropBox->x2, cropBox->y1);
 
510
    state->lineTo(cropBox->x2, cropBox->y2);
 
511
    state->lineTo(cropBox->x1, cropBox->y2);
 
512
    state->closePath();
 
513
    state->clip();
 
514
    out->clip(state);
 
515
    state->clearPath();
 
516
  }
 
517
}
 
518
 
 
519
Gfx::~Gfx() {
 
520
  while (state->hasSaves()) {
 
521
    restoreState();
 
522
  }
 
523
  if (!subPage) {
 
524
    out->endPage();
 
525
  }
 
526
  while (res) {
 
527
    popResources();
 
528
  }
 
529
  if (state) {
 
530
    delete state;
 
531
  }
 
532
}
 
533
 
 
534
void Gfx::display(Object *obj, GBool topLevel) {
 
535
  Object obj2;
 
536
  int i;
 
537
 
 
538
  if (obj->isArray()) {
 
539
    for (i = 0; i < obj->arrayGetLength(); ++i) {
 
540
      obj->arrayGet(i, &obj2);
 
541
      if (!obj2.isStream()) {
 
542
        error(-1, "Weird page contents");
 
543
        obj2.free();
 
544
        return;
 
545
      }
 
546
      obj2.free();
 
547
    }
 
548
  } else if (!obj->isStream()) {
 
549
    error(-1, "Weird page contents");
 
550
    return;
 
551
  }
 
552
  parser = new Parser(xref, new Lexer(xref, obj), gFalse);
 
553
  go(topLevel);
 
554
  delete parser;
 
555
  parser = NULL;
 
556
}
 
557
 
 
558
void Gfx::go(GBool topLevel) {
 
559
  Object obj;
 
560
  Object args[maxArgs];
 
561
  int numArgs, i;
 
562
  int lastAbortCheck;
 
563
 
 
564
  // scan a sequence of objects
 
565
  updateLevel = lastAbortCheck = 0;
 
566
  numArgs = 0;
 
567
  parser->getObj(&obj);
 
568
  while (!obj.isEOF()) {
 
569
 
 
570
    // got a command - execute it
 
571
    if (obj.isCmd()) {
 
572
      if (printCommands) {
 
573
        obj.print(stdout);
 
574
        for (i = 0; i < numArgs; ++i) {
 
575
          printf(" ");
 
576
          args[i].print(stdout);
 
577
        }
 
578
        printf("\n");
 
579
        fflush(stdout);
 
580
      }
 
581
      execOp(&obj, args, numArgs);
 
582
      obj.free();
 
583
      for (i = 0; i < numArgs; ++i)
 
584
        args[i].free();
 
585
      numArgs = 0;
 
586
 
 
587
      // periodically update display
 
588
      if (++updateLevel >= 20000) {
 
589
        out->dump();
 
590
        updateLevel = 0;
 
591
      }
 
592
 
 
593
      // check for an abort
 
594
      if (abortCheckCbk) {
 
595
        if (updateLevel - lastAbortCheck > 10) {
 
596
          if ((*abortCheckCbk)(abortCheckCbkData)) {
 
597
            break;
 
598
          }
 
599
          lastAbortCheck = updateLevel;
 
600
        }
 
601
      }
 
602
 
 
603
    // got an argument - save it
 
604
    } else if (numArgs < maxArgs) {
 
605
      args[numArgs++] = obj;
 
606
 
 
607
    // too many arguments - something is wrong
 
608
    } else {
 
609
      error(getPos(), "Too many args in content stream");
 
610
      if (printCommands) {
 
611
        printf("throwing away arg: ");
 
612
        obj.print(stdout);
 
613
        printf("\n");
 
614
        fflush(stdout);
 
615
      }
 
616
      obj.free();
 
617
    }
 
618
 
 
619
    // grab the next object
 
620
    parser->getObj(&obj);
 
621
  }
 
622
  obj.free();
 
623
 
 
624
  // args at end with no command
 
625
  if (numArgs > 0) {
 
626
    error(getPos(), "Leftover args in content stream");
 
627
    if (printCommands) {
 
628
      printf("%d leftovers:", numArgs);
 
629
      for (i = 0; i < numArgs; ++i) {
 
630
        printf(" ");
 
631
        args[i].print(stdout);
 
632
      }
 
633
      printf("\n");
 
634
      fflush(stdout);
 
635
    }
 
636
    for (i = 0; i < numArgs; ++i)
 
637
      args[i].free();
 
638
  }
 
639
 
 
640
  // update display
 
641
  if (topLevel && updateLevel > 0) {
 
642
    out->dump();
 
643
  }
 
644
}
 
645
 
 
646
void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
 
647
  Operator *op;
 
648
  char *name;
 
649
  Object *argPtr;
 
650
  int i;
 
651
 
 
652
  // find operator
 
653
  name = cmd->getCmd();
 
654
  if (!(op = findOp(name))) {
 
655
    if (ignoreUndef == 0)
 
656
      error(getPos(), "Unknown operator '%s'", name);
 
657
    return;
 
658
  }
 
659
 
 
660
  // type check args
 
661
  argPtr = args;
 
662
  if (op->numArgs >= 0) {
 
663
    if (numArgs < op->numArgs) {
 
664
      error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
 
665
      return;
 
666
    }
 
667
    if (numArgs > op->numArgs) {
 
668
#if 0
 
669
      error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
 
670
#endif
 
671
      argPtr += numArgs - op->numArgs;
 
672
      numArgs = op->numArgs;
 
673
    }
 
674
  } else {
 
675
    if (numArgs > -op->numArgs) {
 
676
      error(getPos(), "Too many (%d) args to '%s' operator",
 
677
            numArgs, name);
 
678
      return;
 
679
    }
 
680
  }
 
681
  for (i = 0; i < numArgs; ++i) {
 
682
    if (!checkArg(&argPtr[i], op->tchk[i])) {
 
683
      error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
 
684
            i, name, argPtr[i].getTypeName());
 
685
      return;
 
686
    }
 
687
  }
 
688
 
 
689
  // do it
 
690
  (this->*op->func)(argPtr, numArgs);
 
691
}
 
692
 
 
693
Operator *Gfx::findOp(char *name) {
 
694
  int a, b, m, cmp;
 
695
 
 
696
  a = -1;
 
697
  b = numOps;
 
698
  // invariant: opTab[a] < name < opTab[b]
 
699
  while (b - a > 1) {
 
700
    m = (a + b) / 2;
 
701
    cmp = strcmp(opTab[m].name, name);
 
702
    if (cmp < 0)
 
703
      a = m;
 
704
    else if (cmp > 0)
 
705
      b = m;
 
706
    else
 
707
      a = b = m;
 
708
  }
 
709
  if (cmp != 0)
 
710
    return NULL;
 
711
  return &opTab[a];
 
712
}
 
713
 
 
714
GBool Gfx::checkArg(Object *arg, TchkType type) {
 
715
  switch (type) {
 
716
  case tchkBool:   return arg->isBool();
 
717
  case tchkInt:    return arg->isInt();
 
718
  case tchkNum:    return arg->isNum();
 
719
  case tchkString: return arg->isString();
 
720
  case tchkName:   return arg->isName();
 
721
  case tchkArray:  return arg->isArray();
 
722
  case tchkProps:  return arg->isDict() || arg->isName();
 
723
  case tchkSCN:    return arg->isNum() || arg->isName();
 
724
  case tchkNone:   return gFalse;
 
725
  }
 
726
  return gFalse;
 
727
}
 
728
 
 
729
int Gfx::getPos() {
 
730
  return parser ? parser->getPos() : -1;
 
731
}
 
732
 
 
733
//------------------------------------------------------------------------
 
734
// graphics state operators
 
735
//------------------------------------------------------------------------
 
736
 
 
737
void Gfx::opSave(Object args[], int numArgs) {
 
738
  saveState();
 
739
}
 
740
 
 
741
void Gfx::opRestore(Object args[], int numArgs) {
 
742
  restoreState();
 
743
}
 
744
 
 
745
void Gfx::opConcat(Object args[], int numArgs) {
 
746
  state->concatCTM(args[0].getNum(), args[1].getNum(),
 
747
                   args[2].getNum(), args[3].getNum(),
 
748
                   args[4].getNum(), args[5].getNum());
 
749
  out->updateCTM(state, args[0].getNum(), args[1].getNum(),
 
750
                 args[2].getNum(), args[3].getNum(),
 
751
                 args[4].getNum(), args[5].getNum());
 
752
  fontChanged = gTrue;
 
753
}
 
754
 
 
755
void Gfx::opSetDash(Object args[], int numArgs) {
 
756
  Array *a;
 
757
  int length;
 
758
  Object obj;
 
759
  double *dash;
 
760
  int i;
 
761
 
 
762
  a = args[0].getArray();
 
763
  length = a->getLength();
 
764
  if (length == 0) {
 
765
    dash = NULL;
 
766
  } else {
 
767
    dash = (double *)gmallocn(length, sizeof(double));
 
768
    for (i = 0; i < length; ++i) {
 
769
      dash[i] = a->get(i, &obj)->getNum();
 
770
      obj.free();
 
771
    }
 
772
  }
 
773
  state->setLineDash(dash, length, args[1].getNum());
 
774
  out->updateLineDash(state);
 
775
}
 
776
 
 
777
void Gfx::opSetFlat(Object args[], int numArgs) {
 
778
  state->setFlatness((int)args[0].getNum());
 
779
  out->updateFlatness(state);
 
780
}
 
781
 
 
782
void Gfx::opSetLineJoin(Object args[], int numArgs) {
 
783
  state->setLineJoin(args[0].getInt());
 
784
  out->updateLineJoin(state);
 
785
}
 
786
 
 
787
void Gfx::opSetLineCap(Object args[], int numArgs) {
 
788
  state->setLineCap(args[0].getInt());
 
789
  out->updateLineCap(state);
 
790
}
 
791
 
 
792
void Gfx::opSetMiterLimit(Object args[], int numArgs) {
 
793
  state->setMiterLimit(args[0].getNum());
 
794
  out->updateMiterLimit(state);
 
795
}
 
796
 
 
797
void Gfx::opSetLineWidth(Object args[], int numArgs) {
 
798
  state->setLineWidth(args[0].getNum());
 
799
  out->updateLineWidth(state);
 
800
}
 
801
 
 
802
void Gfx::opSetExtGState(Object args[], int numArgs) {
 
803
  Object obj1, obj2, obj3, obj4, obj5;
 
804
  GfxBlendMode mode;
 
805
  GBool haveFillOP;
 
806
  Function *funcs[4];
 
807
  GfxColor backdropColor;
 
808
  GBool haveBackdropColor;
 
809
  GfxColorSpace *blendingColorSpace;
 
810
  GBool alpha, isolated, knockout;
 
811
  int i;
 
812
 
 
813
  if (!res->lookupGState(args[0].getName(), &obj1)) {
 
814
    return;
 
815
  }
 
816
  if (!obj1.isDict()) {
 
817
    error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
 
818
    obj1.free();
 
819
    return;
 
820
  }
 
821
  if (printCommands) {
 
822
    printf("  gfx state dict: ");
 
823
    obj1.print();
 
824
    printf("\n");
 
825
  }
 
826
 
 
827
  // transparency support: blend mode, fill/stroke opacity
 
828
  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
 
829
    if (state->parseBlendMode(&obj2, &mode)) {
 
830
      state->setBlendMode(mode);
 
831
      out->updateBlendMode(state);
 
832
    } else {
 
833
      error(getPos(), "Invalid blend mode in ExtGState");
 
834
    }
 
835
  }
 
836
  obj2.free();
 
837
  if (obj1.dictLookup("ca", &obj2)->isNum()) {
 
838
    state->setFillOpacity(obj2.getNum());
 
839
    out->updateFillOpacity(state);
 
840
  }
 
841
  obj2.free();
 
842
  if (obj1.dictLookup("CA", &obj2)->isNum()) {
 
843
    state->setStrokeOpacity(obj2.getNum());
 
844
    out->updateStrokeOpacity(state);
 
845
  }
 
846
  obj2.free();
 
847
 
 
848
  // fill/stroke overprint
 
849
  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
 
850
    state->setFillOverprint(obj2.getBool());
 
851
    out->updateFillOverprint(state);
 
852
  }
 
853
  obj2.free();
 
854
  if (obj1.dictLookup("OP", &obj2)->isBool()) {
 
855
    state->setStrokeOverprint(obj2.getBool());
 
856
    out->updateStrokeOverprint(state);
 
857
    if (!haveFillOP) {
 
858
      state->setFillOverprint(obj2.getBool());
 
859
      out->updateFillOverprint(state);
 
860
    }
 
861
  }
 
862
  obj2.free();
 
863
 
 
864
  // stroke adjust
 
865
  if (obj1.dictLookup("SA", &obj2)->isBool()) {
 
866
    state->setStrokeAdjust(obj2.getBool());
 
867
    out->updateStrokeAdjust(state);
 
868
  }
 
869
  obj2.free();
 
870
 
 
871
  // transfer function
 
872
  if (obj1.dictLookup("TR2", &obj2)->isNull()) {
 
873
    obj2.free();
 
874
    obj1.dictLookup("TR", &obj2);
 
875
  }
 
876
  if (obj2.isName("Default") ||
 
877
      obj2.isName("Identity")) {
 
878
    funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
 
879
    state->setTransfer(funcs);
 
880
    out->updateTransfer(state);
 
881
  } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
 
882
    for (i = 0; i < 4; ++i) {
 
883
      obj2.arrayGet(i, &obj3);
 
884
      funcs[i] = Function::parse(&obj3);
 
885
      obj3.free();
 
886
      if (!funcs[i]) {
 
887
        break;
 
888
      }
 
889
    }
 
890
    if (i == 4) {
 
891
      state->setTransfer(funcs);
 
892
      out->updateTransfer(state);
 
893
    }
 
894
  } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
 
895
    if ((funcs[0] = Function::parse(&obj2))) {
 
896
      funcs[1] = funcs[2] = funcs[3] = NULL;
 
897
      state->setTransfer(funcs);
 
898
      out->updateTransfer(state);
 
899
    }
 
900
  } else if (!obj2.isNull()) {
 
901
    error(getPos(), "Invalid transfer function in ExtGState");
 
902
  }
 
903
  obj2.free();
 
904
 
 
905
  // soft mask
 
906
  if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
 
907
    if (obj2.isName("None")) {
 
908
      out->clearSoftMask(state);
 
909
    } else if (obj2.isDict()) {
 
910
      if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
 
911
        alpha = gTrue;
 
912
      } else { // "Luminosity"
 
913
        alpha = gFalse;
 
914
      }
 
915
      obj3.free();
 
916
      funcs[0] = NULL;
 
917
      if (!obj2.dictLookup("TR", &obj3)->isNull()) {
 
918
        funcs[0] = Function::parse(&obj3);
 
919
        if (funcs[0]->getInputSize() != 1 ||
 
920
            funcs[0]->getOutputSize() != 1) {
 
921
          error(getPos(),
 
922
                "Invalid transfer function in soft mask in ExtGState");
 
923
          delete funcs[0];
 
924
          funcs[0] = NULL;
 
925
        }
 
926
      }
 
927
      obj3.free();
 
928
      if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
 
929
        for (i = 0; i < gfxColorMaxComps; ++i) {
 
930
          backdropColor.c[i] = 0;
 
931
        }
 
932
        for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
 
933
          obj3.arrayGet(i, &obj4);
 
934
          if (obj4.isNum()) {
 
935
            backdropColor.c[i] = dblToCol(obj4.getNum());
 
936
          }
 
937
          obj4.free();
 
938
        }
 
939
      }
 
940
      obj3.free();
 
941
      if (obj2.dictLookup("G", &obj3)->isStream()) {
 
942
        if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
 
943
          blendingColorSpace = NULL;
 
944
          isolated = knockout = gFalse;
 
945
          if (!obj4.dictLookup("CS", &obj5)->isNull()) {
 
946
            blendingColorSpace = GfxColorSpace::parse(&obj5);
 
947
          }
 
948
          obj5.free();
 
949
          if (obj4.dictLookup("I", &obj5)->isBool()) {
 
950
            isolated = obj5.getBool();
 
951
          }
 
952
          obj5.free();
 
953
          if (obj4.dictLookup("K", &obj5)->isBool()) {
 
954
            knockout = obj5.getBool();
 
955
          }
 
956
          obj5.free();
 
957
          if (!haveBackdropColor) {
 
958
            if (blendingColorSpace) {
 
959
              blendingColorSpace->getDefaultColor(&backdropColor);
 
960
            } else {
 
961
              //~ need to get the parent or default color space (?)
 
962
              for (i = 0; i < gfxColorMaxComps; ++i) {
 
963
                backdropColor.c[i] = 0;
 
964
              }
 
965
            }
 
966
          }
 
967
          doSoftMask(&obj3, alpha, blendingColorSpace,
 
968
                     isolated, knockout, funcs[0], &backdropColor);
 
969
          if (funcs[0]) {
 
970
            delete funcs[0];
 
971
          }
 
972
        } else {
 
973
          error(getPos(), "Invalid soft mask in ExtGState - missing group");
 
974
        }
 
975
        obj4.free();
 
976
      } else {
 
977
        error(getPos(), "Invalid soft mask in ExtGState - missing group");
 
978
      }
 
979
      obj3.free();
 
980
    } else if (!obj2.isNull()) {
 
981
      error(getPos(), "Invalid soft mask in ExtGState");
 
982
    }
 
983
  }
 
984
  obj2.free();
 
985
 
 
986
  obj1.free();
 
987
}
 
988
 
 
989
void Gfx::doSoftMask(Object *str, GBool alpha,
 
990
                     GfxColorSpace *blendingColorSpace,
 
991
                     GBool isolated, GBool knockout,
 
992
                     Function *transferFunc, GfxColor *backdropColor) {
 
993
  Dict *dict, *resDict;
 
994
  double m[6], bbox[4];
 
995
  Object obj1, obj2;
 
996
  int i;
 
997
 
 
998
  // check for excessive recursion
 
999
  if (formDepth > 20) {
 
1000
    return;
 
1001
  }
 
1002
 
 
1003
  // get stream dict
 
1004
  dict = str->streamGetDict();
 
1005
 
 
1006
  // check form type
 
1007
  dict->lookup("FormType", &obj1);
 
1008
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
 
1009
    error(getPos(), "Unknown form type");
 
1010
  }
 
1011
  obj1.free();
 
1012
 
 
1013
  // get bounding box
 
1014
  dict->lookup("BBox", &obj1);
 
1015
  if (!obj1.isArray()) {
 
1016
    obj1.free();
 
1017
    error(getPos(), "Bad form bounding box");
 
1018
    return;
 
1019
  }
 
1020
  for (i = 0; i < 4; ++i) {
 
1021
    obj1.arrayGet(i, &obj2);
 
1022
    bbox[i] = obj2.getNum();
 
1023
    obj2.free();
 
1024
  }
 
1025
  obj1.free();
 
1026
 
 
1027
  // get matrix
 
1028
  dict->lookup("Matrix", &obj1);
 
1029
  if (obj1.isArray()) {
 
1030
    for (i = 0; i < 6; ++i) {
 
1031
      obj1.arrayGet(i, &obj2);
 
1032
      m[i] = obj2.getNum();
 
1033
      obj2.free();
 
1034
    }
 
1035
  } else {
 
1036
    m[0] = 1; m[1] = 0;
 
1037
    m[2] = 0; m[3] = 1;
 
1038
    m[4] = 0; m[5] = 0;
 
1039
  }
 
1040
  obj1.free();
 
1041
 
 
1042
  // get resources
 
1043
  dict->lookup("Resources", &obj1);
 
1044
  resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
 
1045
 
 
1046
  // draw it
 
1047
  ++formDepth;
 
1048
  doForm1(str, resDict, m, bbox, gTrue, gTrue,
 
1049
          blendingColorSpace, isolated, knockout,
 
1050
          alpha, transferFunc, backdropColor);
 
1051
  --formDepth;
 
1052
 
 
1053
  if (blendingColorSpace) {
 
1054
    delete blendingColorSpace;
 
1055
  }
 
1056
  obj1.free();
 
1057
}
 
1058
 
 
1059
void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
 
1060
}
 
1061
 
 
1062
//------------------------------------------------------------------------
 
1063
// color operators
 
1064
//------------------------------------------------------------------------
 
1065
 
 
1066
void Gfx::opSetFillGray(Object args[], int numArgs) {
 
1067
  GfxColor color;
 
1068
 
 
1069
  state->setFillPattern(NULL);
 
1070
  state->setFillColorSpace(new GfxDeviceGrayColorSpace());
 
1071
  out->updateFillColorSpace(state);
 
1072
  color.c[0] = dblToCol(args[0].getNum());
 
1073
  state->setFillColor(&color);
 
1074
  out->updateFillColor(state);
 
1075
}
 
1076
 
 
1077
void Gfx::opSetStrokeGray(Object args[], int numArgs) {
 
1078
  GfxColor color;
 
1079
 
 
1080
  state->setStrokePattern(NULL);
 
1081
  state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
 
1082
  out->updateStrokeColorSpace(state);
 
1083
  color.c[0] = dblToCol(args[0].getNum());
 
1084
  state->setStrokeColor(&color);
 
1085
  out->updateStrokeColor(state);
 
1086
}
 
1087
 
 
1088
void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
 
1089
  GfxColor color;
 
1090
  int i;
 
1091
 
 
1092
  state->setFillPattern(NULL);
 
1093
  state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
 
1094
  out->updateFillColorSpace(state);
 
1095
  for (i = 0; i < 4; ++i) {
 
1096
    color.c[i] = dblToCol(args[i].getNum());
 
1097
  }
 
1098
  state->setFillColor(&color);
 
1099
  out->updateFillColor(state);
 
1100
}
 
1101
 
 
1102
void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
 
1103
  GfxColor color;
 
1104
  int i;
 
1105
 
 
1106
  state->setStrokePattern(NULL);
 
1107
  state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
 
1108
  out->updateStrokeColorSpace(state);
 
1109
  for (i = 0; i < 4; ++i) {
 
1110
    color.c[i] = dblToCol(args[i].getNum());
 
1111
  }
 
1112
  state->setStrokeColor(&color);
 
1113
  out->updateStrokeColor(state);
 
1114
}
 
1115
 
 
1116
void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
 
1117
  GfxColor color;
 
1118
  int i;
 
1119
 
 
1120
  state->setFillPattern(NULL);
 
1121
  state->setFillColorSpace(new GfxDeviceRGBColorSpace());
 
1122
  out->updateFillColorSpace(state);
 
1123
  for (i = 0; i < 3; ++i) {
 
1124
    color.c[i] = dblToCol(args[i].getNum());
 
1125
  }
 
1126
  state->setFillColor(&color);
 
1127
  out->updateFillColor(state);
 
1128
}
 
1129
 
 
1130
void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
 
1131
  GfxColor color;
 
1132
  int i;
 
1133
 
 
1134
  state->setStrokePattern(NULL);
 
1135
  state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
 
1136
  out->updateStrokeColorSpace(state);
 
1137
  for (i = 0; i < 3; ++i) {
 
1138
    color.c[i] = dblToCol(args[i].getNum());
 
1139
  }
 
1140
  state->setStrokeColor(&color);
 
1141
  out->updateStrokeColor(state);
 
1142
}
 
1143
 
 
1144
void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
 
1145
  Object obj;
 
1146
  GfxColorSpace *colorSpace;
 
1147
  GfxColor color;
 
1148
 
 
1149
  state->setFillPattern(NULL);
 
1150
  res->lookupColorSpace(args[0].getName(), &obj);
 
1151
  if (obj.isNull()) {
 
1152
    colorSpace = GfxColorSpace::parse(&args[0]);
 
1153
  } else {
 
1154
    colorSpace = GfxColorSpace::parse(&obj);
 
1155
  }
 
1156
  obj.free();
 
1157
  if (colorSpace) {
 
1158
    state->setFillColorSpace(colorSpace);
 
1159
    out->updateFillColorSpace(state);
 
1160
    colorSpace->getDefaultColor(&color);
 
1161
    state->setFillColor(&color);
 
1162
    out->updateFillColor(state);
 
1163
  } else {
 
1164
    error(getPos(), "Bad color space (fill)");
 
1165
  }
 
1166
}
 
1167
 
 
1168
void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
 
1169
  Object obj;
 
1170
  GfxColorSpace *colorSpace;
 
1171
  GfxColor color;
 
1172
 
 
1173
  state->setStrokePattern(NULL);
 
1174
  res->lookupColorSpace(args[0].getName(), &obj);
 
1175
  if (obj.isNull()) {
 
1176
    colorSpace = GfxColorSpace::parse(&args[0]);
 
1177
  } else {
 
1178
    colorSpace = GfxColorSpace::parse(&obj);
 
1179
  }
 
1180
  obj.free();
 
1181
  if (colorSpace) {
 
1182
    state->setStrokeColorSpace(colorSpace);
 
1183
    out->updateStrokeColorSpace(state);
 
1184
    colorSpace->getDefaultColor(&color);
 
1185
    state->setStrokeColor(&color);
 
1186
    out->updateStrokeColor(state);
 
1187
  } else {
 
1188
    error(getPos(), "Bad color space (stroke)");
 
1189
  }
 
1190
}
 
1191
 
 
1192
void Gfx::opSetFillColor(Object args[], int numArgs) {
 
1193
  GfxColor color;
 
1194
  int i;
 
1195
 
 
1196
  if (numArgs != state->getFillColorSpace()->getNComps()) {
 
1197
    error(getPos(), "Incorrect number of arguments in 'sc' command");
 
1198
    return;
 
1199
  }
 
1200
  state->setFillPattern(NULL);
 
1201
  for (i = 0; i < numArgs; ++i) {
 
1202
    color.c[i] = dblToCol(args[i].getNum());
 
1203
  }
 
1204
  state->setFillColor(&color);
 
1205
  out->updateFillColor(state);
 
1206
}
 
1207
 
 
1208
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
 
1209
  GfxColor color;
 
1210
  int i;
 
1211
 
 
1212
  if (numArgs != state->getStrokeColorSpace()->getNComps()) {
 
1213
    error(getPos(), "Incorrect number of arguments in 'SC' command");
 
1214
    return;
 
1215
  }
 
1216
  state->setStrokePattern(NULL);
 
1217
  for (i = 0; i < numArgs; ++i) {
 
1218
    color.c[i] = dblToCol(args[i].getNum());
 
1219
  }
 
1220
  state->setStrokeColor(&color);
 
1221
  out->updateStrokeColor(state);
 
1222
}
 
1223
 
 
1224
void Gfx::opSetFillColorN(Object args[], int numArgs) {
 
1225
  GfxColor color;
 
1226
  GfxPattern *pattern;
 
1227
  int i;
 
1228
 
 
1229
  if (state->getFillColorSpace()->getMode() == csPattern) {
 
1230
    if (numArgs > 1) {
 
1231
      if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
 
1232
          numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
 
1233
                             ->getUnder()->getNComps()) {
 
1234
        error(getPos(), "Incorrect number of arguments in 'scn' command");
 
1235
        return;
 
1236
      }
 
1237
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
 
1238
        if (args[i].isNum()) {
 
1239
          color.c[i] = dblToCol(args[i].getNum());
 
1240
        }
 
1241
      }
 
1242
      state->setFillColor(&color);
 
1243
      out->updateFillColor(state);
 
1244
    }
 
1245
    if (args[numArgs-1].isName() &&
 
1246
        (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
 
1247
      state->setFillPattern(pattern);
 
1248
    }
 
1249
 
 
1250
  } else {
 
1251
    if (numArgs != state->getFillColorSpace()->getNComps()) {
 
1252
      error(getPos(), "Incorrect number of arguments in 'scn' command");
 
1253
      return;
 
1254
    }
 
1255
    state->setFillPattern(NULL);
 
1256
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
 
1257
      if (args[i].isNum()) {
 
1258
        color.c[i] = dblToCol(args[i].getNum());
 
1259
      }
 
1260
    }
 
1261
    state->setFillColor(&color);
 
1262
    out->updateFillColor(state);
 
1263
  }
 
1264
}
 
1265
 
 
1266
void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
 
1267
  GfxColor color;
 
1268
  GfxPattern *pattern;
 
1269
  int i;
 
1270
 
 
1271
  if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1272
    if (numArgs > 1) {
 
1273
      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
 
1274
               ->getUnder() ||
 
1275
          numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
 
1276
                             ->getUnder()->getNComps()) {
 
1277
        error(getPos(), "Incorrect number of arguments in 'SCN' command");
 
1278
        return;
 
1279
      }
 
1280
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
 
1281
        if (args[i].isNum()) {
 
1282
          color.c[i] = dblToCol(args[i].getNum());
 
1283
        }
 
1284
      }
 
1285
      state->setStrokeColor(&color);
 
1286
      out->updateStrokeColor(state);
 
1287
    }
 
1288
    if (args[numArgs-1].isName() &&
 
1289
        (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
 
1290
      state->setStrokePattern(pattern);
 
1291
    }
 
1292
 
 
1293
  } else {
 
1294
    if (numArgs != state->getStrokeColorSpace()->getNComps()) {
 
1295
      error(getPos(), "Incorrect number of arguments in 'SCN' command");
 
1296
      return;
 
1297
    }
 
1298
    state->setStrokePattern(NULL);
 
1299
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
 
1300
      if (args[i].isNum()) {
 
1301
        color.c[i] = dblToCol(args[i].getNum());
 
1302
      }
 
1303
    }
 
1304
    state->setStrokeColor(&color);
 
1305
    out->updateStrokeColor(state);
 
1306
  }
 
1307
}
 
1308
 
 
1309
//------------------------------------------------------------------------
 
1310
// path segment operators
 
1311
//------------------------------------------------------------------------
 
1312
 
 
1313
void Gfx::opMoveTo(Object args[], int numArgs) {
 
1314
  state->moveTo(args[0].getNum(), args[1].getNum());
 
1315
}
 
1316
 
 
1317
void Gfx::opLineTo(Object args[], int numArgs) {
 
1318
  if (!state->isCurPt()) {
 
1319
    error(getPos(), "No current point in lineto");
 
1320
    return;
 
1321
  }
 
1322
  state->lineTo(args[0].getNum(), args[1].getNum());
 
1323
}
 
1324
 
 
1325
void Gfx::opCurveTo(Object args[], int numArgs) {
 
1326
  double x1, y1, x2, y2, x3, y3;
 
1327
 
 
1328
  if (!state->isCurPt()) {
 
1329
    error(getPos(), "No current point in curveto");
 
1330
    return;
 
1331
  }
 
1332
  x1 = args[0].getNum();
 
1333
  y1 = args[1].getNum();
 
1334
  x2 = args[2].getNum();
 
1335
  y2 = args[3].getNum();
 
1336
  x3 = args[4].getNum();
 
1337
  y3 = args[5].getNum();
 
1338
  state->curveTo(x1, y1, x2, y2, x3, y3);
 
1339
}
 
1340
 
 
1341
void Gfx::opCurveTo1(Object args[], int numArgs) {
 
1342
  double x1, y1, x2, y2, x3, y3;
 
1343
 
 
1344
  if (!state->isCurPt()) {
 
1345
    error(getPos(), "No current point in curveto1");
 
1346
    return;
 
1347
  }
 
1348
  x1 = state->getCurX();
 
1349
  y1 = state->getCurY();
 
1350
  x2 = args[0].getNum();
 
1351
  y2 = args[1].getNum();
 
1352
  x3 = args[2].getNum();
 
1353
  y3 = args[3].getNum();
 
1354
  state->curveTo(x1, y1, x2, y2, x3, y3);
 
1355
}
 
1356
 
 
1357
void Gfx::opCurveTo2(Object args[], int numArgs) {
 
1358
  double x1, y1, x2, y2, x3, y3;
 
1359
 
 
1360
  if (!state->isCurPt()) {
 
1361
    error(getPos(), "No current point in curveto2");
 
1362
    return;
 
1363
  }
 
1364
  x1 = args[0].getNum();
 
1365
  y1 = args[1].getNum();
 
1366
  x2 = args[2].getNum();
 
1367
  y2 = args[3].getNum();
 
1368
  x3 = x2;
 
1369
  y3 = y2;
 
1370
  state->curveTo(x1, y1, x2, y2, x3, y3);
 
1371
}
 
1372
 
 
1373
void Gfx::opRectangle(Object args[], int numArgs) {
 
1374
  double x, y, w, h;
 
1375
 
 
1376
  x = args[0].getNum();
 
1377
  y = args[1].getNum();
 
1378
  w = args[2].getNum();
 
1379
  h = args[3].getNum();
 
1380
  state->moveTo(x, y);
 
1381
  state->lineTo(x + w, y);
 
1382
  state->lineTo(x + w, y + h);
 
1383
  state->lineTo(x, y + h);
 
1384
  state->closePath();
 
1385
}
 
1386
 
 
1387
void Gfx::opClosePath(Object args[], int numArgs) {
 
1388
  if (!state->isCurPt()) {
 
1389
    error(getPos(), "No current point in closepath");
 
1390
    return;
 
1391
  }
 
1392
  state->closePath();
 
1393
}
 
1394
 
 
1395
//------------------------------------------------------------------------
 
1396
// path painting operators
 
1397
//------------------------------------------------------------------------
 
1398
 
 
1399
void Gfx::opEndPath(Object args[], int numArgs) {
 
1400
  doEndPath();
 
1401
}
 
1402
 
 
1403
void Gfx::opStroke(Object args[], int numArgs) {
 
1404
  if (!state->isCurPt()) {
 
1405
    //error(getPos(), "No path in stroke");
 
1406
    return;
 
1407
  }
 
1408
  if (state->isPath()) {
 
1409
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1410
      doPatternStroke();
 
1411
    } else {
 
1412
      out->stroke(state);
 
1413
    }
 
1414
  }
 
1415
  doEndPath();
 
1416
}
 
1417
 
 
1418
void Gfx::opCloseStroke(Object args[], int numArgs) {
 
1419
  if (!state->isCurPt()) {
 
1420
    //error(getPos(), "No path in closepath/stroke");
 
1421
    return;
 
1422
  }
 
1423
  if (state->isPath()) {
 
1424
    state->closePath();
 
1425
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1426
      doPatternStroke();
 
1427
    } else {
 
1428
      out->stroke(state);
 
1429
    }
 
1430
  }
 
1431
  doEndPath();
 
1432
}
 
1433
 
 
1434
void Gfx::opFill(Object args[], int numArgs) {
 
1435
  if (!state->isCurPt()) {
 
1436
    //error(getPos(), "No path in fill");
 
1437
    return;
 
1438
  }
 
1439
  if (state->isPath()) {
 
1440
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1441
      doPatternFill(gFalse);
 
1442
    } else {
 
1443
      out->fill(state);
 
1444
    }
 
1445
  }
 
1446
  doEndPath();
 
1447
}
 
1448
 
 
1449
void Gfx::opEOFill(Object args[], int numArgs) {
 
1450
  if (!state->isCurPt()) {
 
1451
    //error(getPos(), "No path in eofill");
 
1452
    return;
 
1453
  }
 
1454
  if (state->isPath()) {
 
1455
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1456
      doPatternFill(gTrue);
 
1457
    } else {
 
1458
      out->eoFill(state);
 
1459
    }
 
1460
  }
 
1461
  doEndPath();
 
1462
}
 
1463
 
 
1464
void Gfx::opFillStroke(Object args[], int numArgs) {
 
1465
  if (!state->isCurPt()) {
 
1466
    //error(getPos(), "No path in fill/stroke");
 
1467
    return;
 
1468
  }
 
1469
  if (state->isPath()) {
 
1470
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1471
      doPatternFill(gFalse);
 
1472
    } else {
 
1473
      out->fill(state);
 
1474
    }
 
1475
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1476
      doPatternStroke();
 
1477
    } else {
 
1478
      out->stroke(state);
 
1479
    }
 
1480
  }
 
1481
  doEndPath();
 
1482
}
 
1483
 
 
1484
void Gfx::opCloseFillStroke(Object args[], int numArgs) {
 
1485
  if (!state->isCurPt()) {
 
1486
    //error(getPos(), "No path in closepath/fill/stroke");
 
1487
    return;
 
1488
  }
 
1489
  if (state->isPath()) {
 
1490
    state->closePath();
 
1491
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1492
      doPatternFill(gFalse);
 
1493
    } else {
 
1494
      out->fill(state);
 
1495
    }
 
1496
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1497
      doPatternStroke();
 
1498
    } else {
 
1499
      out->stroke(state);
 
1500
    }
 
1501
  }
 
1502
  doEndPath();
 
1503
}
 
1504
 
 
1505
void Gfx::opEOFillStroke(Object args[], int numArgs) {
 
1506
  if (!state->isCurPt()) {
 
1507
    //error(getPos(), "No path in eofill/stroke");
 
1508
    return;
 
1509
  }
 
1510
  if (state->isPath()) {
 
1511
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1512
      doPatternFill(gTrue);
 
1513
    } else {
 
1514
      out->eoFill(state);
 
1515
    }
 
1516
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1517
      doPatternStroke();
 
1518
    } else {
 
1519
      out->stroke(state);
 
1520
    }
 
1521
  }
 
1522
  doEndPath();
 
1523
}
 
1524
 
 
1525
void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
 
1526
  if (!state->isCurPt()) {
 
1527
    //error(getPos(), "No path in closepath/eofill/stroke");
 
1528
    return;
 
1529
  }
 
1530
  if (state->isPath()) {
 
1531
    state->closePath();
 
1532
    if (state->getFillColorSpace()->getMode() == csPattern) {
 
1533
      doPatternFill(gTrue);
 
1534
    } else {
 
1535
      out->eoFill(state);
 
1536
    }
 
1537
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
 
1538
      doPatternStroke();
 
1539
    } else {
 
1540
      out->stroke(state);
 
1541
    }
 
1542
  }
 
1543
  doEndPath();
 
1544
}
 
1545
 
 
1546
void Gfx::doPatternFill(GBool eoFill) {
 
1547
  GfxPattern *pattern;
 
1548
 
 
1549
  // this is a bit of a kludge -- patterns can be really slow, so we
 
1550
  // skip them if we're only doing text extraction, since they almost
 
1551
  // certainly don't contain any text
 
1552
  if (!out->needNonText()) {
 
1553
    return;
 
1554
  }
 
1555
 
 
1556
  if (!(pattern = state->getFillPattern())) {
 
1557
    return;
 
1558
  }
 
1559
  switch (pattern->getType()) {
 
1560
  case 1:
 
1561
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill);
 
1562
    break;
 
1563
  case 2:
 
1564
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);
 
1565
    break;
 
1566
  default:
 
1567
    error(getPos(), "Unimplemented pattern type (%d) in fill",
 
1568
          pattern->getType());
 
1569
    break;
 
1570
  }
 
1571
}
 
1572
 
 
1573
void Gfx::doPatternStroke() {
 
1574
  GfxPattern *pattern;
 
1575
 
 
1576
  // this is a bit of a kludge -- patterns can be really slow, so we
 
1577
  // skip them if we're only doing text extraction, since they almost
 
1578
  // certainly don't contain any text
 
1579
  if (!out->needNonText()) {
 
1580
    return;
 
1581
  }
 
1582
 
 
1583
  if (!(pattern = state->getStrokePattern())) {
 
1584
    return;
 
1585
  }
 
1586
  switch (pattern->getType()) {
 
1587
  case 1:
 
1588
    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse);
 
1589
    break;
 
1590
  case 2:
 
1591
    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);
 
1592
    break;
 
1593
  default:
 
1594
    error(getPos(), "Unimplemented pattern type (%d) in stroke",
 
1595
          pattern->getType());
 
1596
    break;
 
1597
  }
 
1598
}
 
1599
 
 
1600
void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
 
1601
                              GBool stroke, GBool eoFill) {
 
1602
  GfxPatternColorSpace *patCS;
 
1603
  GfxColorSpace *cs;
 
1604
  GfxPath *savedPath;
 
1605
  double xMin, yMin, xMax, yMax, x, y, x1, y1;
 
1606
  double cxMin, cyMin, cxMax, cyMax;
 
1607
  int xi0, yi0, xi1, yi1, xi, yi;
 
1608
  double *ctm, *btm, *ptm;
 
1609
  double m[6], ictm[6], m1[6], imb[6];
 
1610
  double det;
 
1611
  double xstep, ystep;
 
1612
  int i;
 
1613
 
 
1614
  // get color space
 
1615
  patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
 
1616
                                          : state->getFillColorSpace());
 
1617
 
 
1618
  // construct a (pattern space) -> (current space) transform matrix
 
1619
  ctm = state->getCTM();
 
1620
  btm = baseMatrix;
 
1621
  ptm = tPat->getMatrix();
 
1622
  // iCTM = invert CTM
 
1623
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
 
1624
  ictm[0] = ctm[3] * det;
 
1625
  ictm[1] = -ctm[1] * det;
 
1626
  ictm[2] = -ctm[2] * det;
 
1627
  ictm[3] = ctm[0] * det;
 
1628
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
 
1629
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
 
1630
  // m1 = PTM * BTM = PTM * base transform matrix
 
1631
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
 
1632
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
 
1633
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
 
1634
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
 
1635
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
 
1636
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
 
1637
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
 
1638
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
 
1639
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
 
1640
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
 
1641
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
 
1642
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
 
1643
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
 
1644
 
 
1645
  // construct a (device space) -> (pattern space) transform matrix
 
1646
  det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
 
1647
  imb[0] = m1[3] * det;
 
1648
  imb[1] = -m1[1] * det;
 
1649
  imb[2] = -m1[2] * det;
 
1650
  imb[3] = m1[0] * det;
 
1651
  imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
 
1652
  imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
 
1653
 
 
1654
  // save current graphics state
 
1655
  savedPath = state->getPath()->copy();
 
1656
  saveState();
 
1657
 
 
1658
  // set underlying color space (for uncolored tiling patterns); set
 
1659
  // various other parameters (stroke color, line width) to match
 
1660
  // Adobe's behavior
 
1661
  if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
 
1662
    state->setFillColorSpace(cs->copy());
 
1663
    out->updateFillColorSpace(state);
 
1664
    state->setStrokeColorSpace(cs->copy());
 
1665
    out->updateStrokeColorSpace(state);
 
1666
    state->setStrokeColor(state->getFillColor());
 
1667
  } else {
 
1668
    state->setFillColorSpace(new GfxDeviceGrayColorSpace());
 
1669
    out->updateFillColorSpace(state);
 
1670
    state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
 
1671
    out->updateStrokeColorSpace(state);
 
1672
  }
 
1673
  state->setFillPattern(NULL);
 
1674
  out->updateFillColor(state);
 
1675
  state->setStrokePattern(NULL);
 
1676
  out->updateStrokeColor(state);
 
1677
  if (!stroke) {
 
1678
    state->setLineWidth(0);
 
1679
    out->updateLineWidth(state);
 
1680
  }
 
1681
 
 
1682
  // clip to current path
 
1683
  if (stroke) {
 
1684
    state->clipToStrokePath();
 
1685
    out->clipToStrokePath(state);
 
1686
  } else {
 
1687
    state->clip();
 
1688
    if (eoFill) {
 
1689
      out->eoClip(state);
 
1690
    } else {
 
1691
      out->clip(state);
 
1692
    }
 
1693
  }
 
1694
  state->clearPath();
 
1695
 
 
1696
  // get the clip region, check for empty
 
1697
  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
 
1698
  if (cxMin > cxMax || cyMin > cyMax) {
 
1699
    goto err;
 
1700
  }
 
1701
 
 
1702
  // transform clip region bbox to pattern space
 
1703
  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
 
1704
  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
 
1705
  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
 
1706
  y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
 
1707
  if (x1 < xMin) {
 
1708
    xMin = x1;
 
1709
  } else if (x1 > xMax) {
 
1710
    xMax = x1;
 
1711
  }
 
1712
  if (y1 < yMin) {
 
1713
    yMin = y1;
 
1714
  } else if (y1 > yMax) {
 
1715
    yMax = y1;
 
1716
  }
 
1717
  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
 
1718
  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
 
1719
  if (x1 < xMin) {
 
1720
    xMin = x1;
 
1721
  } else if (x1 > xMax) {
 
1722
    xMax = x1;
 
1723
  }
 
1724
  if (y1 < yMin) {
 
1725
    yMin = y1;
 
1726
  } else if (y1 > yMax) {
 
1727
    yMax = y1;
 
1728
  }
 
1729
  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
 
1730
  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
 
1731
  if (x1 < xMin) {
 
1732
    xMin = x1;
 
1733
  } else if (x1 > xMax) {
 
1734
    xMax = x1;
 
1735
  }
 
1736
  if (y1 < yMin) {
 
1737
    yMin = y1;
 
1738
  } else if (y1 > yMax) {
 
1739
    yMax = y1;
 
1740
  }
 
1741
 
 
1742
  // draw the pattern
 
1743
  //~ this should treat negative steps differently -- start at right/top
 
1744
  //~ edge instead of left/bottom (?)
 
1745
  xstep = fabs(tPat->getXStep());
 
1746
  ystep = fabs(tPat->getYStep());
 
1747
  xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);
 
1748
  xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;
 
1749
  yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);
 
1750
  yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1;
 
1751
  for (i = 0; i < 4; ++i) {
 
1752
    m1[i] = m[i];
 
1753
  }
 
1754
  if (out->useTilingPatternFill()) {
 
1755
    m1[4] = m[4];
 
1756
    m1[5] = m[5];
 
1757
    out->tilingPatternFill(state, tPat->getContentStream(),
 
1758
                           tPat->getPaintType(), tPat->getResDict(),
 
1759
                           m1, tPat->getBBox(),
 
1760
                           xi0, yi0, xi1, yi1, xstep, ystep);
 
1761
  } else {
 
1762
    for (yi = yi0; yi < yi1; ++yi) {
 
1763
      for (xi = xi0; xi < xi1; ++xi) {
 
1764
        x = xi * xstep;
 
1765
        y = yi * ystep;
 
1766
        m1[4] = x * m[0] + y * m[2] + m[4];
 
1767
        m1[5] = x * m[1] + y * m[3] + m[5];
 
1768
        doForm1(tPat->getContentStream(), tPat->getResDict(),
 
1769
                m1, tPat->getBBox());
 
1770
      }
 
1771
    }
 
1772
  }
 
1773
 
 
1774
  // restore graphics state
 
1775
 err:
 
1776
  restoreState();
 
1777
  state->setPath(savedPath);
 
1778
}
 
1779
 
 
1780
void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
 
1781
                               GBool stroke, GBool eoFill) {
 
1782
  GfxShading *shading;
 
1783
  GfxPath *savedPath;
 
1784
  double *ctm, *btm, *ptm;
 
1785
  double m[6], ictm[6], m1[6];
 
1786
  double xMin, yMin, xMax, yMax;
 
1787
  double det;
 
1788
 
 
1789
  shading = sPat->getShading();
 
1790
 
 
1791
  // save current graphics state
 
1792
  savedPath = state->getPath()->copy();
 
1793
  saveState();
 
1794
 
 
1795
  // clip to bbox
 
1796
  if (shading->getHasBBox()) {
 
1797
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
 
1798
    state->moveTo(xMin, yMin);
 
1799
    state->lineTo(xMax, yMin);
 
1800
    state->lineTo(xMax, yMax);
 
1801
    state->lineTo(xMin, yMax);
 
1802
    state->closePath();
 
1803
    state->clip();
 
1804
    out->clip(state);
 
1805
    state->setPath(savedPath->copy());
 
1806
  }
 
1807
 
 
1808
  // clip to current path
 
1809
  if (stroke) {
 
1810
    state->clipToStrokePath();
 
1811
    out->clipToStrokePath(state);
 
1812
  } else {
 
1813
    state->clip();
 
1814
    if (eoFill) {
 
1815
      out->eoClip(state);
 
1816
    } else {
 
1817
      out->clip(state);
 
1818
    }
 
1819
  }
 
1820
 
 
1821
  // set the color space
 
1822
  state->setFillColorSpace(shading->getColorSpace()->copy());
 
1823
  out->updateFillColorSpace(state);
 
1824
 
 
1825
  // background color fill
 
1826
  if (shading->getHasBackground()) {
 
1827
    state->setFillColor(shading->getBackground());
 
1828
    out->updateFillColor(state);
 
1829
    out->fill(state);
 
1830
  }
 
1831
  state->clearPath();
 
1832
 
 
1833
  // construct a (pattern space) -> (current space) transform matrix
 
1834
  ctm = state->getCTM();
 
1835
  btm = baseMatrix;
 
1836
  ptm = sPat->getMatrix();
 
1837
  // iCTM = invert CTM
 
1838
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
 
1839
  ictm[0] = ctm[3] * det;
 
1840
  ictm[1] = -ctm[1] * det;
 
1841
  ictm[2] = -ctm[2] * det;
 
1842
  ictm[3] = ctm[0] * det;
 
1843
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
 
1844
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
 
1845
  // m1 = PTM * BTM = PTM * base transform matrix
 
1846
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
 
1847
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
 
1848
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
 
1849
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
 
1850
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
 
1851
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
 
1852
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
 
1853
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
 
1854
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
 
1855
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
 
1856
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
 
1857
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
 
1858
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
 
1859
 
 
1860
  // set the new matrix
 
1861
  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
 
1862
  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
 
1863
 
 
1864
#if 1 //~tmp: turn off anti-aliasing temporarily
 
1865
  GBool vaa = out->getVectorAntialias();
 
1866
  if (vaa) {
 
1867
    out->setVectorAntialias(gFalse);
 
1868
  }
 
1869
#endif
 
1870
 
 
1871
  // do shading type-specific operations
 
1872
  switch (shading->getType()) {
 
1873
  case 1:
 
1874
    doFunctionShFill((GfxFunctionShading *)shading);
 
1875
    break;
 
1876
  case 2:
 
1877
    doAxialShFill((GfxAxialShading *)shading);
 
1878
    break;
 
1879
  case 3:
 
1880
    doRadialShFill((GfxRadialShading *)shading);
 
1881
    break;
 
1882
  case 4:
 
1883
  case 5:
 
1884
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
 
1885
    break;
 
1886
  case 6:
 
1887
  case 7:
 
1888
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
 
1889
    break;
 
1890
  }
 
1891
 
 
1892
#if 1 //~tmp: turn off anti-aliasing temporarily
 
1893
  if (vaa) {
 
1894
    out->setVectorAntialias(gTrue);
 
1895
  }
 
1896
#endif
 
1897
 
 
1898
  // restore graphics state
 
1899
  restoreState();
 
1900
  state->setPath(savedPath);
 
1901
}
 
1902
 
 
1903
void Gfx::opShFill(Object args[], int numArgs) {
 
1904
  GfxShading *shading;
 
1905
  GfxPath *savedPath;
 
1906
  double xMin, yMin, xMax, yMax;
 
1907
 
 
1908
  if (!(shading = res->lookupShading(args[0].getName()))) {
 
1909
    return;
 
1910
  }
 
1911
 
 
1912
  // save current graphics state
 
1913
  savedPath = state->getPath()->copy();
 
1914
  saveState();
 
1915
 
 
1916
  // clip to bbox
 
1917
  if (shading->getHasBBox()) {
 
1918
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
 
1919
    state->moveTo(xMin, yMin);
 
1920
    state->lineTo(xMax, yMin);
 
1921
    state->lineTo(xMax, yMax);
 
1922
    state->lineTo(xMin, yMax);
 
1923
    state->closePath();
 
1924
    state->clip();
 
1925
    out->clip(state);
 
1926
    state->clearPath();
 
1927
  }
 
1928
 
 
1929
  // set the color space
 
1930
  state->setFillColorSpace(shading->getColorSpace()->copy());
 
1931
  out->updateFillColorSpace(state);
 
1932
 
 
1933
#if 1 //~tmp: turn off anti-aliasing temporarily
 
1934
  GBool vaa = out->getVectorAntialias();
 
1935
  if (vaa) {
 
1936
    out->setVectorAntialias(gFalse);
 
1937
  }
 
1938
#endif
 
1939
 
 
1940
  // do shading type-specific operations
 
1941
  switch (shading->getType()) {
 
1942
  case 1:
 
1943
    doFunctionShFill((GfxFunctionShading *)shading);
 
1944
    break;
 
1945
  case 2:
 
1946
    doAxialShFill((GfxAxialShading *)shading);
 
1947
    break;
 
1948
  case 3:
 
1949
    doRadialShFill((GfxRadialShading *)shading);
 
1950
    break;
 
1951
  case 4:
 
1952
  case 5:
 
1953
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
 
1954
    break;
 
1955
  case 6:
 
1956
  case 7:
 
1957
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
 
1958
    break;
 
1959
  }
 
1960
 
 
1961
#if 1 //~tmp: turn off anti-aliasing temporarily
 
1962
  if (vaa) {
 
1963
    out->setVectorAntialias(gTrue);
 
1964
  }
 
1965
#endif
 
1966
 
 
1967
  // restore graphics state
 
1968
  restoreState();
 
1969
  state->setPath(savedPath);
 
1970
 
 
1971
  delete shading;
 
1972
}
 
1973
 
 
1974
void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
 
1975
  double x0, y0, x1, y1;
 
1976
  GfxColor colors[4];
 
1977
 
 
1978
  if (out->useShadedFills() &&
 
1979
      out->functionShadedFill(state, shading)) {
 
1980
    return;
 
1981
  }
 
1982
 
 
1983
  shading->getDomain(&x0, &y0, &x1, &y1);
 
1984
  shading->getColor(x0, y0, &colors[0]);
 
1985
  shading->getColor(x0, y1, &colors[1]);
 
1986
  shading->getColor(x1, y0, &colors[2]);
 
1987
  shading->getColor(x1, y1, &colors[3]);
 
1988
  doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
 
1989
}
 
1990
 
 
1991
void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
 
1992
                            double x0, double y0,
 
1993
                            double x1, double y1,
 
1994
                            GfxColor *colors, int depth) {
 
1995
  GfxColor fillColor;
 
1996
  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
 
1997
  GfxColor colors2[4];
 
1998
  double *matrix;
 
1999
  double xM, yM;
 
2000
  int nComps, i, j;
 
2001
 
 
2002
  nComps = shading->getColorSpace()->getNComps();
 
2003
  matrix = shading->getMatrix();
 
2004
 
 
2005
  // compare the four corner colors
 
2006
  for (i = 0; i < 4; ++i) {
 
2007
    for (j = 0; j < nComps; ++j) {
 
2008
      if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
 
2009
        break;
 
2010
      }
 
2011
    }
 
2012
    if (j < nComps) {
 
2013
      break;
 
2014
    }
 
2015
  }
 
2016
 
 
2017
  // center of the rectangle
 
2018
  xM = 0.5 * (x0 + x1);
 
2019
  yM = 0.5 * (y0 + y1);
 
2020
 
 
2021
  // the four corner colors are close (or we hit the recursive limit)
 
2022
  // -- fill the rectangle; but require at least one subdivision
 
2023
  // (depth==0) to avoid problems when the four outer corners of the
 
2024
  // shaded region are the same color
 
2025
  if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
 
2026
 
 
2027
    // use the center color
 
2028
    shading->getColor(xM, yM, &fillColor);
 
2029
    state->setFillColor(&fillColor);
 
2030
    out->updateFillColor(state);
 
2031
 
 
2032
    // fill the rectangle
 
2033
    state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
 
2034
                  x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
 
2035
    state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
 
2036
                  x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
 
2037
    state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
 
2038
                  x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
 
2039
    state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
 
2040
                  x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
 
2041
    state->closePath();
 
2042
    out->fill(state);
 
2043
    state->clearPath();
 
2044
 
 
2045
  // the four corner colors are not close enough -- subdivide the
 
2046
  // rectangle
 
2047
  } else {
 
2048
 
 
2049
    // colors[0]       colorM0       colors[2]
 
2050
    //   (x0,y0)       (xM,y0)       (x1,y0)
 
2051
    //         +----------+----------+
 
2052
    //         |          |          |
 
2053
    //         |    UL    |    UR    |
 
2054
    // color0M |       colorMM       | color1M
 
2055
    // (x0,yM) +----------+----------+ (x1,yM)
 
2056
    //         |       (xM,yM)       |
 
2057
    //         |    LL    |    LR    |
 
2058
    //         |          |          |
 
2059
    //         +----------+----------+
 
2060
    // colors[1]       colorM1       colors[3]
 
2061
    //   (x0,y1)       (xM,y1)       (x1,y1)
 
2062
 
 
2063
    shading->getColor(x0, yM, &color0M);
 
2064
    shading->getColor(x1, yM, &color1M);
 
2065
    shading->getColor(xM, y0, &colorM0);
 
2066
    shading->getColor(xM, y1, &colorM1);
 
2067
    shading->getColor(xM, yM, &colorMM);
 
2068
 
 
2069
    // upper-left sub-rectangle
 
2070
    colors2[0] = colors[0];
 
2071
    colors2[1] = color0M;
 
2072
    colors2[2] = colorM0;
 
2073
    colors2[3] = colorMM;
 
2074
    doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
 
2075
    
 
2076
    // lower-left sub-rectangle
 
2077
    colors2[0] = color0M;
 
2078
    colors2[1] = colors[1];
 
2079
    colors2[2] = colorMM;
 
2080
    colors2[3] = colorM1;
 
2081
    doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
 
2082
    
 
2083
    // upper-right sub-rectangle
 
2084
    colors2[0] = colorM0;
 
2085
    colors2[1] = colorMM;
 
2086
    colors2[2] = colors[2];
 
2087
    colors2[3] = color1M;
 
2088
    doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
 
2089
 
 
2090
    // lower-right sub-rectangle
 
2091
    colors2[0] = colorMM;
 
2092
    colors2[1] = colorM1;
 
2093
    colors2[2] = color1M;
 
2094
    colors2[3] = colors[3];
 
2095
    doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
 
2096
  }
 
2097
}
 
2098
 
 
2099
void Gfx::doAxialShFill(GfxAxialShading *shading) {
 
2100
  double xMin, yMin, xMax, yMax;
 
2101
  double x0, y0, x1, y1;
 
2102
  double dx, dy, mul;
 
2103
  GBool dxZero, dyZero;
 
2104
  double tMin, tMax, t, tx, ty;
 
2105
  double s[4], sMin, sMax, tmp;
 
2106
  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
 
2107
  double t0, t1, tt;
 
2108
  double ta[axialMaxSplits + 1];
 
2109
  int next[axialMaxSplits + 1];
 
2110
  GfxColor color0, color1;
 
2111
  int nComps;
 
2112
  int i, j, k, kk;
 
2113
 
 
2114
  if (out->useShadedFills() &&
 
2115
      out->axialShadedFill(state, shading)) {
 
2116
    return;
 
2117
  }
 
2118
 
 
2119
  // get the clip region bbox
 
2120
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
 
2121
 
 
2122
  // compute min and max t values, based on the four corners of the
 
2123
  // clip region bbox
 
2124
  shading->getCoords(&x0, &y0, &x1, &y1);
 
2125
  dx = x1 - x0;
 
2126
  dy = y1 - y0;
 
2127
  dxZero = fabs(dx) < 0.01;
 
2128
  dyZero = fabs(dy) < 0.01;
 
2129
  if (dxZero && dyZero) {
 
2130
    tMin = tMax = 0;
 
2131
  } else {
 
2132
    mul = 1 / (dx * dx + dy * dy);
 
2133
    tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
 
2134
    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
 
2135
    if (t < tMin) {
 
2136
      tMin = t;
 
2137
    } else if (t > tMax) {
 
2138
      tMax = t;
 
2139
    }
 
2140
    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
 
2141
    if (t < tMin) {
 
2142
      tMin = t;
 
2143
    } else if (t > tMax) {
 
2144
      tMax = t;
 
2145
    }
 
2146
    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
 
2147
    if (t < tMin) {
 
2148
      tMin = t;
 
2149
    } else if (t > tMax) {
 
2150
      tMax = t;
 
2151
    }
 
2152
    if (tMin < 0 && !shading->getExtend0()) {
 
2153
      tMin = 0;
 
2154
    }
 
2155
    if (tMax > 1 && !shading->getExtend1()) {
 
2156
      tMax = 1;
 
2157
    }
 
2158
  }
 
2159
 
 
2160
  // get the function domain
 
2161
  t0 = shading->getDomain0();
 
2162
  t1 = shading->getDomain1();
 
2163
 
 
2164
  // Traverse the t axis and do the shading.
 
2165
  //
 
2166
  // For each point (tx, ty) on the t axis, consider a line through
 
2167
  // that point perpendicular to the t axis:
 
2168
  //
 
2169
  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
 
2170
  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
 
2171
  //
 
2172
  // Then look at the intersection of this line with the bounding box
 
2173
  // (xMin, yMin, xMax, yMax).  In the general case, there are four
 
2174
  // intersection points:
 
2175
  //
 
2176
  //     s0 = (xMin - tx) / -dy
 
2177
  //     s1 = (xMax - tx) / -dy
 
2178
  //     s2 = (yMin - ty) / dx
 
2179
  //     s3 = (yMax - ty) / dx
 
2180
  //
 
2181
  // and we want the middle two s values.
 
2182
  //
 
2183
  // In the case where dx = 0, take s0 and s1; in the case where dy =
 
2184
  // 0, take s2 and s3.
 
2185
  //
 
2186
  // Each filled polygon is bounded by two of these line segments
 
2187
  // perpdendicular to the t axis.
 
2188
  //
 
2189
  // The t axis is bisected into smaller regions until the color
 
2190
  // difference across a region is small enough, and then the region
 
2191
  // is painted with a single color.
 
2192
 
 
2193
  // set up: require at least one split to avoid problems when the two
 
2194
  // ends of the t axis have the same color
 
2195
  nComps = shading->getColorSpace()->getNComps();
 
2196
  ta[0] = tMin;
 
2197
  next[0] = axialMaxSplits / 2;
 
2198
  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
 
2199
  next[axialMaxSplits / 2] = axialMaxSplits;
 
2200
  ta[axialMaxSplits] = tMax;
 
2201
 
 
2202
  // compute the color at t = tMin
 
2203
  if (tMin < 0) {
 
2204
    tt = t0;
 
2205
  } else if (tMin > 1) {
 
2206
    tt = t1;
 
2207
  } else {
 
2208
    tt = t0 + (t1 - t0) * tMin;
 
2209
  }
 
2210
  shading->getColor(tt, &color0);
 
2211
 
 
2212
  // compute the coordinates of the point on the t axis at t = tMin;
 
2213
  // then compute the intersection of the perpendicular line with the
 
2214
  // bounding box
 
2215
  tx = x0 + tMin * dx;
 
2216
  ty = y0 + tMin * dy;
 
2217
  if (dxZero && dyZero) {
 
2218
    sMin = sMax = 0;
 
2219
  } else if (dxZero) {
 
2220
    sMin = (xMin - tx) / -dy;
 
2221
    sMax = (xMax - tx) / -dy;
 
2222
    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
 
2223
  } else if (dyZero) {
 
2224
    sMin = (yMin - ty) / dx;
 
2225
    sMax = (yMax - ty) / dx;
 
2226
    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
 
2227
  } else {
 
2228
    s[0] = (yMin - ty) / dx;
 
2229
    s[1] = (yMax - ty) / dx;
 
2230
    s[2] = (xMin - tx) / -dy;
 
2231
    s[3] = (xMax - tx) / -dy;
 
2232
    for (j = 0; j < 3; ++j) {
 
2233
      kk = j;
 
2234
      for (k = j + 1; k < 4; ++k) {
 
2235
        if (s[k] < s[kk]) {
 
2236
          kk = k;
 
2237
        }
 
2238
      }
 
2239
      tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
 
2240
    }
 
2241
    sMin = s[1];
 
2242
    sMax = s[2];
 
2243
  }
 
2244
  ux0 = tx - sMin * dy;
 
2245
  uy0 = ty + sMin * dx;
 
2246
  vx0 = tx - sMax * dy;
 
2247
  vy0 = ty + sMax * dx;
 
2248
 
 
2249
  i = 0;
 
2250
  while (i < axialMaxSplits) {
 
2251
 
 
2252
    // bisect until color difference is small enough or we hit the
 
2253
    // bisection limit
 
2254
    j = next[i];
 
2255
    while (j > i + 1) {
 
2256
      if (ta[j] < 0) {
 
2257
        tt = t0;
 
2258
      } else if (ta[j] > 1) {
 
2259
        tt = t1;
 
2260
      } else {
 
2261
        tt = t0 + (t1 - t0) * ta[j];
 
2262
      }
 
2263
      shading->getColor(tt, &color1);
 
2264
      for (k = 0; k < nComps; ++k) {
 
2265
        if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
 
2266
          break;
 
2267
        }
 
2268
      }
 
2269
      if (k == nComps) {
 
2270
        break;
 
2271
      }
 
2272
      k = (i + j) / 2;
 
2273
      ta[k] = 0.5 * (ta[i] + ta[j]);
 
2274
      next[i] = k;
 
2275
      next[k] = j;
 
2276
      j = k;
 
2277
    }
 
2278
 
 
2279
    // use the average of the colors of the two sides of the region
 
2280
    for (k = 0; k < nComps; ++k) {
 
2281
      color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
 
2282
    }
 
2283
 
 
2284
    // compute the coordinates of the point on the t axis; then
 
2285
    // compute the intersection of the perpendicular line with the
 
2286
    // bounding box
 
2287
    tx = x0 + ta[j] * dx;
 
2288
    ty = y0 + ta[j] * dy;
 
2289
    if (dxZero && dyZero) {
 
2290
      sMin = sMax = 0;
 
2291
    } else if (dxZero) {
 
2292
      sMin = (xMin - tx) / -dy;
 
2293
      sMax = (xMax - tx) / -dy;
 
2294
      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
 
2295
    } else if (dyZero) {
 
2296
      sMin = (yMin - ty) / dx;
 
2297
      sMax = (yMax - ty) / dx;
 
2298
      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
 
2299
    } else {
 
2300
      s[0] = (yMin - ty) / dx;
 
2301
      s[1] = (yMax - ty) / dx;
 
2302
      s[2] = (xMin - tx) / -dy;
 
2303
      s[3] = (xMax - tx) / -dy;
 
2304
      for (j = 0; j < 3; ++j) {
 
2305
        kk = j;
 
2306
        for (k = j + 1; k < 4; ++k) {
 
2307
          if (s[k] < s[kk]) {
 
2308
            kk = k;
 
2309
          }
 
2310
        }
 
2311
        tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
 
2312
      }
 
2313
      sMin = s[1];
 
2314
      sMax = s[2];
 
2315
    }
 
2316
    ux1 = tx - sMin * dy;
 
2317
    uy1 = ty + sMin * dx;
 
2318
    vx1 = tx - sMax * dy;
 
2319
    vy1 = ty + sMax * dx;
 
2320
 
 
2321
    // set the color
 
2322
    state->setFillColor(&color0);
 
2323
    out->updateFillColor(state);
 
2324
 
 
2325
    // fill the region
 
2326
    state->moveTo(ux0, uy0);
 
2327
    state->lineTo(vx0, vy0);
 
2328
    state->lineTo(vx1, vy1);
 
2329
    state->lineTo(ux1, uy1);
 
2330
    state->closePath();
 
2331
    out->fill(state);
 
2332
    state->clearPath();
 
2333
 
 
2334
    // set up for next region
 
2335
    ux0 = ux1;
 
2336
    uy0 = uy1;
 
2337
    vx0 = vx1;
 
2338
    vy0 = vy1;
 
2339
    color0 = color1;
 
2340
    i = next[i];
 
2341
  }
 
2342
}
 
2343
 
 
2344
void Gfx::doRadialShFill(GfxRadialShading *shading) {
 
2345
  double xMin, yMin, xMax, yMax;
 
2346
  double x0, y0, r0, x1, y1, r1, t0, t1;
 
2347
  int nComps;
 
2348
  GfxColor colorA, colorB;
 
2349
  double xa, ya, xb, yb, ra, rb;
 
2350
  double ta, tb, sa, sb;
 
2351
  double sz, xz, yz, sMin, sMax;
 
2352
  GBool enclosed;
 
2353
  int ia, ib, k, n;
 
2354
  double *ctm;
 
2355
  double theta, alpha, angle, t;
 
2356
 
 
2357
  if (out->useShadedFills() &&
 
2358
      out->radialShadedFill(state, shading)) {
 
2359
    return;
 
2360
  }
 
2361
 
 
2362
  // get the shading info
 
2363
  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
 
2364
  t0 = shading->getDomain0();
 
2365
  t1 = shading->getDomain1();
 
2366
  nComps = shading->getColorSpace()->getNComps();
 
2367
 
 
2368
  // Compute the point at which r(s) = 0; check for the enclosed
 
2369
  // circles case; and compute the angles for the tangent lines.
 
2370
  if (x0 == x1 && y0 == y1) {
 
2371
    enclosed = gTrue;
 
2372
    theta = 0; // make gcc happy
 
2373
    sz = 0; // make gcc happy
 
2374
  } else if (r0 == r1) {
 
2375
    enclosed = gFalse;
 
2376
    theta = 0;
 
2377
    sz = 0; // make gcc happy
 
2378
  } else {
 
2379
    sz = -r0 / (r1 - r0);
 
2380
    xz = x0 + sz * (x1 - x0);
 
2381
    yz = y0 + sz * (y1 - y0);
 
2382
    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
 
2383
    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
 
2384
    if (r0 > r1) {
 
2385
      theta = -theta;
 
2386
    }
 
2387
  }
 
2388
  if (enclosed) {
 
2389
    alpha = 0;
 
2390
  } else {
 
2391
    alpha = atan2(y1 - y0, x1 - x0);
 
2392
  }
 
2393
 
 
2394
  // compute the (possibly extended) s range
 
2395
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
 
2396
  if (enclosed) {
 
2397
    sMin = 0;
 
2398
    sMax = 1;
 
2399
  } else {
 
2400
    sMin = 1;
 
2401
    sMax = 0;
 
2402
    // solve for x(s) + r(s) = xMin
 
2403
    if ((x1 + r1) - (x0 + r0) != 0) {
 
2404
      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
 
2405
      if (sa < sMin) {
 
2406
        sMin = sa;
 
2407
      } else if (sa > sMax) {
 
2408
        sMax = sa;
 
2409
      }
 
2410
    }
 
2411
    // solve for x(s) - r(s) = xMax
 
2412
    if ((x1 - r1) - (x0 - r0) != 0) {
 
2413
      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
 
2414
      if (sa < sMin) {
 
2415
        sMin = sa;
 
2416
      } else if (sa > sMax) {
 
2417
        sMax = sa;
 
2418
      }
 
2419
    }
 
2420
    // solve for y(s) + r(s) = yMin
 
2421
    if ((y1 + r1) - (y0 + r0) != 0) {
 
2422
      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
 
2423
      if (sa < sMin) {
 
2424
        sMin = sa;
 
2425
      } else if (sa > sMax) {
 
2426
        sMax = sa;
 
2427
      }
 
2428
    }
 
2429
    // solve for y(s) - r(s) = yMax
 
2430
    if ((y1 - r1) - (y0 - r0) != 0) {
 
2431
      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
 
2432
      if (sa < sMin) {
 
2433
        sMin = sa;
 
2434
      } else if (sa > sMax) {
 
2435
        sMax = sa;
 
2436
      }
 
2437
    }
 
2438
    // check against sz
 
2439
    if (r0 < r1) {
 
2440
      if (sMin < sz) {
 
2441
        sMin = sz;
 
2442
      }
 
2443
    } else if (r0 > r1) {
 
2444
      if (sMax > sz) {
 
2445
        sMax = sz;
 
2446
      }
 
2447
    }
 
2448
    // check the 'extend' flags
 
2449
    if (!shading->getExtend0() && sMin < 0) {
 
2450
      sMin = 0;
 
2451
    }
 
2452
    if (!shading->getExtend1() && sMax > 1) {
 
2453
      sMax = 1;
 
2454
    }
 
2455
  }
 
2456
 
 
2457
  // compute the number of steps into which circles must be divided to
 
2458
  // achieve a curve flatness of 0.1 pixel in device space for the
 
2459
  // largest circle (note that "device space" is 72 dpi when generating
 
2460
  // PostScript, hence the relatively small 0.1 pixel accuracy)
 
2461
  ctm = state->getCTM();
 
2462
  t = fabs(ctm[0]);
 
2463
  if (fabs(ctm[1]) > t) {
 
2464
    t = fabs(ctm[1]);
 
2465
  }
 
2466
  if (fabs(ctm[2]) > t) {
 
2467
    t = fabs(ctm[2]);
 
2468
  }
 
2469
  if (fabs(ctm[3]) > t) {
 
2470
    t = fabs(ctm[3]);
 
2471
  }
 
2472
  if (r0 > r1) {
 
2473
    t *= r0;
 
2474
  } else {
 
2475
    t *= r1;
 
2476
  }
 
2477
  if (t < 1) {
 
2478
    n = 3;
 
2479
  } else {
 
2480
    n = (int)(M_PI / acos(1 - 0.1 / t));
 
2481
    if (n < 3) {
 
2482
      n = 3;
 
2483
    } else if (n > 200) {
 
2484
      n = 200;
 
2485
    }
 
2486
  }
 
2487
 
 
2488
  // setup for the start circle
 
2489
  ia = 0;
 
2490
  sa = sMin;
 
2491
  ta = t0 + sa * (t1 - t0);
 
2492
  xa = x0 + sa * (x1 - x0);
 
2493
  ya = y0 + sa * (y1 - y0);
 
2494
  ra = r0 + sa * (r1 - r0);
 
2495
  if (ta < t0) {
 
2496
    shading->getColor(t0, &colorA);
 
2497
  } else if (ta > t1) {
 
2498
    shading->getColor(t1, &colorA);
 
2499
  } else {
 
2500
    shading->getColor(ta, &colorA);
 
2501
  }
 
2502
 
 
2503
  // fill the circles
 
2504
  while (ia < radialMaxSplits) {
 
2505
 
 
2506
    // go as far along the t axis (toward t1) as we can, such that the
 
2507
    // color difference is within the tolerance (radialColorDelta) --
 
2508
    // this uses bisection (between the current value, t, and t1),
 
2509
    // limited to radialMaxSplits points along the t axis; require at
 
2510
    // least one split to avoid problems when the innermost and
 
2511
    // outermost colors are the same
 
2512
    ib = radialMaxSplits;
 
2513
    sb = sMax;
 
2514
    tb = t0 + sb * (t1 - t0);
 
2515
    if (tb < t0) {
 
2516
      shading->getColor(t0, &colorB);
 
2517
    } else if (tb > t1) {
 
2518
      shading->getColor(t1, &colorB);
 
2519
    } else {
 
2520
      shading->getColor(tb, &colorB);
 
2521
    }
 
2522
    while (ib - ia > 1) {
 
2523
      for (k = 0; k < nComps; ++k) {
 
2524
        if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
 
2525
          break;
 
2526
        }
 
2527
      }
 
2528
      if (k == nComps && ib < radialMaxSplits) {
 
2529
        break;
 
2530
      }
 
2531
      ib = (ia + ib) / 2;
 
2532
      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
 
2533
      tb = t0 + sb * (t1 - t0);
 
2534
      if (tb < t0) {
 
2535
        shading->getColor(t0, &colorB);
 
2536
      } else if (tb > t1) {
 
2537
        shading->getColor(t1, &colorB);
 
2538
      } else {
 
2539
        shading->getColor(tb, &colorB);
 
2540
      }
 
2541
    }
 
2542
 
 
2543
    // compute center and radius of the circle
 
2544
    xb = x0 + sb * (x1 - x0);
 
2545
    yb = y0 + sb * (y1 - y0);
 
2546
    rb = r0 + sb * (r1 - r0);
 
2547
 
 
2548
    // use the average of the colors at the two circles
 
2549
    for (k = 0; k < nComps; ++k) {
 
2550
      colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
 
2551
    }
 
2552
    state->setFillColor(&colorA);
 
2553
    out->updateFillColor(state);
 
2554
 
 
2555
    if (enclosed) {
 
2556
 
 
2557
      // construct path for first circle (counterclockwise)
 
2558
      state->moveTo(xa + ra, ya);
 
2559
      for (k = 1; k < n; ++k) {
 
2560
        angle = ((double)k / (double)n) * 2 * M_PI;
 
2561
        state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
 
2562
      }
 
2563
      state->closePath();
 
2564
 
 
2565
      // construct and append path for second circle (clockwise)
 
2566
      state->moveTo(xb + rb, yb);
 
2567
      for (k = 1; k < n; ++k) {
 
2568
        angle = -((double)k / (double)n) * 2 * M_PI;
 
2569
        state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
 
2570
      }
 
2571
      state->closePath();
 
2572
 
 
2573
    } else {
 
2574
 
 
2575
      // construct the first subpath (clockwise)
 
2576
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
 
2577
                    ya + ra * sin(alpha + theta + 0.5 * M_PI));
 
2578
      for (k = 0; k < n; ++k) {
 
2579
        angle = alpha + theta + 0.5 * M_PI
 
2580
          - ((double)k / (double)n) * (2 * theta + M_PI);
 
2581
        state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
 
2582
      }
 
2583
      for (k = 0; k < n; ++k) {
 
2584
        angle = alpha - theta - 0.5 * M_PI
 
2585
          + ((double)k / (double)n) * (2 * theta - M_PI);
 
2586
        state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
 
2587
      }
 
2588
      state->closePath();
 
2589
 
 
2590
      // construct the second subpath (counterclockwise)
 
2591
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
 
2592
                    ya + ra * sin(alpha + theta + 0.5 * M_PI));
 
2593
      for (k = 0; k < n; ++k) {
 
2594
        angle = alpha + theta + 0.5 * M_PI
 
2595
                + ((double)k / (double)n) * (-2 * theta + M_PI);
 
2596
        state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
 
2597
      }
 
2598
      for (k = 0; k < n; ++k) {
 
2599
        angle = alpha - theta - 0.5 * M_PI
 
2600
                + ((double)k / (double)n) * (2 * theta + M_PI);
 
2601
        state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
 
2602
      }
 
2603
      state->closePath();
 
2604
    }
 
2605
 
 
2606
    // fill the path
 
2607
    out->fill(state);
 
2608
    state->clearPath();
 
2609
 
 
2610
    // step to the next value of t
 
2611
    ia = ib;
 
2612
    sa = sb;
 
2613
    ta = tb;
 
2614
    xa = xb;
 
2615
    ya = yb;
 
2616
    ra = rb;
 
2617
    colorA = colorB;
 
2618
  }
 
2619
 
 
2620
  if (enclosed) {
 
2621
    // extend the smaller circle
 
2622
    if ((shading->getExtend0() && r0 <= r1) ||
 
2623
        (shading->getExtend1() && r1 < r0)) {
 
2624
      if (r0 <= r1) {
 
2625
        ta = t0;
 
2626
        ra = r0;
 
2627
        xa = x0;
 
2628
        ya = y0;
 
2629
      } else {
 
2630
        ta = t1;
 
2631
        ra = r1;
 
2632
        xa = x1;
 
2633
        ya = y1;
 
2634
      }
 
2635
      shading->getColor(ta, &colorA);
 
2636
      state->setFillColor(&colorA);
 
2637
      out->updateFillColor(state);
 
2638
      state->moveTo(xa + ra, ya);
 
2639
      for (k = 1; k < n; ++k) {
 
2640
        angle = ((double)k / (double)n) * 2 * M_PI;
 
2641
        state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
 
2642
      }
 
2643
      state->closePath();
 
2644
      out->fill(state);
 
2645
      state->clearPath();
 
2646
    }
 
2647
 
 
2648
    // extend the larger circle
 
2649
    if ((shading->getExtend0() && r0 > r1) ||
 
2650
        (shading->getExtend1() && r1 >= r0)) {
 
2651
      if (r0 > r1) {
 
2652
        ta = t0;
 
2653
        ra = r0;
 
2654
        xa = x0;
 
2655
        ya = y0;
 
2656
      } else {
 
2657
        ta = t1;
 
2658
        ra = r1;
 
2659
        xa = x1;
 
2660
        ya = y1;
 
2661
      }
 
2662
      shading->getColor(ta, &colorA);
 
2663
      state->setFillColor(&colorA);
 
2664
      out->updateFillColor(state);
 
2665
      state->moveTo(xMin, yMin);
 
2666
      state->lineTo(xMin, yMax);
 
2667
      state->lineTo(xMax, yMax);
 
2668
      state->lineTo(xMax, yMin);
 
2669
      state->closePath();
 
2670
      state->moveTo(xa + ra, ya);
 
2671
      for (k = 1; k < n; ++k) {
 
2672
        angle = ((double)k / (double)n) * 2 * M_PI;
 
2673
        state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
 
2674
      }
 
2675
      state->closePath();
 
2676
      out->fill(state);
 
2677
      state->clearPath();
 
2678
    }
 
2679
  }
 
2680
}
 
2681
 
 
2682
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
 
2683
  double x0, y0, x1, y1, x2, y2;
 
2684
  GfxColor color0, color1, color2;
 
2685
  int i;
 
2686
 
 
2687
  for (i = 0; i < shading->getNTriangles(); ++i) {
 
2688
    shading->getTriangle(i, &x0, &y0, &color0,
 
2689
                         &x1, &y1, &color1,
 
2690
                         &x2, &y2, &color2);
 
2691
    gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
 
2692
                        shading->getColorSpace()->getNComps(), 0);
 
2693
  }
 
2694
}
 
2695
 
 
2696
void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
 
2697
                              double x1, double y1, GfxColor *color1,
 
2698
                              double x2, double y2, GfxColor *color2,
 
2699
                              int nComps, int depth) {
 
2700
  double x01, y01, x12, y12, x20, y20;
 
2701
  GfxColor color01, color12, color20;
 
2702
  int i;
 
2703
 
 
2704
  for (i = 0; i < nComps; ++i) {
 
2705
    if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
 
2706
        abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
 
2707
      break;
 
2708
    }
 
2709
  }
 
2710
  if (i == nComps || depth == gouraudMaxDepth) {
 
2711
    state->setFillColor(color0);
 
2712
    out->updateFillColor(state);
 
2713
    state->moveTo(x0, y0);
 
2714
    state->lineTo(x1, y1);
 
2715
    state->lineTo(x2, y2);
 
2716
    state->closePath();
 
2717
    out->fill(state);
 
2718
    state->clearPath();
 
2719
  } else {
 
2720
    x01 = 0.5 * (x0 + x1);
 
2721
    y01 = 0.5 * (y0 + y1);
 
2722
    x12 = 0.5 * (x1 + x2);
 
2723
    y12 = 0.5 * (y1 + y2);
 
2724
    x20 = 0.5 * (x2 + x0);
 
2725
    y20 = 0.5 * (y2 + y0);
 
2726
    //~ if the shading has a Function, this should interpolate on the
 
2727
    //~ function parameter, not on the color components
 
2728
    for (i = 0; i < nComps; ++i) {
 
2729
      color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
 
2730
      color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
 
2731
      color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
 
2732
    }
 
2733
    gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
 
2734
                        x20, y20, &color20, nComps, depth + 1);
 
2735
    gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
 
2736
                        x12, y12, &color12, nComps, depth + 1);
 
2737
    gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
 
2738
                        x20, y20, &color20, nComps, depth + 1);
 
2739
    gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
 
2740
                        x2, y2, color2, nComps, depth + 1);
 
2741
  }
 
2742
}
 
2743
 
 
2744
void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
 
2745
  int start, i;
 
2746
 
 
2747
  if (shading->getNPatches() > 128) {
 
2748
    start = 3;
 
2749
  } else if (shading->getNPatches() > 64) {
 
2750
    start = 2;
 
2751
  } else if (shading->getNPatches() > 16) {
 
2752
    start = 1;
 
2753
  } else {
 
2754
    start = 0;
 
2755
  }
 
2756
  for (i = 0; i < shading->getNPatches(); ++i) {
 
2757
    fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
 
2758
              start);
 
2759
  }
 
2760
}
 
2761
 
 
2762
void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
 
2763
  GfxPatch patch00, patch01, patch10, patch11;
 
2764
  double xx[4][8], yy[4][8];
 
2765
  double xxm, yym;
 
2766
  int i;
 
2767
 
 
2768
  for (i = 0; i < nComps; ++i) {
 
2769
    if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
 
2770
          > patchColorDelta ||
 
2771
        abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
 
2772
          > patchColorDelta ||
 
2773
        abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
 
2774
          > patchColorDelta ||
 
2775
        abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
 
2776
          > patchColorDelta) {
 
2777
      break;
 
2778
    }
 
2779
  }
 
2780
  if (i == nComps || depth == patchMaxDepth) {
 
2781
    state->setFillColor(&patch->color[0][0]);
 
2782
    out->updateFillColor(state);
 
2783
    state->moveTo(patch->x[0][0], patch->y[0][0]);
 
2784
    state->curveTo(patch->x[0][1], patch->y[0][1],
 
2785
                   patch->x[0][2], patch->y[0][2],
 
2786
                   patch->x[0][3], patch->y[0][3]);
 
2787
    state->curveTo(patch->x[1][3], patch->y[1][3],
 
2788
                   patch->x[2][3], patch->y[2][3],
 
2789
                   patch->x[3][3], patch->y[3][3]);
 
2790
    state->curveTo(patch->x[3][2], patch->y[3][2],
 
2791
                   patch->x[3][1], patch->y[3][1],
 
2792
                   patch->x[3][0], patch->y[3][0]);
 
2793
    state->curveTo(patch->x[2][0], patch->y[2][0],
 
2794
                   patch->x[1][0], patch->y[1][0],
 
2795
                   patch->x[0][0], patch->y[0][0]);
 
2796
    state->closePath();
 
2797
    out->fill(state);
 
2798
    state->clearPath();
 
2799
  } else {
 
2800
    for (i = 0; i < 4; ++i) {
 
2801
      xx[i][0] = patch->x[i][0];
 
2802
      yy[i][0] = patch->y[i][0];
 
2803
      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
 
2804
      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
 
2805
      xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
 
2806
      yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
 
2807
      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
 
2808
      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
 
2809
      xx[i][2] = 0.5 * (xx[i][1] + xxm);
 
2810
      yy[i][2] = 0.5 * (yy[i][1] + yym);
 
2811
      xx[i][5] = 0.5 * (xxm + xx[i][6]);
 
2812
      yy[i][5] = 0.5 * (yym + yy[i][6]);
 
2813
      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
 
2814
      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
 
2815
      xx[i][7] = patch->x[i][3];
 
2816
      yy[i][7] = patch->y[i][3];
 
2817
    }
 
2818
    for (i = 0; i < 4; ++i) {
 
2819
      patch00.x[0][i] = xx[0][i];
 
2820
      patch00.y[0][i] = yy[0][i];
 
2821
      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
 
2822
      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
 
2823
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
 
2824
      yym = 0.5 * (yy[1][i] + yy[2][i]);
 
2825
      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
 
2826
      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
 
2827
      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
 
2828
      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
 
2829
      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
 
2830
      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
 
2831
      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
 
2832
      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
 
2833
      patch10.x[0][i] = patch00.x[3][i];
 
2834
      patch10.y[0][i] = patch00.y[3][i];
 
2835
      patch10.x[3][i] = xx[3][i];
 
2836
      patch10.y[3][i] = yy[3][i];
 
2837
    }
 
2838
    for (i = 4; i < 8; ++i) {
 
2839
      patch01.x[0][i-4] = xx[0][i];
 
2840
      patch01.y[0][i-4] = yy[0][i];
 
2841
      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
 
2842
      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
 
2843
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
 
2844
      yym = 0.5 * (yy[1][i] + yy[2][i]);
 
2845
      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
 
2846
      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
 
2847
      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
 
2848
      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
 
2849
      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
 
2850
      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
 
2851
      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
 
2852
      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
 
2853
      patch11.x[0][i-4] = patch01.x[3][i-4];
 
2854
      patch11.y[0][i-4] = patch01.y[3][i-4];
 
2855
      patch11.x[3][i-4] = xx[3][i];
 
2856
      patch11.y[3][i-4] = yy[3][i];
 
2857
    }
 
2858
    //~ if the shading has a Function, this should interpolate on the
 
2859
    //~ function parameter, not on the color components
 
2860
    for (i = 0; i < nComps; ++i) {
 
2861
      patch00.color[0][0].c[i] = patch->color[0][0].c[i];
 
2862
      patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
 
2863
                                  patch->color[0][1].c[i]) / 2;
 
2864
      patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
 
2865
      patch01.color[0][1].c[i] = patch->color[0][1].c[i];
 
2866
      patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
 
2867
                                  patch->color[1][1].c[i]) / 2;
 
2868
      patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
 
2869
      patch11.color[1][1].c[i] = patch->color[1][1].c[i];
 
2870
      patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
 
2871
                                  patch->color[1][0].c[i]) / 2;
 
2872
      patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
 
2873
      patch10.color[1][0].c[i] = patch->color[1][0].c[i];
 
2874
      patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
 
2875
                                  patch->color[0][0].c[i]) / 2;
 
2876
      patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
 
2877
      patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
 
2878
                                  patch01.color[1][1].c[i]) / 2;
 
2879
      patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
 
2880
      patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
 
2881
      patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
 
2882
    }
 
2883
    fillPatch(&patch00, nComps, depth + 1);
 
2884
    fillPatch(&patch10, nComps, depth + 1);
 
2885
    fillPatch(&patch01, nComps, depth + 1);
 
2886
    fillPatch(&patch11, nComps, depth + 1);
 
2887
  }
 
2888
}
 
2889
 
 
2890
void Gfx::doEndPath() {
 
2891
  if (state->isCurPt() && clip != clipNone) {
 
2892
    state->clip();
 
2893
    if (clip == clipNormal) {
 
2894
      out->clip(state);
 
2895
    } else {
 
2896
      out->eoClip(state);
 
2897
    }
 
2898
  }
 
2899
  clip = clipNone;
 
2900
  state->clearPath();
 
2901
}
 
2902
 
 
2903
//------------------------------------------------------------------------
 
2904
// path clipping operators
 
2905
//------------------------------------------------------------------------
 
2906
 
 
2907
void Gfx::opClip(Object args[], int numArgs) {
 
2908
  clip = clipNormal;
 
2909
}
 
2910
 
 
2911
void Gfx::opEOClip(Object args[], int numArgs) {
 
2912
  clip = clipEO;
 
2913
}
 
2914
 
 
2915
//------------------------------------------------------------------------
 
2916
// text object operators
 
2917
//------------------------------------------------------------------------
 
2918
 
 
2919
void Gfx::opBeginText(Object args[], int numArgs) {
 
2920
  state->setTextMat(1, 0, 0, 1, 0, 0);
 
2921
  state->textMoveTo(0, 0);
 
2922
  out->updateTextMat(state);
 
2923
  out->updateTextPos(state);
 
2924
  fontChanged = gTrue;
 
2925
}
 
2926
 
 
2927
void Gfx::opEndText(Object args[], int numArgs) {
 
2928
  out->endTextObject(state);
 
2929
}
 
2930
 
 
2931
//------------------------------------------------------------------------
 
2932
// text state operators
 
2933
//------------------------------------------------------------------------
 
2934
 
 
2935
void Gfx::opSetCharSpacing(Object args[], int numArgs) {
 
2936
  state->setCharSpace(args[0].getNum());
 
2937
  out->updateCharSpace(state);
 
2938
}
 
2939
 
 
2940
void Gfx::opSetFont(Object args[], int numArgs) {
 
2941
  GfxFont *font;
 
2942
 
 
2943
  if (!(font = res->lookupFont(args[0].getName()))) {
 
2944
    return;
 
2945
  }
 
2946
  if (printCommands) {
 
2947
    printf("  font: tag=%s name='%s' %g\n",
 
2948
           font->getTag()->getCString(),
 
2949
           font->getName() ? font->getName()->getCString() : "???",
 
2950
           args[1].getNum());
 
2951
    fflush(stdout);
 
2952
  }
 
2953
  state->setFont(font, args[1].getNum());
 
2954
  fontChanged = gTrue;
 
2955
}
 
2956
 
 
2957
void Gfx::opSetTextLeading(Object args[], int numArgs) {
 
2958
  state->setLeading(args[0].getNum());
 
2959
}
 
2960
 
 
2961
void Gfx::opSetTextRender(Object args[], int numArgs) {
 
2962
  state->setRender(args[0].getInt());
 
2963
  out->updateRender(state);
 
2964
}
 
2965
 
 
2966
void Gfx::opSetTextRise(Object args[], int numArgs) {
 
2967
  state->setRise(args[0].getNum());
 
2968
  out->updateRise(state);
 
2969
}
 
2970
 
 
2971
void Gfx::opSetWordSpacing(Object args[], int numArgs) {
 
2972
  state->setWordSpace(args[0].getNum());
 
2973
  out->updateWordSpace(state);
 
2974
}
 
2975
 
 
2976
void Gfx::opSetHorizScaling(Object args[], int numArgs) {
 
2977
  state->setHorizScaling(args[0].getNum());
 
2978
  out->updateHorizScaling(state);
 
2979
  fontChanged = gTrue;
 
2980
}
 
2981
 
 
2982
//------------------------------------------------------------------------
 
2983
// text positioning operators
 
2984
//------------------------------------------------------------------------
 
2985
 
 
2986
void Gfx::opTextMove(Object args[], int numArgs) {
 
2987
  double tx, ty;
 
2988
 
 
2989
  tx = state->getLineX() + args[0].getNum();
 
2990
  ty = state->getLineY() + args[1].getNum();
 
2991
  state->textMoveTo(tx, ty);
 
2992
  out->updateTextPos(state);
 
2993
}
 
2994
 
 
2995
void Gfx::opTextMoveSet(Object args[], int numArgs) {
 
2996
  double tx, ty;
 
2997
 
 
2998
  tx = state->getLineX() + args[0].getNum();
 
2999
  ty = args[1].getNum();
 
3000
  state->setLeading(-ty);
 
3001
  ty += state->getLineY();
 
3002
  state->textMoveTo(tx, ty);
 
3003
  out->updateTextPos(state);
 
3004
}
 
3005
 
 
3006
void Gfx::opSetTextMatrix(Object args[], int numArgs) {
 
3007
  state->setTextMat(args[0].getNum(), args[1].getNum(),
 
3008
                    args[2].getNum(), args[3].getNum(),
 
3009
                    args[4].getNum(), args[5].getNum());
 
3010
  state->textMoveTo(0, 0);
 
3011
  out->updateTextMat(state);
 
3012
  out->updateTextPos(state);
 
3013
  fontChanged = gTrue;
 
3014
}
 
3015
 
 
3016
void Gfx::opTextNextLine(Object args[], int numArgs) {
 
3017
  double tx, ty;
 
3018
 
 
3019
  tx = state->getLineX();
 
3020
  ty = state->getLineY() - state->getLeading();
 
3021
  state->textMoveTo(tx, ty);
 
3022
  out->updateTextPos(state);
 
3023
}
 
3024
 
 
3025
//------------------------------------------------------------------------
 
3026
// text string operators
 
3027
//------------------------------------------------------------------------
 
3028
 
 
3029
void Gfx::opShowText(Object args[], int numArgs) {
 
3030
  if (!state->getFont()) {
 
3031
    error(getPos(), "No font in show");
 
3032
    return;
 
3033
  }
 
3034
  if (fontChanged) {
 
3035
    out->updateFont(state);
 
3036
    fontChanged = gFalse;
 
3037
  }
 
3038
  out->beginStringOp(state);
 
3039
  doShowText(args[0].getString());
 
3040
  out->endStringOp(state);
 
3041
}
 
3042
 
 
3043
void Gfx::opMoveShowText(Object args[], int numArgs) {
 
3044
  double tx, ty;
 
3045
 
 
3046
  if (!state->getFont()) {
 
3047
    error(getPos(), "No font in move/show");
 
3048
    return;
 
3049
  }
 
3050
  if (fontChanged) {
 
3051
    out->updateFont(state);
 
3052
    fontChanged = gFalse;
 
3053
  }
 
3054
  tx = state->getLineX();
 
3055
  ty = state->getLineY() - state->getLeading();
 
3056
  state->textMoveTo(tx, ty);
 
3057
  out->updateTextPos(state);
 
3058
  out->beginStringOp(state);
 
3059
  doShowText(args[0].getString());
 
3060
  out->endStringOp(state);
 
3061
}
 
3062
 
 
3063
void Gfx::opMoveSetShowText(Object args[], int numArgs) {
 
3064
  double tx, ty;
 
3065
 
 
3066
  if (!state->getFont()) {
 
3067
    error(getPos(), "No font in move/set/show");
 
3068
    return;
 
3069
  }
 
3070
  if (fontChanged) {
 
3071
    out->updateFont(state);
 
3072
    fontChanged = gFalse;
 
3073
  }
 
3074
  state->setWordSpace(args[0].getNum());
 
3075
  state->setCharSpace(args[1].getNum());
 
3076
  tx = state->getLineX();
 
3077
  ty = state->getLineY() - state->getLeading();
 
3078
  state->textMoveTo(tx, ty);
 
3079
  out->updateWordSpace(state);
 
3080
  out->updateCharSpace(state);
 
3081
  out->updateTextPos(state);
 
3082
  out->beginStringOp(state);
 
3083
  doShowText(args[2].getString());
 
3084
  out->endStringOp(state);
 
3085
}
 
3086
 
 
3087
void Gfx::opShowSpaceText(Object args[], int numArgs) {
 
3088
  Array *a;
 
3089
  Object obj;
 
3090
  int wMode;
 
3091
  int i;
 
3092
 
 
3093
  if (!state->getFont()) {
 
3094
    error(getPos(), "No font in show/space");
 
3095
    return;
 
3096
  }
 
3097
  if (fontChanged) {
 
3098
    out->updateFont(state);
 
3099
    fontChanged = gFalse;
 
3100
  }
 
3101
  out->beginStringOp(state);
 
3102
  wMode = state->getFont()->getWMode();
 
3103
  a = args[0].getArray();
 
3104
  for (i = 0; i < a->getLength(); ++i) {
 
3105
    a->get(i, &obj);
 
3106
    if (obj.isNum()) {
 
3107
      // this uses the absolute value of the font size to match
 
3108
      // Acrobat's behavior
 
3109
      if (wMode) {
 
3110
        state->textShift(0, -obj.getNum() * 0.001 *
 
3111
                            fabs(state->getFontSize()));
 
3112
      } else {
 
3113
        state->textShift(-obj.getNum() * 0.001 *
 
3114
                         fabs(state->getFontSize()), 0);
 
3115
      }
 
3116
      out->updateTextShift(state, obj.getNum());
 
3117
    } else if (obj.isString()) {
 
3118
      doShowText(obj.getString());
 
3119
    } else {
 
3120
      error(getPos(), "Element of show/space array must be number or string");
 
3121
    }
 
3122
    obj.free();
 
3123
  }
 
3124
  out->endStringOp(state);
 
3125
}
 
3126
 
 
3127
void Gfx::doShowText(GString *s) {
 
3128
  GfxFont *font;
 
3129
  int wMode;
 
3130
  double riseX, riseY;
 
3131
  CharCode code;
 
3132
  Unicode u[8];
 
3133
  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
 
3134
  double originX, originY, tOriginX, tOriginY;
 
3135
  double oldCTM[6], newCTM[6];
 
3136
  double *mat;
 
3137
  Object charProc;
 
3138
  Dict *resDict;
 
3139
  Parser *oldParser;
 
3140
  char *p;
 
3141
  int len, n, uLen, nChars, nSpaces, i;
 
3142
 
 
3143
  font = state->getFont();
 
3144
  wMode = font->getWMode();
 
3145
 
 
3146
  if (out->useDrawChar()) {
 
3147
    out->beginString(state, s);
 
3148
  }
 
3149
 
 
3150
  // handle a Type 3 char
 
3151
  if (font->getType() == fontType3 && out->interpretType3Chars()) {
 
3152
    mat = state->getCTM();
 
3153
    for (i = 0; i < 6; ++i) {
 
3154
      oldCTM[i] = mat[i];
 
3155
    }
 
3156
    mat = state->getTextMat();
 
3157
    newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
 
3158
    newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
 
3159
    newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
 
3160
    newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
 
3161
    mat = font->getFontMatrix();
 
3162
    newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
 
3163
    newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
 
3164
    newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
 
3165
    newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
 
3166
    newCTM[0] *= state->getFontSize();
 
3167
    newCTM[1] *= state->getFontSize();
 
3168
    newCTM[2] *= state->getFontSize();
 
3169
    newCTM[3] *= state->getFontSize();
 
3170
    newCTM[0] *= state->getHorizScaling();
 
3171
    newCTM[2] *= state->getHorizScaling();
 
3172
    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
 
3173
    curX = state->getCurX();
 
3174
    curY = state->getCurY();
 
3175
    lineX = state->getLineX();
 
3176
    lineY = state->getLineY();
 
3177
    oldParser = parser;
 
3178
    p = s->getCString();
 
3179
    len = s->getLength();
 
3180
    while (len > 0) {
 
3181
      n = font->getNextChar(p, len, &code,
 
3182
                            u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
 
3183
                            &dx, &dy, &originX, &originY);
 
3184
      dx = dx * state->getFontSize() + state->getCharSpace();
 
3185
      if (n == 1 && *p == ' ') {
 
3186
        dx += state->getWordSpace();
 
3187
      }
 
3188
      dx *= state->getHorizScaling();
 
3189
      dy *= state->getFontSize();
 
3190
      state->textTransformDelta(dx, dy, &tdx, &tdy);
 
3191
      state->transform(curX + riseX, curY + riseY, &x, &y);
 
3192
      saveState();
 
3193
      state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
 
3194
      //~ the CTM concat values here are wrong (but never used)
 
3195
      out->updateCTM(state, 1, 0, 0, 1, 0, 0);
 
3196
      if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
 
3197
                               code, u, uLen)) {
 
3198
        ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
 
3199
        if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
 
3200
          pushResources(resDict);
 
3201
        }
 
3202
        if (charProc.isStream()) {
 
3203
          display(&charProc, gFalse);
 
3204
        } else {
 
3205
          error(getPos(), "Missing or bad Type3 CharProc entry");
 
3206
        }
 
3207
        out->endType3Char(state);
 
3208
        if (resDict) {
 
3209
          popResources();
 
3210
        }
 
3211
        charProc.free();
 
3212
      }
 
3213
      restoreState();
 
3214
      // GfxState::restore() does *not* restore the current position,
 
3215
      // so we deal with it here using (curX, curY) and (lineX, lineY)
 
3216
      curX += tdx;
 
3217
      curY += tdy;
 
3218
      state->moveTo(curX, curY);
 
3219
      state->textSetPos(lineX, lineY);
 
3220
      p += n;
 
3221
      len -= n;
 
3222
    }
 
3223
    parser = oldParser;
 
3224
 
 
3225
  } else if (out->useDrawChar()) {
 
3226
    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
 
3227
    p = s->getCString();
 
3228
    len = s->getLength();
 
3229
    while (len > 0) {
 
3230
      n = font->getNextChar(p, len, &code,
 
3231
                            u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
 
3232
                            &dx, &dy, &originX, &originY);
 
3233
      if (wMode) {
 
3234
        dx *= state->getFontSize();
 
3235
        dy = dy * state->getFontSize() + state->getCharSpace();
 
3236
        if (n == 1 && *p == ' ') {
 
3237
          dy += state->getWordSpace();
 
3238
        }
 
3239
      } else {
 
3240
        dx = dx * state->getFontSize() + state->getCharSpace();
 
3241
        if (n == 1 && *p == ' ') {
 
3242
          dx += state->getWordSpace();
 
3243
        }
 
3244
        dx *= state->getHorizScaling();
 
3245
        dy *= state->getFontSize();
 
3246
      }
 
3247
      state->textTransformDelta(dx, dy, &tdx, &tdy);
 
3248
      originX *= state->getFontSize();
 
3249
      originY *= state->getFontSize();
 
3250
      state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
 
3251
      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
 
3252
                    tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
 
3253
      state->shift(tdx, tdy);
 
3254
      p += n;
 
3255
      len -= n;
 
3256
    }
 
3257
 
 
3258
  } else {
 
3259
    dx = dy = 0;
 
3260
    p = s->getCString();
 
3261
    len = s->getLength();
 
3262
    nChars = nSpaces = 0;
 
3263
    while (len > 0) {
 
3264
      n = font->getNextChar(p, len, &code,
 
3265
                            u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
 
3266
                            &dx2, &dy2, &originX, &originY);
 
3267
      dx += dx2;
 
3268
      dy += dy2;
 
3269
      if (n == 1 && *p == ' ') {
 
3270
        ++nSpaces;
 
3271
      }
 
3272
      ++nChars;
 
3273
      p += n;
 
3274
      len -= n;
 
3275
    }
 
3276
    if (wMode) {
 
3277
      dx *= state->getFontSize();
 
3278
      dy = dy * state->getFontSize()
 
3279
           + nChars * state->getCharSpace()
 
3280
           + nSpaces * state->getWordSpace();
 
3281
    } else {
 
3282
      dx = dx * state->getFontSize()
 
3283
           + nChars * state->getCharSpace()
 
3284
           + nSpaces * state->getWordSpace();
 
3285
      dx *= state->getHorizScaling();
 
3286
      dy *= state->getFontSize();
 
3287
    }
 
3288
    state->textTransformDelta(dx, dy, &tdx, &tdy);
 
3289
    out->drawString(state, s);
 
3290
    state->shift(tdx, tdy);
 
3291
  }
 
3292
 
 
3293
  if (out->useDrawChar()) {
 
3294
    out->endString(state);
 
3295
  }
 
3296
 
 
3297
  updateLevel += 10 * s->getLength();
 
3298
}
 
3299
 
 
3300
//------------------------------------------------------------------------
 
3301
// XObject operators
 
3302
//------------------------------------------------------------------------
 
3303
 
 
3304
void Gfx::opXObject(Object args[], int numArgs) {
 
3305
  char *name;
 
3306
  Object obj1, obj2, obj3, refObj;
 
3307
#if OPI_SUPPORT
 
3308
  Object opiDict;
 
3309
#endif
 
3310
 
 
3311
  name = args[0].getName();
 
3312
  if (!res->lookupXObject(name, &obj1)) {
 
3313
    return;
 
3314
  }
 
3315
  if (!obj1.isStream()) {
 
3316
    error(getPos(), "XObject '%s' is wrong type", name);
 
3317
    obj1.free();
 
3318
    return;
 
3319
  }
 
3320
#if OPI_SUPPORT
 
3321
  obj1.streamGetDict()->lookup("OPI", &opiDict);
 
3322
  if (opiDict.isDict()) {
 
3323
    out->opiBegin(state, opiDict.getDict());
 
3324
  }
 
3325
#endif
 
3326
  obj1.streamGetDict()->lookup("Subtype", &obj2);
 
3327
  if (obj2.isName("Image")) {
 
3328
    if (out->needNonText()) {
 
3329
      res->lookupXObjectNF(name, &refObj);
 
3330
      doImage(&refObj, obj1.getStream(), gFalse);
 
3331
      refObj.free();
 
3332
    }
 
3333
  } else if (obj2.isName("Form")) {
 
3334
    res->lookupXObjectNF(name, &refObj);
 
3335
    if (out->useDrawForm() && refObj.isRef()) {
 
3336
      out->drawForm(refObj.getRef());
 
3337
    } else {
 
3338
      doForm(&obj1);
 
3339
    }
 
3340
    refObj.free();
 
3341
  } else if (obj2.isName("PS")) {
 
3342
    obj1.streamGetDict()->lookup("Level1", &obj3);
 
3343
    out->psXObject(obj1.getStream(),
 
3344
                   obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
 
3345
  } else if (obj2.isName()) {
 
3346
    error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
 
3347
  } else {
 
3348
    error(getPos(), "XObject subtype is missing or wrong type");
 
3349
  }
 
3350
  obj2.free();
 
3351
#if OPI_SUPPORT
 
3352
  if (opiDict.isDict()) {
 
3353
    out->opiEnd(state, opiDict.getDict());
 
3354
  }
 
3355
  opiDict.free();
 
3356
#endif
 
3357
  obj1.free();
 
3358
}
 
3359
 
 
3360
void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
 
3361
  Dict *dict, *maskDict;
 
3362
  int width, height;
 
3363
  int bits, maskBits;
 
3364
  StreamColorSpaceMode csMode;
 
3365
  GBool mask;
 
3366
  GBool invert;
 
3367
  GfxColorSpace *colorSpace, *maskColorSpace;
 
3368
  GfxImageColorMap *colorMap, *maskColorMap;
 
3369
  Object maskObj, smaskObj;
 
3370
  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
 
3371
  int maskColors[2*gfxColorMaxComps];
 
3372
  int maskWidth, maskHeight;
 
3373
  GBool maskInvert;
 
3374
  Stream *maskStr;
 
3375
  Object obj1, obj2;
 
3376
  int i;
 
3377
 
 
3378
  // get info from the stream
 
3379
  bits = 0;
 
3380
  csMode = streamCSNone;
 
3381
  str->getImageParams(&bits, &csMode);
 
3382
 
 
3383
  // get stream dict
 
3384
  dict = str->getDict();
 
3385
 
 
3386
  // get size
 
3387
  dict->lookup("Width", &obj1);
 
3388
  if (obj1.isNull()) {
 
3389
    obj1.free();
 
3390
    dict->lookup("W", &obj1);
 
3391
  }
 
3392
  if (!obj1.isInt())
 
3393
    goto err2;
 
3394
  width = obj1.getInt();
 
3395
  obj1.free();
 
3396
  dict->lookup("Height", &obj1);
 
3397
  if (obj1.isNull()) {
 
3398
    obj1.free();
 
3399
    dict->lookup("H", &obj1);
 
3400
  }
 
3401
  if (!obj1.isInt())
 
3402
    goto err2;
 
3403
  height = obj1.getInt();
 
3404
  obj1.free();
 
3405
 
 
3406
  // image or mask?
 
3407
  dict->lookup("ImageMask", &obj1);
 
3408
  if (obj1.isNull()) {
 
3409
    obj1.free();
 
3410
    dict->lookup("IM", &obj1);
 
3411
  }
 
3412
  mask = gFalse;
 
3413
  if (obj1.isBool())
 
3414
    mask = obj1.getBool();
 
3415
  else if (!obj1.isNull())
 
3416
    goto err2;
 
3417
  obj1.free();
 
3418
 
 
3419
  // bit depth
 
3420
  if (bits == 0) {
 
3421
    dict->lookup("BitsPerComponent", &obj1);
 
3422
    if (obj1.isNull()) {
 
3423
      obj1.free();
 
3424
      dict->lookup("BPC", &obj1);
 
3425
    }
 
3426
    if (obj1.isInt()) {
 
3427
      bits = obj1.getInt();
 
3428
    } else if (mask) {
 
3429
      bits = 1;
 
3430
    } else {
 
3431
      goto err2;
 
3432
    }
 
3433
    obj1.free();
 
3434
  }
 
3435
 
 
3436
  // display a mask
 
3437
  if (mask) {
 
3438
 
 
3439
    // check for inverted mask
 
3440
    if (bits != 1)
 
3441
      goto err1;
 
3442
    invert = gFalse;
 
3443
    dict->lookup("Decode", &obj1);
 
3444
    if (obj1.isNull()) {
 
3445
      obj1.free();
 
3446
      dict->lookup("D", &obj1);
 
3447
    }
 
3448
    if (obj1.isArray()) {
 
3449
      obj1.arrayGet(0, &obj2);
 
3450
      if (obj2.isInt() && obj2.getInt() == 1)
 
3451
        invert = gTrue;
 
3452
      obj2.free();
 
3453
    } else if (!obj1.isNull()) {
 
3454
      goto err2;
 
3455
    }
 
3456
    obj1.free();
 
3457
 
 
3458
    // draw it
 
3459
    out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
 
3460
 
 
3461
  } else {
 
3462
 
 
3463
    // get color space and color map
 
3464
    dict->lookup("ColorSpace", &obj1);
 
3465
    if (obj1.isNull()) {
 
3466
      obj1.free();
 
3467
      dict->lookup("CS", &obj1);
 
3468
    }
 
3469
    if (obj1.isName()) {
 
3470
      res->lookupColorSpace(obj1.getName(), &obj2);
 
3471
      if (!obj2.isNull()) {
 
3472
        obj1.free();
 
3473
        obj1 = obj2;
 
3474
      } else {
 
3475
        obj2.free();
 
3476
      }
 
3477
    }
 
3478
    if (!obj1.isNull()) {
 
3479
      colorSpace = GfxColorSpace::parse(&obj1);
 
3480
    } else if (csMode == streamCSDeviceGray) {
 
3481
      colorSpace = new GfxDeviceGrayColorSpace();
 
3482
    } else if (csMode == streamCSDeviceRGB) {
 
3483
      colorSpace = new GfxDeviceRGBColorSpace();
 
3484
    } else if (csMode == streamCSDeviceCMYK) {
 
3485
      colorSpace = new GfxDeviceCMYKColorSpace();
 
3486
    } else {
 
3487
      colorSpace = NULL;
 
3488
    }
 
3489
    obj1.free();
 
3490
    if (!colorSpace) {
 
3491
      goto err1;
 
3492
    }
 
3493
    dict->lookup("Decode", &obj1);
 
3494
    if (obj1.isNull()) {
 
3495
      obj1.free();
 
3496
      dict->lookup("D", &obj1);
 
3497
    }
 
3498
    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
 
3499
    obj1.free();
 
3500
    if (!colorMap->isOk()) {
 
3501
      delete colorMap;
 
3502
      goto err1;
 
3503
    }
 
3504
 
 
3505
    // get the mask
 
3506
    haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
 
3507
    maskStr = NULL; // make gcc happy
 
3508
    maskWidth = maskHeight = 0; // make gcc happy
 
3509
    maskInvert = gFalse; // make gcc happy
 
3510
    maskColorMap = NULL; // make gcc happy
 
3511
    dict->lookup("Mask", &maskObj);
 
3512
    dict->lookup("SMask", &smaskObj);
 
3513
    if (smaskObj.isStream()) {
 
3514
      // soft mask
 
3515
      if (inlineImg) {
 
3516
        goto err1;
 
3517
      }
 
3518
      maskStr = smaskObj.getStream();
 
3519
      maskDict = smaskObj.streamGetDict();
 
3520
      maskDict->lookup("Width", &obj1);
 
3521
      if (obj1.isNull()) {
 
3522
        obj1.free();
 
3523
        maskDict->lookup("W", &obj1);
 
3524
      }
 
3525
      if (!obj1.isInt()) {
 
3526
        goto err2;
 
3527
      }
 
3528
      maskWidth = obj1.getInt();
 
3529
      obj1.free();
 
3530
      maskDict->lookup("Height", &obj1);
 
3531
      if (obj1.isNull()) {
 
3532
        obj1.free();
 
3533
        maskDict->lookup("H", &obj1);
 
3534
      }
 
3535
      if (!obj1.isInt()) {
 
3536
        goto err2;
 
3537
      }
 
3538
      maskHeight = obj1.getInt();
 
3539
      obj1.free();
 
3540
      maskDict->lookup("BitsPerComponent", &obj1);
 
3541
      if (obj1.isNull()) {
 
3542
        obj1.free();
 
3543
        maskDict->lookup("BPC", &obj1);
 
3544
      }
 
3545
      if (!obj1.isInt()) {
 
3546
        goto err2;
 
3547
      }
 
3548
      maskBits = obj1.getInt();
 
3549
      obj1.free();
 
3550
      maskDict->lookup("ColorSpace", &obj1);
 
3551
      if (obj1.isNull()) {
 
3552
        obj1.free();
 
3553
        maskDict->lookup("CS", &obj1);
 
3554
      }
 
3555
      if (obj1.isName()) {
 
3556
        res->lookupColorSpace(obj1.getName(), &obj2);
 
3557
        if (!obj2.isNull()) {
 
3558
          obj1.free();
 
3559
          obj1 = obj2;
 
3560
        } else {
 
3561
          obj2.free();
 
3562
        }
 
3563
      }
 
3564
      maskColorSpace = GfxColorSpace::parse(&obj1);
 
3565
      obj1.free();
 
3566
      if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
 
3567
        goto err1;
 
3568
      }
 
3569
      maskDict->lookup("Decode", &obj1);
 
3570
      if (obj1.isNull()) {
 
3571
        obj1.free();
 
3572
        maskDict->lookup("D", &obj1);
 
3573
      }
 
3574
      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
 
3575
      obj1.free();
 
3576
      if (!maskColorMap->isOk()) {
 
3577
        delete maskColorMap;
 
3578
        goto err1;
 
3579
      }
 
3580
      //~ handle the Matte entry
 
3581
      haveSoftMask = gTrue;
 
3582
    } else if (maskObj.isArray()) {
 
3583
      // color key mask
 
3584
      for (i = 0;
 
3585
           i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
 
3586
           ++i) {
 
3587
        maskObj.arrayGet(i, &obj1);
 
3588
        maskColors[i] = obj1.getInt();
 
3589
        obj1.free();
 
3590
      }
 
3591
      haveColorKeyMask = gTrue;
 
3592
    } else if (maskObj.isStream()) {
 
3593
      // explicit mask
 
3594
      if (inlineImg) {
 
3595
        goto err1;
 
3596
      }
 
3597
      maskStr = maskObj.getStream();
 
3598
      maskDict = maskObj.streamGetDict();
 
3599
      maskDict->lookup("Width", &obj1);
 
3600
      if (obj1.isNull()) {
 
3601
        obj1.free();
 
3602
        maskDict->lookup("W", &obj1);
 
3603
      }
 
3604
      if (!obj1.isInt()) {
 
3605
        goto err2;
 
3606
      }
 
3607
      maskWidth = obj1.getInt();
 
3608
      obj1.free();
 
3609
      maskDict->lookup("Height", &obj1);
 
3610
      if (obj1.isNull()) {
 
3611
        obj1.free();
 
3612
        maskDict->lookup("H", &obj1);
 
3613
      }
 
3614
      if (!obj1.isInt()) {
 
3615
        goto err2;
 
3616
      }
 
3617
      maskHeight = obj1.getInt();
 
3618
      obj1.free();
 
3619
      maskDict->lookup("ImageMask", &obj1);
 
3620
      if (obj1.isNull()) {
 
3621
        obj1.free();
 
3622
        maskDict->lookup("IM", &obj1);
 
3623
      }
 
3624
      if (!obj1.isBool() || !obj1.getBool()) {
 
3625
        goto err2;
 
3626
      }
 
3627
      obj1.free();
 
3628
      maskInvert = gFalse;
 
3629
      maskDict->lookup("Decode", &obj1);
 
3630
      if (obj1.isNull()) {
 
3631
        obj1.free();
 
3632
        maskDict->lookup("D", &obj1);
 
3633
      }
 
3634
      if (obj1.isArray()) {
 
3635
        obj1.arrayGet(0, &obj2);
 
3636
        if (obj2.isInt() && obj2.getInt() == 1) {
 
3637
          maskInvert = gTrue;
 
3638
        }
 
3639
        obj2.free();
 
3640
      } else if (!obj1.isNull()) {
 
3641
        goto err2;
 
3642
      }
 
3643
      obj1.free();
 
3644
      haveExplicitMask = gTrue;
 
3645
    }
 
3646
 
 
3647
    // draw it
 
3648
    if (haveSoftMask) {
 
3649
      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
 
3650
                               maskStr, maskWidth, maskHeight, maskColorMap);
 
3651
      delete maskColorMap;
 
3652
    } else if (haveExplicitMask) {
 
3653
      out->drawMaskedImage(state, ref, str, width, height, colorMap,
 
3654
                           maskStr, maskWidth, maskHeight, maskInvert);
 
3655
    } else {
 
3656
      out->drawImage(state, ref, str, width, height, colorMap,
 
3657
                     haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
 
3658
    }
 
3659
    delete colorMap;
 
3660
 
 
3661
    maskObj.free();
 
3662
    smaskObj.free();
 
3663
  }
 
3664
 
 
3665
  if ((i = width * height) > 1000) {
 
3666
    i = 1000;
 
3667
  }
 
3668
  updateLevel += i;
 
3669
 
 
3670
  return;
 
3671
 
 
3672
 err2:
 
3673
  obj1.free();
 
3674
 err1:
 
3675
  error(getPos(), "Bad image parameters");
 
3676
}
 
3677
 
 
3678
void Gfx::doForm(Object *str) {
 
3679
  Dict *dict;
 
3680
  GBool transpGroup, isolated, knockout;
 
3681
  GfxColorSpace *blendingColorSpace;
 
3682
  Object matrixObj, bboxObj;
 
3683
  double m[6], bbox[4];
 
3684
  Object resObj;
 
3685
  Dict *resDict;
 
3686
  Object obj1, obj2, obj3;
 
3687
  int i;
 
3688
 
 
3689
  // check for excessive recursion
 
3690
  if (formDepth > 20) {
 
3691
    return;
 
3692
  }
 
3693
 
 
3694
  // get stream dict
 
3695
  dict = str->streamGetDict();
 
3696
 
 
3697
  // check form type
 
3698
  dict->lookup("FormType", &obj1);
 
3699
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
 
3700
    error(getPos(), "Unknown form type");
 
3701
  }
 
3702
  obj1.free();
 
3703
 
 
3704
  // get bounding box
 
3705
  dict->lookup("BBox", &bboxObj);
 
3706
  if (!bboxObj.isArray()) {
 
3707
    bboxObj.free();
 
3708
    error(getPos(), "Bad form bounding box");
 
3709
    return;
 
3710
  }
 
3711
  for (i = 0; i < 4; ++i) {
 
3712
    bboxObj.arrayGet(i, &obj1);
 
3713
    bbox[i] = obj1.getNum();
 
3714
    obj1.free();
 
3715
  }
 
3716
  bboxObj.free();
 
3717
 
 
3718
  // get matrix
 
3719
  dict->lookup("Matrix", &matrixObj);
 
3720
  if (matrixObj.isArray()) {
 
3721
    for (i = 0; i < 6; ++i) {
 
3722
      matrixObj.arrayGet(i, &obj1);
 
3723
      m[i] = obj1.getNum();
 
3724
      obj1.free();
 
3725
    }
 
3726
  } else {
 
3727
    m[0] = 1; m[1] = 0;
 
3728
    m[2] = 0; m[3] = 1;
 
3729
    m[4] = 0; m[5] = 0;
 
3730
  }
 
3731
  matrixObj.free();
 
3732
 
 
3733
  // get resources
 
3734
  dict->lookup("Resources", &resObj);
 
3735
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
3736
 
 
3737
  // check for a transparency group
 
3738
  transpGroup = isolated = knockout = gFalse;
 
3739
  blendingColorSpace = NULL;
 
3740
  if (dict->lookup("Group", &obj1)->isDict()) {
 
3741
    if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
 
3742
      transpGroup = gTrue;
 
3743
      if (!obj1.dictLookup("CS", &obj3)->isNull()) {
 
3744
        blendingColorSpace = GfxColorSpace::parse(&obj3);
 
3745
      }
 
3746
      obj3.free();
 
3747
      if (obj1.dictLookup("I", &obj3)->isBool()) {
 
3748
        isolated = obj3.getBool();
 
3749
      }
 
3750
      obj3.free();
 
3751
      if (obj1.dictLookup("K", &obj3)->isBool()) {
 
3752
        knockout = obj3.getBool();
 
3753
      }
 
3754
      obj3.free();
 
3755
    }
 
3756
    obj2.free();
 
3757
  }
 
3758
  obj1.free();
 
3759
 
 
3760
  // draw it
 
3761
  ++formDepth;
 
3762
  doForm1(str, resDict, m, bbox,
 
3763
          transpGroup, gFalse, blendingColorSpace, isolated, knockout);
 
3764
  --formDepth;
 
3765
 
 
3766
  if (blendingColorSpace) {
 
3767
    delete blendingColorSpace;
 
3768
  }
 
3769
  resObj.free();
 
3770
}
 
3771
 
 
3772
void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
 
3773
                  GBool transpGroup, GBool softMask,
 
3774
                  GfxColorSpace *blendingColorSpace,
 
3775
                  GBool isolated, GBool knockout,
 
3776
                  GBool alpha, Function *transferFunc,
 
3777
                  GfxColor *backdropColor) {
 
3778
  Parser *oldParser;
 
3779
  double oldBaseMatrix[6];
 
3780
  int i;
 
3781
 
 
3782
  // push new resources on stack
 
3783
  pushResources(resDict);
 
3784
 
 
3785
  // save current graphics state
 
3786
  saveState();
 
3787
 
 
3788
  // kill any pre-existing path
 
3789
  state->clearPath();
 
3790
 
 
3791
  // save current parser
 
3792
  oldParser = parser;
 
3793
 
 
3794
  // set form transformation matrix
 
3795
  state->concatCTM(matrix[0], matrix[1], matrix[2],
 
3796
                   matrix[3], matrix[4], matrix[5]);
 
3797
  out->updateCTM(state, matrix[0], matrix[1], matrix[2],
 
3798
                 matrix[3], matrix[4], matrix[5]);
 
3799
 
 
3800
  // set form bounding box
 
3801
  state->moveTo(bbox[0], bbox[1]);
 
3802
  state->lineTo(bbox[2], bbox[1]);
 
3803
  state->lineTo(bbox[2], bbox[3]);
 
3804
  state->lineTo(bbox[0], bbox[3]);
 
3805
  state->closePath();
 
3806
  state->clip();
 
3807
  out->clip(state);
 
3808
  state->clearPath();
 
3809
 
 
3810
  if (softMask || transpGroup) {
 
3811
    if (state->getBlendMode() != gfxBlendNormal) {
 
3812
      state->setBlendMode(gfxBlendNormal);
 
3813
      out->updateBlendMode(state);
 
3814
    }
 
3815
    if (state->getFillOpacity() != 1) {
 
3816
      state->setFillOpacity(1);
 
3817
      out->updateFillOpacity(state);
 
3818
    }
 
3819
    if (state->getStrokeOpacity() != 1) {
 
3820
      state->setStrokeOpacity(1);
 
3821
      out->updateStrokeOpacity(state);
 
3822
    }
 
3823
    out->clearSoftMask(state);
 
3824
    out->beginTransparencyGroup(state, bbox, blendingColorSpace,
 
3825
                                isolated, knockout, softMask);
 
3826
  }
 
3827
 
 
3828
  // set new base matrix
 
3829
  for (i = 0; i < 6; ++i) {
 
3830
    oldBaseMatrix[i] = baseMatrix[i];
 
3831
    baseMatrix[i] = state->getCTM()[i];
 
3832
  }
 
3833
 
 
3834
  // draw the form
 
3835
  display(str, gFalse);
 
3836
 
 
3837
  if (softMask || transpGroup) {
 
3838
    out->endTransparencyGroup(state);
 
3839
  }
 
3840
 
 
3841
  // restore base matrix
 
3842
  for (i = 0; i < 6; ++i) {
 
3843
    baseMatrix[i] = oldBaseMatrix[i];
 
3844
  }
 
3845
 
 
3846
  // restore parser
 
3847
  parser = oldParser;
 
3848
 
 
3849
  // restore graphics state
 
3850
  restoreState();
 
3851
 
 
3852
  // pop resource stack
 
3853
  popResources();
 
3854
 
 
3855
  if (softMask) {
 
3856
    out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
 
3857
  } else if (transpGroup) {
 
3858
    out->paintTransparencyGroup(state, bbox);
 
3859
  }
 
3860
 
 
3861
  return;
 
3862
}
 
3863
 
 
3864
//------------------------------------------------------------------------
 
3865
// in-line image operators
 
3866
//------------------------------------------------------------------------
 
3867
 
 
3868
void Gfx::opBeginImage(Object args[], int numArgs) {
 
3869
  Stream *str;
 
3870
  int c1, c2;
 
3871
 
 
3872
  // build dict/stream
 
3873
  str = buildImageStream();
 
3874
 
 
3875
  // display the image
 
3876
  if (str) {
 
3877
    doImage(NULL, str, gTrue);
 
3878
  
 
3879
    // skip 'EI' tag
 
3880
    c1 = str->getUndecodedStream()->getChar();
 
3881
    c2 = str->getUndecodedStream()->getChar();
 
3882
    while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
 
3883
      c1 = c2;
 
3884
      c2 = str->getUndecodedStream()->getChar();
 
3885
    }
 
3886
    delete str;
 
3887
  }
 
3888
}
 
3889
 
 
3890
Stream *Gfx::buildImageStream() {
 
3891
  Object dict;
 
3892
  Object obj;
 
3893
  char *key;
 
3894
  Stream *str;
 
3895
 
 
3896
  // build dictionary
 
3897
  dict.initDict(xref);
 
3898
  parser->getObj(&obj);
 
3899
  while (!obj.isCmd("ID") && !obj.isEOF()) {
 
3900
    if (!obj.isName()) {
 
3901
      error(getPos(), "Inline image dictionary key must be a name object");
 
3902
      obj.free();
 
3903
    } else {
 
3904
      key = copyString(obj.getName());
 
3905
      obj.free();
 
3906
      parser->getObj(&obj);
 
3907
      if (obj.isEOF() || obj.isError()) {
 
3908
        gfree(key);
 
3909
        break;
 
3910
      }
 
3911
      dict.dictAdd(key, &obj);
 
3912
    }
 
3913
    parser->getObj(&obj);
 
3914
  }
 
3915
  if (obj.isEOF()) {
 
3916
    error(getPos(), "End of file in inline image");
 
3917
    obj.free();
 
3918
    dict.free();
 
3919
    return NULL;
 
3920
  }
 
3921
  obj.free();
 
3922
 
 
3923
  // make stream
 
3924
  str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
 
3925
  str = str->addFilters(&dict);
 
3926
 
 
3927
  return str;
 
3928
}
 
3929
 
 
3930
void Gfx::opImageData(Object args[], int numArgs) {
 
3931
  error(getPos(), "Internal: got 'ID' operator");
 
3932
}
 
3933
 
 
3934
void Gfx::opEndImage(Object args[], int numArgs) {
 
3935
  error(getPos(), "Internal: got 'EI' operator");
 
3936
}
 
3937
 
 
3938
//------------------------------------------------------------------------
 
3939
// type 3 font operators
 
3940
//------------------------------------------------------------------------
 
3941
 
 
3942
void Gfx::opSetCharWidth(Object args[], int numArgs) {
 
3943
  out->type3D0(state, args[0].getNum(), args[1].getNum());
 
3944
}
 
3945
 
 
3946
void Gfx::opSetCacheDevice(Object args[], int numArgs) {
 
3947
  out->type3D1(state, args[0].getNum(), args[1].getNum(),
 
3948
               args[2].getNum(), args[3].getNum(),
 
3949
               args[4].getNum(), args[5].getNum());
 
3950
}
 
3951
 
 
3952
//------------------------------------------------------------------------
 
3953
// compatibility operators
 
3954
//------------------------------------------------------------------------
 
3955
 
 
3956
void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
 
3957
  ++ignoreUndef;
 
3958
}
 
3959
 
 
3960
void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
 
3961
  if (ignoreUndef > 0)
 
3962
    --ignoreUndef;
 
3963
}
 
3964
 
 
3965
//------------------------------------------------------------------------
 
3966
// marked content operators
 
3967
//------------------------------------------------------------------------
 
3968
 
 
3969
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
 
3970
  if (printCommands) {
 
3971
    printf("  marked content: %s ", args[0].getName());
 
3972
    if (numArgs == 2)
 
3973
      args[2].print(stdout);
 
3974
    printf("\n");
 
3975
    fflush(stdout);
 
3976
  }
 
3977
}
 
3978
 
 
3979
void Gfx::opEndMarkedContent(Object args[], int numArgs) {
 
3980
}
 
3981
 
 
3982
void Gfx::opMarkPoint(Object args[], int numArgs) {
 
3983
  if (printCommands) {
 
3984
    printf("  mark point: %s ", args[0].getName());
 
3985
    if (numArgs == 2)
 
3986
      args[2].print(stdout);
 
3987
    printf("\n");
 
3988
    fflush(stdout);
 
3989
  }
 
3990
}
 
3991
 
 
3992
//------------------------------------------------------------------------
 
3993
// misc
 
3994
//------------------------------------------------------------------------
 
3995
 
 
3996
void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
 
3997
                    double xMin, double yMin, double xMax, double yMax) {
 
3998
  Dict *dict, *resDict;
 
3999
  Object matrixObj, bboxObj, resObj;
 
4000
  Object obj1;
 
4001
  double m[6], bbox[4], ictm[6];
 
4002
  double *ctm;
 
4003
  double formX0, formY0, formX1, formY1;
 
4004
  double annotX0, annotY0, annotX1, annotY1;
 
4005
  double det, x, y, sx, sy;
 
4006
  double r, g, b;
 
4007
  GfxColor color;
 
4008
  double *dash, *dash2;
 
4009
  int dashLength;
 
4010
  int i;
 
4011
 
 
4012
  //~ can we assume that we're in default user space?
 
4013
  //~ (i.e., baseMatrix = ctm)
 
4014
 
 
4015
  // transform the annotation bbox from default user space to user
 
4016
  // space: (bbox * baseMatrix) * iCTM
 
4017
  ctm = state->getCTM();
 
4018
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
 
4019
  ictm[0] = ctm[3] * det;
 
4020
  ictm[1] = -ctm[1] * det;
 
4021
  ictm[2] = -ctm[2] * det;
 
4022
  ictm[3] = ctm[0] * det;
 
4023
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
 
4024
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
 
4025
  x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
 
4026
  y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
 
4027
  annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
 
4028
  annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
 
4029
  x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
 
4030
  y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
 
4031
  annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
 
4032
  annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
 
4033
  if (annotX0 > annotX1) {
 
4034
    x = annotX0; annotX0 = annotX1; annotX1 = x;
 
4035
  }
 
4036
  if (annotY0 > annotY1) {
 
4037
    y = annotY0; annotY0 = annotY1; annotY1 = y;
 
4038
  }
 
4039
 
 
4040
  // draw the appearance stream (if there is one)
 
4041
  if (str->isStream()) {
 
4042
 
 
4043
    // get stream dict
 
4044
    dict = str->streamGetDict();
 
4045
 
 
4046
    // get the form bounding box
 
4047
    dict->lookup("BBox", &bboxObj);
 
4048
    if (!bboxObj.isArray()) {
 
4049
      bboxObj.free();
 
4050
      error(getPos(), "Bad form bounding box");
 
4051
      return;
 
4052
    }
 
4053
    for (i = 0; i < 4; ++i) {
 
4054
      bboxObj.arrayGet(i, &obj1);
 
4055
      bbox[i] = obj1.getNum();
 
4056
      obj1.free();
 
4057
    }
 
4058
    bboxObj.free();
 
4059
 
 
4060
    // get the form matrix
 
4061
    dict->lookup("Matrix", &matrixObj);
 
4062
    if (matrixObj.isArray()) {
 
4063
      for (i = 0; i < 6; ++i) {
 
4064
        matrixObj.arrayGet(i, &obj1);
 
4065
        m[i] = obj1.getNum();
 
4066
        obj1.free();
 
4067
      }
 
4068
    } else {
 
4069
      m[0] = 1; m[1] = 0;
 
4070
      m[2] = 0; m[3] = 1;
 
4071
      m[4] = 0; m[5] = 0;
 
4072
    }
 
4073
    matrixObj.free();
 
4074
 
 
4075
    // transform the form bbox from form space to user space
 
4076
    formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
 
4077
    formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
 
4078
    formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
 
4079
    formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
 
4080
    if (formX0 > formX1) {
 
4081
      x = formX0; formX0 = formX1; formX1 = x;
 
4082
    }
 
4083
    if (formY0 > formY1) {
 
4084
      y = formY0; formY0 = formY1; formY1 = y;
 
4085
    }
 
4086
 
 
4087
    // scale the form to fit the annotation bbox
 
4088
    if (formX1 == formX0) {
 
4089
      // this shouldn't happen
 
4090
      sx = 1;
 
4091
    } else {
 
4092
      sx = (annotX1 - annotX0) / (formX1 - formX0);
 
4093
    }
 
4094
    if (formY1 == formY0) {
 
4095
      // this shouldn't happen
 
4096
      sy = 1;
 
4097
    } else {
 
4098
      sy = (annotY1 - annotY0) / (formY1 - formY0);
 
4099
    }
 
4100
    m[0] *= sx;
 
4101
    m[2] *= sx;
 
4102
    m[4] = (m[4] - formX0) * sx + annotX0;
 
4103
    m[1] *= sy;
 
4104
    m[3] *= sy;
 
4105
    m[5] = (m[5] - formY0) * sy + annotY0;
 
4106
 
 
4107
    // get resources
 
4108
    dict->lookup("Resources", &resObj);
 
4109
    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
 
4110
 
 
4111
    // draw it
 
4112
    doForm1(str, resDict, m, bbox);
 
4113
 
 
4114
    resObj.free();
 
4115
  }
 
4116
 
 
4117
  // draw the border
 
4118
  if (borderStyle && borderStyle->getWidth() > 0) {
 
4119
    if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
 
4120
      state->setStrokePattern(NULL);
 
4121
      state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
 
4122
      out->updateStrokeColorSpace(state);
 
4123
    }
 
4124
    borderStyle->getColor(&r, &g, &b);
 
4125
    color.c[0] = dblToCol(r);
 
4126
    color.c[1] = dblToCol(g);
 
4127
    color.c[2] = dblToCol(b);
 
4128
    state->setStrokeColor(&color);
 
4129
    out->updateStrokeColor(state);
 
4130
    // compute the width scale factor when going from default user
 
4131
    // space to user space
 
4132
    x = (baseMatrix[0] + baseMatrix[2]) * ictm[0] +
 
4133
        (baseMatrix[1] + baseMatrix[3]) * ictm[2];
 
4134
    y = (baseMatrix[0] + baseMatrix[2]) * ictm[1] +
 
4135
        (baseMatrix[1] + baseMatrix[3]) * ictm[3];
 
4136
    x = sqrt(0.5 * (x * x + y * y));
 
4137
    state->setLineWidth(x * borderStyle->getWidth());
 
4138
    out->updateLineWidth(state);
 
4139
    borderStyle->getDash(&dash, &dashLength);
 
4140
    if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
 
4141
      dash2 = (double *)gmallocn(dashLength, sizeof(double));
 
4142
      for (i = 0; i < dashLength; ++i) {
 
4143
        dash2[i] = x * dash[i];
 
4144
      }
 
4145
      state->setLineDash(dash2, dashLength, 0);
 
4146
      out->updateLineDash(state);
 
4147
    }
 
4148
    //~ this doesn't currently handle the beveled and engraved styles
 
4149
    state->clearPath();
 
4150
    state->moveTo(annotX0, out->upsideDown() ? annotY1 : annotY0);
 
4151
    state->lineTo(annotX1, out->upsideDown() ? annotY1 : annotY0);
 
4152
    if (borderStyle->getType() != annotBorderUnderlined) {
 
4153
      state->lineTo(annotX1, out->upsideDown() ? annotY0 : annotY1);
 
4154
      state->lineTo(annotX0, out->upsideDown() ? annotY0 : annotY1);
 
4155
      state->closePath();
 
4156
    }
 
4157
    out->stroke(state);
 
4158
  }
 
4159
}
 
4160
 
 
4161
void Gfx::saveState() {
 
4162
  out->saveState(state);
 
4163
  state = state->save();
 
4164
}
 
4165
 
 
4166
void Gfx::restoreState() {
 
4167
  state = state->restore();
 
4168
  out->restoreState(state);
 
4169
}
 
4170
 
 
4171
void Gfx::pushResources(Dict *resDict) {
 
4172
  res = new GfxResources(xref, resDict, res);
 
4173
}
 
4174
 
 
4175
void Gfx::popResources() {
 
4176
  GfxResources *resPtr;
 
4177
 
 
4178
  resPtr = res->getNext();
 
4179
  delete res;
 
4180
  res = resPtr;
 
4181
}