Code

Patch from Lubomir Rintel to fix build with Poppler 0.15.1
[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 #ifdef POPPLER_NEW_GFXPATCH
1878   GfxColor color;
1879 #endif
1880   double xx[4][8], yy[4][8];
1881   double xxm, yym;
1882   double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1883   int i;
1885   for (i = 0; i < nComps; ++i) {
1886     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1887           > patchColorDelta ||
1888         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1889           > patchColorDelta ||
1890         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1891           > patchColorDelta ||
1892         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1893           > patchColorDelta) {
1894       break;
1895     }
1896 #ifdef POPPLER_NEW_GFXPATCH
1897     color.c[i] = GfxColorComp(patch->color[0][0].c[i]);
1898 #endif
1899   }
1900   if (i == nComps || depth == maxDepths[pdfPatchMeshShading-1]) {
1901 #ifdef POPPLER_NEW_GFXPATCH
1902     state->setFillColor(&color);
1903 #else
1904     state->setFillColor(&patch->color[0][0]);
1905 #endif
1906     state->moveTo(patch->x[0][0], patch->y[0][0]);
1907     state->curveTo(patch->x[0][1], patch->y[0][1],
1908                    patch->x[0][2], patch->y[0][2],
1909                    patch->x[0][3], patch->y[0][3]);
1910     state->curveTo(patch->x[1][3], patch->y[1][3],
1911                    patch->x[2][3], patch->y[2][3],
1912                    patch->x[3][3], patch->y[3][3]);
1913     state->curveTo(patch->x[3][2], patch->y[3][2],
1914                    patch->x[3][1], patch->y[3][1],
1915                    patch->x[3][0], patch->y[3][0]);
1916     state->curveTo(patch->x[2][0], patch->y[2][0],
1917                    patch->x[1][0], patch->y[1][0],
1918                    patch->x[0][0], patch->y[0][0]);
1919     state->closePath();
1920     builder->addPath(state, true, false);
1921     state->clearPath();
1922   } else {
1923     for (i = 0; i < 4; ++i) {
1924       xx[i][0] = patch->x[i][0];
1925       yy[i][0] = patch->y[i][0];
1926       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1927       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1928       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1929       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1930       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1931       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1932       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1933       yy[i][2] = 0.5 * (yy[i][1] + yym);
1934       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1935       yy[i][5] = 0.5 * (yym + yy[i][6]);
1936       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1937       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1938       xx[i][7] = patch->x[i][3];
1939       yy[i][7] = patch->y[i][3];
1940     }
1941     for (i = 0; i < 4; ++i) {
1942       patch00.x[0][i] = xx[0][i];
1943       patch00.y[0][i] = yy[0][i];
1944       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1945       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1946       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1947       yym = 0.5 * (yy[1][i] + yy[2][i]);
1948       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1949       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1950       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1951       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1952       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1953       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1954       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1955       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1956       patch10.x[0][i] = patch00.x[3][i];
1957       patch10.y[0][i] = patch00.y[3][i];
1958       patch10.x[3][i] = xx[3][i];
1959       patch10.y[3][i] = yy[3][i];
1960     }
1961     for (i = 4; i < 8; ++i) {
1962       patch01.x[0][i-4] = xx[0][i];
1963       patch01.y[0][i-4] = yy[0][i];
1964       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1965       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1966       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1967       yym = 0.5 * (yy[1][i] + yy[2][i]);
1968       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1969       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1970       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1971       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1972       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1973       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1974       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1975       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1976       patch11.x[0][i-4] = patch01.x[3][i-4];
1977       patch11.y[0][i-4] = patch01.y[3][i-4];
1978       patch11.x[3][i-4] = xx[3][i];
1979       patch11.y[3][i-4] = yy[3][i];
1980     }
1981     //~ if the shading has a Function, this should interpolate on the
1982     //~ function parameter, not on the color components
1983     for (i = 0; i < nComps; ++i) {
1984       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1985       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1986                                   patch->color[0][1].c[i]) / 2;
1987       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1988       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1989       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1990                                   patch->color[1][1].c[i]) / 2;
1991       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1992       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1993       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1994                                   patch->color[1][0].c[i]) / 2;
1995       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1996       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1997       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1998                                   patch->color[0][0].c[i]) / 2;
1999       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
2000       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
2001                                   patch01.color[1][1].c[i]) / 2;
2002       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
2003       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
2004       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
2005     }
2006     fillPatch(&patch00, nComps, depth + 1);
2007     fillPatch(&patch10, nComps, depth + 1);
2008     fillPatch(&patch01, nComps, depth + 1);
2009     fillPatch(&patch11, nComps, depth + 1);
2010   }
2013 void PdfParser::doEndPath() {
2014   if (state->isCurPt() && clip != clipNone) {
2015     state->clip();
2016     if (clip == clipNormal) {
2017       clipHistory->setClip(state->getPath(), clipNormal);
2018       builder->clip(state);
2019     } else {
2020       clipHistory->setClip(state->getPath(), clipEO);
2021       builder->clip(state, true);
2022     }
2023   }
2024   clip = clipNone;
2025   state->clearPath();
2028 //------------------------------------------------------------------------
2029 // path clipping operators
2030 //------------------------------------------------------------------------
2032 void PdfParser::opClip(Object args[], int numArgs) {
2033   clip = clipNormal;
2036 void PdfParser::opEOClip(Object args[], int numArgs) {
2037   clip = clipEO;
2040 //------------------------------------------------------------------------
2041 // text object operators
2042 //------------------------------------------------------------------------
2044 void PdfParser::opBeginText(Object args[], int numArgs) {
2045   state->setTextMat(1, 0, 0, 1, 0, 0);
2046   state->textMoveTo(0, 0);
2047   builder->updateTextPosition(0.0, 0.0);
2048   fontChanged = gTrue;
2049   builder->beginTextObject(state);
2052 void PdfParser::opEndText(Object args[], int numArgs) {
2053   builder->endTextObject(state);
2056 //------------------------------------------------------------------------
2057 // text state operators
2058 //------------------------------------------------------------------------
2060 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2061   state->setCharSpace(args[0].getNum());
2064 void PdfParser::opSetFont(Object args[], int numArgs) {
2065   GfxFont *font;
2067   if (!(font = res->lookupFont(args[0].getName()))) {
2068     // unsetting the font (drawing no text) is better than using the
2069     // previous one and drawing random glyphs from it
2070     state->setFont(NULL, args[1].getNum());
2071     fontChanged = gTrue;
2072     return;
2073   }
2074   if (printCommands) {
2075     printf("  font: tag=%s name='%s' %g\n",
2076            font->getTag()->getCString(),
2077            font->getName() ? font->getName()->getCString() : "???",
2078            args[1].getNum());
2079     fflush(stdout);
2080   }
2082   font->incRefCnt();
2083   state->setFont(font, args[1].getNum());
2084   fontChanged = gTrue;
2087 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2088   state->setLeading(args[0].getNum());
2091 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2092   state->setRender(args[0].getInt());
2093   builder->updateStyle(state);
2096 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2097   state->setRise(args[0].getNum());
2100 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2101   state->setWordSpace(args[0].getNum());
2104 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2105   state->setHorizScaling(args[0].getNum());
2106   builder->updateTextMatrix(state);
2107   fontChanged = gTrue;
2110 //------------------------------------------------------------------------
2111 // text positioning operators
2112 //------------------------------------------------------------------------
2114 void PdfParser::opTextMove(Object args[], int numArgs) {
2115   double tx, ty;
2117   tx = state->getLineX() + args[0].getNum();
2118   ty = state->getLineY() + args[1].getNum();
2119   state->textMoveTo(tx, ty);
2120   builder->updateTextPosition(tx, ty);
2123 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2124   double tx, ty;
2126   tx = state->getLineX() + args[0].getNum();
2127   ty = args[1].getNum();
2128   state->setLeading(-ty);
2129   ty += state->getLineY();
2130   state->textMoveTo(tx, ty);
2131   builder->updateTextPosition(tx, ty);
2134 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2135   state->setTextMat(args[0].getNum(), args[1].getNum(),
2136                     args[2].getNum(), args[3].getNum(),
2137                     args[4].getNum(), args[5].getNum());
2138   state->textMoveTo(0, 0);
2139   builder->updateTextMatrix(state);
2140   builder->updateTextPosition(0.0, 0.0);
2141   fontChanged = gTrue;
2144 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2145   double tx, ty;
2147   tx = state->getLineX();
2148   ty = state->getLineY() - state->getLeading();
2149   state->textMoveTo(tx, ty);
2150   builder->updateTextPosition(tx, ty);
2153 //------------------------------------------------------------------------
2154 // text string operators
2155 //------------------------------------------------------------------------
2157 void PdfParser::opShowText(Object args[], int numArgs) {
2158   if (!state->getFont()) {
2159     error(getPos(), const_cast<char*>("No font in show"));
2160     return;
2161   }
2162   if (fontChanged) {
2163     builder->updateFont(state);
2164     fontChanged = gFalse;
2165   }
2166   doShowText(args[0].getString());
2169 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2170   double tx, ty;
2172   if (!state->getFont()) {
2173     error(getPos(), const_cast<char*>("No font in move/show"));
2174     return;
2175   }
2176   if (fontChanged) {
2177     builder->updateFont(state);
2178     fontChanged = gFalse;
2179   }
2180   tx = state->getLineX();
2181   ty = state->getLineY() - state->getLeading();
2182   state->textMoveTo(tx, ty);
2183   builder->updateTextPosition(tx, ty);
2184   doShowText(args[0].getString());
2187 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2188   double tx, ty;
2190   if (!state->getFont()) {
2191     error(getPos(), const_cast<char*>("No font in move/set/show"));
2192     return;
2193   }
2194   if (fontChanged) {
2195     builder->updateFont(state);
2196     fontChanged = gFalse;
2197   }
2198   state->setWordSpace(args[0].getNum());
2199   state->setCharSpace(args[1].getNum());
2200   tx = state->getLineX();
2201   ty = state->getLineY() - state->getLeading();
2202   state->textMoveTo(tx, ty);
2203   builder->updateTextPosition(tx, ty);
2204   doShowText(args[2].getString());
2207 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2208   Array *a;
2209   Object obj;
2210   int wMode;
2211   int i;
2213   if (!state->getFont()) {
2214     error(getPos(), const_cast<char*>("No font in show/space"));
2215     return;
2216   }
2217   if (fontChanged) {
2218     builder->updateFont(state);
2219     fontChanged = gFalse;
2220   }
2221   wMode = state->getFont()->getWMode();
2222   a = args[0].getArray();
2223   for (i = 0; i < a->getLength(); ++i) {
2224     a->get(i, &obj);
2225     if (obj.isNum()) {
2226       // this uses the absolute value of the font size to match
2227       // Acrobat's behavior
2228       if (wMode) {
2229         state->textShift(0, -obj.getNum() * 0.001 *
2230                             fabs(state->getFontSize()));
2231       } else {
2232         state->textShift(-obj.getNum() * 0.001 *
2233                          fabs(state->getFontSize()), 0);
2234       }
2235       builder->updateTextShift(state, obj.getNum());
2236     } else if (obj.isString()) {
2237       doShowText(obj.getString());
2238     } else {
2239       error(getPos(), const_cast<char*>("Element of show/space array must be number or string"));
2240     }
2241     obj.free();
2242   }
2247 /*
2248  * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2249  * Thanks to tsdgeos for the fix.
2250  * Miklos, does this look ok?
2251  */   
2253 void PdfParser::doShowText(GooString *s) {
2254   GfxFont *font;
2255   int wMode;
2256   double riseX, riseY;
2257   CharCode code;
2258 #ifdef POPPLER_NEW_GFXFONT
2259   Unicode *u = NULL;
2260 #else
2261   Unicode u[8];
2262 #endif
2263   double x, y, dx, dy, curX, curY, tdx, tdy, lineX, lineY;
2264   double originX, originY, tOriginX, tOriginY;
2265   double oldCTM[6], newCTM[6];
2266   double *mat;
2267   Object charProc;
2268   Dict *resDict;
2269   Parser *oldParser;
2270   char *p;
2271   int len, n, uLen, i;
2273   font = state->getFont();
2274   wMode = font->getWMode();
2276   builder->beginString(state, s);
2278   // handle a Type 3 char
2279   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2280     mat = state->getCTM();
2281     for (i = 0; i < 6; ++i) {
2282       oldCTM[i] = mat[i];
2283     }
2284     mat = state->getTextMat();
2285     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2286     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2287     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2288     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2289     mat = font->getFontMatrix();
2290     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2291     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2292     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2293     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2294     newCTM[0] *= state->getFontSize();
2295     newCTM[1] *= state->getFontSize();
2296     newCTM[2] *= state->getFontSize();
2297     newCTM[3] *= state->getFontSize();
2298     newCTM[0] *= state->getHorizScaling();
2299     newCTM[2] *= state->getHorizScaling();
2300     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2301     curX = state->getCurX();
2302     curY = state->getCurY();
2303     lineX = state->getLineX();
2304     lineY = state->getLineY();
2305     oldParser = parser;
2306     p = s->getCString();
2307     len = s->getLength();
2308     while (len > 0) {
2309       n = font->getNextChar(p, len, &code,
2310 #ifdef POPPLER_NEW_GFXFONT
2311                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2312 #else
2313                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2314 #endif
2315                             &dx, &dy, &originX, &originY);
2316       dx = dx * state->getFontSize() + state->getCharSpace();
2317       if (n == 1 && *p == ' ') {
2318         dx += state->getWordSpace();
2319       }
2320       dx *= state->getHorizScaling();
2321       dy *= state->getFontSize();
2322       state->textTransformDelta(dx, dy, &tdx, &tdy);
2323       state->transform(curX + riseX, curY + riseY, &x, &y);
2324       saveState();
2325       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2326       //~ the CTM concat values here are wrong (but never used)
2327       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2328       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2329                                code, u, uLen)) {*/
2330         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2331         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2332           pushResources(resDict);
2333         }
2334         if (charProc.isStream()) {
2335           //parse(&charProc, gFalse); // TODO: parse into SVG font
2336         } else {
2337           error(getPos(), const_cast<char*>("Missing or bad Type3 CharProc entry"));
2338         }
2339         //out->endType3Char(state);
2340         if (resDict) {
2341           popResources();
2342         }
2343         charProc.free();
2344       }
2345       restoreState();
2346       // GfxState::restore() does *not* restore the current position,
2347       // so we deal with it here using (curX, curY) and (lineX, lineY)
2348       curX += tdx;
2349       curY += tdy;
2350       state->moveTo(curX, curY);
2351       state->textSetPos(lineX, lineY);
2352       p += n;
2353       len -= n;
2354     }
2355     parser = oldParser;
2357   } else {
2358     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2359     p = s->getCString();
2360     len = s->getLength();
2361     while (len > 0) {
2362       n = font->getNextChar(p, len, &code,
2363 #ifdef POPPLER_NEW_GFXFONT
2364                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2365 #else
2366                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2367 #endif
2368                             &dx, &dy, &originX, &originY);
2369       
2370       if (wMode) {
2371         dx *= state->getFontSize();
2372         dy = dy * state->getFontSize() + state->getCharSpace();
2373         if (n == 1 && *p == ' ') {
2374           dy += state->getWordSpace();
2375         }
2376       } else {
2377         dx = dx * state->getFontSize() + state->getCharSpace();
2378         if (n == 1 && *p == ' ') {
2379           dx += state->getWordSpace();
2380         }
2381         dx *= state->getHorizScaling();
2382         dy *= state->getFontSize();
2383       }
2384       state->textTransformDelta(dx, dy, &tdx, &tdy);
2385       originX *= state->getFontSize();
2386       originY *= state->getFontSize();
2387       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2388       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2389                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2390       state->shift(tdx, tdy);
2391       p += n;
2392       len -= n;
2393     }
2394   }
2396   builder->endString(state);
2400 //------------------------------------------------------------------------
2401 // XObject operators
2402 //------------------------------------------------------------------------
2404 void PdfParser::opXObject(Object args[], int numArgs) {
2405   char *name;
2406   Object obj1, obj2, obj3, refObj;
2408   name = args[0].getName();
2409   if (!res->lookupXObject(name, &obj1)) {
2410     return;
2411   }
2412   if (!obj1.isStream()) {
2413     error(getPos(), const_cast<char*>("XObject '%s' is wrong type"), name);
2414     obj1.free();
2415     return;
2416   }
2417   obj1.streamGetDict()->lookup(const_cast<char*>("Subtype"), &obj2);
2418   if (obj2.isName(const_cast<char*>("Image"))) {
2419     res->lookupXObjectNF(name, &refObj);
2420     doImage(&refObj, obj1.getStream(), gFalse);
2421     refObj.free();
2422   } else if (obj2.isName(const_cast<char*>("Form"))) {
2423     doForm(&obj1);
2424   } else if (obj2.isName(const_cast<char*>("PS"))) {
2425     obj1.streamGetDict()->lookup(const_cast<char*>("Level1"), &obj3);
2426 /*    out->psXObject(obj1.getStream(),
2427                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2428   } else if (obj2.isName()) {
2429     error(getPos(), const_cast<char*>("Unknown XObject subtype '%s'"), obj2.getName());
2430   } else {
2431     error(getPos(), const_cast<char*>("XObject subtype is missing or wrong type"));
2432   }
2433   obj2.free();
2434   obj1.free();
2437 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2438   Dict *dict, *maskDict;
2439   int width, height;
2440   int bits, maskBits;
2441   StreamColorSpaceMode csMode;
2442   GBool mask;
2443   GBool invert;
2444   GfxColorSpace *colorSpace, *maskColorSpace;
2445   GfxImageColorMap *colorMap, *maskColorMap;
2446   Object maskObj, smaskObj;
2447   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2448   int maskColors[2*gfxColorMaxComps];
2449   int maskWidth, maskHeight;
2450   GBool maskInvert;
2451   Stream *maskStr;
2452   Object obj1, obj2;
2453   int i;
2455   // get info from the stream
2456   bits = 0;
2457   csMode = streamCSNone;
2458   str->getImageParams(&bits, &csMode);
2460   // get stream dict
2461   dict = str->getDict();
2463   // get size
2464   dict->lookup(const_cast<char*>("Width"), &obj1);
2465   if (obj1.isNull()) {
2466     obj1.free();
2467     dict->lookup(const_cast<char*>("W"), &obj1);
2468   }
2469   if (obj1.isInt())
2470     width = obj1.getInt();
2471   else if (obj1.isReal())
2472     width = (int)obj1.getReal();
2473   else
2474     goto err2;
2475   obj1.free();
2476   dict->lookup(const_cast<char*>("Height"), &obj1);
2477   if (obj1.isNull()) {
2478     obj1.free();
2479     dict->lookup(const_cast<char*>("H"), &obj1);
2480   }
2481   if (obj1.isInt())
2482     height = obj1.getInt();
2483   else if (obj1.isReal())
2484     height = (int)obj1.getReal();
2485   else
2486     goto err2;
2487   obj1.free();
2489   // image or mask?
2490   dict->lookup(const_cast<char*>("ImageMask"), &obj1);
2491   if (obj1.isNull()) {
2492     obj1.free();
2493     dict->lookup(const_cast<char*>("IM"), &obj1);
2494   }
2495   mask = gFalse;
2496   if (obj1.isBool())
2497     mask = obj1.getBool();
2498   else if (!obj1.isNull())
2499     goto err2;
2500   obj1.free();
2502   // bit depth
2503   if (bits == 0) {
2504     dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2505     if (obj1.isNull()) {
2506       obj1.free();
2507       dict->lookup(const_cast<char*>("BPC"), &obj1);
2508     }
2509     if (obj1.isInt()) {
2510       bits = obj1.getInt();
2511     } else if (mask) {
2512       bits = 1;
2513     } else {
2514       goto err2;
2515     }
2516     obj1.free();
2517   }
2519   // display a mask
2520   if (mask) {
2522     // check for inverted mask
2523     if (bits != 1)
2524       goto err1;
2525     invert = gFalse;
2526     dict->lookup(const_cast<char*>("Decode"), &obj1);
2527     if (obj1.isNull()) {
2528       obj1.free();
2529       dict->lookup(const_cast<char*>("D"), &obj1);
2530     }
2531     if (obj1.isArray()) {
2532       obj1.arrayGet(0, &obj2);
2533       if (obj2.isInt() && obj2.getInt() == 1)
2534         invert = gTrue;
2535       obj2.free();
2536     } else if (!obj1.isNull()) {
2537       goto err2;
2538     }
2539     obj1.free();
2541     // draw it
2542     builder->addImageMask(state, str, width, height, invert);
2544   } else {
2546     // get color space and color map
2547     dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2548     if (obj1.isNull()) {
2549       obj1.free();
2550       dict->lookup(const_cast<char*>("CS"), &obj1);
2551     }
2552     if (obj1.isName()) {
2553       res->lookupColorSpace(obj1.getName(), &obj2);
2554       if (!obj2.isNull()) {
2555         obj1.free();
2556         obj1 = obj2;
2557       } else {
2558         obj2.free();
2559       }
2560     }
2561     if (!obj1.isNull()) {
2562 #ifdef POPPLER_NEW_COLOR_SPACE_API
2563       colorSpace = GfxColorSpace::parse(&obj1, NULL);
2564 #else
2565       colorSpace = GfxColorSpace::parse(&obj1);
2566 #endif
2567     } else if (csMode == streamCSDeviceGray) {
2568       colorSpace = new GfxDeviceGrayColorSpace();
2569     } else if (csMode == streamCSDeviceRGB) {
2570       colorSpace = new GfxDeviceRGBColorSpace();
2571     } else if (csMode == streamCSDeviceCMYK) {
2572       colorSpace = new GfxDeviceCMYKColorSpace();
2573     } else {
2574       colorSpace = NULL;
2575     }
2576     obj1.free();
2577     if (!colorSpace) {
2578       goto err1;
2579     }
2580     dict->lookup(const_cast<char*>("Decode"), &obj1);
2581     if (obj1.isNull()) {
2582       obj1.free();
2583       dict->lookup(const_cast<char*>("D"), &obj1);
2584     }
2585     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2586     obj1.free();
2587     if (!colorMap->isOk()) {
2588       delete colorMap;
2589       goto err1;
2590     }
2592     // get the mask
2593     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2594     maskStr = NULL; // make gcc happy
2595     maskWidth = maskHeight = 0; // make gcc happy
2596     maskInvert = gFalse; // make gcc happy
2597     maskColorMap = NULL; // make gcc happy
2598     dict->lookup(const_cast<char*>("Mask"), &maskObj);
2599     dict->lookup(const_cast<char*>("SMask"), &smaskObj);
2600     if (smaskObj.isStream()) {
2601       // soft mask
2602       if (inlineImg) {
2603         goto err1;
2604       }
2605       maskStr = smaskObj.getStream();
2606       maskDict = smaskObj.streamGetDict();
2607       maskDict->lookup(const_cast<char*>("Width"), &obj1);
2608       if (obj1.isNull()) {
2609         obj1.free();
2610         maskDict->lookup(const_cast<char*>("W"), &obj1);
2611       }
2612       if (!obj1.isInt()) {
2613         goto err2;
2614       }
2615       maskWidth = obj1.getInt();
2616       obj1.free();
2617       maskDict->lookup(const_cast<char*>("Height"), &obj1);
2618       if (obj1.isNull()) {
2619         obj1.free();
2620         maskDict->lookup(const_cast<char*>("H"), &obj1);
2621       }
2622       if (!obj1.isInt()) {
2623         goto err2;
2624       }
2625       maskHeight = obj1.getInt();
2626       obj1.free();
2627       maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2628       if (obj1.isNull()) {
2629         obj1.free();
2630         maskDict->lookup(const_cast<char*>("BPC"), &obj1);
2631       }
2632       if (!obj1.isInt()) {
2633         goto err2;
2634       }
2635       maskBits = obj1.getInt();
2636       obj1.free();
2637       maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2638       if (obj1.isNull()) {
2639         obj1.free();
2640         maskDict->lookup(const_cast<char*>("CS"), &obj1);
2641       }
2642       if (obj1.isName()) {
2643         res->lookupColorSpace(obj1.getName(), &obj2);
2644         if (!obj2.isNull()) {
2645           obj1.free();
2646           obj1 = obj2;
2647         } else {
2648           obj2.free();
2649         }
2650       }
2651 #ifdef POPPLER_NEW_COLOR_SPACE_API
2652       maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
2653 #else
2654       maskColorSpace = GfxColorSpace::parse(&obj1);
2655 #endif
2656       obj1.free();
2657       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2658         goto err1;
2659       }
2660       maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2661       if (obj1.isNull()) {
2662         obj1.free();
2663         maskDict->lookup(const_cast<char*>("D"), &obj1);
2664       }
2665       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2666       obj1.free();
2667       if (!maskColorMap->isOk()) {
2668         delete maskColorMap;
2669         goto err1;
2670       }
2671       //~ handle the Matte entry
2672       haveSoftMask = gTrue;
2673     } else if (maskObj.isArray()) {
2674       // color key mask
2675       for (i = 0;
2676            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2677            ++i) {
2678         maskObj.arrayGet(i, &obj1);
2679         maskColors[i] = obj1.getInt();
2680         obj1.free();
2681       }
2682       haveColorKeyMask = gTrue;
2683     } else if (maskObj.isStream()) {
2684       // explicit mask
2685       if (inlineImg) {
2686         goto err1;
2687       }
2688       maskStr = maskObj.getStream();
2689       maskDict = maskObj.streamGetDict();
2690       maskDict->lookup(const_cast<char*>("Width"), &obj1);
2691       if (obj1.isNull()) {
2692         obj1.free();
2693         maskDict->lookup(const_cast<char*>("W"), &obj1);
2694       }
2695       if (!obj1.isInt()) {
2696         goto err2;
2697       }
2698       maskWidth = obj1.getInt();
2699       obj1.free();
2700       maskDict->lookup(const_cast<char*>("Height"), &obj1);
2701       if (obj1.isNull()) {
2702         obj1.free();
2703         maskDict->lookup(const_cast<char*>("H"), &obj1);
2704       }
2705       if (!obj1.isInt()) {
2706         goto err2;
2707       }
2708       maskHeight = obj1.getInt();
2709       obj1.free();
2710       maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
2711       if (obj1.isNull()) {
2712         obj1.free();
2713         maskDict->lookup(const_cast<char*>("IM"), &obj1);
2714       }
2715       if (!obj1.isBool() || !obj1.getBool()) {
2716         goto err2;
2717       }
2718       obj1.free();
2719       maskInvert = gFalse;
2720       maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2721       if (obj1.isNull()) {
2722         obj1.free();
2723         maskDict->lookup(const_cast<char*>("D"), &obj1);
2724       }
2725       if (obj1.isArray()) {
2726         obj1.arrayGet(0, &obj2);
2727         if (obj2.isInt() && obj2.getInt() == 1) {
2728           maskInvert = gTrue;
2729         }
2730         obj2.free();
2731       } else if (!obj1.isNull()) {
2732         goto err2;
2733       }
2734       obj1.free();
2735       haveExplicitMask = gTrue;
2736     }
2738     // draw it
2739     if (haveSoftMask) {
2740         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2741                                     maskStr, maskWidth, maskHeight, maskColorMap);
2742       delete maskColorMap;
2743     } else if (haveExplicitMask) {
2744         builder->addMaskedImage(state, str, width, height, colorMap,
2745                                 maskStr, maskWidth, maskHeight, maskInvert);
2746     } else {
2747       builder->addImage(state, str, width, height, colorMap,
2748                         haveColorKeyMask ? maskColors : (int *)NULL);
2749     }
2750     delete colorMap;
2752     maskObj.free();
2753     smaskObj.free();
2754   }
2756   return;
2758  err2:
2759   obj1.free();
2760  err1:
2761   error(getPos(), const_cast<char*>("Bad image parameters"));
2764 void PdfParser::doForm(Object *str) {
2765   Dict *dict;
2766   GBool transpGroup, isolated, knockout;
2767   GfxColorSpace *blendingColorSpace;
2768   Object matrixObj, bboxObj;
2769   double m[6], bbox[4];
2770   Object resObj;
2771   Dict *resDict;
2772   Object obj1, obj2, obj3;
2773   int i;
2775   // check for excessive recursion
2776   if (formDepth > 20) {
2777     return;
2778   }
2780   // get stream dict
2781   dict = str->streamGetDict();
2783   // check form type
2784   dict->lookup(const_cast<char*>("FormType"), &obj1);
2785   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2786     error(getPos(), const_cast<char*>("Unknown form type"));
2787   }
2788   obj1.free();
2790   // get bounding box
2791   dict->lookup(const_cast<char*>("BBox"), &bboxObj);
2792   if (!bboxObj.isArray()) {
2793     bboxObj.free();
2794     error(getPos(), const_cast<char*>("Bad form bounding box"));
2795     return;
2796   }
2797   for (i = 0; i < 4; ++i) {
2798     bboxObj.arrayGet(i, &obj1);
2799     bbox[i] = obj1.getNum();
2800     obj1.free();
2801   }
2802   bboxObj.free();
2804   // get matrix
2805   dict->lookup(const_cast<char*>("Matrix"), &matrixObj);
2806   if (matrixObj.isArray()) {
2807     for (i = 0; i < 6; ++i) {
2808       matrixObj.arrayGet(i, &obj1);
2809       m[i] = obj1.getNum();
2810       obj1.free();
2811     }
2812   } else {
2813     m[0] = 1; m[1] = 0;
2814     m[2] = 0; m[3] = 1;
2815     m[4] = 0; m[5] = 0;
2816   }
2817   matrixObj.free();
2819   // get resources
2820   dict->lookup(const_cast<char*>("Resources"), &resObj);
2821   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2823   // check for a transparency group
2824   transpGroup = isolated = knockout = gFalse;
2825   blendingColorSpace = NULL;
2826   if (dict->lookup(const_cast<char*>("Group"), &obj1)->isDict()) {
2827     if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
2828       transpGroup = gTrue;
2829       if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
2830 #ifdef POPPLER_NEW_COLOR_SPACE_API
2831         blendingColorSpace = GfxColorSpace::parse(&obj3, NULL);
2832 #else
2833         blendingColorSpace = GfxColorSpace::parse(&obj3);
2834 #endif
2835       }
2836       obj3.free();
2837       if (obj1.dictLookup(const_cast<char*>("I"), &obj3)->isBool()) {
2838         isolated = obj3.getBool();
2839       }
2840       obj3.free();
2841       if (obj1.dictLookup(const_cast<char*>("K"), &obj3)->isBool()) {
2842         knockout = obj3.getBool();
2843       }
2844       obj3.free();
2845     }
2846     obj2.free();
2847   }
2848   obj1.free();
2850   // draw it
2851   ++formDepth;
2852   doForm1(str, resDict, m, bbox,
2853           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2854   --formDepth;
2856   if (blendingColorSpace) {
2857     delete blendingColorSpace;
2858   }
2859   resObj.free();
2862 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2863                   GBool transpGroup, GBool softMask,
2864                   GfxColorSpace *blendingColorSpace,
2865                   GBool isolated, GBool knockout,
2866                   GBool alpha, Function *transferFunc,
2867                   GfxColor *backdropColor) {
2868   Parser *oldParser;
2869   double oldBaseMatrix[6];
2870   int i;
2872   // push new resources on stack
2873   pushResources(resDict);
2875   // save current graphics state
2876   saveState();
2878   // kill any pre-existing path
2879   state->clearPath();
2881   if (softMask || transpGroup) {
2882     builder->clearSoftMask(state);
2883     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2884                                    isolated, knockout, softMask);
2885   }
2887   // save current parser
2888   oldParser = parser;
2890   // set form transformation matrix
2891   state->concatCTM(matrix[0], matrix[1], matrix[2],
2892                    matrix[3], matrix[4], matrix[5]);
2893   builder->setTransform(matrix[0], matrix[1], matrix[2],
2894                         matrix[3], matrix[4], matrix[5]);
2896   // set form bounding box
2897   state->moveTo(bbox[0], bbox[1]);
2898   state->lineTo(bbox[2], bbox[1]);
2899   state->lineTo(bbox[2], bbox[3]);
2900   state->lineTo(bbox[0], bbox[3]);
2901   state->closePath();
2902   state->clip();
2903   clipHistory->setClip(state->getPath());
2904   builder->clip(state);
2905   state->clearPath();
2907   if (softMask || transpGroup) {
2908     if (state->getBlendMode() != gfxBlendNormal) {
2909       state->setBlendMode(gfxBlendNormal);
2910     }
2911     if (state->getFillOpacity() != 1) {
2912       builder->setGroupOpacity(state->getFillOpacity());
2913       state->setFillOpacity(1);
2914     }
2915     if (state->getStrokeOpacity() != 1) {
2916       state->setStrokeOpacity(1);
2917     }
2918   }
2920   // set new base matrix
2921   for (i = 0; i < 6; ++i) {
2922     oldBaseMatrix[i] = baseMatrix[i];
2923     baseMatrix[i] = state->getCTM()[i];
2924   }
2926   // draw the form
2927   parse(str, gFalse);
2929   // restore base matrix
2930   for (i = 0; i < 6; ++i) {
2931     baseMatrix[i] = oldBaseMatrix[i];
2932   }
2934   // restore parser
2935   parser = oldParser;
2937   if (softMask || transpGroup) {
2938       builder->popTransparencyGroup(state);
2939   }
2941   // restore graphics state
2942   restoreState();
2944   // pop resource stack
2945   popResources();
2947   if (softMask) {
2948     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2949   } else if (transpGroup) {
2950     builder->paintTransparencyGroup(state, bbox);
2951   }
2953   return;
2956 //------------------------------------------------------------------------
2957 // in-line image operators
2958 //------------------------------------------------------------------------
2960 void PdfParser::opBeginImage(Object args[], int numArgs) {
2961   Stream *str;
2962   int c1, c2;
2964   // build dict/stream
2965   str = buildImageStream();
2967   // display the image
2968   if (str) {
2969     doImage(NULL, str, gTrue);
2970   
2971     // skip 'EI' tag
2972     c1 = str->getUndecodedStream()->getChar();
2973     c2 = str->getUndecodedStream()->getChar();
2974     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2975       c1 = c2;
2976       c2 = str->getUndecodedStream()->getChar();
2977     }
2978     delete str;
2979   }
2982 Stream *PdfParser::buildImageStream() {
2983   Object dict;
2984   Object obj;
2985   char *key;
2986   Stream *str;
2988   // build dictionary
2989   dict.initDict(xref);
2990   parser->getObj(&obj);
2991   while (!obj.isCmd(const_cast<char*>("ID")) && !obj.isEOF()) {
2992     if (!obj.isName()) {
2993       error(getPos(), const_cast<char*>("Inline image dictionary key must be a name object"));
2994       obj.free();
2995     } else {
2996       key = copyString(obj.getName());
2997       obj.free();
2998       parser->getObj(&obj);
2999       if (obj.isEOF() || obj.isError()) {
3000         gfree(key);
3001         break;
3002       }
3003       dict.dictAdd(key, &obj);
3004     }
3005     parser->getObj(&obj);
3006   }
3007   if (obj.isEOF()) {
3008     error(getPos(), const_cast<char*>("End of file in inline image"));
3009     obj.free();
3010     dict.free();
3011     return NULL;
3012   }
3013   obj.free();
3015   // make stream
3016   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3017   str = str->addFilters(&dict);
3019   return str;
3022 void PdfParser::opImageData(Object args[], int numArgs) {
3023   error(getPos(), const_cast<char*>("Internal: got 'ID' operator"));
3026 void PdfParser::opEndImage(Object args[], int numArgs) {
3027   error(getPos(), const_cast<char*>("Internal: got 'EI' operator"));
3030 //------------------------------------------------------------------------
3031 // type 3 font operators
3032 //------------------------------------------------------------------------
3034 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
3037 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
3040 //------------------------------------------------------------------------
3041 // compatibility operators
3042 //------------------------------------------------------------------------
3044 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
3045   ++ignoreUndef;
3048 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
3049   if (ignoreUndef > 0)
3050     --ignoreUndef;
3053 //------------------------------------------------------------------------
3054 // marked content operators
3055 //------------------------------------------------------------------------
3057 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
3058   if (printCommands) {
3059     printf("  marked content: %s ", args[0].getName());
3060     if (numArgs == 2)
3061       args[2].print(stdout);
3062     printf("\n");
3063     fflush(stdout);
3064   }
3066   if(numArgs == 2) {
3067     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3068   } else {
3069     //out->beginMarkedContent(args[0].getName());
3070   }
3073 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3074   //out->endMarkedContent();
3077 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3078   if (printCommands) {
3079     printf("  mark point: %s ", args[0].getName());
3080     if (numArgs == 2)
3081       args[2].print(stdout);
3082     printf("\n");
3083     fflush(stdout);
3084   }
3086   if(numArgs == 2) {
3087     //out->markPoint(args[0].getName(),args[1].getDict());
3088   } else {
3089     //out->markPoint(args[0].getName());
3090   }
3094 //------------------------------------------------------------------------
3095 // misc
3096 //------------------------------------------------------------------------
3098 void PdfParser::saveState() {
3099   builder->saveState();
3100   state = state->save();
3101   clipHistory = clipHistory->save();
3104 void PdfParser::restoreState() {
3105   clipHistory = clipHistory->restore();
3106   state = state->restore();
3107   builder->restoreState();
3110 void PdfParser::pushResources(Dict *resDict) {
3111   res = new GfxResources(xref, resDict, res);
3114 void PdfParser::popResources() {
3115   GfxResources *resPtr;
3117   resPtr = res->getNext();
3118   delete res;
3119   res = resPtr;
3122 void PdfParser::setDefaultApproximationPrecision() {
3123   int i;
3125   for (i = 1; i <= pdfNumShadingTypes; ++i) {
3126     setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3127   }
3130 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3131                                           int maxDepth) {
3133   if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3134     return;
3135   }
3136   colorDeltas[shadingType-1] = dblToCol(colorDelta);
3137   maxDepths[shadingType-1] = maxDepth;
3140 //------------------------------------------------------------------------
3141 // ClipHistoryEntry
3142 //------------------------------------------------------------------------
3144 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3145     if (clipPathA) {
3146         clipPath = clipPathA->copy();
3147     } else {
3148         clipPath = NULL;
3149     }
3150     clipType = clipTypeA;
3151     saved = NULL;
3154 ClipHistoryEntry::~ClipHistoryEntry() {
3155     if (clipPath) {
3156         delete clipPath;
3157     }
3160 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3161     // Free previous clip path
3162     if (clipPath) {
3163         delete clipPath;
3164     }
3165     if (clipPathA) {
3166         clipPath = clipPathA->copy();
3167         clipType = clipTypeA;
3168     } else {
3169         clipPath = NULL;
3170     }
3173 ClipHistoryEntry *ClipHistoryEntry::save() {
3174     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3175     newEntry->saved = this;
3177     return newEntry;
3180 ClipHistoryEntry *ClipHistoryEntry::restore() {
3181     ClipHistoryEntry *oldEntry;
3183     if (saved) {
3184         oldEntry = saved;
3185         saved = NULL;
3186         delete this;
3187     } else {
3188         oldEntry = this;
3189     }
3191     return oldEntry;
3194 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3195     if (other->clipPath) {
3196         this->clipPath = other->clipPath->copy();
3197         this->clipType = other->clipType;
3198     } else {
3199         this->clipPath = NULL;
3200     }
3201     saved = NULL;
3204 #endif /* HAVE_POPPLER */