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

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

PDF plugin: Poppler library updated to version 0.10.0

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