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

Last change on this file since 277 was 277, checked in by rbri, 11 years ago

PDF plugin: Poppler library updated to version 0.12.3

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