root/middleware-offline/trunk/_src/eidmw/pteid-poppler/poppler/Gfx.cc @ 226

Revision 226, 143.2 KB (checked in by vsilva, 8 years ago)

Improve PDF native Signature

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