source: trunk/poppler/mypoppler/poppler/Gfx.cc @ 271

Last change on this file since 271 was 271, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.10.5

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