Code

fix crash, allow combine to work transparently on groups
[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, "Weird page contents");
371         obj2.free();
372         return;
373       }
374       obj2.free();
375     }
376   } else if (!obj->isStream()) {
377     error(-1, "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;
390   int lastAbortCheck;
392   // scan a sequence of objects
393   numArgs = 0;
394   parser->getObj(&obj);
395   while (!obj.isEOF()) {
397     // got a command - execute it
398     if (obj.isCmd()) {
399       if (printCommands) {
400         obj.print(stdout);
401         for (i = 0; i < numArgs; ++i) {
402           printf(" ");
403           args[i].print(stdout);
404         }
405         printf("\n");
406         fflush(stdout);
407       }
409       // Run the operation
410       execOp(&obj, args, numArgs);
412       obj.free();
413       for (i = 0; i < numArgs; ++i)
414         args[i].free();
415       numArgs = 0;
417     // got an argument - save it
418     } else if (numArgs < maxArgs) {
419       args[numArgs++] = obj;
421     // too many arguments - something is wrong
422     } else {
423       error(getPos(), "Too many args in content stream");
424       if (printCommands) {
425         printf("throwing away arg: ");
426         obj.print(stdout);
427         printf("\n");
428         fflush(stdout);
429       }
430       obj.free();
431     }
433     // grab the next object
434     parser->getObj(&obj);
435   }
436   obj.free();
438   // args at end with no command
439   if (numArgs > 0) {
440     error(getPos(), "Leftover args in content stream");
441     if (printCommands) {
442       printf("%d leftovers:", numArgs);
443       for (i = 0; i < numArgs; ++i) {
444         printf(" ");
445         args[i].print(stdout);
446       }
447       printf("\n");
448       fflush(stdout);
449     }
450     for (i = 0; i < numArgs; ++i)
451       args[i].free();
452   }
455 void PdfParser::pushOperator(const char *name) {
456     OpHistoryEntry *newEntry = new OpHistoryEntry;
457     newEntry->name = name;
458     newEntry->state = NULL;
459     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
460     newEntry->next = operatorHistory;
461     operatorHistory = newEntry;
463     // Truncate list if needed
464     if (operatorHistory->depth > maxOperatorHistoryDepth) {
465         OpHistoryEntry *curr = operatorHistory;
466         OpHistoryEntry *prev = NULL;
467         while (curr && curr->next != NULL) {
468             curr->depth--;
469             prev = curr;
470             curr = curr->next;
471         }
472         if (prev) {
473             if (curr->state != NULL)
474                 delete curr->state;
475             delete curr;
476             prev->next = NULL;
477         }
478     }
481 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
482     OpHistoryEntry *prev = NULL;
483     if (operatorHistory != NULL && look_back > 0) {
484         prev = operatorHistory->next;
485         while (--look_back > 0 && prev != NULL) {
486             prev = prev->next;
487         }
488     }
489     if (prev != NULL) {
490         return prev->name;
491     } else {
492         return "";
493     }
496 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
497   PdfOperator *op;
498   char *name;
499   Object *argPtr;
500   int i;
502   // find operator
503   name = cmd->getCmd();
504   if (!(op = findOp(name))) {
505     if (ignoreUndef == 0)
506       error(getPos(), "Unknown operator '%s'", name);
507     return;
508   }
510   // type check args
511   argPtr = args;
512   if (op->numArgs >= 0) {
513     if (numArgs < op->numArgs) {
514       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
515       return;
516     }
517     if (numArgs > op->numArgs) {
518 #if 0
519       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
520 #endif
521       argPtr += numArgs - op->numArgs;
522       numArgs = op->numArgs;
523     }
524   } else {
525     if (numArgs > -op->numArgs) {
526       error(getPos(), "Too many (%d) args to '%s' operator",
527             numArgs, name);
528       return;
529     }
530   }
531   for (i = 0; i < numArgs; ++i) {
532     if (!checkArg(&argPtr[i], op->tchk[i])) {
533       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
534             i, name, argPtr[i].getTypeName());
535       return;
536     }
537   }
539   // add to history
540   pushOperator((char*)&op->name);
542   // do it
543   (this->*op->func)(argPtr, numArgs);
546 PdfOperator *PdfParser::findOp(char *name) {
547   int a, b, m, cmp;
549   a = -1;
550   b = numOps;
551   // invariant: opTab[a] < name < opTab[b]
552   while (b - a > 1) {
553     m = (a + b) / 2;
554     cmp = strcmp(opTab[m].name, name);
555     if (cmp < 0)
556       a = m;
557     else if (cmp > 0)
558       b = m;
559     else
560       a = b = m;
561   }
562   if (cmp != 0)
563     return NULL;
564   return &opTab[a];
567 GBool PdfParser::checkArg(Object *arg, TchkType type) {
568   switch (type) {
569   case tchkBool:   return arg->isBool();
570   case tchkInt:    return arg->isInt();
571   case tchkNum:    return arg->isNum();
572   case tchkString: return arg->isString();
573   case tchkName:   return arg->isName();
574   case tchkArray:  return arg->isArray();
575   case tchkProps:  return arg->isDict() || arg->isName();
576   case tchkSCN:    return arg->isNum() || arg->isName();
577   case tchkNone:   return gFalse;
578   }
579   return gFalse;
582 int PdfParser::getPos() {
583   return parser ? parser->getPos() : -1;
586 //------------------------------------------------------------------------
587 // graphics state operators
588 //------------------------------------------------------------------------
590 void PdfParser::opSave(Object args[], int numArgs) {
591   saveState();
594 void PdfParser::opRestore(Object args[], int numArgs) {
595   restoreState();
598 void PdfParser::opConcat(Object args[], int numArgs) {
599   state->concatCTM(args[0].getNum(), args[1].getNum(),
600                    args[2].getNum(), args[3].getNum(),
601                    args[4].getNum(), args[5].getNum());
602   const char *prevOp = getPreviousOperator();
603   double a0 = args[0].getNum();
604   double a1 = args[1].getNum();
605   double a2 = args[2].getNum();
606   double a3 = args[3].getNum();
607   double a4 = args[4].getNum();
608   double a5 = args[5].getNum();
609   if (!strcmp(prevOp, "q")) {
610       builder->setTransform(a0, a1, a2, a3, a4, a5);
611   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
612       // multiply it with the previous transform
613       double otherMatrix[6];
614       if (!builder->getTransform(otherMatrix)) { // invalid transform
615           // construct identity matrix
616           otherMatrix[0] = otherMatrix[3] = 1.0;
617           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
618       }
619       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
620       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
621       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
622       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
623       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
624       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
625       builder->setTransform(c0, c1, c2, c3, c4, c5);
626   } else {
627       builder->pushGroup();
628       builder->setTransform(a0, a1, a2, a3, a4, a5);
629   }
630   fontChanged = gTrue;
633 void PdfParser::opSetDash(Object args[], int numArgs) {
634   Array *a;
635   int length;
636   Object obj;
637   double *dash;
638   int i;
640   a = args[0].getArray();
641   length = a->getLength();
642   if (length == 0) {
643     dash = NULL;
644   } else {
645     dash = (double *)gmallocn(length, sizeof(double));
646     for (i = 0; i < length; ++i) {
647       dash[i] = a->get(i, &obj)->getNum();
648       obj.free();
649     }
650   }
651   state->setLineDash(dash, length, args[1].getNum());
652   builder->updateStyle(state);
655 void PdfParser::opSetFlat(Object args[], int numArgs) {
656   state->setFlatness((int)args[0].getNum());
659 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
660   state->setLineJoin(args[0].getInt());
661   builder->updateStyle(state);
664 void PdfParser::opSetLineCap(Object args[], int numArgs) {
665   state->setLineCap(args[0].getInt());
666   builder->updateStyle(state);
669 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
670   state->setMiterLimit(args[0].getNum());
671   builder->updateStyle(state);
674 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
675   state->setLineWidth(args[0].getNum());
676   builder->updateStyle(state);
679 void PdfParser::opSetExtGState(Object args[], int numArgs) {
680   Object obj1, obj2, obj3, obj4, obj5;
681   GfxBlendMode mode;
682   GBool haveFillOP;
683   Function *funcs[4];
684   GfxColor backdropColor;
685   GBool haveBackdropColor;
686   GfxColorSpace *blendingColorSpace;
687   GBool alpha, isolated, knockout;
688   int i;
690   if (!res->lookupGState(args[0].getName(), &obj1)) {
691     return;
692   }
693   if (!obj1.isDict()) {
694     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
695     obj1.free();
696     return;
697   }
698   if (printCommands) {
699     printf("  gfx state dict: ");
700     obj1.print();
701     printf("\n");
702   }
704   // transparency support: blend mode, fill/stroke opacity
705   if (!obj1.dictLookup("BM", &obj2)->isNull()) {
706     if (state->parseBlendMode(&obj2, &mode)) {
707       state->setBlendMode(mode);
708     } else {
709       error(getPos(), "Invalid blend mode in ExtGState");
710     }
711   }
712   obj2.free();
713   if (obj1.dictLookup("ca", &obj2)->isNum()) {
714     state->setFillOpacity(obj2.getNum());
715   }
716   obj2.free();
717   if (obj1.dictLookup("CA", &obj2)->isNum()) {
718     state->setStrokeOpacity(obj2.getNum());
719   }
720   obj2.free();
722   // fill/stroke overprint
723   if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
724     state->setFillOverprint(obj2.getBool());
725   }
726   obj2.free();
727   if (obj1.dictLookup("OP", &obj2)->isBool()) {
728     state->setStrokeOverprint(obj2.getBool());
729     if (!haveFillOP) {
730       state->setFillOverprint(obj2.getBool());
731     }
732   }
733   obj2.free();
735   // stroke adjust
736   if (obj1.dictLookup("SA", &obj2)->isBool()) {
737     state->setStrokeAdjust(obj2.getBool());
738   }
739   obj2.free();
741   // transfer function
742   if (obj1.dictLookup("TR2", &obj2)->isNull()) {
743     obj2.free();
744     obj1.dictLookup("TR", &obj2);
745   }
746   if (obj2.isName("Default") ||
747       obj2.isName("Identity")) {
748     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
749     state->setTransfer(funcs);
750   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
751     for (i = 0; i < 4; ++i) {
752       obj2.arrayGet(i, &obj3);
753       funcs[i] = Function::parse(&obj3);
754       obj3.free();
755       if (!funcs[i]) {
756         break;
757       }
758     }
759     if (i == 4) {
760       state->setTransfer(funcs);
761     }
762   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
763     if ((funcs[0] = Function::parse(&obj2))) {
764       funcs[1] = funcs[2] = funcs[3] = NULL;
765       state->setTransfer(funcs);
766     }
767   } else if (!obj2.isNull()) {
768     error(getPos(), "Invalid transfer function in ExtGState");
769   }
770   obj2.free();
772   // soft mask
773   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
774     if (obj2.isName("None")) {
775       builder->clearSoftMask(state);
776     } else if (obj2.isDict()) {
777       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
778         alpha = gTrue;
779       } else { // "Luminosity"
780         alpha = gFalse;
781       }
782       obj3.free();
783       funcs[0] = NULL;
784       if (!obj2.dictLookup("TR", &obj3)->isNull()) {
785         funcs[0] = Function::parse(&obj3);
786         if (funcs[0]->getInputSize() != 1 ||
787             funcs[0]->getOutputSize() != 1) {
788           error(getPos(),
789                 "Invalid transfer function in soft mask in ExtGState");
790           delete funcs[0];
791           funcs[0] = NULL;
792         }
793       }
794       obj3.free();
795       if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
796         for (i = 0; i < gfxColorMaxComps; ++i) {
797           backdropColor.c[i] = 0;
798         }
799         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
800           obj3.arrayGet(i, &obj4);
801           if (obj4.isNum()) {
802             backdropColor.c[i] = dblToCol(obj4.getNum());
803           }
804           obj4.free();
805         }
806       }
807       obj3.free();
808       if (obj2.dictLookup("G", &obj3)->isStream()) {
809         if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
810           blendingColorSpace = NULL;
811           isolated = knockout = gFalse;
812           if (!obj4.dictLookup("CS", &obj5)->isNull()) {
813             blendingColorSpace = GfxColorSpace::parse(&obj5);
814           }
815           obj5.free();
816           if (obj4.dictLookup("I", &obj5)->isBool()) {
817             isolated = obj5.getBool();
818           }
819           obj5.free();
820           if (obj4.dictLookup("K", &obj5)->isBool()) {
821             knockout = obj5.getBool();
822           }
823           obj5.free();
824           if (!haveBackdropColor) {
825             if (blendingColorSpace) {
826               blendingColorSpace->getDefaultColor(&backdropColor);
827             } else {
828               //~ need to get the parent or default color space (?)
829               for (i = 0; i < gfxColorMaxComps; ++i) {
830                 backdropColor.c[i] = 0;
831               }
832             }
833           }
834           doSoftMask(&obj3, alpha, blendingColorSpace,
835                      isolated, knockout, funcs[0], &backdropColor);
836           if (funcs[0]) {
837             delete funcs[0];
838           }
839         } else {
840           error(getPos(), "Invalid soft mask in ExtGState - missing group");
841         }
842         obj4.free();
843       } else {
844         error(getPos(), "Invalid soft mask in ExtGState - missing group");
845       }
846       obj3.free();
847     } else if (!obj2.isNull()) {
848       error(getPos(), "Invalid soft mask in ExtGState");
849     }
850   }
851   obj2.free();
853   obj1.free();
856 void PdfParser::doSoftMask(Object *str, GBool alpha,
857                      GfxColorSpace *blendingColorSpace,
858                      GBool isolated, GBool knockout,
859                      Function *transferFunc, GfxColor *backdropColor) {
860   Dict *dict, *resDict;
861   double m[6], bbox[4];
862   Object obj1, obj2;
863   int i;
865   // check for excessive recursion
866   if (formDepth > 20) {
867     return;
868   }
870   // get stream dict
871   dict = str->streamGetDict();
873   // check form type
874   dict->lookup("FormType", &obj1);
875   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
876     error(getPos(), "Unknown form type");
877   }
878   obj1.free();
880   // get bounding box
881   dict->lookup("BBox", &obj1);
882   if (!obj1.isArray()) {
883     obj1.free();
884     error(getPos(), "Bad form bounding box");
885     return;
886   }
887   for (i = 0; i < 4; ++i) {
888     obj1.arrayGet(i, &obj2);
889     bbox[i] = obj2.getNum();
890     obj2.free();
891   }
892   obj1.free();
894   // get matrix
895   dict->lookup("Matrix", &obj1);
896   if (obj1.isArray()) {
897     for (i = 0; i < 6; ++i) {
898       obj1.arrayGet(i, &obj2);
899       m[i] = obj2.getNum();
900       obj2.free();
901     }
902   } else {
903     m[0] = 1; m[1] = 0;
904     m[2] = 0; m[3] = 1;
905     m[4] = 0; m[5] = 0;
906   }
907   obj1.free();
909   // get resources
910   dict->lookup("Resources", &obj1);
911   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
913   // draw it
914   ++formDepth;
915   doForm1(str, resDict, m, bbox, gTrue, gTrue,
916           blendingColorSpace, isolated, knockout,
917           alpha, transferFunc, backdropColor);
918   --formDepth;
920   if (blendingColorSpace) {
921     delete blendingColorSpace;
922   }
923   obj1.free();
926 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
929 //------------------------------------------------------------------------
930 // color operators
931 //------------------------------------------------------------------------
933 void PdfParser::opSetFillGray(Object args[], int numArgs) {
934   GfxColor color;
936   state->setFillPattern(NULL);
937   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
938   color.c[0] = dblToCol(args[0].getNum());
939   state->setFillColor(&color);
940   builder->updateStyle(state);
943 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
944   GfxColor color;
946   state->setStrokePattern(NULL);
947   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
948   color.c[0] = dblToCol(args[0].getNum());
949   state->setStrokeColor(&color);
950   builder->updateStyle(state);
953 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
954   GfxColor color;
955   int i;
957   state->setFillPattern(NULL);
958   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
959   for (i = 0; i < 4; ++i) {
960     color.c[i] = dblToCol(args[i].getNum());
961   }
962   state->setFillColor(&color);
963   builder->updateStyle(state);
966 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
967   GfxColor color;
968   int i;
970   state->setStrokePattern(NULL);
971   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
972   for (i = 0; i < 4; ++i) {
973     color.c[i] = dblToCol(args[i].getNum());
974   }
975   state->setStrokeColor(&color);
976   builder->updateStyle(state);
979 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
980   GfxColor color;
981   int i;
983   state->setFillPattern(NULL);
984   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
985   for (i = 0; i < 3; ++i) {
986     color.c[i] = dblToCol(args[i].getNum());
987   }
988   state->setFillColor(&color);
989   builder->updateStyle(state);
992 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
993   GfxColor color;
994   int i;
996   state->setStrokePattern(NULL);
997   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
998   for (i = 0; i < 3; ++i) {
999     color.c[i] = dblToCol(args[i].getNum());
1000   }
1001   state->setStrokeColor(&color);
1002   builder->updateStyle(state);
1005 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1006   Object obj;
1007   GfxColorSpace *colorSpace;
1008   GfxColor color;
1010   state->setFillPattern(NULL);
1011   res->lookupColorSpace(args[0].getName(), &obj);
1012   if (obj.isNull()) {
1013     colorSpace = GfxColorSpace::parse(&args[0]);
1014   } else {
1015     colorSpace = GfxColorSpace::parse(&obj);
1016   }
1017   obj.free();
1018   if (colorSpace) {
1019     state->setFillColorSpace(colorSpace);
1020     colorSpace->getDefaultColor(&color);
1021     state->setFillColor(&color);
1022     builder->updateStyle(state);
1023   } else {
1024     error(getPos(), "Bad color space (fill)");
1025   }
1028 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1029   Object obj;
1030   GfxColorSpace *colorSpace;
1031   GfxColor color;
1033   state->setStrokePattern(NULL);
1034   res->lookupColorSpace(args[0].getName(), &obj);
1035   if (obj.isNull()) {
1036     colorSpace = GfxColorSpace::parse(&args[0]);
1037   } else {
1038     colorSpace = GfxColorSpace::parse(&obj);
1039   }
1040   obj.free();
1041   if (colorSpace) {
1042     state->setStrokeColorSpace(colorSpace);
1043     colorSpace->getDefaultColor(&color);
1044     state->setStrokeColor(&color);
1045     builder->updateStyle(state);
1046   } else {
1047     error(getPos(), "Bad color space (stroke)");
1048   }
1051 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1052   GfxColor color;
1053   int i;
1055   if (numArgs != state->getFillColorSpace()->getNComps()) {
1056     error(getPos(), "Incorrect number of arguments in 'sc' command");
1057     return;
1058   }
1059   state->setFillPattern(NULL);
1060   for (i = 0; i < numArgs; ++i) {
1061     color.c[i] = dblToCol(args[i].getNum());
1062   }
1063   state->setFillColor(&color);
1064   builder->updateStyle(state);
1067 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1068   GfxColor color;
1069   int i;
1071   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1072     error(getPos(), "Incorrect number of arguments in 'SC' command");
1073     return;
1074   }
1075   state->setStrokePattern(NULL);
1076   for (i = 0; i < numArgs; ++i) {
1077     color.c[i] = dblToCol(args[i].getNum());
1078   }
1079   state->setStrokeColor(&color);
1080   builder->updateStyle(state);
1083 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1084   GfxColor color;
1085   GfxPattern *pattern;
1086   int i;
1088   if (state->getFillColorSpace()->getMode() == csPattern) {
1089     if (numArgs > 1) {
1090       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1091           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1092                              ->getUnder()->getNComps()) {
1093         error(getPos(), "Incorrect number of arguments in 'scn' command");
1094         return;
1095       }
1096       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1097         if (args[i].isNum()) {
1098           color.c[i] = dblToCol(args[i].getNum());
1099         }
1100       }
1101       state->setFillColor(&color);
1102       builder->updateStyle(state);
1103     }
1104     if (args[numArgs-1].isName() &&
1105         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1106       state->setFillPattern(pattern);
1107       builder->updateStyle(state);
1108     }
1110   } else {
1111     if (numArgs != state->getFillColorSpace()->getNComps()) {
1112       error(getPos(), "Incorrect number of arguments in 'scn' command");
1113       return;
1114     }
1115     state->setFillPattern(NULL);
1116     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1117       if (args[i].isNum()) {
1118         color.c[i] = dblToCol(args[i].getNum());
1119       }
1120     }
1121     state->setFillColor(&color);
1122     builder->updateStyle(state);
1123   }
1126 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1127   GfxColor color;
1128   GfxPattern *pattern;
1129   int i;
1131   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1132     if (numArgs > 1) {
1133       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1134                ->getUnder() ||
1135           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1136                              ->getUnder()->getNComps()) {
1137         error(getPos(), "Incorrect number of arguments in 'SCN' command");
1138         return;
1139       }
1140       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1141         if (args[i].isNum()) {
1142           color.c[i] = dblToCol(args[i].getNum());
1143         }
1144       }
1145       state->setStrokeColor(&color);
1146       builder->updateStyle(state);
1147     }
1148     if (args[numArgs-1].isName() &&
1149         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1150       state->setStrokePattern(pattern);
1151       builder->updateStyle(state);
1152     }
1154   } else {
1155     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1156       error(getPos(), "Incorrect number of arguments in 'SCN' command");
1157       return;
1158     }
1159     state->setStrokePattern(NULL);
1160     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1161       if (args[i].isNum()) {
1162         color.c[i] = dblToCol(args[i].getNum());
1163       }
1164     }
1165     state->setStrokeColor(&color);
1166     builder->updateStyle(state);
1167   }
1170 //------------------------------------------------------------------------
1171 // path segment operators
1172 //------------------------------------------------------------------------
1174 void PdfParser::opMoveTo(Object args[], int numArgs) {
1175   state->moveTo(args[0].getNum(), args[1].getNum());
1178 void PdfParser::opLineTo(Object args[], int numArgs) {
1179   if (!state->isCurPt()) {
1180     error(getPos(), "No current point in lineto");
1181     return;
1182   }
1183   state->lineTo(args[0].getNum(), args[1].getNum());
1186 void PdfParser::opCurveTo(Object args[], int numArgs) {
1187   double x1, y1, x2, y2, x3, y3;
1189   if (!state->isCurPt()) {
1190     error(getPos(), "No current point in curveto");
1191     return;
1192   }
1193   x1 = args[0].getNum();
1194   y1 = args[1].getNum();
1195   x2 = args[2].getNum();
1196   y2 = args[3].getNum();
1197   x3 = args[4].getNum();
1198   y3 = args[5].getNum();
1199   state->curveTo(x1, y1, x2, y2, x3, y3);
1202 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1203   double x1, y1, x2, y2, x3, y3;
1205   if (!state->isCurPt()) {
1206     error(getPos(), "No current point in curveto1");
1207     return;
1208   }
1209   x1 = state->getCurX();
1210   y1 = state->getCurY();
1211   x2 = args[0].getNum();
1212   y2 = args[1].getNum();
1213   x3 = args[2].getNum();
1214   y3 = args[3].getNum();
1215   state->curveTo(x1, y1, x2, y2, x3, y3);
1218 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1219   double x1, y1, x2, y2, x3, y3;
1221   if (!state->isCurPt()) {
1222     error(getPos(), "No current point in curveto2");
1223     return;
1224   }
1225   x1 = args[0].getNum();
1226   y1 = args[1].getNum();
1227   x2 = args[2].getNum();
1228   y2 = args[3].getNum();
1229   x3 = x2;
1230   y3 = y2;
1231   state->curveTo(x1, y1, x2, y2, x3, y3);
1234 void PdfParser::opRectangle(Object args[], int numArgs) {
1235   double x, y, w, h;
1237   x = args[0].getNum();
1238   y = args[1].getNum();
1239   w = args[2].getNum();
1240   h = args[3].getNum();
1241   state->moveTo(x, y);
1242   state->lineTo(x + w, y);
1243   state->lineTo(x + w, y + h);
1244   state->lineTo(x, y + h);
1245   state->closePath();
1248 void PdfParser::opClosePath(Object args[], int numArgs) {
1249   if (!state->isCurPt()) {
1250     error(getPos(), "No current point in closepath");
1251     return;
1252   }
1253   state->closePath();
1256 //------------------------------------------------------------------------
1257 // path painting operators
1258 //------------------------------------------------------------------------
1260 void PdfParser::opEndPath(Object args[], int numArgs) {
1261   doEndPath();
1264 void PdfParser::opStroke(Object args[], int numArgs) {
1265   if (!state->isCurPt()) {
1266     //error(getPos(), "No path in stroke");
1267     return;
1268   }
1269   if (state->isPath()) {
1270     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1271         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1272           doPatternStrokeFallback();
1273     } else {
1274       builder->addPath(state, false, true);
1275     }
1276   }
1277   doEndPath();
1280 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1281   if (!state->isCurPt()) {
1282     //error(getPos(), "No path in closepath/stroke");
1283     return;
1284   }
1285   state->closePath();
1286   if (state->isPath()) {
1287     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1288         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1289       doPatternStrokeFallback();
1290     } else {
1291       builder->addPath(state, false, true);
1292     }
1293   }
1294   doEndPath();
1297 void PdfParser::opFill(Object args[], int numArgs) {
1298   if (!state->isCurPt()) {
1299     //error(getPos(), "No path in fill");
1300     return;
1301   }
1302   if (state->isPath()) {
1303     if (state->getFillColorSpace()->getMode() == csPattern &&
1304         !builder->isPatternTypeSupported(state->getFillPattern())) {
1305       doPatternFillFallback(gFalse);
1306     } else {
1307       builder->addPath(state, true, false);
1308     }
1309   }
1310   doEndPath();
1313 void PdfParser::opEOFill(Object args[], int numArgs) {
1314   if (!state->isCurPt()) {
1315     //error(getPos(), "No path in eofill");
1316     return;
1317   }
1318   if (state->isPath()) {
1319     if (state->getFillColorSpace()->getMode() == csPattern &&
1320         !builder->isPatternTypeSupported(state->getFillPattern())) {
1321       doPatternFillFallback(gTrue);
1322     } else {
1323       builder->addPath(state, true, false, true);
1324     }
1325   }
1326   doEndPath();
1329 void PdfParser::opFillStroke(Object args[], int numArgs) {
1330   if (!state->isCurPt()) {
1331     //error(getPos(), "No path in fill/stroke");
1332     return;
1333   }
1334   if (state->isPath()) {
1335     doFillAndStroke(gFalse);
1336   } else {
1337     builder->addPath(state, true, true);
1338   }
1339   doEndPath();
1342 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1343   if (!state->isCurPt()) {
1344     //error(getPos(), "No path in closepath/fill/stroke");
1345     return;
1346   }
1347   if (state->isPath()) {
1348     state->closePath();
1349     doFillAndStroke(gFalse);
1350   }
1351   doEndPath();
1354 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1355   if (!state->isCurPt()) {
1356     //error(getPos(), "No path in eofill/stroke");
1357     return;
1358   }
1359   if (state->isPath()) {
1360     doFillAndStroke(gTrue);
1361   }
1362   doEndPath();
1365 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1366   if (!state->isCurPt()) {
1367     //error(getPos(), "No path in closepath/eofill/stroke");
1368     return;
1369   }
1370   if (state->isPath()) {
1371     state->closePath();
1372     doFillAndStroke(gTrue);
1373   }
1374   doEndPath();
1377 void PdfParser::doFillAndStroke(GBool eoFill) {
1378     GBool fillOk = gTrue, strokeOk = gTrue;
1379     if (state->getFillColorSpace()->getMode() == csPattern &&
1380         !builder->isPatternTypeSupported(state->getFillPattern())) {
1381         fillOk = gFalse;
1382     }
1383     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1384         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1385         strokeOk = gFalse;
1386     }
1387     if (fillOk && strokeOk) {
1388         builder->addPath(state, true, true, eoFill);
1389     } else {
1390         doPatternFillFallback(eoFill);
1391         doPatternStrokeFallback();
1392     }
1395 void PdfParser::doPatternFillFallback(GBool eoFill) {
1396   GfxPattern *pattern;
1398   if (!(pattern = state->getFillPattern())) {
1399     return;
1400   }
1401   switch (pattern->getType()) {
1402   case 1:
1403     break;
1404   case 2:
1405     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1406     break;
1407   default:
1408     error(getPos(), "Unimplemented pattern type (%d) in fill",
1409           pattern->getType());
1410     break;
1411   }
1414 void PdfParser::doPatternStrokeFallback() {
1415   GfxPattern *pattern;
1417   if (!(pattern = state->getStrokePattern())) {
1418     return;
1419   }
1420   switch (pattern->getType()) {
1421   case 1:
1422     break;
1423   case 2:
1424     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1425     break;
1426   default:
1427     error(getPos(), "Unimplemented pattern type (%d) in stroke",
1428           pattern->getType());
1429     break;
1430   }
1433 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1434                                              GBool stroke, GBool eoFill) {
1435   GfxShading *shading;
1436   GfxPath *savedPath;
1437   double *ctm, *btm, *ptm;
1438   double m[6], ictm[6], m1[6];
1439   double xMin, yMin, xMax, yMax;
1440   double det;
1442   shading = sPat->getShading();
1444   // save current graphics state
1445   savedPath = state->getPath()->copy();
1446   saveState();
1448   // clip to bbox
1449   if (0 ){//shading->getHasBBox()) {
1450     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1451     state->moveTo(xMin, yMin);
1452     state->lineTo(xMax, yMin);
1453     state->lineTo(xMax, yMax);
1454     state->lineTo(xMin, yMax);
1455     state->closePath();
1456     state->clip();
1457     //builder->clip(state);
1458     state->setPath(savedPath->copy());
1459   }
1461   // clip to current path
1462   if (stroke) {
1463     state->clipToStrokePath();
1464     //out->clipToStrokePath(state);
1465   } else {
1466     state->clip();
1467     if (eoFill) {
1468       builder->setClipPath(state, true);
1469     } else {
1470       builder->setClipPath(state);
1471     }
1472   }
1474   // set the color space
1475   state->setFillColorSpace(shading->getColorSpace()->copy());
1477   // background color fill
1478   if (shading->getHasBackground()) {
1479     state->setFillColor(shading->getBackground());
1480     builder->addPath(state, true, false);
1481   }
1482   state->clearPath();
1484   // construct a (pattern space) -> (current space) transform matrix
1485   ctm = state->getCTM();
1486   btm = baseMatrix;
1487   ptm = sPat->getMatrix();
1488   // iCTM = invert CTM
1489   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1490   ictm[0] = ctm[3] * det;
1491   ictm[1] = -ctm[1] * det;
1492   ictm[2] = -ctm[2] * det;
1493   ictm[3] = ctm[0] * det;
1494   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1495   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1496   // m1 = PTM * BTM = PTM * base transform matrix
1497   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1498   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1499   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1500   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1501   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1502   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1503   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1504   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1505   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1506   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1507   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1508   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1509   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1511   // set the new matrix
1512   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1513   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1515   // do shading type-specific operations
1516   switch (shading->getType()) {
1517   case 1:
1518     doFunctionShFill((GfxFunctionShading *)shading);
1519     break;
1520   case 2:
1521   case 3:
1522     // no need to implement these
1523     break;
1524   case 4:
1525   case 5:
1526     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1527     break;
1528   case 6:
1529   case 7:
1530     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1531     break;
1532   }
1534   // restore graphics state
1535   restoreState();
1536   state->setPath(savedPath);
1539 void PdfParser::opShFill(Object args[], int numArgs) {
1540   GfxShading *shading;
1541   GfxPath *savedPath;
1542   double xMin, yMin, xMax, yMax;
1543   double gradientTransform[6];
1544   double *matrix = NULL;
1545   GBool savedState = gFalse;
1547   if (!(shading = res->lookupShading(args[0].getName()))) {
1548     return;
1549   }
1551   // save current graphics state
1552   if (shading->getType() != 2 && shading->getType() != 3) {
1553     savedPath = state->getPath()->copy();
1554     saveState();
1555     savedState = gTrue;
1556   } else {  // get gradient transform if possible
1557       // check proper operator sequence
1558       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1559       GBool seenClip, seenConcat;
1560       seenClip = (clipHistory->getClipPath() != NULL);
1561       seenConcat = gFalse;
1562       int i = 1;
1563       while (i <= maxOperatorHistoryDepth) {
1564         const char *opName = getPreviousOperator(i);
1565         if (!strcmp(opName, "cm")) {
1566           if (seenConcat) {   // more than one 'cm'
1567             break;
1568           } else {
1569             seenConcat = gTrue;
1570           }
1571         }
1572         i++;
1573       }
1575       if (seenConcat && seenClip) {
1576         if (builder->getTransform(gradientTransform)) {
1577           matrix = (double*)&gradientTransform;
1578           builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);  // remove transform
1579         }
1580       }
1581   }
1583   // clip to bbox
1584   if (shading->getHasBBox()) {
1585     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1586     state->moveTo(xMin, yMin);
1587     state->lineTo(xMax, yMin);
1588     state->lineTo(xMax, yMax);
1589     state->lineTo(xMin, yMax);
1590     state->closePath();
1591     state->clip();
1592     if (savedState)
1593       builder->setClipPath(state);
1594     else
1595       builder->clip(state);
1596     state->clearPath();
1597   }
1599   // set the color space
1600   if (savedState)
1601     state->setFillColorSpace(shading->getColorSpace()->copy());
1603   // do shading type-specific operations
1604   switch (shading->getType()) {
1605   case 1:
1606     doFunctionShFill((GfxFunctionShading *)shading);
1607     break;
1608   case 2:
1609   case 3:
1610     if (clipHistory->getClipPath()) {
1611       builder->addShadedFill(shading, matrix, clipHistory->getClipPath(),
1612                              clipHistory->getClipType() == clipEO ? true : false);
1613     }
1614     break;
1615   case 4:
1616   case 5:
1617     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1618     break;
1619   case 6:
1620   case 7:
1621     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1622     break;
1623   }
1625   // restore graphics state
1626   if (savedState) {
1627     restoreState();
1628     state->setPath(savedPath);
1629   }
1631   delete shading;
1634 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1635   double x0, y0, x1, y1;
1636   GfxColor colors[4];
1638   shading->getDomain(&x0, &y0, &x1, &y1);
1639   shading->getColor(x0, y0, &colors[0]);
1640   shading->getColor(x0, y1, &colors[1]);
1641   shading->getColor(x1, y0, &colors[2]);
1642   shading->getColor(x1, y1, &colors[3]);
1643   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1646 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1647                             double x0, double y0,
1648                             double x1, double y1,
1649                             GfxColor *colors, int depth) {
1650   GfxColor fillColor;
1651   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1652   GfxColor colors2[4];
1653   double functionColorDelta = colorDeltas[pdfFunctionShading-1];
1654   double *matrix;
1655   double xM, yM;
1656   int nComps, i, j;
1658   nComps = shading->getColorSpace()->getNComps();
1659   matrix = shading->getMatrix();
1661   // compare the four corner colors
1662   for (i = 0; i < 4; ++i) {
1663     for (j = 0; j < nComps; ++j) {
1664       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1665         break;
1666       }
1667     }
1668     if (j < nComps) {
1669       break;
1670     }
1671   }
1673   // center of the rectangle
1674   xM = 0.5 * (x0 + x1);
1675   yM = 0.5 * (y0 + y1);
1677   // the four corner colors are close (or we hit the recursive limit)
1678   // -- fill the rectangle; but require at least one subdivision
1679   // (depth==0) to avoid problems when the four outer corners of the
1680   // shaded region are the same color
1681   if ((i == 4 && depth > 0) || depth == maxDepths[pdfFunctionShading-1]) {
1683     // use the center color
1684     shading->getColor(xM, yM, &fillColor);
1685     state->setFillColor(&fillColor);
1687     // fill the rectangle
1688     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1689                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1690     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1691                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1692     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1693                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1694     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1695                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1696     state->closePath();
1697     builder->addPath(state, true, false);
1698     state->clearPath();
1700   // the four corner colors are not close enough -- subdivide the
1701   // rectangle
1702   } else {
1704     // colors[0]       colorM0       colors[2]
1705     //   (x0,y0)       (xM,y0)       (x1,y0)
1706     //         +----------+----------+
1707     //         |          |          |
1708     //         |    UL    |    UR    |
1709     // color0M |       colorMM       | color1M
1710     // (x0,yM) +----------+----------+ (x1,yM)
1711     //         |       (xM,yM)       |
1712     //         |    LL    |    LR    |
1713     //         |          |          |
1714     //         +----------+----------+
1715     // colors[1]       colorM1       colors[3]
1716     //   (x0,y1)       (xM,y1)       (x1,y1)
1718     shading->getColor(x0, yM, &color0M);
1719     shading->getColor(x1, yM, &color1M);
1720     shading->getColor(xM, y0, &colorM0);
1721     shading->getColor(xM, y1, &colorM1);
1722     shading->getColor(xM, yM, &colorMM);
1724     // upper-left sub-rectangle
1725     colors2[0] = colors[0];
1726     colors2[1] = color0M;
1727     colors2[2] = colorM0;
1728     colors2[3] = colorMM;
1729     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1730     
1731     // lower-left sub-rectangle
1732     colors2[0] = color0M;
1733     colors2[1] = colors[1];
1734     colors2[2] = colorMM;
1735     colors2[3] = colorM1;
1736     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1737     
1738     // upper-right sub-rectangle
1739     colors2[0] = colorM0;
1740     colors2[1] = colorMM;
1741     colors2[2] = colors[2];
1742     colors2[3] = color1M;
1743     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1745     // lower-right sub-rectangle
1746     colors2[0] = colorMM;
1747     colors2[1] = colorM1;
1748     colors2[2] = color1M;
1749     colors2[3] = colors[3];
1750     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1751   }
1754 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1755   double x0, y0, x1, y1, x2, y2;
1756   GfxColor color0, color1, color2;
1757   int i;
1759   for (i = 0; i < shading->getNTriangles(); ++i) {
1760     shading->getTriangle(i, &x0, &y0, &color0,
1761                          &x1, &y1, &color1,
1762                          &x2, &y2, &color2);
1763     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1764                         shading->getColorSpace()->getNComps(), 0);
1765   }
1768 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1769                               double x1, double y1, GfxColor *color1,
1770                               double x2, double y2, GfxColor *color2,
1771                               int nComps, int depth) {
1772   double x01, y01, x12, y12, x20, y20;
1773   double gouraudColorDelta = colorDeltas[pdfGouraudTriangleShading-1];
1774   GfxColor color01, color12, color20;
1775   int i;
1777   for (i = 0; i < nComps; ++i) {
1778     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1779        abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1780       break;
1781     }
1782   }
1783   if (i == nComps || depth == maxDepths[pdfGouraudTriangleShading-1]) {
1784     state->setFillColor(color0);
1785     state->moveTo(x0, y0);
1786     state->lineTo(x1, y1);
1787     state->lineTo(x2, y2);
1788     state->closePath();
1789     builder->addPath(state, true, false);
1790     state->clearPath();
1791   } else {
1792     x01 = 0.5 * (x0 + x1);
1793     y01 = 0.5 * (y0 + y1);
1794     x12 = 0.5 * (x1 + x2);
1795     y12 = 0.5 * (y1 + y2);
1796     x20 = 0.5 * (x2 + x0);
1797     y20 = 0.5 * (y2 + y0);
1798     //~ if the shading has a Function, this should interpolate on the
1799     //~ function parameter, not on the color components
1800     for (i = 0; i < nComps; ++i) {
1801       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1802       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1803       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1804     }
1805     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1806                         x20, y20, &color20, nComps, depth + 1);
1807     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1808                         x12, y12, &color12, nComps, depth + 1);
1809     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1810                         x20, y20, &color20, nComps, depth + 1);
1811     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1812                         x2, y2, color2, nComps, depth + 1);
1813   }
1816 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1817   int start, i;
1819   if (shading->getNPatches() > 128) {
1820     start = 3;
1821   } else if (shading->getNPatches() > 64) {
1822     start = 2;
1823   } else if (shading->getNPatches() > 16) {
1824     start = 1;
1825   } else {
1826     start = 0;
1827   }
1828   for (i = 0; i < shading->getNPatches(); ++i) {
1829     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1830               start);
1831   }
1834 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1835   GfxPatch patch00, patch01, patch10, patch11;
1836   double xx[4][8], yy[4][8];
1837   double xxm, yym;
1838   double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1839   int i;
1841   for (i = 0; i < nComps; ++i) {
1842     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1843           > patchColorDelta ||
1844         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1845           > patchColorDelta ||
1846         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1847           > patchColorDelta ||
1848         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1849           > patchColorDelta) {
1850       break;
1851     }
1852   }
1853   if (i == nComps || depth == maxDepths[pdfPatchMeshShading-1]) {
1854     state->setFillColor(&patch->color[0][0]);
1855     state->moveTo(patch->x[0][0], patch->y[0][0]);
1856     state->curveTo(patch->x[0][1], patch->y[0][1],
1857                    patch->x[0][2], patch->y[0][2],
1858                    patch->x[0][3], patch->y[0][3]);
1859     state->curveTo(patch->x[1][3], patch->y[1][3],
1860                    patch->x[2][3], patch->y[2][3],
1861                    patch->x[3][3], patch->y[3][3]);
1862     state->curveTo(patch->x[3][2], patch->y[3][2],
1863                    patch->x[3][1], patch->y[3][1],
1864                    patch->x[3][0], patch->y[3][0]);
1865     state->curveTo(patch->x[2][0], patch->y[2][0],
1866                    patch->x[1][0], patch->y[1][0],
1867                    patch->x[0][0], patch->y[0][0]);
1868     state->closePath();
1869     builder->addPath(state, true, false);
1870     state->clearPath();
1871   } else {
1872     for (i = 0; i < 4; ++i) {
1873       xx[i][0] = patch->x[i][0];
1874       yy[i][0] = patch->y[i][0];
1875       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1876       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1877       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1878       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1879       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1880       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1881       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1882       yy[i][2] = 0.5 * (yy[i][1] + yym);
1883       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1884       yy[i][5] = 0.5 * (yym + yy[i][6]);
1885       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1886       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1887       xx[i][7] = patch->x[i][3];
1888       yy[i][7] = patch->y[i][3];
1889     }
1890     for (i = 0; i < 4; ++i) {
1891       patch00.x[0][i] = xx[0][i];
1892       patch00.y[0][i] = yy[0][i];
1893       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1894       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1895       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1896       yym = 0.5 * (yy[1][i] + yy[2][i]);
1897       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1898       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1899       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1900       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1901       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1902       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1903       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1904       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1905       patch10.x[0][i] = patch00.x[3][i];
1906       patch10.y[0][i] = patch00.y[3][i];
1907       patch10.x[3][i] = xx[3][i];
1908       patch10.y[3][i] = yy[3][i];
1909     }
1910     for (i = 4; i < 8; ++i) {
1911       patch01.x[0][i-4] = xx[0][i];
1912       patch01.y[0][i-4] = yy[0][i];
1913       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1914       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1915       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1916       yym = 0.5 * (yy[1][i] + yy[2][i]);
1917       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1918       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1919       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1920       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1921       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1922       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1923       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1924       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1925       patch11.x[0][i-4] = patch01.x[3][i-4];
1926       patch11.y[0][i-4] = patch01.y[3][i-4];
1927       patch11.x[3][i-4] = xx[3][i];
1928       patch11.y[3][i-4] = yy[3][i];
1929     }
1930     //~ if the shading has a Function, this should interpolate on the
1931     //~ function parameter, not on the color components
1932     for (i = 0; i < nComps; ++i) {
1933       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1934       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1935                                   patch->color[0][1].c[i]) / 2;
1936       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1937       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1938       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1939                                   patch->color[1][1].c[i]) / 2;
1940       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1941       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1942       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1943                                   patch->color[1][0].c[i]) / 2;
1944       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1945       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1946       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1947                                   patch->color[0][0].c[i]) / 2;
1948       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1949       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1950                                   patch01.color[1][1].c[i]) / 2;
1951       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1952       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1953       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1954     }
1955     fillPatch(&patch00, nComps, depth + 1);
1956     fillPatch(&patch10, nComps, depth + 1);
1957     fillPatch(&patch01, nComps, depth + 1);
1958     fillPatch(&patch11, nComps, depth + 1);
1959   }
1962 void PdfParser::doEndPath() {
1963   if (state->isCurPt() && clip != clipNone) {
1964     state->clip();
1965     if (clip == clipNormal) {
1966       clipHistory->setClip(state->getPath(), clipNormal);
1967       builder->clip(state);
1968     } else {
1969       clipHistory->setClip(state->getPath(), clipEO);
1970       builder->clip(state, true);
1971     }
1972   }
1973   clip = clipNone;
1974   state->clearPath();
1977 //------------------------------------------------------------------------
1978 // path clipping operators
1979 //------------------------------------------------------------------------
1981 void PdfParser::opClip(Object args[], int numArgs) {
1982   clip = clipNormal;
1985 void PdfParser::opEOClip(Object args[], int numArgs) {
1986   clip = clipEO;
1989 //------------------------------------------------------------------------
1990 // text object operators
1991 //------------------------------------------------------------------------
1993 void PdfParser::opBeginText(Object args[], int numArgs) {
1994   state->setTextMat(1, 0, 0, 1, 0, 0);
1995   state->textMoveTo(0, 0);
1996   builder->updateTextPosition(0.0, 0.0);
1997   fontChanged = gTrue;
1998   builder->beginTextObject(state);
2001 void PdfParser::opEndText(Object args[], int numArgs) {
2002   builder->endTextObject(state);
2005 //------------------------------------------------------------------------
2006 // text state operators
2007 //------------------------------------------------------------------------
2009 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2010   state->setCharSpace(args[0].getNum());
2013 void PdfParser::opSetFont(Object args[], int numArgs) {
2014   GfxFont *font;
2016   if (!(font = res->lookupFont(args[0].getName()))) {
2017     // unsetting the font (drawing no text) is better than using the
2018     // previous one and drawing random glyphs from it
2019     state->setFont(NULL, args[1].getNum());
2020     fontChanged = gTrue;
2021     return;
2022   }
2023   if (printCommands) {
2024     printf("  font: tag=%s name='%s' %g\n",
2025            font->getTag()->getCString(),
2026            font->getName() ? font->getName()->getCString() : "???",
2027            args[1].getNum());
2028     fflush(stdout);
2029   }
2031   font->incRefCnt();
2032   state->setFont(font, args[1].getNum());
2033   fontChanged = gTrue;
2036 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2037   state->setLeading(args[0].getNum());
2040 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2041   state->setRender(args[0].getInt());
2042   builder->updateStyle(state);
2045 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2046   state->setRise(args[0].getNum());
2049 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2050   state->setWordSpace(args[0].getNum());
2053 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2054   state->setHorizScaling(args[0].getNum());
2055   builder->updateTextMatrix(state);
2056   fontChanged = gTrue;
2059 //------------------------------------------------------------------------
2060 // text positioning operators
2061 //------------------------------------------------------------------------
2063 void PdfParser::opTextMove(Object args[], int numArgs) {
2064   double tx, ty;
2066   tx = state->getLineX() + args[0].getNum();
2067   ty = state->getLineY() + args[1].getNum();
2068   state->textMoveTo(tx, ty);
2069   builder->updateTextPosition(tx, ty);
2072 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2073   double tx, ty;
2075   tx = state->getLineX() + args[0].getNum();
2076   ty = args[1].getNum();
2077   state->setLeading(-ty);
2078   ty += state->getLineY();
2079   state->textMoveTo(tx, ty);
2080   builder->updateTextPosition(tx, ty);
2083 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2084   state->setTextMat(args[0].getNum(), args[1].getNum(),
2085                     args[2].getNum(), args[3].getNum(),
2086                     args[4].getNum(), args[5].getNum());
2087   state->textMoveTo(0, 0);
2088   builder->updateTextMatrix(state);
2089   builder->updateTextPosition(0.0, 0.0);
2090   fontChanged = gTrue;
2093 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2094   double tx, ty;
2096   tx = state->getLineX();
2097   ty = state->getLineY() - state->getLeading();
2098   state->textMoveTo(tx, ty);
2099   builder->updateTextPosition(tx, ty);
2102 //------------------------------------------------------------------------
2103 // text string operators
2104 //------------------------------------------------------------------------
2106 void PdfParser::opShowText(Object args[], int numArgs) {
2107   if (!state->getFont()) {
2108     error(getPos(), "No font in show");
2109     return;
2110   }
2111   if (fontChanged) {
2112     builder->updateFont(state);
2113     fontChanged = gFalse;
2114   }
2115   doShowText(args[0].getString());
2118 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2119   double tx, ty;
2121   if (!state->getFont()) {
2122     error(getPos(), "No font in move/show");
2123     return;
2124   }
2125   if (fontChanged) {
2126     builder->updateFont(state);
2127     fontChanged = gFalse;
2128   }
2129   tx = state->getLineX();
2130   ty = state->getLineY() - state->getLeading();
2131   state->textMoveTo(tx, ty);
2132   builder->updateTextPosition(tx, ty);
2133   doShowText(args[0].getString());
2136 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2137   double tx, ty;
2139   if (!state->getFont()) {
2140     error(getPos(), "No font in move/set/show");
2141     return;
2142   }
2143   if (fontChanged) {
2144     builder->updateFont(state);
2145     fontChanged = gFalse;
2146   }
2147   state->setWordSpace(args[0].getNum());
2148   state->setCharSpace(args[1].getNum());
2149   tx = state->getLineX();
2150   ty = state->getLineY() - state->getLeading();
2151   state->textMoveTo(tx, ty);
2152   builder->updateTextPosition(tx, ty);
2153   doShowText(args[2].getString());
2156 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2157   Array *a;
2158   Object obj;
2159   int wMode;
2160   int i;
2162   if (!state->getFont()) {
2163     error(getPos(), "No font in show/space");
2164     return;
2165   }
2166   if (fontChanged) {
2167     builder->updateFont(state);
2168     fontChanged = gFalse;
2169   }
2170   wMode = state->getFont()->getWMode();
2171   a = args[0].getArray();
2172   for (i = 0; i < a->getLength(); ++i) {
2173     a->get(i, &obj);
2174     if (obj.isNum()) {
2175       // this uses the absolute value of the font size to match
2176       // Acrobat's behavior
2177       if (wMode) {
2178         state->textShift(0, -obj.getNum() * 0.001 *
2179                             fabs(state->getFontSize()));
2180       } else {
2181         state->textShift(-obj.getNum() * 0.001 *
2182                          fabs(state->getFontSize()), 0);
2183       }
2184       builder->updateTextShift(state, obj.getNum());
2185     } else if (obj.isString()) {
2186       doShowText(obj.getString());
2187     } else {
2188       error(getPos(), "Element of show/space array must be number or string");
2189     }
2190     obj.free();
2191   }
2196 /*
2197  * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2198  * Thanks to tsdgeos for the fix.
2199  * Miklos, does this look ok?
2200  */   
2202 void PdfParser::doShowText(GooString *s) {
2203   GfxFont *font;
2204   int wMode;
2205   double riseX, riseY;
2206   CharCode code;
2207 #ifdef POPPLER_NEW_GFXFONT
2208   Unicode *u = NULL;
2209 #else
2210   Unicode u[8];
2211 #endif
2212   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2213   double originX, originY, tOriginX, tOriginY;
2214   double oldCTM[6], newCTM[6];
2215   double *mat;
2216   Object charProc;
2217   Dict *resDict;
2218   Parser *oldParser;
2219   char *p;
2220   int len, n, uLen, nChars, nSpaces, i;
2222   font = state->getFont();
2223   wMode = font->getWMode();
2225   builder->beginString(state, s);
2227   // handle a Type 3 char
2228   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2229     mat = state->getCTM();
2230     for (i = 0; i < 6; ++i) {
2231       oldCTM[i] = mat[i];
2232     }
2233     mat = state->getTextMat();
2234     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2235     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2236     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2237     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2238     mat = font->getFontMatrix();
2239     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2240     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2241     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2242     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2243     newCTM[0] *= state->getFontSize();
2244     newCTM[1] *= state->getFontSize();
2245     newCTM[2] *= state->getFontSize();
2246     newCTM[3] *= state->getFontSize();
2247     newCTM[0] *= state->getHorizScaling();
2248     newCTM[2] *= state->getHorizScaling();
2249     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2250     curX = state->getCurX();
2251     curY = state->getCurY();
2252     lineX = state->getLineX();
2253     lineY = state->getLineY();
2254     oldParser = parser;
2255     p = s->getCString();
2256     len = s->getLength();
2257     while (len > 0) {
2258       n = font->getNextChar(p, len, &code,
2259 #ifdef POPPLER_NEW_GFXFONT
2260                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2261 #else
2262                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2263 #endif
2264                             &dx, &dy, &originX, &originY);
2265       dx = dx * state->getFontSize() + state->getCharSpace();
2266       if (n == 1 && *p == ' ') {
2267         dx += state->getWordSpace();
2268       }
2269       dx *= state->getHorizScaling();
2270       dy *= state->getFontSize();
2271       state->textTransformDelta(dx, dy, &tdx, &tdy);
2272       state->transform(curX + riseX, curY + riseY, &x, &y);
2273       saveState();
2274       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2275       //~ the CTM concat values here are wrong (but never used)
2276       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2277       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2278                                code, u, uLen)) {*/
2279         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2280         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2281           pushResources(resDict);
2282         }
2283         if (charProc.isStream()) {
2284           //parse(&charProc, gFalse); // TODO: parse into SVG font
2285         } else {
2286           error(getPos(), "Missing or bad Type3 CharProc entry");
2287         }
2288         //out->endType3Char(state);
2289         if (resDict) {
2290           popResources();
2291         }
2292         charProc.free();
2293       }
2294       restoreState();
2295       // GfxState::restore() does *not* restore the current position,
2296       // so we deal with it here using (curX, curY) and (lineX, lineY)
2297       curX += tdx;
2298       curY += tdy;
2299       state->moveTo(curX, curY);
2300       state->textSetPos(lineX, lineY);
2301       p += n;
2302       len -= n;
2303     }
2304     parser = oldParser;
2306   } else {
2307     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2308     p = s->getCString();
2309     len = s->getLength();
2310     while (len > 0) {
2311       n = font->getNextChar(p, len, &code,
2312 #ifdef POPPLER_NEW_GFXFONT
2313                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2314 #else
2315                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2316 #endif
2317                             &dx, &dy, &originX, &originY);
2318       
2319       if (wMode) {
2320         dx *= state->getFontSize();
2321         dy = dy * state->getFontSize() + state->getCharSpace();
2322         if (n == 1 && *p == ' ') {
2323           dy += state->getWordSpace();
2324         }
2325       } else {
2326         dx = dx * state->getFontSize() + state->getCharSpace();
2327         if (n == 1 && *p == ' ') {
2328           dx += state->getWordSpace();
2329         }
2330         dx *= state->getHorizScaling();
2331         dy *= state->getFontSize();
2332       }
2333       state->textTransformDelta(dx, dy, &tdx, &tdy);
2334       originX *= state->getFontSize();
2335       originY *= state->getFontSize();
2336       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2337       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2338                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2339       state->shift(tdx, tdy);
2340       p += n;
2341       len -= n;
2342     }
2343   }
2345   builder->endString(state);
2349 //------------------------------------------------------------------------
2350 // XObject operators
2351 //------------------------------------------------------------------------
2353 void PdfParser::opXObject(Object args[], int numArgs) {
2354   char *name;
2355   Object obj1, obj2, obj3, refObj;
2357   name = args[0].getName();
2358   if (!res->lookupXObject(name, &obj1)) {
2359     return;
2360   }
2361   if (!obj1.isStream()) {
2362     error(getPos(), "XObject '%s' is wrong type", name);
2363     obj1.free();
2364     return;
2365   }
2366   obj1.streamGetDict()->lookup("Subtype", &obj2);
2367   if (obj2.isName("Image")) {
2368     res->lookupXObjectNF(name, &refObj);
2369     doImage(&refObj, obj1.getStream(), gFalse);
2370     refObj.free();
2371   } else if (obj2.isName("Form")) {
2372     doForm(&obj1);
2373   } else if (obj2.isName("PS")) {
2374     obj1.streamGetDict()->lookup("Level1", &obj3);
2375 /*    out->psXObject(obj1.getStream(),
2376                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2377   } else if (obj2.isName()) {
2378     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2379   } else {
2380     error(getPos(), "XObject subtype is missing or wrong type");
2381   }
2382   obj2.free();
2383   obj1.free();
2386 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2387   Dict *dict, *maskDict;
2388   int width, height;
2389   int bits, maskBits;
2390   StreamColorSpaceMode csMode;
2391   GBool mask;
2392   GBool invert;
2393   GfxColorSpace *colorSpace, *maskColorSpace;
2394   GfxImageColorMap *colorMap, *maskColorMap;
2395   Object maskObj, smaskObj;
2396   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2397   int maskColors[2*gfxColorMaxComps];
2398   int maskWidth, maskHeight;
2399   GBool maskInvert;
2400   Stream *maskStr;
2401   Object obj1, obj2;
2402   int i;
2404   // get info from the stream
2405   bits = 0;
2406   csMode = streamCSNone;
2407   str->getImageParams(&bits, &csMode);
2409   // get stream dict
2410   dict = str->getDict();
2412   // get size
2413   dict->lookup("Width", &obj1);
2414   if (obj1.isNull()) {
2415     obj1.free();
2416     dict->lookup("W", &obj1);
2417   }
2418   if (obj1.isInt())
2419     width = obj1.getInt();
2420   else if (obj1.isReal())
2421     width = (int)obj1.getReal();
2422   else
2423     goto err2;
2424   obj1.free();
2425   dict->lookup("Height", &obj1);
2426   if (obj1.isNull()) {
2427     obj1.free();
2428     dict->lookup("H", &obj1);
2429   }
2430   if (obj1.isInt())
2431     height = obj1.getInt();
2432   else if (obj1.isReal())
2433     height = (int)obj1.getReal();
2434   else
2435     goto err2;
2436   obj1.free();
2438   // image or mask?
2439   dict->lookup("ImageMask", &obj1);
2440   if (obj1.isNull()) {
2441     obj1.free();
2442     dict->lookup("IM", &obj1);
2443   }
2444   mask = gFalse;
2445   if (obj1.isBool())
2446     mask = obj1.getBool();
2447   else if (!obj1.isNull())
2448     goto err2;
2449   obj1.free();
2451   // bit depth
2452   if (bits == 0) {
2453     dict->lookup("BitsPerComponent", &obj1);
2454     if (obj1.isNull()) {
2455       obj1.free();
2456       dict->lookup("BPC", &obj1);
2457     }
2458     if (obj1.isInt()) {
2459       bits = obj1.getInt();
2460     } else if (mask) {
2461       bits = 1;
2462     } else {
2463       goto err2;
2464     }
2465     obj1.free();
2466   }
2468   // display a mask
2469   if (mask) {
2471     // check for inverted mask
2472     if (bits != 1)
2473       goto err1;
2474     invert = gFalse;
2475     dict->lookup("Decode", &obj1);
2476     if (obj1.isNull()) {
2477       obj1.free();
2478       dict->lookup("D", &obj1);
2479     }
2480     if (obj1.isArray()) {
2481       obj1.arrayGet(0, &obj2);
2482       if (obj2.isInt() && obj2.getInt() == 1)
2483         invert = gTrue;
2484       obj2.free();
2485     } else if (!obj1.isNull()) {
2486       goto err2;
2487     }
2488     obj1.free();
2490     // draw it
2491     builder->addImageMask(state, str, width, height, invert);
2493   } else {
2495     // get color space and color map
2496     dict->lookup("ColorSpace", &obj1);
2497     if (obj1.isNull()) {
2498       obj1.free();
2499       dict->lookup("CS", &obj1);
2500     }
2501     if (obj1.isName()) {
2502       res->lookupColorSpace(obj1.getName(), &obj2);
2503       if (!obj2.isNull()) {
2504         obj1.free();
2505         obj1 = obj2;
2506       } else {
2507         obj2.free();
2508       }
2509     }
2510     if (!obj1.isNull()) {
2511       colorSpace = GfxColorSpace::parse(&obj1);
2512     } else if (csMode == streamCSDeviceGray) {
2513       colorSpace = new GfxDeviceGrayColorSpace();
2514     } else if (csMode == streamCSDeviceRGB) {
2515       colorSpace = new GfxDeviceRGBColorSpace();
2516     } else if (csMode == streamCSDeviceCMYK) {
2517       colorSpace = new GfxDeviceCMYKColorSpace();
2518     } else {
2519       colorSpace = NULL;
2520     }
2521     obj1.free();
2522     if (!colorSpace) {
2523       goto err1;
2524     }
2525     dict->lookup("Decode", &obj1);
2526     if (obj1.isNull()) {
2527       obj1.free();
2528       dict->lookup("D", &obj1);
2529     }
2530     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2531     obj1.free();
2532     if (!colorMap->isOk()) {
2533       delete colorMap;
2534       goto err1;
2535     }
2537     // get the mask
2538     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2539     maskStr = NULL; // make gcc happy
2540     maskWidth = maskHeight = 0; // make gcc happy
2541     maskInvert = gFalse; // make gcc happy
2542     maskColorMap = NULL; // make gcc happy
2543     dict->lookup("Mask", &maskObj);
2544     dict->lookup("SMask", &smaskObj);
2545     if (smaskObj.isStream()) {
2546       // soft mask
2547       if (inlineImg) {
2548         goto err1;
2549       }
2550       maskStr = smaskObj.getStream();
2551       maskDict = smaskObj.streamGetDict();
2552       maskDict->lookup("Width", &obj1);
2553       if (obj1.isNull()) {
2554         obj1.free();
2555         maskDict->lookup("W", &obj1);
2556       }
2557       if (!obj1.isInt()) {
2558         goto err2;
2559       }
2560       maskWidth = obj1.getInt();
2561       obj1.free();
2562       maskDict->lookup("Height", &obj1);
2563       if (obj1.isNull()) {
2564         obj1.free();
2565         maskDict->lookup("H", &obj1);
2566       }
2567       if (!obj1.isInt()) {
2568         goto err2;
2569       }
2570       maskHeight = obj1.getInt();
2571       obj1.free();
2572       maskDict->lookup("BitsPerComponent", &obj1);
2573       if (obj1.isNull()) {
2574         obj1.free();
2575         maskDict->lookup("BPC", &obj1);
2576       }
2577       if (!obj1.isInt()) {
2578         goto err2;
2579       }
2580       maskBits = obj1.getInt();
2581       obj1.free();
2582       maskDict->lookup("ColorSpace", &obj1);
2583       if (obj1.isNull()) {
2584         obj1.free();
2585         maskDict->lookup("CS", &obj1);
2586       }
2587       if (obj1.isName()) {
2588         res->lookupColorSpace(obj1.getName(), &obj2);
2589         if (!obj2.isNull()) {
2590           obj1.free();
2591           obj1 = obj2;
2592         } else {
2593           obj2.free();
2594         }
2595       }
2596       maskColorSpace = GfxColorSpace::parse(&obj1);
2597       obj1.free();
2598       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2599         goto err1;
2600       }
2601       maskDict->lookup("Decode", &obj1);
2602       if (obj1.isNull()) {
2603         obj1.free();
2604         maskDict->lookup("D", &obj1);
2605       }
2606       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2607       obj1.free();
2608       if (!maskColorMap->isOk()) {
2609         delete maskColorMap;
2610         goto err1;
2611       }
2612       //~ handle the Matte entry
2613       haveSoftMask = gTrue;
2614     } else if (maskObj.isArray()) {
2615       // color key mask
2616       for (i = 0;
2617            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2618            ++i) {
2619         maskObj.arrayGet(i, &obj1);
2620         maskColors[i] = obj1.getInt();
2621         obj1.free();
2622       }
2623       haveColorKeyMask = gTrue;
2624     } else if (maskObj.isStream()) {
2625       // explicit mask
2626       if (inlineImg) {
2627         goto err1;
2628       }
2629       maskStr = maskObj.getStream();
2630       maskDict = maskObj.streamGetDict();
2631       maskDict->lookup("Width", &obj1);
2632       if (obj1.isNull()) {
2633         obj1.free();
2634         maskDict->lookup("W", &obj1);
2635       }
2636       if (!obj1.isInt()) {
2637         goto err2;
2638       }
2639       maskWidth = obj1.getInt();
2640       obj1.free();
2641       maskDict->lookup("Height", &obj1);
2642       if (obj1.isNull()) {
2643         obj1.free();
2644         maskDict->lookup("H", &obj1);
2645       }
2646       if (!obj1.isInt()) {
2647         goto err2;
2648       }
2649       maskHeight = obj1.getInt();
2650       obj1.free();
2651       maskDict->lookup("ImageMask", &obj1);
2652       if (obj1.isNull()) {
2653         obj1.free();
2654         maskDict->lookup("IM", &obj1);
2655       }
2656       if (!obj1.isBool() || !obj1.getBool()) {
2657         goto err2;
2658       }
2659       obj1.free();
2660       maskInvert = gFalse;
2661       maskDict->lookup("Decode", &obj1);
2662       if (obj1.isNull()) {
2663         obj1.free();
2664         maskDict->lookup("D", &obj1);
2665       }
2666       if (obj1.isArray()) {
2667         obj1.arrayGet(0, &obj2);
2668         if (obj2.isInt() && obj2.getInt() == 1) {
2669           maskInvert = gTrue;
2670         }
2671         obj2.free();
2672       } else if (!obj1.isNull()) {
2673         goto err2;
2674       }
2675       obj1.free();
2676       haveExplicitMask = gTrue;
2677     }
2679     // draw it
2680     if (haveSoftMask) {
2681         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2682                                     maskStr, maskWidth, maskHeight, maskColorMap);
2683       delete maskColorMap;
2684     } else if (haveExplicitMask) {
2685         builder->addMaskedImage(state, str, width, height, colorMap,
2686                                 maskStr, maskWidth, maskHeight, maskInvert);
2687     } else {
2688       builder->addImage(state, str, width, height, colorMap,
2689                         haveColorKeyMask ? maskColors : (int *)NULL);
2690     }
2691     delete colorMap;
2693     maskObj.free();
2694     smaskObj.free();
2695   }
2697   return;
2699  err2:
2700   obj1.free();
2701  err1:
2702   error(getPos(), "Bad image parameters");
2705 void PdfParser::doForm(Object *str) {
2706   Dict *dict;
2707   GBool transpGroup, isolated, knockout;
2708   GfxColorSpace *blendingColorSpace;
2709   Object matrixObj, bboxObj;
2710   double m[6], bbox[4];
2711   Object resObj;
2712   Dict *resDict;
2713   Object obj1, obj2, obj3;
2714   int i;
2716   // check for excessive recursion
2717   if (formDepth > 20) {
2718     return;
2719   }
2721   // get stream dict
2722   dict = str->streamGetDict();
2724   // check form type
2725   dict->lookup("FormType", &obj1);
2726   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2727     error(getPos(), "Unknown form type");
2728   }
2729   obj1.free();
2731   // get bounding box
2732   dict->lookup("BBox", &bboxObj);
2733   if (!bboxObj.isArray()) {
2734     bboxObj.free();
2735     error(getPos(), "Bad form bounding box");
2736     return;
2737   }
2738   for (i = 0; i < 4; ++i) {
2739     bboxObj.arrayGet(i, &obj1);
2740     bbox[i] = obj1.getNum();
2741     obj1.free();
2742   }
2743   bboxObj.free();
2745   // get matrix
2746   dict->lookup("Matrix", &matrixObj);
2747   if (matrixObj.isArray()) {
2748     for (i = 0; i < 6; ++i) {
2749       matrixObj.arrayGet(i, &obj1);
2750       m[i] = obj1.getNum();
2751       obj1.free();
2752     }
2753   } else {
2754     m[0] = 1; m[1] = 0;
2755     m[2] = 0; m[3] = 1;
2756     m[4] = 0; m[5] = 0;
2757   }
2758   matrixObj.free();
2760   // get resources
2761   dict->lookup("Resources", &resObj);
2762   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2764   // check for a transparency group
2765   transpGroup = isolated = knockout = gFalse;
2766   blendingColorSpace = NULL;
2767   if (dict->lookup("Group", &obj1)->isDict()) {
2768     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2769       transpGroup = gTrue;
2770       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2771         blendingColorSpace = GfxColorSpace::parse(&obj3);
2772       }
2773       obj3.free();
2774       if (obj1.dictLookup("I", &obj3)->isBool()) {
2775         isolated = obj3.getBool();
2776       }
2777       obj3.free();
2778       if (obj1.dictLookup("K", &obj3)->isBool()) {
2779         knockout = obj3.getBool();
2780       }
2781       obj3.free();
2782     }
2783     obj2.free();
2784   }
2785   obj1.free();
2787   // draw it
2788   ++formDepth;
2789   doForm1(str, resDict, m, bbox,
2790           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2791   --formDepth;
2793   if (blendingColorSpace) {
2794     delete blendingColorSpace;
2795   }
2796   resObj.free();
2799 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2800                   GBool transpGroup, GBool softMask,
2801                   GfxColorSpace *blendingColorSpace,
2802                   GBool isolated, GBool knockout,
2803                   GBool alpha, Function *transferFunc,
2804                   GfxColor *backdropColor) {
2805   Parser *oldParser;
2806   double oldBaseMatrix[6];
2807   int i;
2809   // push new resources on stack
2810   pushResources(resDict);
2812   // save current graphics state
2813   saveState();
2815   // kill any pre-existing path
2816   state->clearPath();
2818   if (softMask || transpGroup) {
2819     builder->clearSoftMask(state);
2820     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2821                                    isolated, knockout, softMask);
2822   }
2824   // save current parser
2825   oldParser = parser;
2827   // set form transformation matrix
2828   state->concatCTM(matrix[0], matrix[1], matrix[2],
2829                    matrix[3], matrix[4], matrix[5]);
2830   builder->setTransform(matrix[0], matrix[1], matrix[2],
2831                         matrix[3], matrix[4], matrix[5]);
2833   // set form bounding box
2834   state->moveTo(bbox[0], bbox[1]);
2835   state->lineTo(bbox[2], bbox[1]);
2836   state->lineTo(bbox[2], bbox[3]);
2837   state->lineTo(bbox[0], bbox[3]);
2838   state->closePath();
2839   state->clip();
2840   clipHistory->setClip(state->getPath());
2841   builder->clip(state);
2842   state->clearPath();
2844   if (softMask || transpGroup) {
2845     if (state->getBlendMode() != gfxBlendNormal) {
2846       state->setBlendMode(gfxBlendNormal);
2847     }
2848     if (state->getFillOpacity() != 1) {
2849       builder->setGroupOpacity(state->getFillOpacity());
2850       state->setFillOpacity(1);
2851     }
2852     if (state->getStrokeOpacity() != 1) {
2853       state->setStrokeOpacity(1);
2854     }
2855   }
2857   // set new base matrix
2858   for (i = 0; i < 6; ++i) {
2859     oldBaseMatrix[i] = baseMatrix[i];
2860     baseMatrix[i] = state->getCTM()[i];
2861   }
2863   // draw the form
2864   parse(str, gFalse);
2866   // restore base matrix
2867   for (i = 0; i < 6; ++i) {
2868     baseMatrix[i] = oldBaseMatrix[i];
2869   }
2871   // restore parser
2872   parser = oldParser;
2874   if (softMask || transpGroup) {
2875       builder->popTransparencyGroup(state);
2876   }
2878   // restore graphics state
2879   restoreState();
2881   // pop resource stack
2882   popResources();
2884   if (softMask) {
2885     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2886   } else if (transpGroup) {
2887     builder->paintTransparencyGroup(state, bbox);
2888   }
2890   return;
2893 //------------------------------------------------------------------------
2894 // in-line image operators
2895 //------------------------------------------------------------------------
2897 void PdfParser::opBeginImage(Object args[], int numArgs) {
2898   Stream *str;
2899   int c1, c2;
2901   // build dict/stream
2902   str = buildImageStream();
2904   // display the image
2905   if (str) {
2906     doImage(NULL, str, gTrue);
2907   
2908     // skip 'EI' tag
2909     c1 = str->getUndecodedStream()->getChar();
2910     c2 = str->getUndecodedStream()->getChar();
2911     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2912       c1 = c2;
2913       c2 = str->getUndecodedStream()->getChar();
2914     }
2915     delete str;
2916   }
2919 Stream *PdfParser::buildImageStream() {
2920   Object dict;
2921   Object obj;
2922   char *key;
2923   Stream *str;
2925   // build dictionary
2926   dict.initDict(xref);
2927   parser->getObj(&obj);
2928   while (!obj.isCmd("ID") && !obj.isEOF()) {
2929     if (!obj.isName()) {
2930       error(getPos(), "Inline image dictionary key must be a name object");
2931       obj.free();
2932     } else {
2933       key = copyString(obj.getName());
2934       obj.free();
2935       parser->getObj(&obj);
2936       if (obj.isEOF() || obj.isError()) {
2937         gfree(key);
2938         break;
2939       }
2940       dict.dictAdd(key, &obj);
2941     }
2942     parser->getObj(&obj);
2943   }
2944   if (obj.isEOF()) {
2945     error(getPos(), "End of file in inline image");
2946     obj.free();
2947     dict.free();
2948     return NULL;
2949   }
2950   obj.free();
2952   // make stream
2953   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2954   str = str->addFilters(&dict);
2956   return str;
2959 void PdfParser::opImageData(Object args[], int numArgs) {
2960   error(getPos(), "Internal: got 'ID' operator");
2963 void PdfParser::opEndImage(Object args[], int numArgs) {
2964   error(getPos(), "Internal: got 'EI' operator");
2967 //------------------------------------------------------------------------
2968 // type 3 font operators
2969 //------------------------------------------------------------------------
2971 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2974 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2977 //------------------------------------------------------------------------
2978 // compatibility operators
2979 //------------------------------------------------------------------------
2981 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2982   ++ignoreUndef;
2985 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2986   if (ignoreUndef > 0)
2987     --ignoreUndef;
2990 //------------------------------------------------------------------------
2991 // marked content operators
2992 //------------------------------------------------------------------------
2994 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2995   if (printCommands) {
2996     printf("  marked content: %s ", args[0].getName());
2997     if (numArgs == 2)
2998       args[2].print(stdout);
2999     printf("\n");
3000     fflush(stdout);
3001   }
3003   if(numArgs == 2) {
3004     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3005   } else {
3006     //out->beginMarkedContent(args[0].getName());
3007   }
3010 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3011   //out->endMarkedContent();
3014 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3015   if (printCommands) {
3016     printf("  mark point: %s ", args[0].getName());
3017     if (numArgs == 2)
3018       args[2].print(stdout);
3019     printf("\n");
3020     fflush(stdout);
3021   }
3023   if(numArgs == 2) {
3024     //out->markPoint(args[0].getName(),args[1].getDict());
3025   } else {
3026     //out->markPoint(args[0].getName());
3027   }
3031 //------------------------------------------------------------------------
3032 // misc
3033 //------------------------------------------------------------------------
3035 void PdfParser::saveState() {
3036   builder->saveState();
3037   state = state->save();
3038   clipHistory = clipHistory->save();
3041 void PdfParser::restoreState() {
3042   clipHistory = clipHistory->restore();
3043   state = state->restore();
3044   builder->restoreState();
3047 void PdfParser::pushResources(Dict *resDict) {
3048   res = new GfxResources(xref, resDict, res);
3051 void PdfParser::popResources() {
3052   GfxResources *resPtr;
3054   resPtr = res->getNext();
3055   delete res;
3056   res = resPtr;
3059 void PdfParser::setDefaultApproximationPrecision() {
3060   int i;
3062   for (i = 1; i <= pdfNumShadingTypes; ++i) {
3063     setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3064   }
3067 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3068                                           int maxDepth) {
3070   if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3071     return;
3072   }
3073   colorDeltas[shadingType-1] = dblToCol(colorDelta);
3074   maxDepths[shadingType-1] = maxDepth;
3077 //------------------------------------------------------------------------
3078 // ClipHistoryEntry
3079 //------------------------------------------------------------------------
3081 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3082     if (clipPathA) {
3083         clipPath = clipPathA->copy();
3084     } else {
3085         clipPath = NULL;
3086     }
3087     clipType = clipTypeA;
3088     saved = NULL;
3091 ClipHistoryEntry::~ClipHistoryEntry() {
3092     if (clipPath) {
3093         delete clipPath;
3094     }
3097 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3098     // Free previous clip path
3099     if (clipPath) {
3100         delete clipPath;
3101     }
3102     if (clipPathA) {
3103         clipPath = clipPathA->copy();
3104         clipType = clipTypeA;
3105     } else {
3106         clipPath = NULL;
3107     }
3110 ClipHistoryEntry *ClipHistoryEntry::save() {
3111     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3112     newEntry->saved = this;
3114     return newEntry;
3117 ClipHistoryEntry *ClipHistoryEntry::restore() {
3118     ClipHistoryEntry *oldEntry;
3120     if (saved) {
3121         oldEntry = saved;
3122         saved = NULL;
3123         delete this;
3124     } else {
3125         oldEntry = this;
3126     }
3128     return oldEntry;
3131 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3132     if (other->clipPath) {
3133         this->clipPath = other->clipPath->copy();
3134         this->clipType = other->clipType;
3135     } else {
3136         this->clipPath = NULL;
3137     }
3138     saved = NULL;
3141 #endif /* HAVE_POPPLER */