Code

non-poppler build fix
[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   }
2194 void PdfParser::doShowText(GooString *s) {
2195   GfxFont *font;
2196   int wMode;
2197   double riseX, riseY;
2198   CharCode code;
2199   Unicode u[8];
2200   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2201   double originX, originY, tOriginX, tOriginY;
2202   double oldCTM[6], newCTM[6];
2203   double *mat;
2204   Object charProc;
2205   Dict *resDict;
2206   Parser *oldParser;
2207   char *p;
2208   int len, n, uLen, nChars, nSpaces, i;
2210   font = state->getFont();
2211   wMode = font->getWMode();
2213   builder->beginString(state, s);
2215   // handle a Type 3 char
2216   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2217     mat = state->getCTM();
2218     for (i = 0; i < 6; ++i) {
2219       oldCTM[i] = mat[i];
2220     }
2221     mat = state->getTextMat();
2222     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2223     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2224     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2225     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2226     mat = font->getFontMatrix();
2227     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2228     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2229     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2230     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2231     newCTM[0] *= state->getFontSize();
2232     newCTM[1] *= state->getFontSize();
2233     newCTM[2] *= state->getFontSize();
2234     newCTM[3] *= state->getFontSize();
2235     newCTM[0] *= state->getHorizScaling();
2236     newCTM[2] *= state->getHorizScaling();
2237     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2238     curX = state->getCurX();
2239     curY = state->getCurY();
2240     lineX = state->getLineX();
2241     lineY = state->getLineY();
2242     oldParser = parser;
2243     p = s->getCString();
2244     len = s->getLength();
2245     while (len > 0) {
2246       n = font->getNextChar(p, len, &code,
2247                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2248                             &dx, &dy, &originX, &originY);
2249       dx = dx * state->getFontSize() + state->getCharSpace();
2250       if (n == 1 && *p == ' ') {
2251         dx += state->getWordSpace();
2252       }
2253       dx *= state->getHorizScaling();
2254       dy *= state->getFontSize();
2255       state->textTransformDelta(dx, dy, &tdx, &tdy);
2256       state->transform(curX + riseX, curY + riseY, &x, &y);
2257       saveState();
2258       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2259       //~ the CTM concat values here are wrong (but never used)
2260       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2261       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2262                                code, u, uLen)) {*/
2263         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2264         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2265           pushResources(resDict);
2266         }
2267         if (charProc.isStream()) {
2268           //parse(&charProc, gFalse); // TODO: parse into SVG font
2269         } else {
2270           error(getPos(), "Missing or bad Type3 CharProc entry");
2271         }
2272         //out->endType3Char(state);
2273         if (resDict) {
2274           popResources();
2275         }
2276         charProc.free();
2277       }
2278       restoreState();
2279       // GfxState::restore() does *not* restore the current position,
2280       // so we deal with it here using (curX, curY) and (lineX, lineY)
2281       curX += tdx;
2282       curY += tdy;
2283       state->moveTo(curX, curY);
2284       state->textSetPos(lineX, lineY);
2285       p += n;
2286       len -= n;
2287     }
2288     parser = oldParser;
2290   } else {
2291     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2292     p = s->getCString();
2293     len = s->getLength();
2294     while (len > 0) {
2295       n = font->getNextChar(p, len, &code,
2296                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2297                             &dx, &dy, &originX, &originY);
2298       
2299       if (wMode) {
2300         dx *= state->getFontSize();
2301         dy = dy * state->getFontSize() + state->getCharSpace();
2302         if (n == 1 && *p == ' ') {
2303           dy += state->getWordSpace();
2304         }
2305       } else {
2306         dx = dx * state->getFontSize() + state->getCharSpace();
2307         if (n == 1 && *p == ' ') {
2308           dx += state->getWordSpace();
2309         }
2310         dx *= state->getHorizScaling();
2311         dy *= state->getFontSize();
2312       }
2313       state->textTransformDelta(dx, dy, &tdx, &tdy);
2314       originX *= state->getFontSize();
2315       originY *= state->getFontSize();
2316       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2317       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2318                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2319       state->shift(tdx, tdy);
2320       p += n;
2321       len -= n;
2322     }
2323   }
2325   builder->endString(state);
2328 //------------------------------------------------------------------------
2329 // XObject operators
2330 //------------------------------------------------------------------------
2332 void PdfParser::opXObject(Object args[], int numArgs) {
2333   char *name;
2334   Object obj1, obj2, obj3, refObj;
2336   name = args[0].getName();
2337   if (!res->lookupXObject(name, &obj1)) {
2338     return;
2339   }
2340   if (!obj1.isStream()) {
2341     error(getPos(), "XObject '%s' is wrong type", name);
2342     obj1.free();
2343     return;
2344   }
2345   obj1.streamGetDict()->lookup("Subtype", &obj2);
2346   if (obj2.isName("Image")) {
2347     res->lookupXObjectNF(name, &refObj);
2348     doImage(&refObj, obj1.getStream(), gFalse);
2349     refObj.free();
2350   } else if (obj2.isName("Form")) {
2351     doForm(&obj1);
2352   } else if (obj2.isName("PS")) {
2353     obj1.streamGetDict()->lookup("Level1", &obj3);
2354 /*    out->psXObject(obj1.getStream(),
2355                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2356   } else if (obj2.isName()) {
2357     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2358   } else {
2359     error(getPos(), "XObject subtype is missing or wrong type");
2360   }
2361   obj2.free();
2362   obj1.free();
2365 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2366   Dict *dict, *maskDict;
2367   int width, height;
2368   int bits, maskBits;
2369   StreamColorSpaceMode csMode;
2370   GBool mask;
2371   GBool invert;
2372   GfxColorSpace *colorSpace, *maskColorSpace;
2373   GfxImageColorMap *colorMap, *maskColorMap;
2374   Object maskObj, smaskObj;
2375   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2376   int maskColors[2*gfxColorMaxComps];
2377   int maskWidth, maskHeight;
2378   GBool maskInvert;
2379   Stream *maskStr;
2380   Object obj1, obj2;
2381   int i;
2383   // get info from the stream
2384   bits = 0;
2385   csMode = streamCSNone;
2386   str->getImageParams(&bits, &csMode);
2388   // get stream dict
2389   dict = str->getDict();
2391   // get size
2392   dict->lookup("Width", &obj1);
2393   if (obj1.isNull()) {
2394     obj1.free();
2395     dict->lookup("W", &obj1);
2396   }
2397   if (obj1.isInt())
2398     width = obj1.getInt();
2399   else if (obj1.isReal())
2400     width = (int)obj1.getReal();
2401   else
2402     goto err2;
2403   obj1.free();
2404   dict->lookup("Height", &obj1);
2405   if (obj1.isNull()) {
2406     obj1.free();
2407     dict->lookup("H", &obj1);
2408   }
2409   if (obj1.isInt())
2410     height = obj1.getInt();
2411   else if (obj1.isReal())
2412     height = (int)obj1.getReal();
2413   else
2414     goto err2;
2415   obj1.free();
2417   // image or mask?
2418   dict->lookup("ImageMask", &obj1);
2419   if (obj1.isNull()) {
2420     obj1.free();
2421     dict->lookup("IM", &obj1);
2422   }
2423   mask = gFalse;
2424   if (obj1.isBool())
2425     mask = obj1.getBool();
2426   else if (!obj1.isNull())
2427     goto err2;
2428   obj1.free();
2430   // bit depth
2431   if (bits == 0) {
2432     dict->lookup("BitsPerComponent", &obj1);
2433     if (obj1.isNull()) {
2434       obj1.free();
2435       dict->lookup("BPC", &obj1);
2436     }
2437     if (obj1.isInt()) {
2438       bits = obj1.getInt();
2439     } else if (mask) {
2440       bits = 1;
2441     } else {
2442       goto err2;
2443     }
2444     obj1.free();
2445   }
2447   // display a mask
2448   if (mask) {
2450     // check for inverted mask
2451     if (bits != 1)
2452       goto err1;
2453     invert = gFalse;
2454     dict->lookup("Decode", &obj1);
2455     if (obj1.isNull()) {
2456       obj1.free();
2457       dict->lookup("D", &obj1);
2458     }
2459     if (obj1.isArray()) {
2460       obj1.arrayGet(0, &obj2);
2461       if (obj2.isInt() && obj2.getInt() == 1)
2462         invert = gTrue;
2463       obj2.free();
2464     } else if (!obj1.isNull()) {
2465       goto err2;
2466     }
2467     obj1.free();
2469     // draw it
2470     builder->addImageMask(state, str, width, height, invert);
2472   } else {
2474     // get color space and color map
2475     dict->lookup("ColorSpace", &obj1);
2476     if (obj1.isNull()) {
2477       obj1.free();
2478       dict->lookup("CS", &obj1);
2479     }
2480     if (obj1.isName()) {
2481       res->lookupColorSpace(obj1.getName(), &obj2);
2482       if (!obj2.isNull()) {
2483         obj1.free();
2484         obj1 = obj2;
2485       } else {
2486         obj2.free();
2487       }
2488     }
2489     if (!obj1.isNull()) {
2490       colorSpace = GfxColorSpace::parse(&obj1);
2491     } else if (csMode == streamCSDeviceGray) {
2492       colorSpace = new GfxDeviceGrayColorSpace();
2493     } else if (csMode == streamCSDeviceRGB) {
2494       colorSpace = new GfxDeviceRGBColorSpace();
2495     } else if (csMode == streamCSDeviceCMYK) {
2496       colorSpace = new GfxDeviceCMYKColorSpace();
2497     } else {
2498       colorSpace = NULL;
2499     }
2500     obj1.free();
2501     if (!colorSpace) {
2502       goto err1;
2503     }
2504     dict->lookup("Decode", &obj1);
2505     if (obj1.isNull()) {
2506       obj1.free();
2507       dict->lookup("D", &obj1);
2508     }
2509     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2510     obj1.free();
2511     if (!colorMap->isOk()) {
2512       delete colorMap;
2513       goto err1;
2514     }
2516     // get the mask
2517     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2518     maskStr = NULL; // make gcc happy
2519     maskWidth = maskHeight = 0; // make gcc happy
2520     maskInvert = gFalse; // make gcc happy
2521     maskColorMap = NULL; // make gcc happy
2522     dict->lookup("Mask", &maskObj);
2523     dict->lookup("SMask", &smaskObj);
2524     if (smaskObj.isStream()) {
2525       // soft mask
2526       if (inlineImg) {
2527         goto err1;
2528       }
2529       maskStr = smaskObj.getStream();
2530       maskDict = smaskObj.streamGetDict();
2531       maskDict->lookup("Width", &obj1);
2532       if (obj1.isNull()) {
2533         obj1.free();
2534         maskDict->lookup("W", &obj1);
2535       }
2536       if (!obj1.isInt()) {
2537         goto err2;
2538       }
2539       maskWidth = obj1.getInt();
2540       obj1.free();
2541       maskDict->lookup("Height", &obj1);
2542       if (obj1.isNull()) {
2543         obj1.free();
2544         maskDict->lookup("H", &obj1);
2545       }
2546       if (!obj1.isInt()) {
2547         goto err2;
2548       }
2549       maskHeight = obj1.getInt();
2550       obj1.free();
2551       maskDict->lookup("BitsPerComponent", &obj1);
2552       if (obj1.isNull()) {
2553         obj1.free();
2554         maskDict->lookup("BPC", &obj1);
2555       }
2556       if (!obj1.isInt()) {
2557         goto err2;
2558       }
2559       maskBits = obj1.getInt();
2560       obj1.free();
2561       maskDict->lookup("ColorSpace", &obj1);
2562       if (obj1.isNull()) {
2563         obj1.free();
2564         maskDict->lookup("CS", &obj1);
2565       }
2566       if (obj1.isName()) {
2567         res->lookupColorSpace(obj1.getName(), &obj2);
2568         if (!obj2.isNull()) {
2569           obj1.free();
2570           obj1 = obj2;
2571         } else {
2572           obj2.free();
2573         }
2574       }
2575       maskColorSpace = GfxColorSpace::parse(&obj1);
2576       obj1.free();
2577       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2578         goto err1;
2579       }
2580       maskDict->lookup("Decode", &obj1);
2581       if (obj1.isNull()) {
2582         obj1.free();
2583         maskDict->lookup("D", &obj1);
2584       }
2585       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2586       obj1.free();
2587       if (!maskColorMap->isOk()) {
2588         delete maskColorMap;
2589         goto err1;
2590       }
2591       //~ handle the Matte entry
2592       haveSoftMask = gTrue;
2593     } else if (maskObj.isArray()) {
2594       // color key mask
2595       for (i = 0;
2596            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2597            ++i) {
2598         maskObj.arrayGet(i, &obj1);
2599         maskColors[i] = obj1.getInt();
2600         obj1.free();
2601       }
2602       haveColorKeyMask = gTrue;
2603     } else if (maskObj.isStream()) {
2604       // explicit mask
2605       if (inlineImg) {
2606         goto err1;
2607       }
2608       maskStr = maskObj.getStream();
2609       maskDict = maskObj.streamGetDict();
2610       maskDict->lookup("Width", &obj1);
2611       if (obj1.isNull()) {
2612         obj1.free();
2613         maskDict->lookup("W", &obj1);
2614       }
2615       if (!obj1.isInt()) {
2616         goto err2;
2617       }
2618       maskWidth = obj1.getInt();
2619       obj1.free();
2620       maskDict->lookup("Height", &obj1);
2621       if (obj1.isNull()) {
2622         obj1.free();
2623         maskDict->lookup("H", &obj1);
2624       }
2625       if (!obj1.isInt()) {
2626         goto err2;
2627       }
2628       maskHeight = obj1.getInt();
2629       obj1.free();
2630       maskDict->lookup("ImageMask", &obj1);
2631       if (obj1.isNull()) {
2632         obj1.free();
2633         maskDict->lookup("IM", &obj1);
2634       }
2635       if (!obj1.isBool() || !obj1.getBool()) {
2636         goto err2;
2637       }
2638       obj1.free();
2639       maskInvert = gFalse;
2640       maskDict->lookup("Decode", &obj1);
2641       if (obj1.isNull()) {
2642         obj1.free();
2643         maskDict->lookup("D", &obj1);
2644       }
2645       if (obj1.isArray()) {
2646         obj1.arrayGet(0, &obj2);
2647         if (obj2.isInt() && obj2.getInt() == 1) {
2648           maskInvert = gTrue;
2649         }
2650         obj2.free();
2651       } else if (!obj1.isNull()) {
2652         goto err2;
2653       }
2654       obj1.free();
2655       haveExplicitMask = gTrue;
2656     }
2658     // draw it
2659     if (haveSoftMask) {
2660         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2661                                     maskStr, maskWidth, maskHeight, maskColorMap);
2662       delete maskColorMap;
2663     } else if (haveExplicitMask) {
2664         builder->addMaskedImage(state, str, width, height, colorMap,
2665                                 maskStr, maskWidth, maskHeight, maskInvert);
2666     } else {
2667       builder->addImage(state, str, width, height, colorMap,
2668                         haveColorKeyMask ? maskColors : (int *)NULL);
2669     }
2670     delete colorMap;
2672     maskObj.free();
2673     smaskObj.free();
2674   }
2676   return;
2678  err2:
2679   obj1.free();
2680  err1:
2681   error(getPos(), "Bad image parameters");
2684 void PdfParser::doForm(Object *str) {
2685   Dict *dict;
2686   GBool transpGroup, isolated, knockout;
2687   GfxColorSpace *blendingColorSpace;
2688   Object matrixObj, bboxObj;
2689   double m[6], bbox[4];
2690   Object resObj;
2691   Dict *resDict;
2692   Object obj1, obj2, obj3;
2693   int i;
2695   // check for excessive recursion
2696   if (formDepth > 20) {
2697     return;
2698   }
2700   // get stream dict
2701   dict = str->streamGetDict();
2703   // check form type
2704   dict->lookup("FormType", &obj1);
2705   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2706     error(getPos(), "Unknown form type");
2707   }
2708   obj1.free();
2710   // get bounding box
2711   dict->lookup("BBox", &bboxObj);
2712   if (!bboxObj.isArray()) {
2713     bboxObj.free();
2714     error(getPos(), "Bad form bounding box");
2715     return;
2716   }
2717   for (i = 0; i < 4; ++i) {
2718     bboxObj.arrayGet(i, &obj1);
2719     bbox[i] = obj1.getNum();
2720     obj1.free();
2721   }
2722   bboxObj.free();
2724   // get matrix
2725   dict->lookup("Matrix", &matrixObj);
2726   if (matrixObj.isArray()) {
2727     for (i = 0; i < 6; ++i) {
2728       matrixObj.arrayGet(i, &obj1);
2729       m[i] = obj1.getNum();
2730       obj1.free();
2731     }
2732   } else {
2733     m[0] = 1; m[1] = 0;
2734     m[2] = 0; m[3] = 1;
2735     m[4] = 0; m[5] = 0;
2736   }
2737   matrixObj.free();
2739   // get resources
2740   dict->lookup("Resources", &resObj);
2741   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2743   // check for a transparency group
2744   transpGroup = isolated = knockout = gFalse;
2745   blendingColorSpace = NULL;
2746   if (dict->lookup("Group", &obj1)->isDict()) {
2747     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2748       transpGroup = gTrue;
2749       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2750         blendingColorSpace = GfxColorSpace::parse(&obj3);
2751       }
2752       obj3.free();
2753       if (obj1.dictLookup("I", &obj3)->isBool()) {
2754         isolated = obj3.getBool();
2755       }
2756       obj3.free();
2757       if (obj1.dictLookup("K", &obj3)->isBool()) {
2758         knockout = obj3.getBool();
2759       }
2760       obj3.free();
2761     }
2762     obj2.free();
2763   }
2764   obj1.free();
2766   // draw it
2767   ++formDepth;
2768   doForm1(str, resDict, m, bbox,
2769           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2770   --formDepth;
2772   if (blendingColorSpace) {
2773     delete blendingColorSpace;
2774   }
2775   resObj.free();
2778 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2779                   GBool transpGroup, GBool softMask,
2780                   GfxColorSpace *blendingColorSpace,
2781                   GBool isolated, GBool knockout,
2782                   GBool alpha, Function *transferFunc,
2783                   GfxColor *backdropColor) {
2784   Parser *oldParser;
2785   double oldBaseMatrix[6];
2786   int i;
2788   // push new resources on stack
2789   pushResources(resDict);
2791   // save current graphics state
2792   saveState();
2794   // kill any pre-existing path
2795   state->clearPath();
2797   if (softMask || transpGroup) {
2798     builder->clearSoftMask(state);
2799     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2800                                    isolated, knockout, softMask);
2801   }
2803   // save current parser
2804   oldParser = parser;
2806   // set form transformation matrix
2807   state->concatCTM(matrix[0], matrix[1], matrix[2],
2808                    matrix[3], matrix[4], matrix[5]);
2809   builder->setTransform(matrix[0], matrix[1], matrix[2],
2810                         matrix[3], matrix[4], matrix[5]);
2812   // set form bounding box
2813   state->moveTo(bbox[0], bbox[1]);
2814   state->lineTo(bbox[2], bbox[1]);
2815   state->lineTo(bbox[2], bbox[3]);
2816   state->lineTo(bbox[0], bbox[3]);
2817   state->closePath();
2818   state->clip();
2819   clipHistory->setClip(state->getPath());
2820   builder->clip(state);
2821   state->clearPath();
2823   if (softMask || transpGroup) {
2824     if (state->getBlendMode() != gfxBlendNormal) {
2825       state->setBlendMode(gfxBlendNormal);
2826     }
2827     if (state->getFillOpacity() != 1) {
2828       builder->setGroupOpacity(state->getFillOpacity());
2829       state->setFillOpacity(1);
2830     }
2831     if (state->getStrokeOpacity() != 1) {
2832       state->setStrokeOpacity(1);
2833     }
2834   }
2836   // set new base matrix
2837   for (i = 0; i < 6; ++i) {
2838     oldBaseMatrix[i] = baseMatrix[i];
2839     baseMatrix[i] = state->getCTM()[i];
2840   }
2842   // draw the form
2843   parse(str, gFalse);
2845   // restore base matrix
2846   for (i = 0; i < 6; ++i) {
2847     baseMatrix[i] = oldBaseMatrix[i];
2848   }
2850   // restore parser
2851   parser = oldParser;
2853   if (softMask || transpGroup) {
2854       builder->popTransparencyGroup(state);
2855   }
2857   // restore graphics state
2858   restoreState();
2860   // pop resource stack
2861   popResources();
2863   if (softMask) {
2864     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2865   } else if (transpGroup) {
2866     builder->paintTransparencyGroup(state, bbox);
2867   }
2869   return;
2872 //------------------------------------------------------------------------
2873 // in-line image operators
2874 //------------------------------------------------------------------------
2876 void PdfParser::opBeginImage(Object args[], int numArgs) {
2877   Stream *str;
2878   int c1, c2;
2880   // build dict/stream
2881   str = buildImageStream();
2883   // display the image
2884   if (str) {
2885     doImage(NULL, str, gTrue);
2886   
2887     // skip 'EI' tag
2888     c1 = str->getUndecodedStream()->getChar();
2889     c2 = str->getUndecodedStream()->getChar();
2890     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2891       c1 = c2;
2892       c2 = str->getUndecodedStream()->getChar();
2893     }
2894     delete str;
2895   }
2898 Stream *PdfParser::buildImageStream() {
2899   Object dict;
2900   Object obj;
2901   char *key;
2902   Stream *str;
2904   // build dictionary
2905   dict.initDict(xref);
2906   parser->getObj(&obj);
2907   while (!obj.isCmd("ID") && !obj.isEOF()) {
2908     if (!obj.isName()) {
2909       error(getPos(), "Inline image dictionary key must be a name object");
2910       obj.free();
2911     } else {
2912       key = copyString(obj.getName());
2913       obj.free();
2914       parser->getObj(&obj);
2915       if (obj.isEOF() || obj.isError()) {
2916         gfree(key);
2917         break;
2918       }
2919       dict.dictAdd(key, &obj);
2920     }
2921     parser->getObj(&obj);
2922   }
2923   if (obj.isEOF()) {
2924     error(getPos(), "End of file in inline image");
2925     obj.free();
2926     dict.free();
2927     return NULL;
2928   }
2929   obj.free();
2931   // make stream
2932   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2933   str = str->addFilters(&dict);
2935   return str;
2938 void PdfParser::opImageData(Object args[], int numArgs) {
2939   error(getPos(), "Internal: got 'ID' operator");
2942 void PdfParser::opEndImage(Object args[], int numArgs) {
2943   error(getPos(), "Internal: got 'EI' operator");
2946 //------------------------------------------------------------------------
2947 // type 3 font operators
2948 //------------------------------------------------------------------------
2950 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2953 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2956 //------------------------------------------------------------------------
2957 // compatibility operators
2958 //------------------------------------------------------------------------
2960 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2961   ++ignoreUndef;
2964 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2965   if (ignoreUndef > 0)
2966     --ignoreUndef;
2969 //------------------------------------------------------------------------
2970 // marked content operators
2971 //------------------------------------------------------------------------
2973 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2974   if (printCommands) {
2975     printf("  marked content: %s ", args[0].getName());
2976     if (numArgs == 2)
2977       args[2].print(stdout);
2978     printf("\n");
2979     fflush(stdout);
2980   }
2982   if(numArgs == 2) {
2983     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2984   } else {
2985     //out->beginMarkedContent(args[0].getName());
2986   }
2989 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2990   //out->endMarkedContent();
2993 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2994   if (printCommands) {
2995     printf("  mark point: %s ", args[0].getName());
2996     if (numArgs == 2)
2997       args[2].print(stdout);
2998     printf("\n");
2999     fflush(stdout);
3000   }
3002   if(numArgs == 2) {
3003     //out->markPoint(args[0].getName(),args[1].getDict());
3004   } else {
3005     //out->markPoint(args[0].getName());
3006   }
3010 //------------------------------------------------------------------------
3011 // misc
3012 //------------------------------------------------------------------------
3014 void PdfParser::saveState() {
3015   builder->saveState();
3016   state = state->save();
3017   clipHistory = clipHistory->save();
3020 void PdfParser::restoreState() {
3021   clipHistory = clipHistory->restore();
3022   state = state->restore();
3023   builder->restoreState();
3026 void PdfParser::pushResources(Dict *resDict) {
3027   res = new GfxResources(xref, resDict, res);
3030 void PdfParser::popResources() {
3031   GfxResources *resPtr;
3033   resPtr = res->getNext();
3034   delete res;
3035   res = resPtr;
3038 void PdfParser::setDefaultApproximationPrecision() {
3039   int i;
3041   for (i = 1; i <= pdfNumShadingTypes; ++i) {
3042     setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3043   }
3046 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3047                                           int maxDepth) {
3049   if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3050     return;
3051   }
3052   colorDeltas[shadingType-1] = dblToCol(colorDelta);
3053   maxDepths[shadingType-1] = maxDepth;
3056 //------------------------------------------------------------------------
3057 // ClipHistoryEntry
3058 //------------------------------------------------------------------------
3060 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3061     if (clipPathA) {
3062         clipPath = clipPathA->copy();
3063     } else {
3064         clipPath = NULL;
3065     }
3066     clipType = clipTypeA;
3067     saved = NULL;
3070 ClipHistoryEntry::~ClipHistoryEntry() {
3071     if (clipPath) {
3072         delete clipPath;
3073     }
3076 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3077     // Free previous clip path
3078     if (clipPath) {
3079         delete clipPath;
3080     }
3081     if (clipPathA) {
3082         clipPath = clipPathA->copy();
3083         clipType = clipTypeA;
3084     } else {
3085         clipPath = NULL;
3086     }
3089 ClipHistoryEntry *ClipHistoryEntry::save() {
3090     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3091     newEntry->saved = this;
3093     return newEntry;
3096 ClipHistoryEntry *ClipHistoryEntry::restore() {
3097     ClipHistoryEntry *oldEntry;
3099     if (saved) {
3100         oldEntry = saved;
3101         saved = NULL;
3102         delete this;
3103     } else {
3104         oldEntry = this;
3105     }
3107     return oldEntry;
3110 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3111     if (other->clipPath) {
3112         this->clipPath = other->clipPath->copy();
3113         this->clipType = other->clipType;
3114     } else {
3115         this->clipPath = NULL;
3116     }
3117     saved = NULL;
3120 #endif /* HAVE_POPPLER */