Code

b37cbb7779ecb237c05e3a23576af3179cb77a3e
[inkscape.git] / src / extension / internal / pdfinput / pdf-parser.cpp
2  /** \file
3  * PDF parsing using libpoppler
4  *
5  * Derived from poppler's Gfx.cc
6  *
7  * Copyright 1996-2003 Glyph & Cog, LLC
8  *
9  */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
15 #ifdef HAVE_POPPLER
17 #ifdef USE_GCC_PRAGMAS
18 #pragma implementation
19 #endif
21 extern "C" {
22         
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <math.h>
29 }
31 #include "svg-builder.h"
32 #include "Gfx.h"
33 #include "pdf-parser.h"
34 #include "unit-constants.h"
36 #include "goo/gmem.h"
37 #include "goo/GooTimer.h"
38 #include "goo/GooHash.h"
39 #include "GlobalParams.h"
40 #include "CharTypes.h"
41 #include "Object.h"
42 #include "Array.h"
43 #include "Dict.h"
44 #include "Stream.h"
45 #include "Lexer.h"
46 #include "Parser.h"
47 #include "GfxFont.h"
48 #include "GfxState.h"
49 #include "OutputDev.h"
50 #include "Page.h"
51 #include "Annot.h"
52 #include "Error.h"
54 // the MSVC math.h doesn't define this
55 #ifndef M_PI
56 #define M_PI 3.14159265358979323846
57 #endif
59 //------------------------------------------------------------------------
60 // constants
61 //------------------------------------------------------------------------
63 // Default max delta allowed in any color component for a shading fill.
64 #define defaultShadingColorDelta (dblToCol( 1 / 2.0 ))
66 // Default max recursive depth for a shading fill.
67 #define defaultShadingMaxDepth 6
69 // Max number of operators kept in the history list.
70 #define maxOperatorHistoryDepth 16
72 //------------------------------------------------------------------------
73 // Operator table
74 //------------------------------------------------------------------------
76 #ifdef WIN32 // this works around a bug in the VC7 compiler
77 #  pragma optimize("",off)
78 #endif
80 PdfOperator PdfParser::opTab[] = {
81   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
82           &PdfParser::opMoveSetShowText},
83   {"'",   1, {tchkString},
84           &PdfParser::opMoveShowText},
85   {"B",   0, {tchkNone},
86           &PdfParser::opFillStroke},
87   {"B*",  0, {tchkNone},
88           &PdfParser::opEOFillStroke},
89   {"BDC", 2, {tchkName,   tchkProps},
90           &PdfParser::opBeginMarkedContent},
91   {"BI",  0, {tchkNone},
92           &PdfParser::opBeginImage},
93   {"BMC", 1, {tchkName},
94           &PdfParser::opBeginMarkedContent},
95   {"BT",  0, {tchkNone},
96           &PdfParser::opBeginText},
97   {"BX",  0, {tchkNone},
98           &PdfParser::opBeginIgnoreUndef},
99   {"CS",  1, {tchkName},
100           &PdfParser::opSetStrokeColorSpace},
101   {"DP",  2, {tchkName,   tchkProps},
102           &PdfParser::opMarkPoint},
103   {"Do",  1, {tchkName},
104           &PdfParser::opXObject},
105   {"EI",  0, {tchkNone},
106           &PdfParser::opEndImage},
107   {"EMC", 0, {tchkNone},
108           &PdfParser::opEndMarkedContent},
109   {"ET",  0, {tchkNone},
110           &PdfParser::opEndText},
111   {"EX",  0, {tchkNone},
112           &PdfParser::opEndIgnoreUndef},
113   {"F",   0, {tchkNone},
114           &PdfParser::opFill},
115   {"G",   1, {tchkNum},
116           &PdfParser::opSetStrokeGray},
117   {"ID",  0, {tchkNone},
118           &PdfParser::opImageData},
119   {"J",   1, {tchkInt},
120           &PdfParser::opSetLineCap},
121   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
122           &PdfParser::opSetStrokeCMYKColor},
123   {"M",   1, {tchkNum},
124           &PdfParser::opSetMiterLimit},
125   {"MP",  1, {tchkName},
126           &PdfParser::opMarkPoint},
127   {"Q",   0, {tchkNone},
128           &PdfParser::opRestore},
129   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
130           &PdfParser::opSetStrokeRGBColor},
131   {"S",   0, {tchkNone},
132           &PdfParser::opStroke},
133   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
134           &PdfParser::opSetStrokeColor},
135   {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
136                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
137                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
138                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
139                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
140                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
141                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
142                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
143                 tchkSCN},
144           &PdfParser::opSetStrokeColorN},
145   {"T*",  0, {tchkNone},
146           &PdfParser::opTextNextLine},
147   {"TD",  2, {tchkNum,    tchkNum},
148           &PdfParser::opTextMoveSet},
149   {"TJ",  1, {tchkArray},
150           &PdfParser::opShowSpaceText},
151   {"TL",  1, {tchkNum},
152           &PdfParser::opSetTextLeading},
153   {"Tc",  1, {tchkNum},
154           &PdfParser::opSetCharSpacing},
155   {"Td",  2, {tchkNum,    tchkNum},
156           &PdfParser::opTextMove},
157   {"Tf",  2, {tchkName,   tchkNum},
158           &PdfParser::opSetFont},
159   {"Tj",  1, {tchkString},
160           &PdfParser::opShowText},
161   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
162               tchkNum,    tchkNum},
163           &PdfParser::opSetTextMatrix},
164   {"Tr",  1, {tchkInt},
165           &PdfParser::opSetTextRender},
166   {"Ts",  1, {tchkNum},
167           &PdfParser::opSetTextRise},
168   {"Tw",  1, {tchkNum},
169           &PdfParser::opSetWordSpacing},
170   {"Tz",  1, {tchkNum},
171           &PdfParser::opSetHorizScaling},
172   {"W",   0, {tchkNone},
173           &PdfParser::opClip},
174   {"W*",  0, {tchkNone},
175           &PdfParser::opEOClip},
176   {"b",   0, {tchkNone},
177           &PdfParser::opCloseFillStroke},
178   {"b*",  0, {tchkNone},
179           &PdfParser::opCloseEOFillStroke},
180   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
181               tchkNum,    tchkNum},
182           &PdfParser::opCurveTo},
183   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
184               tchkNum,    tchkNum},
185           &PdfParser::opConcat},
186   {"cs",  1, {tchkName},
187           &PdfParser::opSetFillColorSpace},
188   {"d",   2, {tchkArray,  tchkNum},
189           &PdfParser::opSetDash},
190   {"d0",  2, {tchkNum,    tchkNum},
191           &PdfParser::opSetCharWidth},
192   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
193               tchkNum,    tchkNum},
194           &PdfParser::opSetCacheDevice},
195   {"f",   0, {tchkNone},
196           &PdfParser::opFill},
197   {"f*",  0, {tchkNone},
198           &PdfParser::opEOFill},
199   {"g",   1, {tchkNum},
200           &PdfParser::opSetFillGray},
201   {"gs",  1, {tchkName},
202           &PdfParser::opSetExtGState},
203   {"h",   0, {tchkNone},
204           &PdfParser::opClosePath},
205   {"i",   1, {tchkNum},
206           &PdfParser::opSetFlat},
207   {"j",   1, {tchkInt},
208           &PdfParser::opSetLineJoin},
209   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
210           &PdfParser::opSetFillCMYKColor},
211   {"l",   2, {tchkNum,    tchkNum},
212           &PdfParser::opLineTo},
213   {"m",   2, {tchkNum,    tchkNum},
214           &PdfParser::opMoveTo},
215   {"n",   0, {tchkNone},
216           &PdfParser::opEndPath},
217   {"q",   0, {tchkNone},
218           &PdfParser::opSave},
219   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
220           &PdfParser::opRectangle},
221   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
222           &PdfParser::opSetFillRGBColor},
223   {"ri",  1, {tchkName},
224           &PdfParser::opSetRenderingIntent},
225   {"s",   0, {tchkNone},
226           &PdfParser::opCloseStroke},
227   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
228           &PdfParser::opSetFillColor},
229   {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
230                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
231                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
232                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
233                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
234                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
235                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
236                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
237                 tchkSCN},
238           &PdfParser::opSetFillColorN},
239   {"sh",  1, {tchkName},
240           &PdfParser::opShFill},
241   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
242           &PdfParser::opCurveTo1},
243   {"w",   1, {tchkNum},
244           &PdfParser::opSetLineWidth},
245   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
246           &PdfParser::opCurveTo2}
247 };
249 #ifdef WIN32 // this works around a bug in the VC7 compiler
250 #  pragma optimize("",on)
251 #endif
253 #define numOps (sizeof(opTab) / sizeof(PdfOperator))
255 //------------------------------------------------------------------------
256 // PdfParser
257 //------------------------------------------------------------------------
259 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
260                      int pageNum, int rotate, Dict *resDict,
261                      PDFRectangle *box, PDFRectangle *cropBox) {
263   int i;
265   xref = xrefA;
266   subPage = gFalse;
267   printCommands = false;
269   // start the resource stack
270   res = new GfxResources(xref, resDict, NULL);
272   // initialize
273   state = new GfxState(72.0, 72.0, box, rotate, gTrue);
274   clipHistory = new ClipHistoryEntry();
275   setDefaultApproximationPrecision();
276   fontChanged = gFalse;
277   clip = clipNone;
278   ignoreUndef = 0;
279   operatorHistory = NULL;
280   builder = builderA;
281   builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
282                            state->getPageHeight()*PX_PER_PT);
284   double *ctm = state->getCTM();
285   double scaledCTM[6];
286   for (i = 0; i < 6; ++i) {
287     baseMatrix[i] = ctm[i];
288     scaledCTM[i] = PX_PER_PT * ctm[i];
289   }
290   saveState();
291   builder->setTransform((double*)&scaledCTM);
292   formDepth = 0;
294   // set crop box
295   if (cropBox) {
296     if (printCommands)
297         printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
298     // do not clip if it's not needed
299     if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
300         cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
301         
302         state->moveTo(cropBox->x1, cropBox->y1);
303         state->lineTo(cropBox->x2, cropBox->y1);
304         state->lineTo(cropBox->x2, cropBox->y2);
305         state->lineTo(cropBox->x1, cropBox->y2);
306         state->closePath();
307         state->clip();
308         clipHistory->setClip(state->getPath(), clipNormal);
309         builder->setClipPath(state);
310         state->clearPath();
311     }
312   }
313   pushOperator("startPage");
316 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
317                      Dict *resDict, PDFRectangle *box) {
319   int i;
321   xref = xrefA;
322   subPage = gTrue;
323   printCommands = false;
325   // start the resource stack
326   res = new GfxResources(xref, resDict, NULL);
328   // initialize
329   operatorHistory = NULL;
330   builder = builderA;
331   state = new GfxState(72, 72, box, 0, gFalse);
332   clipHistory = new ClipHistoryEntry();
333   setDefaultApproximationPrecision();
334   
335   fontChanged = gFalse;
336   clip = clipNone;
337   ignoreUndef = 0;
338   for (i = 0; i < 6; ++i) {
339     baseMatrix[i] = state->getCTM()[i];
340   }
341   formDepth = 0;
344 PdfParser::~PdfParser() {
345   while (state->hasSaves()) {
346     restoreState();
347   }
348   if (!subPage) {
349     //out->endPage();
350   }
351   while (res) {
352     popResources();
353   }
354   if (state) {
355     delete state;
356   }
357   if (clipHistory) {
358     delete clipHistory;
359   }
362 void PdfParser::parse(Object *obj, GBool topLevel) {
363   Object obj2;
364   int i;
366   if (obj->isArray()) {
367     for (i = 0; i < obj->arrayGetLength(); ++i) {
368       obj->arrayGet(i, &obj2);
369       if (!obj2.isStream()) {
370         error(-1, const_cast<char*>("Weird page contents"));
371         obj2.free();
372         return;
373       }
374       obj2.free();
375     }
376   } else if (!obj->isStream()) {
377     error(-1, const_cast<char*>("Weird page contents"));
378     return;
379   }
380   parser = new Parser(xref, new Lexer(xref, obj), gFalse);
381   go(topLevel);
382   delete parser;
383   parser = NULL;
386 void PdfParser::go(GBool topLevel) {
387   Object obj;
388   Object args[maxArgs];
389   int numArgs, i;
391   // scan a sequence of objects
392   numArgs = 0;
393   parser->getObj(&obj);
394   while (!obj.isEOF()) {
396     // got a command - execute it
397     if (obj.isCmd()) {
398       if (printCommands) {
399         obj.print(stdout);
400         for (i = 0; i < numArgs; ++i) {
401           printf(" ");
402           args[i].print(stdout);
403         }
404         printf("\n");
405         fflush(stdout);
406       }
408       // Run the operation
409       execOp(&obj, args, numArgs);
411       obj.free();
412       for (i = 0; i < numArgs; ++i)
413         args[i].free();
414       numArgs = 0;
416     // got an argument - save it
417     } else if (numArgs < maxArgs) {
418       args[numArgs++] = obj;
420     // too many arguments - something is wrong
421     } else {
422       error(getPos(), const_cast<char*>("Too many args in content stream"));
423       if (printCommands) {
424         printf("throwing away arg: ");
425         obj.print(stdout);
426         printf("\n");
427         fflush(stdout);
428       }
429       obj.free();
430     }
432     // grab the next object
433     parser->getObj(&obj);
434   }
435   obj.free();
437   // args at end with no command
438   if (numArgs > 0) {
439     error(getPos(), const_cast<char*>("Leftover args in content stream"));
440     if (printCommands) {
441       printf("%d leftovers:", numArgs);
442       for (i = 0; i < numArgs; ++i) {
443         printf(" ");
444         args[i].print(stdout);
445       }
446       printf("\n");
447       fflush(stdout);
448     }
449     for (i = 0; i < numArgs; ++i)
450       args[i].free();
451   }
454 void PdfParser::pushOperator(const char *name) {
455     OpHistoryEntry *newEntry = new OpHistoryEntry;
456     newEntry->name = name;
457     newEntry->state = NULL;
458     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
459     newEntry->next = operatorHistory;
460     operatorHistory = newEntry;
462     // Truncate list if needed
463     if (operatorHistory->depth > maxOperatorHistoryDepth) {
464         OpHistoryEntry *curr = operatorHistory;
465         OpHistoryEntry *prev = NULL;
466         while (curr && curr->next != NULL) {
467             curr->depth--;
468             prev = curr;
469             curr = curr->next;
470         }
471         if (prev) {
472             if (curr->state != NULL)
473                 delete curr->state;
474             delete curr;
475             prev->next = NULL;
476         }
477     }
480 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
481     OpHistoryEntry *prev = NULL;
482     if (operatorHistory != NULL && look_back > 0) {
483         prev = operatorHistory->next;
484         while (--look_back > 0 && prev != NULL) {
485             prev = prev->next;
486         }
487     }
488     if (prev != NULL) {
489         return prev->name;
490     } else {
491         return "";
492     }
495 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
496   PdfOperator *op;
497   char *name;
498   Object *argPtr;
499   int i;
501   // find operator
502   name = cmd->getCmd();
503   if (!(op = findOp(name))) {
504     if (ignoreUndef == 0)
505       error(getPos(), const_cast<char*>("Unknown operator '%s'"), name);
506     return;
507   }
509   // type check args
510   argPtr = args;
511   if (op->numArgs >= 0) {
512     if (numArgs < op->numArgs) {
513       error(getPos(), const_cast<char*>("Too few (%d) args to '%s' operator"), numArgs, name);
514       return;
515     }
516     if (numArgs > op->numArgs) {
517 #if 0
518       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
519 #endif
520       argPtr += numArgs - op->numArgs;
521       numArgs = op->numArgs;
522     }
523   } else {
524     if (numArgs > -op->numArgs) {
525       error(getPos(), const_cast<char*>("Too many (%d) args to '%s' operator"),
526             numArgs, name);
527       return;
528     }
529   }
530   for (i = 0; i < numArgs; ++i) {
531     if (!checkArg(&argPtr[i], op->tchk[i])) {
532       error(getPos(), const_cast<char*>("Arg #%d to '%s' operator is wrong type (%s)"),
533             i, name, argPtr[i].getTypeName());
534       return;
535     }
536   }
538   // add to history
539   pushOperator((char*)&op->name);
541   // do it
542   (this->*op->func)(argPtr, numArgs);
545 PdfOperator *PdfParser::findOp(char *name) {
546   int a, b, m, cmp;
548   a = -1;
549   b = numOps;
550   // invariant: opTab[a] < name < opTab[b]
551   while (b - a > 1) {
552     m = (a + b) / 2;
553     cmp = strcmp(opTab[m].name, name);
554     if (cmp < 0)
555       a = m;
556     else if (cmp > 0)
557       b = m;
558     else
559       a = b = m;
560   }
561   if (cmp != 0)
562     return NULL;
563   return &opTab[a];
566 GBool PdfParser::checkArg(Object *arg, TchkType type) {
567   switch (type) {
568   case tchkBool:   return arg->isBool();
569   case tchkInt:    return arg->isInt();
570   case tchkNum:    return arg->isNum();
571   case tchkString: return arg->isString();
572   case tchkName:   return arg->isName();
573   case tchkArray:  return arg->isArray();
574   case tchkProps:  return arg->isDict() || arg->isName();
575   case tchkSCN:    return arg->isNum() || arg->isName();
576   case tchkNone:   return gFalse;
577   }
578   return gFalse;
581 int PdfParser::getPos() {
582   return parser ? parser->getPos() : -1;
585 //------------------------------------------------------------------------
586 // graphics state operators
587 //------------------------------------------------------------------------
589 void PdfParser::opSave(Object args[], int numArgs) {
590   saveState();
593 void PdfParser::opRestore(Object args[], int numArgs) {
594   restoreState();
597 void PdfParser::opConcat(Object args[], int numArgs) {
598   state->concatCTM(args[0].getNum(), args[1].getNum(),
599                    args[2].getNum(), args[3].getNum(),
600                    args[4].getNum(), args[5].getNum());
601   const char *prevOp = getPreviousOperator();
602   double a0 = args[0].getNum();
603   double a1 = args[1].getNum();
604   double a2 = args[2].getNum();
605   double a3 = args[3].getNum();
606   double a4 = args[4].getNum();
607   double a5 = args[5].getNum();
608   if (!strcmp(prevOp, "q")) {
609       builder->setTransform(a0, a1, a2, a3, a4, a5);
610   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
611       // multiply it with the previous transform
612       double otherMatrix[6];
613       if (!builder->getTransform(otherMatrix)) { // invalid transform
614           // construct identity matrix
615           otherMatrix[0] = otherMatrix[3] = 1.0;
616           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
617       }
618       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
619       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
620       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
621       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
622       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
623       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
624       builder->setTransform(c0, c1, c2, c3, c4, c5);
625   } else {
626       builder->pushGroup();
627       builder->setTransform(a0, a1, a2, a3, a4, a5);
628   }
629   fontChanged = gTrue;
632 void PdfParser::opSetDash(Object args[], int numArgs) {
633   Array *a;
634   int length;
635   Object obj;
636   double *dash;
637   int i;
639   a = args[0].getArray();
640   length = a->getLength();
641   if (length == 0) {
642     dash = NULL;
643   } else {
644     dash = (double *)gmallocn(length, sizeof(double));
645     for (i = 0; i < length; ++i) {
646       dash[i] = a->get(i, &obj)->getNum();
647       obj.free();
648     }
649   }
650   state->setLineDash(dash, length, args[1].getNum());
651   builder->updateStyle(state);
654 void PdfParser::opSetFlat(Object args[], int numArgs) {
655   state->setFlatness((int)args[0].getNum());
658 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
659   state->setLineJoin(args[0].getInt());
660   builder->updateStyle(state);
663 void PdfParser::opSetLineCap(Object args[], int numArgs) {
664   state->setLineCap(args[0].getInt());
665   builder->updateStyle(state);
668 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
669   state->setMiterLimit(args[0].getNum());
670   builder->updateStyle(state);
673 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
674   state->setLineWidth(args[0].getNum());
675   builder->updateStyle(state);
678 void PdfParser::opSetExtGState(Object args[], int numArgs) {
679   Object obj1, obj2, obj3, obj4, obj5;
680   GfxBlendMode mode;
681   GBool haveFillOP;
682   Function *funcs[4];
683   GfxColor backdropColor;
684   GBool haveBackdropColor;
685   GfxColorSpace *blendingColorSpace;
686   GBool alpha, isolated, knockout;
687   int i;
689   if (!res->lookupGState(args[0].getName(), &obj1)) {
690     return;
691   }
692   if (!obj1.isDict()) {
693     error(getPos(), const_cast<char*>("ExtGState '%s' is wrong type"), args[0].getName());
694     obj1.free();
695     return;
696   }
697   if (printCommands) {
698     printf("  gfx state dict: ");
699     obj1.print();
700     printf("\n");
701   }
703   // transparency support: blend mode, fill/stroke opacity
704   if (!obj1.dictLookup(const_cast<char*>("BM"), &obj2)->isNull()) {
705     if (state->parseBlendMode(&obj2, &mode)) {
706       state->setBlendMode(mode);
707     } else {
708       error(getPos(), const_cast<char*>("Invalid blend mode in ExtGState"));
709     }
710   }
711   obj2.free();
712   if (obj1.dictLookup(const_cast<char*>("ca"), &obj2)->isNum()) {
713     state->setFillOpacity(obj2.getNum());
714   }
715   obj2.free();
716   if (obj1.dictLookup(const_cast<char*>("CA"), &obj2)->isNum()) {
717     state->setStrokeOpacity(obj2.getNum());
718   }
719   obj2.free();
721   // fill/stroke overprint
722   if ((haveFillOP = (obj1.dictLookup(const_cast<char*>("op"), &obj2)->isBool()))) {
723     state->setFillOverprint(obj2.getBool());
724   }
725   obj2.free();
726   if (obj1.dictLookup(const_cast<char*>("OP"), &obj2)->isBool()) {
727     state->setStrokeOverprint(obj2.getBool());
728     if (!haveFillOP) {
729       state->setFillOverprint(obj2.getBool());
730     }
731   }
732   obj2.free();
734   // stroke adjust
735   if (obj1.dictLookup(const_cast<char*>("SA"), &obj2)->isBool()) {
736     state->setStrokeAdjust(obj2.getBool());
737   }
738   obj2.free();
740   // transfer function
741   if (obj1.dictLookup(const_cast<char*>("TR2"), &obj2)->isNull()) {
742     obj2.free();
743     obj1.dictLookup(const_cast<char*>("TR"), &obj2);
744   }
745   if (obj2.isName(const_cast<char*>("Default")) ||
746       obj2.isName(const_cast<char*>("Identity"))) {
747     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
748     state->setTransfer(funcs);
749   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
750     for (i = 0; i < 4; ++i) {
751       obj2.arrayGet(i, &obj3);
752       funcs[i] = Function::parse(&obj3);
753       obj3.free();
754       if (!funcs[i]) {
755         break;
756       }
757     }
758     if (i == 4) {
759       state->setTransfer(funcs);
760     }
761   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
762     if ((funcs[0] = Function::parse(&obj2))) {
763       funcs[1] = funcs[2] = funcs[3] = NULL;
764       state->setTransfer(funcs);
765     }
766   } else if (!obj2.isNull()) {
767     error(getPos(), const_cast<char*>("Invalid transfer function in ExtGState"));
768   }
769   obj2.free();
771   // soft mask
772   if (!obj1.dictLookup(const_cast<char*>("SMask"), &obj2)->isNull()) {
773     if (obj2.isName(const_cast<char*>("None"))) {
774       builder->clearSoftMask(state);
775     } else if (obj2.isDict()) {
776       if (obj2.dictLookup(const_cast<char*>("S"), &obj3)->isName(const_cast<char*>("Alpha"))) {
777         alpha = gTrue;
778       } else { // "Luminosity"
779         alpha = gFalse;
780       }
781       obj3.free();
782       funcs[0] = NULL;
783       if (!obj2.dictLookup(const_cast<char*>("TR"), &obj3)->isNull()) {
784         funcs[0] = Function::parse(&obj3);
785         if (funcs[0]->getInputSize() != 1 ||
786             funcs[0]->getOutputSize() != 1) {
787           error(getPos(),
788                 const_cast<char*>("Invalid transfer function in soft mask in ExtGState"));
789           delete funcs[0];
790           funcs[0] = NULL;
791         }
792       }
793       obj3.free();
794       if ((haveBackdropColor = obj2.dictLookup(const_cast<char*>("BC"), &obj3)->isArray())) {
795         for (i = 0; i < gfxColorMaxComps; ++i) {
796           backdropColor.c[i] = 0;
797         }
798         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
799           obj3.arrayGet(i, &obj4);
800           if (obj4.isNum()) {
801             backdropColor.c[i] = dblToCol(obj4.getNum());
802           }
803           obj4.free();
804         }
805       }
806       obj3.free();
807       if (obj2.dictLookup(const_cast<char*>("G"), &obj3)->isStream()) {
808         if (obj3.streamGetDict()->lookup(const_cast<char*>("Group"), &obj4)->isDict()) {
809           blendingColorSpace = NULL;
810           isolated = knockout = gFalse;
811           if (!obj4.dictLookup(const_cast<char*>("CS"), &obj5)->isNull()) {
812 #ifdef POPPLER_NEW_COLOR_SPACE_API
813             blendingColorSpace = GfxColorSpace::parse(&obj5, NULL);
814 #else
815             blendingColorSpace = GfxColorSpace::parse(&obj5);
816 #endif
817           }
818           obj5.free();
819           if (obj4.dictLookup(const_cast<char*>("I"), &obj5)->isBool()) {
820             isolated = obj5.getBool();
821           }
822           obj5.free();
823           if (obj4.dictLookup(const_cast<char*>("K"), &obj5)->isBool()) {
824             knockout = obj5.getBool();
825           }
826           obj5.free();
827           if (!haveBackdropColor) {
828             if (blendingColorSpace) {
829               blendingColorSpace->getDefaultColor(&backdropColor);
830             } else {
831               //~ need to get the parent or default color space (?)
832               for (i = 0; i < gfxColorMaxComps; ++i) {
833                 backdropColor.c[i] = 0;
834               }
835             }
836           }
837           doSoftMask(&obj3, alpha, blendingColorSpace,
838                      isolated, knockout, funcs[0], &backdropColor);
839           if (funcs[0]) {
840             delete funcs[0];
841           }
842         } else {
843           error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
844         }
845         obj4.free();
846       } else {
847         error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
848       }
849       obj3.free();
850     } else if (!obj2.isNull()) {
851       error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState"));
852     }
853   }
854   obj2.free();
856   obj1.free();
859 void PdfParser::doSoftMask(Object *str, GBool alpha,
860                      GfxColorSpace *blendingColorSpace,
861                      GBool isolated, GBool knockout,
862                      Function *transferFunc, GfxColor *backdropColor) {
863   Dict *dict, *resDict;
864   double m[6], bbox[4];
865   Object obj1, obj2;
866   int i;
868   // check for excessive recursion
869   if (formDepth > 20) {
870     return;
871   }
873   // get stream dict
874   dict = str->streamGetDict();
876   // check form type
877   dict->lookup(const_cast<char*>("FormType"), &obj1);
878   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
879     error(getPos(), const_cast<char*>("Unknown form type"));
880   }
881   obj1.free();
883   // get bounding box
884   dict->lookup(const_cast<char*>("BBox"), &obj1);
885   if (!obj1.isArray()) {
886     obj1.free();
887     error(getPos(), const_cast<char*>("Bad form bounding box"));
888     return;
889   }
890   for (i = 0; i < 4; ++i) {
891     obj1.arrayGet(i, &obj2);
892     bbox[i] = obj2.getNum();
893     obj2.free();
894   }
895   obj1.free();
897   // get matrix
898   dict->lookup(const_cast<char*>("Matrix"), &obj1);
899   if (obj1.isArray()) {
900     for (i = 0; i < 6; ++i) {
901       obj1.arrayGet(i, &obj2);
902       m[i] = obj2.getNum();
903       obj2.free();
904     }
905   } else {
906     m[0] = 1; m[1] = 0;
907     m[2] = 0; m[3] = 1;
908     m[4] = 0; m[5] = 0;
909   }
910   obj1.free();
912   // get resources
913   dict->lookup(const_cast<char*>("Resources"), &obj1);
914   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
916   // draw it
917   ++formDepth;
918   doForm1(str, resDict, m, bbox, gTrue, gTrue,
919           blendingColorSpace, isolated, knockout,
920           alpha, transferFunc, backdropColor);
921   --formDepth;
923   if (blendingColorSpace) {
924     delete blendingColorSpace;
925   }
926   obj1.free();
929 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
932 //------------------------------------------------------------------------
933 // color operators
934 //------------------------------------------------------------------------
936 void PdfParser::opSetFillGray(Object args[], int numArgs) {
937   GfxColor color;
939   state->setFillPattern(NULL);
940   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
941   color.c[0] = dblToCol(args[0].getNum());
942   state->setFillColor(&color);
943   builder->updateStyle(state);
946 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
947   GfxColor color;
949   state->setStrokePattern(NULL);
950   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
951   color.c[0] = dblToCol(args[0].getNum());
952   state->setStrokeColor(&color);
953   builder->updateStyle(state);
956 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
957   GfxColor color;
958   int i;
960   state->setFillPattern(NULL);
961   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
962   for (i = 0; i < 4; ++i) {
963     color.c[i] = dblToCol(args[i].getNum());
964   }
965   state->setFillColor(&color);
966   builder->updateStyle(state);
969 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
970   GfxColor color;
971   int i;
973   state->setStrokePattern(NULL);
974   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
975   for (i = 0; i < 4; ++i) {
976     color.c[i] = dblToCol(args[i].getNum());
977   }
978   state->setStrokeColor(&color);
979   builder->updateStyle(state);
982 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
983   GfxColor color;
984   int i;
986   state->setFillPattern(NULL);
987   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
988   for (i = 0; i < 3; ++i) {
989     color.c[i] = dblToCol(args[i].getNum());
990   }
991   state->setFillColor(&color);
992   builder->updateStyle(state);
995 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
996   GfxColor color;
997   int i;
999   state->setStrokePattern(NULL);
1000   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1001   for (i = 0; i < 3; ++i) {
1002     color.c[i] = dblToCol(args[i].getNum());
1003   }
1004   state->setStrokeColor(&color);
1005   builder->updateStyle(state);
1008 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1009   Object obj;
1010   GfxColorSpace *colorSpace;
1011   GfxColor color;
1013   state->setFillPattern(NULL);
1014   res->lookupColorSpace(args[0].getName(), &obj);
1015 #ifdef POPPLER_NEW_COLOR_SPACE_API
1016   if (obj.isNull()) {
1017     colorSpace = GfxColorSpace::parse(&args[0], NULL);
1018   } else {
1019     colorSpace = GfxColorSpace::parse(&obj, NULL);
1020   }
1021 #else
1022   if (obj.isNull()) {
1023     colorSpace = GfxColorSpace::parse(&args[0]);
1024   } else {
1025     colorSpace = GfxColorSpace::parse(&obj);
1026   }
1027 #endif
1028   obj.free();
1029   if (colorSpace) {
1030     state->setFillColorSpace(colorSpace);
1031     colorSpace->getDefaultColor(&color);
1032     state->setFillColor(&color);
1033     builder->updateStyle(state);
1034   } else {
1035     error(getPos(), const_cast<char*>("Bad color space (fill)"));
1036   }
1039 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1040   Object obj;
1041   GfxColorSpace *colorSpace;
1042   GfxColor color;
1044   state->setStrokePattern(NULL);
1045   res->lookupColorSpace(args[0].getName(), &obj);
1046 #ifdef POPPLER_NEW_COLOR_SPACE_API
1047   if (obj.isNull()) {
1048     colorSpace = GfxColorSpace::parse(&args[0], NULL);
1049   } else {
1050     colorSpace = GfxColorSpace::parse(&obj, NULL);
1051   }
1052 #else
1053   if (obj.isNull()) {
1054     colorSpace = GfxColorSpace::parse(&args[0]);
1055   } else {
1056     colorSpace = GfxColorSpace::parse(&obj);
1057   }
1058 #endif
1059   obj.free();
1060   if (colorSpace) {
1061     state->setStrokeColorSpace(colorSpace);
1062     colorSpace->getDefaultColor(&color);
1063     state->setStrokeColor(&color);
1064     builder->updateStyle(state);
1065   } else {
1066     error(getPos(), const_cast<char*>("Bad color space (stroke)"));
1067   }
1070 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1071   GfxColor color;
1072   int i;
1074   if (numArgs != state->getFillColorSpace()->getNComps()) {
1075     error(getPos(), const_cast<char*>("Incorrect number of arguments in 'sc' command"));
1076     return;
1077   }
1078   state->setFillPattern(NULL);
1079   for (i = 0; i < numArgs; ++i) {
1080     color.c[i] = dblToCol(args[i].getNum());
1081   }
1082   state->setFillColor(&color);
1083   builder->updateStyle(state);
1086 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1087   GfxColor color;
1088   int i;
1090   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1091     error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SC' command"));
1092     return;
1093   }
1094   state->setStrokePattern(NULL);
1095   for (i = 0; i < numArgs; ++i) {
1096     color.c[i] = dblToCol(args[i].getNum());
1097   }
1098   state->setStrokeColor(&color);
1099   builder->updateStyle(state);
1102 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1103   GfxColor color;
1104   GfxPattern *pattern;
1105   int i;
1107   if (state->getFillColorSpace()->getMode() == csPattern) {
1108     if (numArgs > 1) {
1109       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1110           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1111                              ->getUnder()->getNComps()) {
1112         error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
1113         return;
1114       }
1115       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1116         if (args[i].isNum()) {
1117           color.c[i] = dblToCol(args[i].getNum());
1118         }
1119       }
1120       state->setFillColor(&color);
1121       builder->updateStyle(state);
1122     }
1123 #ifdef POPPLER_NEW_COLOR_SPACE_API
1124     if (args[numArgs-1].isName() &&
1125         (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
1126       state->setFillPattern(pattern);
1127       builder->updateStyle(state);
1128     }
1129 #else
1130     if (args[numArgs-1].isName() &&
1131         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1132       state->setFillPattern(pattern);
1133       builder->updateStyle(state);
1134     }
1135 #endif
1137   } else {
1138     if (numArgs != state->getFillColorSpace()->getNComps()) {
1139       error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
1140       return;
1141     }
1142     state->setFillPattern(NULL);
1143     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1144       if (args[i].isNum()) {
1145         color.c[i] = dblToCol(args[i].getNum());
1146       }
1147     }
1148     state->setFillColor(&color);
1149     builder->updateStyle(state);
1150   }
1153 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1154   GfxColor color;
1155   GfxPattern *pattern;
1156   int i;
1158   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1159     if (numArgs > 1) {
1160       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1161                ->getUnder() ||
1162           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1163                              ->getUnder()->getNComps()) {
1164         error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
1165         return;
1166       }
1167       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1168         if (args[i].isNum()) {
1169           color.c[i] = dblToCol(args[i].getNum());
1170         }
1171       }
1172       state->setStrokeColor(&color);
1173       builder->updateStyle(state);
1174     }
1175 #ifdef POPPLER_NEW_COLOR_SPACE_API
1176     if (args[numArgs-1].isName() &&
1177         (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
1178       state->setStrokePattern(pattern);
1179       builder->updateStyle(state);
1180     }
1181 #else
1182     if (args[numArgs-1].isName() &&
1183         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1184       state->setStrokePattern(pattern);
1185       builder->updateStyle(state);
1186     }
1187 #endif
1189   } else {
1190     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1191       error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
1192       return;
1193     }
1194     state->setStrokePattern(NULL);
1195     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1196       if (args[i].isNum()) {
1197         color.c[i] = dblToCol(args[i].getNum());
1198       }
1199     }
1200     state->setStrokeColor(&color);
1201     builder->updateStyle(state);
1202   }
1205 //------------------------------------------------------------------------
1206 // path segment operators
1207 //------------------------------------------------------------------------
1209 void PdfParser::opMoveTo(Object args[], int numArgs) {
1210   state->moveTo(args[0].getNum(), args[1].getNum());
1213 void PdfParser::opLineTo(Object args[], int numArgs) {
1214   if (!state->isCurPt()) {
1215     error(getPos(), const_cast<char*>("No current point in lineto"));
1216     return;
1217   }
1218   state->lineTo(args[0].getNum(), args[1].getNum());
1221 void PdfParser::opCurveTo(Object args[], int numArgs) {
1222   double x1, y1, x2, y2, x3, y3;
1224   if (!state->isCurPt()) {
1225     error(getPos(), const_cast<char*>("No current point in curveto"));
1226     return;
1227   }
1228   x1 = args[0].getNum();
1229   y1 = args[1].getNum();
1230   x2 = args[2].getNum();
1231   y2 = args[3].getNum();
1232   x3 = args[4].getNum();
1233   y3 = args[5].getNum();
1234   state->curveTo(x1, y1, x2, y2, x3, y3);
1237 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1238   double x1, y1, x2, y2, x3, y3;
1240   if (!state->isCurPt()) {
1241     error(getPos(), const_cast<char*>("No current point in curveto1"));
1242     return;
1243   }
1244   x1 = state->getCurX();
1245   y1 = state->getCurY();
1246   x2 = args[0].getNum();
1247   y2 = args[1].getNum();
1248   x3 = args[2].getNum();
1249   y3 = args[3].getNum();
1250   state->curveTo(x1, y1, x2, y2, x3, y3);
1253 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1254   double x1, y1, x2, y2, x3, y3;
1256   if (!state->isCurPt()) {
1257     error(getPos(), const_cast<char*>("No current point in curveto2"));
1258     return;
1259   }
1260   x1 = args[0].getNum();
1261   y1 = args[1].getNum();
1262   x2 = args[2].getNum();
1263   y2 = args[3].getNum();
1264   x3 = x2;
1265   y3 = y2;
1266   state->curveTo(x1, y1, x2, y2, x3, y3);
1269 void PdfParser::opRectangle(Object args[], int numArgs) {
1270   double x, y, w, h;
1272   x = args[0].getNum();
1273   y = args[1].getNum();
1274   w = args[2].getNum();
1275   h = args[3].getNum();
1276   state->moveTo(x, y);
1277   state->lineTo(x + w, y);
1278   state->lineTo(x + w, y + h);
1279   state->lineTo(x, y + h);
1280   state->closePath();
1283 void PdfParser::opClosePath(Object args[], int numArgs) {
1284   if (!state->isCurPt()) {
1285     error(getPos(), const_cast<char*>("No current point in closepath"));
1286     return;
1287   }
1288   state->closePath();
1291 //------------------------------------------------------------------------
1292 // path painting operators
1293 //------------------------------------------------------------------------
1295 void PdfParser::opEndPath(Object args[], int numArgs) {
1296   doEndPath();
1299 void PdfParser::opStroke(Object args[], int numArgs) {
1300   if (!state->isCurPt()) {
1301     //error(getPos(), const_cast<char*>("No path in stroke"));
1302     return;
1303   }
1304   if (state->isPath()) {
1305     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1306         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1307           doPatternStrokeFallback();
1308     } else {
1309       builder->addPath(state, false, true);
1310     }
1311   }
1312   doEndPath();
1315 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1316   if (!state->isCurPt()) {
1317     //error(getPos(), const_cast<char*>("No path in closepath/stroke"));
1318     return;
1319   }
1320   state->closePath();
1321   if (state->isPath()) {
1322     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1323         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1324       doPatternStrokeFallback();
1325     } else {
1326       builder->addPath(state, false, true);
1327     }
1328   }
1329   doEndPath();
1332 void PdfParser::opFill(Object args[], int numArgs) {
1333   if (!state->isCurPt()) {
1334     //error(getPos(), const_cast<char*>("No path in fill"));
1335     return;
1336   }
1337   if (state->isPath()) {
1338     if (state->getFillColorSpace()->getMode() == csPattern &&
1339         !builder->isPatternTypeSupported(state->getFillPattern())) {
1340       doPatternFillFallback(gFalse);
1341     } else {
1342       builder->addPath(state, true, false);
1343     }
1344   }
1345   doEndPath();
1348 void PdfParser::opEOFill(Object args[], int numArgs) {
1349   if (!state->isCurPt()) {
1350     //error(getPos(), const_cast<char*>("No path in eofill"));
1351     return;
1352   }
1353   if (state->isPath()) {
1354     if (state->getFillColorSpace()->getMode() == csPattern &&
1355         !builder->isPatternTypeSupported(state->getFillPattern())) {
1356       doPatternFillFallback(gTrue);
1357     } else {
1358       builder->addPath(state, true, false, true);
1359     }
1360   }
1361   doEndPath();
1364 void PdfParser::opFillStroke(Object args[], int numArgs) {
1365   if (!state->isCurPt()) {
1366     //error(getPos(), const_cast<char*>("No path in fill/stroke"));
1367     return;
1368   }
1369   if (state->isPath()) {
1370     doFillAndStroke(gFalse);
1371   } else {
1372     builder->addPath(state, true, true);
1373   }
1374   doEndPath();
1377 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1378   if (!state->isCurPt()) {
1379     //error(getPos(), const_cast<char*>("No path in closepath/fill/stroke"));
1380     return;
1381   }
1382   if (state->isPath()) {
1383     state->closePath();
1384     doFillAndStroke(gFalse);
1385   }
1386   doEndPath();
1389 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1390   if (!state->isCurPt()) {
1391     //error(getPos(), const_cast<char*>("No path in eofill/stroke"));
1392     return;
1393   }
1394   if (state->isPath()) {
1395     doFillAndStroke(gTrue);
1396   }
1397   doEndPath();
1400 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1401   if (!state->isCurPt()) {
1402     //error(getPos(), const_cast<char*>("No path in closepath/eofill/stroke"));
1403     return;
1404   }
1405   if (state->isPath()) {
1406     state->closePath();
1407     doFillAndStroke(gTrue);
1408   }
1409   doEndPath();
1412 void PdfParser::doFillAndStroke(GBool eoFill) {
1413     GBool fillOk = gTrue, strokeOk = gTrue;
1414     if (state->getFillColorSpace()->getMode() == csPattern &&
1415         !builder->isPatternTypeSupported(state->getFillPattern())) {
1416         fillOk = gFalse;
1417     }
1418     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1419         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1420         strokeOk = gFalse;
1421     }
1422     if (fillOk && strokeOk) {
1423         builder->addPath(state, true, true, eoFill);
1424     } else {
1425         doPatternFillFallback(eoFill);
1426         doPatternStrokeFallback();
1427     }
1430 void PdfParser::doPatternFillFallback(GBool eoFill) {
1431   GfxPattern *pattern;
1433   if (!(pattern = state->getFillPattern())) {
1434     return;
1435   }
1436   switch (pattern->getType()) {
1437   case 1:
1438     break;
1439   case 2:
1440     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1441     break;
1442   default:
1443     error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in fill"),
1444           pattern->getType());
1445     break;
1446   }
1449 void PdfParser::doPatternStrokeFallback() {
1450   GfxPattern *pattern;
1452   if (!(pattern = state->getStrokePattern())) {
1453     return;
1454   }
1455   switch (pattern->getType()) {
1456   case 1:
1457     break;
1458   case 2:
1459     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1460     break;
1461   default:
1462     error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in stroke"),
1463           pattern->getType());
1464     break;
1465   }
1468 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1469                                              GBool stroke, GBool eoFill) {
1470   GfxShading *shading;
1471   GfxPath *savedPath;
1472   double *ctm, *btm, *ptm;
1473   double m[6], ictm[6], m1[6];
1474   double xMin, yMin, xMax, yMax;
1475   double det;
1477   shading = sPat->getShading();
1479   // save current graphics state
1480   savedPath = state->getPath()->copy();
1481   saveState();
1483   // clip to bbox
1484   if (0 ){//shading->getHasBBox()) {
1485     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1486     state->moveTo(xMin, yMin);
1487     state->lineTo(xMax, yMin);
1488     state->lineTo(xMax, yMax);
1489     state->lineTo(xMin, yMax);
1490     state->closePath();
1491     state->clip();
1492     //builder->clip(state);
1493     state->setPath(savedPath->copy());
1494   }
1496   // clip to current path
1497   if (stroke) {
1498     state->clipToStrokePath();
1499     //out->clipToStrokePath(state);
1500   } else {
1501     state->clip();
1502     if (eoFill) {
1503       builder->setClipPath(state, true);
1504     } else {
1505       builder->setClipPath(state);
1506     }
1507   }
1509   // set the color space
1510   state->setFillColorSpace(shading->getColorSpace()->copy());
1512   // background color fill
1513   if (shading->getHasBackground()) {
1514     state->setFillColor(shading->getBackground());
1515     builder->addPath(state, true, false);
1516   }
1517   state->clearPath();
1519   // construct a (pattern space) -> (current space) transform matrix
1520   ctm = state->getCTM();
1521   btm = baseMatrix;
1522   ptm = sPat->getMatrix();
1523   // iCTM = invert CTM
1524   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1525   ictm[0] = ctm[3] * det;
1526   ictm[1] = -ctm[1] * det;
1527   ictm[2] = -ctm[2] * det;
1528   ictm[3] = ctm[0] * det;
1529   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1530   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1531   // m1 = PTM * BTM = PTM * base transform matrix
1532   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1533   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1534   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1535   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1536   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1537   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1538   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1539   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1540   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1541   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1542   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1543   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1544   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1546   // set the new matrix
1547   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1548   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1550   // do shading type-specific operations
1551   switch (shading->getType()) {
1552   case 1:
1553     doFunctionShFill((GfxFunctionShading *)shading);
1554     break;
1555   case 2:
1556   case 3:
1557     // no need to implement these
1558     break;
1559   case 4:
1560   case 5:
1561     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1562     break;
1563   case 6:
1564   case 7:
1565     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1566     break;
1567   }
1569   // restore graphics state
1570   restoreState();
1571   state->setPath(savedPath);
1574 void PdfParser::opShFill(Object args[], int numArgs) {
1575   GfxShading *shading;
1576   GfxPath *savedPath = NULL;
1577   double xMin, yMin, xMax, yMax;
1578   double gradientTransform[6];
1579   double *matrix = NULL;
1580   GBool savedState = gFalse;
1582 #ifdef POPPLER_NEW_COLOR_SPACE_API
1583   if (!(shading = res->lookupShading(args[0].getName(), NULL))) {
1584     return;
1585   }
1586 #else
1587   if (!(shading = res->lookupShading(args[0].getName()))) {
1588     return;
1589   }
1590 #endif
1592   // save current graphics state
1593   if (shading->getType() != 2 && shading->getType() != 3) {
1594     savedPath = state->getPath()->copy();
1595     saveState();
1596     savedState = gTrue;
1597   } else {  // get gradient transform if possible
1598       // check proper operator sequence
1599       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1600       GBool seenClip, seenConcat;
1601       seenClip = (clipHistory->getClipPath() != NULL);
1602       seenConcat = gFalse;
1603       int i = 1;
1604       while (i <= maxOperatorHistoryDepth) {
1605         const char *opName = getPreviousOperator(i);
1606         if (!strcmp(opName, "cm")) {
1607           if (seenConcat) {   // more than one 'cm'
1608             break;
1609           } else {
1610             seenConcat = gTrue;
1611           }
1612         }
1613         i++;
1614       }
1616       if (seenConcat && seenClip) {
1617         if (builder->getTransform(gradientTransform)) {
1618           matrix = (double*)&gradientTransform;
1619           builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);  // remove transform
1620         }
1621       }
1622   }
1624   // clip to bbox
1625   if (shading->getHasBBox()) {
1626     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1627     state->moveTo(xMin, yMin);
1628     state->lineTo(xMax, yMin);
1629     state->lineTo(xMax, yMax);
1630     state->lineTo(xMin, yMax);
1631     state->closePath();
1632     state->clip();
1633     if (savedState)
1634       builder->setClipPath(state);
1635     else
1636       builder->clip(state);
1637     state->clearPath();
1638   }
1640   // set the color space
1641   if (savedState)
1642     state->setFillColorSpace(shading->getColorSpace()->copy());
1644   // do shading type-specific operations
1645   switch (shading->getType()) {
1646   case 1:
1647     doFunctionShFill((GfxFunctionShading *)shading);
1648     break;
1649   case 2:
1650   case 3:
1651     if (clipHistory->getClipPath()) {
1652       builder->addShadedFill(shading, matrix, clipHistory->getClipPath(),
1653                              clipHistory->getClipType() == clipEO ? true : false);
1654     }
1655     break;
1656   case 4:
1657   case 5:
1658     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1659     break;
1660   case 6:
1661   case 7:
1662     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1663     break;
1664   }
1666   // restore graphics state
1667   if (savedState) {
1668     restoreState();
1669     state->setPath(savedPath);
1670   }
1672   delete shading;
1675 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1676   double x0, y0, x1, y1;
1677   GfxColor colors[4];
1679   shading->getDomain(&x0, &y0, &x1, &y1);
1680   shading->getColor(x0, y0, &colors[0]);
1681   shading->getColor(x0, y1, &colors[1]);
1682   shading->getColor(x1, y0, &colors[2]);
1683   shading->getColor(x1, y1, &colors[3]);
1684   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1687 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1688                             double x0, double y0,
1689                             double x1, double y1,
1690                             GfxColor *colors, int depth) {
1691   GfxColor fillColor;
1692   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1693   GfxColor colors2[4];
1694   double functionColorDelta = colorDeltas[pdfFunctionShading-1];
1695   double *matrix;
1696   double xM, yM;
1697   int nComps, i, j;
1699   nComps = shading->getColorSpace()->getNComps();
1700   matrix = shading->getMatrix();
1702   // compare the four corner colors
1703   for (i = 0; i < 4; ++i) {
1704     for (j = 0; j < nComps; ++j) {
1705       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1706         break;
1707       }
1708     }
1709     if (j < nComps) {
1710       break;
1711     }
1712   }
1714   // center of the rectangle
1715   xM = 0.5 * (x0 + x1);
1716   yM = 0.5 * (y0 + y1);
1718   // the four corner colors are close (or we hit the recursive limit)
1719   // -- fill the rectangle; but require at least one subdivision
1720   // (depth==0) to avoid problems when the four outer corners of the
1721   // shaded region are the same color
1722   if ((i == 4 && depth > 0) || depth == maxDepths[pdfFunctionShading-1]) {
1724     // use the center color
1725     shading->getColor(xM, yM, &fillColor);
1726     state->setFillColor(&fillColor);
1728     // fill the rectangle
1729     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1730                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1731     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1732                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1733     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1734                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1735     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1736                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1737     state->closePath();
1738     builder->addPath(state, true, false);
1739     state->clearPath();
1741   // the four corner colors are not close enough -- subdivide the
1742   // rectangle
1743   } else {
1745     // colors[0]       colorM0       colors[2]
1746     //   (x0,y0)       (xM,y0)       (x1,y0)
1747     //         +----------+----------+
1748     //         |          |          |
1749     //         |    UL    |    UR    |
1750     // color0M |       colorMM       | color1M
1751     // (x0,yM) +----------+----------+ (x1,yM)
1752     //         |       (xM,yM)       |
1753     //         |    LL    |    LR    |
1754     //         |          |          |
1755     //         +----------+----------+
1756     // colors[1]       colorM1       colors[3]
1757     //   (x0,y1)       (xM,y1)       (x1,y1)
1759     shading->getColor(x0, yM, &color0M);
1760     shading->getColor(x1, yM, &color1M);
1761     shading->getColor(xM, y0, &colorM0);
1762     shading->getColor(xM, y1, &colorM1);
1763     shading->getColor(xM, yM, &colorMM);
1765     // upper-left sub-rectangle
1766     colors2[0] = colors[0];
1767     colors2[1] = color0M;
1768     colors2[2] = colorM0;
1769     colors2[3] = colorMM;
1770     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1771     
1772     // lower-left sub-rectangle
1773     colors2[0] = color0M;
1774     colors2[1] = colors[1];
1775     colors2[2] = colorMM;
1776     colors2[3] = colorM1;
1777     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1778     
1779     // upper-right sub-rectangle
1780     colors2[0] = colorM0;
1781     colors2[1] = colorMM;
1782     colors2[2] = colors[2];
1783     colors2[3] = color1M;
1784     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1786     // lower-right sub-rectangle
1787     colors2[0] = colorMM;
1788     colors2[1] = colorM1;
1789     colors2[2] = color1M;
1790     colors2[3] = colors[3];
1791     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1792   }
1795 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1796   double x0, y0, x1, y1, x2, y2;
1797   GfxColor color0, color1, color2;
1798   int i;
1800   for (i = 0; i < shading->getNTriangles(); ++i) {
1801     shading->getTriangle(i, &x0, &y0, &color0,
1802                          &x1, &y1, &color1,
1803                          &x2, &y2, &color2);
1804     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1805                         shading->getColorSpace()->getNComps(), 0);
1806   }
1809 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1810                               double x1, double y1, GfxColor *color1,
1811                               double x2, double y2, GfxColor *color2,
1812                               int nComps, int depth) {
1813   double x01, y01, x12, y12, x20, y20;
1814   double gouraudColorDelta = colorDeltas[pdfGouraudTriangleShading-1];
1815   GfxColor color01, color12, color20;
1816   int i;
1818   for (i = 0; i < nComps; ++i) {
1819     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1820        abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1821       break;
1822     }
1823   }
1824   if (i == nComps || depth == maxDepths[pdfGouraudTriangleShading-1]) {
1825     state->setFillColor(color0);
1826     state->moveTo(x0, y0);
1827     state->lineTo(x1, y1);
1828     state->lineTo(x2, y2);
1829     state->closePath();
1830     builder->addPath(state, true, false);
1831     state->clearPath();
1832   } else {
1833     x01 = 0.5 * (x0 + x1);
1834     y01 = 0.5 * (y0 + y1);
1835     x12 = 0.5 * (x1 + x2);
1836     y12 = 0.5 * (y1 + y2);
1837     x20 = 0.5 * (x2 + x0);
1838     y20 = 0.5 * (y2 + y0);
1839     //~ if the shading has a Function, this should interpolate on the
1840     //~ function parameter, not on the color components
1841     for (i = 0; i < nComps; ++i) {
1842       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1843       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1844       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1845     }
1846     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1847                         x20, y20, &color20, nComps, depth + 1);
1848     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1849                         x12, y12, &color12, nComps, depth + 1);
1850     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1851                         x20, y20, &color20, nComps, depth + 1);
1852     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1853                         x2, y2, color2, nComps, depth + 1);
1854   }
1857 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1858   int start, i;
1860   if (shading->getNPatches() > 128) {
1861     start = 3;
1862   } else if (shading->getNPatches() > 64) {
1863     start = 2;
1864   } else if (shading->getNPatches() > 16) {
1865     start = 1;
1866   } else {
1867     start = 0;
1868   }
1869   for (i = 0; i < shading->getNPatches(); ++i) {
1870     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1871               start);
1872   }
1875 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1876   GfxPatch patch00, patch01, patch10, patch11;
1877   double xx[4][8], yy[4][8];
1878   double xxm, yym;
1879   double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1880   int i;
1882   for (i = 0; i < nComps; ++i) {
1883     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1884           > patchColorDelta ||
1885         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1886           > patchColorDelta ||
1887         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1888           > patchColorDelta ||
1889         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1890           > patchColorDelta) {
1891       break;
1892     }
1893   }
1894   if (i == nComps || depth == maxDepths[pdfPatchMeshShading-1]) {
1895     state->setFillColor(&patch->color[0][0]);
1896     state->moveTo(patch->x[0][0], patch->y[0][0]);
1897     state->curveTo(patch->x[0][1], patch->y[0][1],
1898                    patch->x[0][2], patch->y[0][2],
1899                    patch->x[0][3], patch->y[0][3]);
1900     state->curveTo(patch->x[1][3], patch->y[1][3],
1901                    patch->x[2][3], patch->y[2][3],
1902                    patch->x[3][3], patch->y[3][3]);
1903     state->curveTo(patch->x[3][2], patch->y[3][2],
1904                    patch->x[3][1], patch->y[3][1],
1905                    patch->x[3][0], patch->y[3][0]);
1906     state->curveTo(patch->x[2][0], patch->y[2][0],
1907                    patch->x[1][0], patch->y[1][0],
1908                    patch->x[0][0], patch->y[0][0]);
1909     state->closePath();
1910     builder->addPath(state, true, false);
1911     state->clearPath();
1912   } else {
1913     for (i = 0; i < 4; ++i) {
1914       xx[i][0] = patch->x[i][0];
1915       yy[i][0] = patch->y[i][0];
1916       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1917       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1918       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1919       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1920       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1921       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1922       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1923       yy[i][2] = 0.5 * (yy[i][1] + yym);
1924       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1925       yy[i][5] = 0.5 * (yym + yy[i][6]);
1926       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1927       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1928       xx[i][7] = patch->x[i][3];
1929       yy[i][7] = patch->y[i][3];
1930     }
1931     for (i = 0; i < 4; ++i) {
1932       patch00.x[0][i] = xx[0][i];
1933       patch00.y[0][i] = yy[0][i];
1934       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1935       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1936       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1937       yym = 0.5 * (yy[1][i] + yy[2][i]);
1938       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1939       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1940       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1941       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1942       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1943       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1944       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1945       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1946       patch10.x[0][i] = patch00.x[3][i];
1947       patch10.y[0][i] = patch00.y[3][i];
1948       patch10.x[3][i] = xx[3][i];
1949       patch10.y[3][i] = yy[3][i];
1950     }
1951     for (i = 4; i < 8; ++i) {
1952       patch01.x[0][i-4] = xx[0][i];
1953       patch01.y[0][i-4] = yy[0][i];
1954       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1955       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1956       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1957       yym = 0.5 * (yy[1][i] + yy[2][i]);
1958       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1959       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1960       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1961       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1962       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1963       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1964       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1965       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1966       patch11.x[0][i-4] = patch01.x[3][i-4];
1967       patch11.y[0][i-4] = patch01.y[3][i-4];
1968       patch11.x[3][i-4] = xx[3][i];
1969       patch11.y[3][i-4] = yy[3][i];
1970     }
1971     //~ if the shading has a Function, this should interpolate on the
1972     //~ function parameter, not on the color components
1973     for (i = 0; i < nComps; ++i) {
1974       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1975       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1976                                   patch->color[0][1].c[i]) / 2;
1977       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1978       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1979       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1980                                   patch->color[1][1].c[i]) / 2;
1981       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1982       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1983       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1984                                   patch->color[1][0].c[i]) / 2;
1985       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1986       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1987       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1988                                   patch->color[0][0].c[i]) / 2;
1989       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1990       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1991                                   patch01.color[1][1].c[i]) / 2;
1992       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1993       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1994       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1995     }
1996     fillPatch(&patch00, nComps, depth + 1);
1997     fillPatch(&patch10, nComps, depth + 1);
1998     fillPatch(&patch01, nComps, depth + 1);
1999     fillPatch(&patch11, nComps, depth + 1);
2000   }
2003 void PdfParser::doEndPath() {
2004   if (state->isCurPt() && clip != clipNone) {
2005     state->clip();
2006     if (clip == clipNormal) {
2007       clipHistory->setClip(state->getPath(), clipNormal);
2008       builder->clip(state);
2009     } else {
2010       clipHistory->setClip(state->getPath(), clipEO);
2011       builder->clip(state, true);
2012     }
2013   }
2014   clip = clipNone;
2015   state->clearPath();
2018 //------------------------------------------------------------------------
2019 // path clipping operators
2020 //------------------------------------------------------------------------
2022 void PdfParser::opClip(Object args[], int numArgs) {
2023   clip = clipNormal;
2026 void PdfParser::opEOClip(Object args[], int numArgs) {
2027   clip = clipEO;
2030 //------------------------------------------------------------------------
2031 // text object operators
2032 //------------------------------------------------------------------------
2034 void PdfParser::opBeginText(Object args[], int numArgs) {
2035   state->setTextMat(1, 0, 0, 1, 0, 0);
2036   state->textMoveTo(0, 0);
2037   builder->updateTextPosition(0.0, 0.0);
2038   fontChanged = gTrue;
2039   builder->beginTextObject(state);
2042 void PdfParser::opEndText(Object args[], int numArgs) {
2043   builder->endTextObject(state);
2046 //------------------------------------------------------------------------
2047 // text state operators
2048 //------------------------------------------------------------------------
2050 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2051   state->setCharSpace(args[0].getNum());
2054 void PdfParser::opSetFont(Object args[], int numArgs) {
2055   GfxFont *font;
2057   if (!(font = res->lookupFont(args[0].getName()))) {
2058     // unsetting the font (drawing no text) is better than using the
2059     // previous one and drawing random glyphs from it
2060     state->setFont(NULL, args[1].getNum());
2061     fontChanged = gTrue;
2062     return;
2063   }
2064   if (printCommands) {
2065     printf("  font: tag=%s name='%s' %g\n",
2066            font->getTag()->getCString(),
2067            font->getName() ? font->getName()->getCString() : "???",
2068            args[1].getNum());
2069     fflush(stdout);
2070   }
2072   font->incRefCnt();
2073   state->setFont(font, args[1].getNum());
2074   fontChanged = gTrue;
2077 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2078   state->setLeading(args[0].getNum());
2081 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2082   state->setRender(args[0].getInt());
2083   builder->updateStyle(state);
2086 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2087   state->setRise(args[0].getNum());
2090 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2091   state->setWordSpace(args[0].getNum());
2094 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2095   state->setHorizScaling(args[0].getNum());
2096   builder->updateTextMatrix(state);
2097   fontChanged = gTrue;
2100 //------------------------------------------------------------------------
2101 // text positioning operators
2102 //------------------------------------------------------------------------
2104 void PdfParser::opTextMove(Object args[], int numArgs) {
2105   double tx, ty;
2107   tx = state->getLineX() + args[0].getNum();
2108   ty = state->getLineY() + args[1].getNum();
2109   state->textMoveTo(tx, ty);
2110   builder->updateTextPosition(tx, ty);
2113 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2114   double tx, ty;
2116   tx = state->getLineX() + args[0].getNum();
2117   ty = args[1].getNum();
2118   state->setLeading(-ty);
2119   ty += state->getLineY();
2120   state->textMoveTo(tx, ty);
2121   builder->updateTextPosition(tx, ty);
2124 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2125   state->setTextMat(args[0].getNum(), args[1].getNum(),
2126                     args[2].getNum(), args[3].getNum(),
2127                     args[4].getNum(), args[5].getNum());
2128   state->textMoveTo(0, 0);
2129   builder->updateTextMatrix(state);
2130   builder->updateTextPosition(0.0, 0.0);
2131   fontChanged = gTrue;
2134 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2135   double tx, ty;
2137   tx = state->getLineX();
2138   ty = state->getLineY() - state->getLeading();
2139   state->textMoveTo(tx, ty);
2140   builder->updateTextPosition(tx, ty);
2143 //------------------------------------------------------------------------
2144 // text string operators
2145 //------------------------------------------------------------------------
2147 void PdfParser::opShowText(Object args[], int numArgs) {
2148   if (!state->getFont()) {
2149     error(getPos(), const_cast<char*>("No font in show"));
2150     return;
2151   }
2152   if (fontChanged) {
2153     builder->updateFont(state);
2154     fontChanged = gFalse;
2155   }
2156   doShowText(args[0].getString());
2159 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2160   double tx, ty;
2162   if (!state->getFont()) {
2163     error(getPos(), const_cast<char*>("No font in move/show"));
2164     return;
2165   }
2166   if (fontChanged) {
2167     builder->updateFont(state);
2168     fontChanged = gFalse;
2169   }
2170   tx = state->getLineX();
2171   ty = state->getLineY() - state->getLeading();
2172   state->textMoveTo(tx, ty);
2173   builder->updateTextPosition(tx, ty);
2174   doShowText(args[0].getString());
2177 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2178   double tx, ty;
2180   if (!state->getFont()) {
2181     error(getPos(), const_cast<char*>("No font in move/set/show"));
2182     return;
2183   }
2184   if (fontChanged) {
2185     builder->updateFont(state);
2186     fontChanged = gFalse;
2187   }
2188   state->setWordSpace(args[0].getNum());
2189   state->setCharSpace(args[1].getNum());
2190   tx = state->getLineX();
2191   ty = state->getLineY() - state->getLeading();
2192   state->textMoveTo(tx, ty);
2193   builder->updateTextPosition(tx, ty);
2194   doShowText(args[2].getString());
2197 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2198   Array *a;
2199   Object obj;
2200   int wMode;
2201   int i;
2203   if (!state->getFont()) {
2204     error(getPos(), const_cast<char*>("No font in show/space"));
2205     return;
2206   }
2207   if (fontChanged) {
2208     builder->updateFont(state);
2209     fontChanged = gFalse;
2210   }
2211   wMode = state->getFont()->getWMode();
2212   a = args[0].getArray();
2213   for (i = 0; i < a->getLength(); ++i) {
2214     a->get(i, &obj);
2215     if (obj.isNum()) {
2216       // this uses the absolute value of the font size to match
2217       // Acrobat's behavior
2218       if (wMode) {
2219         state->textShift(0, -obj.getNum() * 0.001 *
2220                             fabs(state->getFontSize()));
2221       } else {
2222         state->textShift(-obj.getNum() * 0.001 *
2223                          fabs(state->getFontSize()), 0);
2224       }
2225       builder->updateTextShift(state, obj.getNum());
2226     } else if (obj.isString()) {
2227       doShowText(obj.getString());
2228     } else {
2229       error(getPos(), const_cast<char*>("Element of show/space array must be number or string"));
2230     }
2231     obj.free();
2232   }
2237 /*
2238  * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2239  * Thanks to tsdgeos for the fix.
2240  * Miklos, does this look ok?
2241  */   
2243 void PdfParser::doShowText(GooString *s) {
2244   GfxFont *font;
2245   int wMode;
2246   double riseX, riseY;
2247   CharCode code;
2248 #ifdef POPPLER_NEW_GFXFONT
2249   Unicode *u = NULL;
2250 #else
2251   Unicode u[8];
2252 #endif
2253   double x, y, dx, dy, curX, curY, tdx, tdy, lineX, lineY;
2254   double originX, originY, tOriginX, tOriginY;
2255   double oldCTM[6], newCTM[6];
2256   double *mat;
2257   Object charProc;
2258   Dict *resDict;
2259   Parser *oldParser;
2260   char *p;
2261   int len, n, uLen, i;
2263   font = state->getFont();
2264   wMode = font->getWMode();
2266   builder->beginString(state, s);
2268   // handle a Type 3 char
2269   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2270     mat = state->getCTM();
2271     for (i = 0; i < 6; ++i) {
2272       oldCTM[i] = mat[i];
2273     }
2274     mat = state->getTextMat();
2275     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2276     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2277     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2278     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2279     mat = font->getFontMatrix();
2280     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2281     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2282     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2283     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2284     newCTM[0] *= state->getFontSize();
2285     newCTM[1] *= state->getFontSize();
2286     newCTM[2] *= state->getFontSize();
2287     newCTM[3] *= state->getFontSize();
2288     newCTM[0] *= state->getHorizScaling();
2289     newCTM[2] *= state->getHorizScaling();
2290     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2291     curX = state->getCurX();
2292     curY = state->getCurY();
2293     lineX = state->getLineX();
2294     lineY = state->getLineY();
2295     oldParser = parser;
2296     p = s->getCString();
2297     len = s->getLength();
2298     while (len > 0) {
2299       n = font->getNextChar(p, len, &code,
2300 #ifdef POPPLER_NEW_GFXFONT
2301                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2302 #else
2303                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2304 #endif
2305                             &dx, &dy, &originX, &originY);
2306       dx = dx * state->getFontSize() + state->getCharSpace();
2307       if (n == 1 && *p == ' ') {
2308         dx += state->getWordSpace();
2309       }
2310       dx *= state->getHorizScaling();
2311       dy *= state->getFontSize();
2312       state->textTransformDelta(dx, dy, &tdx, &tdy);
2313       state->transform(curX + riseX, curY + riseY, &x, &y);
2314       saveState();
2315       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2316       //~ the CTM concat values here are wrong (but never used)
2317       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2318       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2319                                code, u, uLen)) {*/
2320         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2321         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2322           pushResources(resDict);
2323         }
2324         if (charProc.isStream()) {
2325           //parse(&charProc, gFalse); // TODO: parse into SVG font
2326         } else {
2327           error(getPos(), const_cast<char*>("Missing or bad Type3 CharProc entry"));
2328         }
2329         //out->endType3Char(state);
2330         if (resDict) {
2331           popResources();
2332         }
2333         charProc.free();
2334       }
2335       restoreState();
2336       // GfxState::restore() does *not* restore the current position,
2337       // so we deal with it here using (curX, curY) and (lineX, lineY)
2338       curX += tdx;
2339       curY += tdy;
2340       state->moveTo(curX, curY);
2341       state->textSetPos(lineX, lineY);
2342       p += n;
2343       len -= n;
2344     }
2345     parser = oldParser;
2347   } else {
2348     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2349     p = s->getCString();
2350     len = s->getLength();
2351     while (len > 0) {
2352       n = font->getNextChar(p, len, &code,
2353 #ifdef POPPLER_NEW_GFXFONT
2354                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2355 #else
2356                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2357 #endif
2358                             &dx, &dy, &originX, &originY);
2359       
2360       if (wMode) {
2361         dx *= state->getFontSize();
2362         dy = dy * state->getFontSize() + state->getCharSpace();
2363         if (n == 1 && *p == ' ') {
2364           dy += state->getWordSpace();
2365         }
2366       } else {
2367         dx = dx * state->getFontSize() + state->getCharSpace();
2368         if (n == 1 && *p == ' ') {
2369           dx += state->getWordSpace();
2370         }
2371         dx *= state->getHorizScaling();
2372         dy *= state->getFontSize();
2373       }
2374       state->textTransformDelta(dx, dy, &tdx, &tdy);
2375       originX *= state->getFontSize();
2376       originY *= state->getFontSize();
2377       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2378       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2379                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2380       state->shift(tdx, tdy);
2381       p += n;
2382       len -= n;
2383     }
2384   }
2386   builder->endString(state);
2390 //------------------------------------------------------------------------
2391 // XObject operators
2392 //------------------------------------------------------------------------
2394 void PdfParser::opXObject(Object args[], int numArgs) {
2395   char *name;
2396   Object obj1, obj2, obj3, refObj;
2398   name = args[0].getName();
2399   if (!res->lookupXObject(name, &obj1)) {
2400     return;
2401   }
2402   if (!obj1.isStream()) {
2403     error(getPos(), const_cast<char*>("XObject '%s' is wrong type"), name);
2404     obj1.free();
2405     return;
2406   }
2407   obj1.streamGetDict()->lookup(const_cast<char*>("Subtype"), &obj2);
2408   if (obj2.isName(const_cast<char*>("Image"))) {
2409     res->lookupXObjectNF(name, &refObj);
2410     doImage(&refObj, obj1.getStream(), gFalse);
2411     refObj.free();
2412   } else if (obj2.isName(const_cast<char*>("Form"))) {
2413     doForm(&obj1);
2414   } else if (obj2.isName(const_cast<char*>("PS"))) {
2415     obj1.streamGetDict()->lookup(const_cast<char*>("Level1"), &obj3);
2416 /*    out->psXObject(obj1.getStream(),
2417                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2418   } else if (obj2.isName()) {
2419     error(getPos(), const_cast<char*>("Unknown XObject subtype '%s'"), obj2.getName());
2420   } else {
2421     error(getPos(), const_cast<char*>("XObject subtype is missing or wrong type"));
2422   }
2423   obj2.free();
2424   obj1.free();
2427 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2428   Dict *dict, *maskDict;
2429   int width, height;
2430   int bits, maskBits;
2431   StreamColorSpaceMode csMode;
2432   GBool mask;
2433   GBool invert;
2434   GfxColorSpace *colorSpace, *maskColorSpace;
2435   GfxImageColorMap *colorMap, *maskColorMap;
2436   Object maskObj, smaskObj;
2437   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2438   int maskColors[2*gfxColorMaxComps];
2439   int maskWidth, maskHeight;
2440   GBool maskInvert;
2441   Stream *maskStr;
2442   Object obj1, obj2;
2443   int i;
2445   // get info from the stream
2446   bits = 0;
2447   csMode = streamCSNone;
2448   str->getImageParams(&bits, &csMode);
2450   // get stream dict
2451   dict = str->getDict();
2453   // get size
2454   dict->lookup(const_cast<char*>("Width"), &obj1);
2455   if (obj1.isNull()) {
2456     obj1.free();
2457     dict->lookup(const_cast<char*>("W"), &obj1);
2458   }
2459   if (obj1.isInt())
2460     width = obj1.getInt();
2461   else if (obj1.isReal())
2462     width = (int)obj1.getReal();
2463   else
2464     goto err2;
2465   obj1.free();
2466   dict->lookup(const_cast<char*>("Height"), &obj1);
2467   if (obj1.isNull()) {
2468     obj1.free();
2469     dict->lookup(const_cast<char*>("H"), &obj1);
2470   }
2471   if (obj1.isInt())
2472     height = obj1.getInt();
2473   else if (obj1.isReal())
2474     height = (int)obj1.getReal();
2475   else
2476     goto err2;
2477   obj1.free();
2479   // image or mask?
2480   dict->lookup(const_cast<char*>("ImageMask"), &obj1);
2481   if (obj1.isNull()) {
2482     obj1.free();
2483     dict->lookup(const_cast<char*>("IM"), &obj1);
2484   }
2485   mask = gFalse;
2486   if (obj1.isBool())
2487     mask = obj1.getBool();
2488   else if (!obj1.isNull())
2489     goto err2;
2490   obj1.free();
2492   // bit depth
2493   if (bits == 0) {
2494     dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2495     if (obj1.isNull()) {
2496       obj1.free();
2497       dict->lookup(const_cast<char*>("BPC"), &obj1);
2498     }
2499     if (obj1.isInt()) {
2500       bits = obj1.getInt();
2501     } else if (mask) {
2502       bits = 1;
2503     } else {
2504       goto err2;
2505     }
2506     obj1.free();
2507   }
2509   // display a mask
2510   if (mask) {
2512     // check for inverted mask
2513     if (bits != 1)
2514       goto err1;
2515     invert = gFalse;
2516     dict->lookup(const_cast<char*>("Decode"), &obj1);
2517     if (obj1.isNull()) {
2518       obj1.free();
2519       dict->lookup(const_cast<char*>("D"), &obj1);
2520     }
2521     if (obj1.isArray()) {
2522       obj1.arrayGet(0, &obj2);
2523       if (obj2.isInt() && obj2.getInt() == 1)
2524         invert = gTrue;
2525       obj2.free();
2526     } else if (!obj1.isNull()) {
2527       goto err2;
2528     }
2529     obj1.free();
2531     // draw it
2532     builder->addImageMask(state, str, width, height, invert);
2534   } else {
2536     // get color space and color map
2537     dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2538     if (obj1.isNull()) {
2539       obj1.free();
2540       dict->lookup(const_cast<char*>("CS"), &obj1);
2541     }
2542     if (obj1.isName()) {
2543       res->lookupColorSpace(obj1.getName(), &obj2);
2544       if (!obj2.isNull()) {
2545         obj1.free();
2546         obj1 = obj2;
2547       } else {
2548         obj2.free();
2549       }
2550     }
2551     if (!obj1.isNull()) {
2552 #ifdef POPPLER_NEW_COLOR_SPACE_API
2553       colorSpace = GfxColorSpace::parse(&obj1, NULL);
2554 #else
2555       colorSpace = GfxColorSpace::parse(&obj1);
2556 #endif
2557     } else if (csMode == streamCSDeviceGray) {
2558       colorSpace = new GfxDeviceGrayColorSpace();
2559     } else if (csMode == streamCSDeviceRGB) {
2560       colorSpace = new GfxDeviceRGBColorSpace();
2561     } else if (csMode == streamCSDeviceCMYK) {
2562       colorSpace = new GfxDeviceCMYKColorSpace();
2563     } else {
2564       colorSpace = NULL;
2565     }
2566     obj1.free();
2567     if (!colorSpace) {
2568       goto err1;
2569     }
2570     dict->lookup(const_cast<char*>("Decode"), &obj1);
2571     if (obj1.isNull()) {
2572       obj1.free();
2573       dict->lookup(const_cast<char*>("D"), &obj1);
2574     }
2575     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2576     obj1.free();
2577     if (!colorMap->isOk()) {
2578       delete colorMap;
2579       goto err1;
2580     }
2582     // get the mask
2583     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2584     maskStr = NULL; // make gcc happy
2585     maskWidth = maskHeight = 0; // make gcc happy
2586     maskInvert = gFalse; // make gcc happy
2587     maskColorMap = NULL; // make gcc happy
2588     dict->lookup(const_cast<char*>("Mask"), &maskObj);
2589     dict->lookup(const_cast<char*>("SMask"), &smaskObj);
2590     if (smaskObj.isStream()) {
2591       // soft mask
2592       if (inlineImg) {
2593         goto err1;
2594       }
2595       maskStr = smaskObj.getStream();
2596       maskDict = smaskObj.streamGetDict();
2597       maskDict->lookup(const_cast<char*>("Width"), &obj1);
2598       if (obj1.isNull()) {
2599         obj1.free();
2600         maskDict->lookup(const_cast<char*>("W"), &obj1);
2601       }
2602       if (!obj1.isInt()) {
2603         goto err2;
2604       }
2605       maskWidth = obj1.getInt();
2606       obj1.free();
2607       maskDict->lookup(const_cast<char*>("Height"), &obj1);
2608       if (obj1.isNull()) {
2609         obj1.free();
2610         maskDict->lookup(const_cast<char*>("H"), &obj1);
2611       }
2612       if (!obj1.isInt()) {
2613         goto err2;
2614       }
2615       maskHeight = obj1.getInt();
2616       obj1.free();
2617       maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2618       if (obj1.isNull()) {
2619         obj1.free();
2620         maskDict->lookup(const_cast<char*>("BPC"), &obj1);
2621       }
2622       if (!obj1.isInt()) {
2623         goto err2;
2624       }
2625       maskBits = obj1.getInt();
2626       obj1.free();
2627       maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2628       if (obj1.isNull()) {
2629         obj1.free();
2630         maskDict->lookup(const_cast<char*>("CS"), &obj1);
2631       }
2632       if (obj1.isName()) {
2633         res->lookupColorSpace(obj1.getName(), &obj2);
2634         if (!obj2.isNull()) {
2635           obj1.free();
2636           obj1 = obj2;
2637         } else {
2638           obj2.free();
2639         }
2640       }
2641 #ifdef POPPLER_NEW_COLOR_SPACE_API
2642       maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
2643 #else
2644       maskColorSpace = GfxColorSpace::parse(&obj1);
2645 #endif
2646       obj1.free();
2647       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2648         goto err1;
2649       }
2650       maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2651       if (obj1.isNull()) {
2652         obj1.free();
2653         maskDict->lookup(const_cast<char*>("D"), &obj1);
2654       }
2655       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2656       obj1.free();
2657       if (!maskColorMap->isOk()) {
2658         delete maskColorMap;
2659         goto err1;
2660       }
2661       //~ handle the Matte entry
2662       haveSoftMask = gTrue;
2663     } else if (maskObj.isArray()) {
2664       // color key mask
2665       for (i = 0;
2666            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2667            ++i) {
2668         maskObj.arrayGet(i, &obj1);
2669         maskColors[i] = obj1.getInt();
2670         obj1.free();
2671       }
2672       haveColorKeyMask = gTrue;
2673     } else if (maskObj.isStream()) {
2674       // explicit mask
2675       if (inlineImg) {
2676         goto err1;
2677       }
2678       maskStr = maskObj.getStream();
2679       maskDict = maskObj.streamGetDict();
2680       maskDict->lookup(const_cast<char*>("Width"), &obj1);
2681       if (obj1.isNull()) {
2682         obj1.free();
2683         maskDict->lookup(const_cast<char*>("W"), &obj1);
2684       }
2685       if (!obj1.isInt()) {
2686         goto err2;
2687       }
2688       maskWidth = obj1.getInt();
2689       obj1.free();
2690       maskDict->lookup(const_cast<char*>("Height"), &obj1);
2691       if (obj1.isNull()) {
2692         obj1.free();
2693         maskDict->lookup(const_cast<char*>("H"), &obj1);
2694       }
2695       if (!obj1.isInt()) {
2696         goto err2;
2697       }
2698       maskHeight = obj1.getInt();
2699       obj1.free();
2700       maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
2701       if (obj1.isNull()) {
2702         obj1.free();
2703         maskDict->lookup(const_cast<char*>("IM"), &obj1);
2704       }
2705       if (!obj1.isBool() || !obj1.getBool()) {
2706         goto err2;
2707       }
2708       obj1.free();
2709       maskInvert = gFalse;
2710       maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2711       if (obj1.isNull()) {
2712         obj1.free();
2713         maskDict->lookup(const_cast<char*>("D"), &obj1);
2714       }
2715       if (obj1.isArray()) {
2716         obj1.arrayGet(0, &obj2);
2717         if (obj2.isInt() && obj2.getInt() == 1) {
2718           maskInvert = gTrue;
2719         }
2720         obj2.free();
2721       } else if (!obj1.isNull()) {
2722         goto err2;
2723       }
2724       obj1.free();
2725       haveExplicitMask = gTrue;
2726     }
2728     // draw it
2729     if (haveSoftMask) {
2730         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2731                                     maskStr, maskWidth, maskHeight, maskColorMap);
2732       delete maskColorMap;
2733     } else if (haveExplicitMask) {
2734         builder->addMaskedImage(state, str, width, height, colorMap,
2735                                 maskStr, maskWidth, maskHeight, maskInvert);
2736     } else {
2737       builder->addImage(state, str, width, height, colorMap,
2738                         haveColorKeyMask ? maskColors : (int *)NULL);
2739     }
2740     delete colorMap;
2742     maskObj.free();
2743     smaskObj.free();
2744   }
2746   return;
2748  err2:
2749   obj1.free();
2750  err1:
2751   error(getPos(), const_cast<char*>("Bad image parameters"));
2754 void PdfParser::doForm(Object *str) {
2755   Dict *dict;
2756   GBool transpGroup, isolated, knockout;
2757   GfxColorSpace *blendingColorSpace;
2758   Object matrixObj, bboxObj;
2759   double m[6], bbox[4];
2760   Object resObj;
2761   Dict *resDict;
2762   Object obj1, obj2, obj3;
2763   int i;
2765   // check for excessive recursion
2766   if (formDepth > 20) {
2767     return;
2768   }
2770   // get stream dict
2771   dict = str->streamGetDict();
2773   // check form type
2774   dict->lookup(const_cast<char*>("FormType"), &obj1);
2775   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2776     error(getPos(), const_cast<char*>("Unknown form type"));
2777   }
2778   obj1.free();
2780   // get bounding box
2781   dict->lookup(const_cast<char*>("BBox"), &bboxObj);
2782   if (!bboxObj.isArray()) {
2783     bboxObj.free();
2784     error(getPos(), const_cast<char*>("Bad form bounding box"));
2785     return;
2786   }
2787   for (i = 0; i < 4; ++i) {
2788     bboxObj.arrayGet(i, &obj1);
2789     bbox[i] = obj1.getNum();
2790     obj1.free();
2791   }
2792   bboxObj.free();
2794   // get matrix
2795   dict->lookup(const_cast<char*>("Matrix"), &matrixObj);
2796   if (matrixObj.isArray()) {
2797     for (i = 0; i < 6; ++i) {
2798       matrixObj.arrayGet(i, &obj1);
2799       m[i] = obj1.getNum();
2800       obj1.free();
2801     }
2802   } else {
2803     m[0] = 1; m[1] = 0;
2804     m[2] = 0; m[3] = 1;
2805     m[4] = 0; m[5] = 0;
2806   }
2807   matrixObj.free();
2809   // get resources
2810   dict->lookup(const_cast<char*>("Resources"), &resObj);
2811   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2813   // check for a transparency group
2814   transpGroup = isolated = knockout = gFalse;
2815   blendingColorSpace = NULL;
2816   if (dict->lookup(const_cast<char*>("Group"), &obj1)->isDict()) {
2817     if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
2818       transpGroup = gTrue;
2819       if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
2820 #ifdef POPPLER_NEW_COLOR_SPACE_API
2821         blendingColorSpace = GfxColorSpace::parse(&obj3, NULL);
2822 #else
2823         blendingColorSpace = GfxColorSpace::parse(&obj3);
2824 #endif
2825       }
2826       obj3.free();
2827       if (obj1.dictLookup(const_cast<char*>("I"), &obj3)->isBool()) {
2828         isolated = obj3.getBool();
2829       }
2830       obj3.free();
2831       if (obj1.dictLookup(const_cast<char*>("K"), &obj3)->isBool()) {
2832         knockout = obj3.getBool();
2833       }
2834       obj3.free();
2835     }
2836     obj2.free();
2837   }
2838   obj1.free();
2840   // draw it
2841   ++formDepth;
2842   doForm1(str, resDict, m, bbox,
2843           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2844   --formDepth;
2846   if (blendingColorSpace) {
2847     delete blendingColorSpace;
2848   }
2849   resObj.free();
2852 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2853                   GBool transpGroup, GBool softMask,
2854                   GfxColorSpace *blendingColorSpace,
2855                   GBool isolated, GBool knockout,
2856                   GBool alpha, Function *transferFunc,
2857                   GfxColor *backdropColor) {
2858   Parser *oldParser;
2859   double oldBaseMatrix[6];
2860   int i;
2862   // push new resources on stack
2863   pushResources(resDict);
2865   // save current graphics state
2866   saveState();
2868   // kill any pre-existing path
2869   state->clearPath();
2871   if (softMask || transpGroup) {
2872     builder->clearSoftMask(state);
2873     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2874                                    isolated, knockout, softMask);
2875   }
2877   // save current parser
2878   oldParser = parser;
2880   // set form transformation matrix
2881   state->concatCTM(matrix[0], matrix[1], matrix[2],
2882                    matrix[3], matrix[4], matrix[5]);
2883   builder->setTransform(matrix[0], matrix[1], matrix[2],
2884                         matrix[3], matrix[4], matrix[5]);
2886   // set form bounding box
2887   state->moveTo(bbox[0], bbox[1]);
2888   state->lineTo(bbox[2], bbox[1]);
2889   state->lineTo(bbox[2], bbox[3]);
2890   state->lineTo(bbox[0], bbox[3]);
2891   state->closePath();
2892   state->clip();
2893   clipHistory->setClip(state->getPath());
2894   builder->clip(state);
2895   state->clearPath();
2897   if (softMask || transpGroup) {
2898     if (state->getBlendMode() != gfxBlendNormal) {
2899       state->setBlendMode(gfxBlendNormal);
2900     }
2901     if (state->getFillOpacity() != 1) {
2902       builder->setGroupOpacity(state->getFillOpacity());
2903       state->setFillOpacity(1);
2904     }
2905     if (state->getStrokeOpacity() != 1) {
2906       state->setStrokeOpacity(1);
2907     }
2908   }
2910   // set new base matrix
2911   for (i = 0; i < 6; ++i) {
2912     oldBaseMatrix[i] = baseMatrix[i];
2913     baseMatrix[i] = state->getCTM()[i];
2914   }
2916   // draw the form
2917   parse(str, gFalse);
2919   // restore base matrix
2920   for (i = 0; i < 6; ++i) {
2921     baseMatrix[i] = oldBaseMatrix[i];
2922   }
2924   // restore parser
2925   parser = oldParser;
2927   if (softMask || transpGroup) {
2928       builder->popTransparencyGroup(state);
2929   }
2931   // restore graphics state
2932   restoreState();
2934   // pop resource stack
2935   popResources();
2937   if (softMask) {
2938     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2939   } else if (transpGroup) {
2940     builder->paintTransparencyGroup(state, bbox);
2941   }
2943   return;
2946 //------------------------------------------------------------------------
2947 // in-line image operators
2948 //------------------------------------------------------------------------
2950 void PdfParser::opBeginImage(Object args[], int numArgs) {
2951   Stream *str;
2952   int c1, c2;
2954   // build dict/stream
2955   str = buildImageStream();
2957   // display the image
2958   if (str) {
2959     doImage(NULL, str, gTrue);
2960   
2961     // skip 'EI' tag
2962     c1 = str->getUndecodedStream()->getChar();
2963     c2 = str->getUndecodedStream()->getChar();
2964     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2965       c1 = c2;
2966       c2 = str->getUndecodedStream()->getChar();
2967     }
2968     delete str;
2969   }
2972 Stream *PdfParser::buildImageStream() {
2973   Object dict;
2974   Object obj;
2975   char *key;
2976   Stream *str;
2978   // build dictionary
2979   dict.initDict(xref);
2980   parser->getObj(&obj);
2981   while (!obj.isCmd(const_cast<char*>("ID")) && !obj.isEOF()) {
2982     if (!obj.isName()) {
2983       error(getPos(), const_cast<char*>("Inline image dictionary key must be a name object"));
2984       obj.free();
2985     } else {
2986       key = copyString(obj.getName());
2987       obj.free();
2988       parser->getObj(&obj);
2989       if (obj.isEOF() || obj.isError()) {
2990         gfree(key);
2991         break;
2992       }
2993       dict.dictAdd(key, &obj);
2994     }
2995     parser->getObj(&obj);
2996   }
2997   if (obj.isEOF()) {
2998     error(getPos(), const_cast<char*>("End of file in inline image"));
2999     obj.free();
3000     dict.free();
3001     return NULL;
3002   }
3003   obj.free();
3005   // make stream
3006   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3007   str = str->addFilters(&dict);
3009   return str;
3012 void PdfParser::opImageData(Object args[], int numArgs) {
3013   error(getPos(), const_cast<char*>("Internal: got 'ID' operator"));
3016 void PdfParser::opEndImage(Object args[], int numArgs) {
3017   error(getPos(), const_cast<char*>("Internal: got 'EI' operator"));
3020 //------------------------------------------------------------------------
3021 // type 3 font operators
3022 //------------------------------------------------------------------------
3024 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
3027 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
3030 //------------------------------------------------------------------------
3031 // compatibility operators
3032 //------------------------------------------------------------------------
3034 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
3035   ++ignoreUndef;
3038 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
3039   if (ignoreUndef > 0)
3040     --ignoreUndef;
3043 //------------------------------------------------------------------------
3044 // marked content operators
3045 //------------------------------------------------------------------------
3047 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
3048   if (printCommands) {
3049     printf("  marked content: %s ", args[0].getName());
3050     if (numArgs == 2)
3051       args[2].print(stdout);
3052     printf("\n");
3053     fflush(stdout);
3054   }
3056   if(numArgs == 2) {
3057     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3058   } else {
3059     //out->beginMarkedContent(args[0].getName());
3060   }
3063 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3064   //out->endMarkedContent();
3067 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3068   if (printCommands) {
3069     printf("  mark point: %s ", args[0].getName());
3070     if (numArgs == 2)
3071       args[2].print(stdout);
3072     printf("\n");
3073     fflush(stdout);
3074   }
3076   if(numArgs == 2) {
3077     //out->markPoint(args[0].getName(),args[1].getDict());
3078   } else {
3079     //out->markPoint(args[0].getName());
3080   }
3084 //------------------------------------------------------------------------
3085 // misc
3086 //------------------------------------------------------------------------
3088 void PdfParser::saveState() {
3089   builder->saveState();
3090   state = state->save();
3091   clipHistory = clipHistory->save();
3094 void PdfParser::restoreState() {
3095   clipHistory = clipHistory->restore();
3096   state = state->restore();
3097   builder->restoreState();
3100 void PdfParser::pushResources(Dict *resDict) {
3101   res = new GfxResources(xref, resDict, res);
3104 void PdfParser::popResources() {
3105   GfxResources *resPtr;
3107   resPtr = res->getNext();
3108   delete res;
3109   res = resPtr;
3112 void PdfParser::setDefaultApproximationPrecision() {
3113   int i;
3115   for (i = 1; i <= pdfNumShadingTypes; ++i) {
3116     setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3117   }
3120 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3121                                           int maxDepth) {
3123   if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3124     return;
3125   }
3126   colorDeltas[shadingType-1] = dblToCol(colorDelta);
3127   maxDepths[shadingType-1] = maxDepth;
3130 //------------------------------------------------------------------------
3131 // ClipHistoryEntry
3132 //------------------------------------------------------------------------
3134 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3135     if (clipPathA) {
3136         clipPath = clipPathA->copy();
3137     } else {
3138         clipPath = NULL;
3139     }
3140     clipType = clipTypeA;
3141     saved = NULL;
3144 ClipHistoryEntry::~ClipHistoryEntry() {
3145     if (clipPath) {
3146         delete clipPath;
3147     }
3150 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3151     // Free previous clip path
3152     if (clipPath) {
3153         delete clipPath;
3154     }
3155     if (clipPathA) {
3156         clipPath = clipPathA->copy();
3157         clipType = clipTypeA;
3158     } else {
3159         clipPath = NULL;
3160     }
3163 ClipHistoryEntry *ClipHistoryEntry::save() {
3164     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3165     newEntry->saved = this;
3167     return newEntry;
3170 ClipHistoryEntry *ClipHistoryEntry::restore() {
3171     ClipHistoryEntry *oldEntry;
3173     if (saved) {
3174         oldEntry = saved;
3175         saved = NULL;
3176         delete this;
3177     } else {
3178         oldEntry = this;
3179     }
3181     return oldEntry;
3184 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3185     if (other->clipPath) {
3186         this->clipPath = other->clipPath->copy();
3187         this->clipType = other->clipType;
3188     } else {
3189         this->clipPath = NULL;
3190     }
3191     saved = NULL;
3194 #endif /* HAVE_POPPLER */