Code

Use the font matrix for Type3 fonts only
[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 // Max recursive depth for a function shading fill.
64 #define functionMaxDepth 6
66 // Max delta allowed in any color component for a function shading fill.
67 #define functionColorDelta (dblToCol(1 / 256.0))
69 // Max number of splits along the t axis for an axial shading fill.
70 #define axialMaxSplits 256
72 // Max delta allowed in any color component for an axial shading fill.
73 #define axialColorDelta (dblToCol(1 / 256.0))
75 // Max number of splits along the t axis for a radial shading fill.
76 #define radialMaxSplits 256
78 // Max delta allowed in any color component for a radial shading fill.
79 #define radialColorDelta (dblToCol(1 / 256.0))
81 // Max recursive depth for a Gouraud triangle shading fill.
82 #define gouraudMaxDepth 6
84 // Max delta allowed in any color component for a Gouraud triangle
85 // shading fill.
86 #define gouraudColorDelta (dblToCol(1 / 256.0))
88 // Max recursive depth for a patch mesh shading fill.
89 #define patchMaxDepth 6
91 // Max delta allowed in any color component for a patch mesh shading
92 // fill.
93 #define patchColorDelta (dblToCol(1 / 256.0))
95 // Max number of operators kept in the history list.
96 #define maxOperatorHistoryDepth 16
98 //------------------------------------------------------------------------
99 // Operator table
100 //------------------------------------------------------------------------
102 #ifdef WIN32 // this works around a bug in the VC7 compiler
103 #  pragma optimize("",off)
104 #endif
106 PdfOperator PdfParser::opTab[] = {
107   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
108           &PdfParser::opMoveSetShowText},
109   {"'",   1, {tchkString},
110           &PdfParser::opMoveShowText},
111   {"B",   0, {tchkNone},
112           &PdfParser::opFillStroke},
113   {"B*",  0, {tchkNone},
114           &PdfParser::opEOFillStroke},
115   {"BDC", 2, {tchkName,   tchkProps},
116           &PdfParser::opBeginMarkedContent},
117   {"BI",  0, {tchkNone},
118           &PdfParser::opBeginImage},
119   {"BMC", 1, {tchkName},
120           &PdfParser::opBeginMarkedContent},
121   {"BT",  0, {tchkNone},
122           &PdfParser::opBeginText},
123   {"BX",  0, {tchkNone},
124           &PdfParser::opBeginIgnoreUndef},
125   {"CS",  1, {tchkName},
126           &PdfParser::opSetStrokeColorSpace},
127   {"DP",  2, {tchkName,   tchkProps},
128           &PdfParser::opMarkPoint},
129   {"Do",  1, {tchkName},
130           &PdfParser::opXObject},
131   {"EI",  0, {tchkNone},
132           &PdfParser::opEndImage},
133   {"EMC", 0, {tchkNone},
134           &PdfParser::opEndMarkedContent},
135   {"ET",  0, {tchkNone},
136           &PdfParser::opEndText},
137   {"EX",  0, {tchkNone},
138           &PdfParser::opEndIgnoreUndef},
139   {"F",   0, {tchkNone},
140           &PdfParser::opFill},
141   {"G",   1, {tchkNum},
142           &PdfParser::opSetStrokeGray},
143   {"ID",  0, {tchkNone},
144           &PdfParser::opImageData},
145   {"J",   1, {tchkInt},
146           &PdfParser::opSetLineCap},
147   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
148           &PdfParser::opSetStrokeCMYKColor},
149   {"M",   1, {tchkNum},
150           &PdfParser::opSetMiterLimit},
151   {"MP",  1, {tchkName},
152           &PdfParser::opMarkPoint},
153   {"Q",   0, {tchkNone},
154           &PdfParser::opRestore},
155   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
156           &PdfParser::opSetStrokeRGBColor},
157   {"S",   0, {tchkNone},
158           &PdfParser::opStroke},
159   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
160           &PdfParser::opSetStrokeColor},
161   {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
162                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
163                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
164                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
165                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
166                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
167                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
168                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
169                 tchkSCN},
170           &PdfParser::opSetStrokeColorN},
171   {"T*",  0, {tchkNone},
172           &PdfParser::opTextNextLine},
173   {"TD",  2, {tchkNum,    tchkNum},
174           &PdfParser::opTextMoveSet},
175   {"TJ",  1, {tchkArray},
176           &PdfParser::opShowSpaceText},
177   {"TL",  1, {tchkNum},
178           &PdfParser::opSetTextLeading},
179   {"Tc",  1, {tchkNum},
180           &PdfParser::opSetCharSpacing},
181   {"Td",  2, {tchkNum,    tchkNum},
182           &PdfParser::opTextMove},
183   {"Tf",  2, {tchkName,   tchkNum},
184           &PdfParser::opSetFont},
185   {"Tj",  1, {tchkString},
186           &PdfParser::opShowText},
187   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
188               tchkNum,    tchkNum},
189           &PdfParser::opSetTextMatrix},
190   {"Tr",  1, {tchkInt},
191           &PdfParser::opSetTextRender},
192   {"Ts",  1, {tchkNum},
193           &PdfParser::opSetTextRise},
194   {"Tw",  1, {tchkNum},
195           &PdfParser::opSetWordSpacing},
196   {"Tz",  1, {tchkNum},
197           &PdfParser::opSetHorizScaling},
198   {"W",   0, {tchkNone},
199           &PdfParser::opClip},
200   {"W*",  0, {tchkNone},
201           &PdfParser::opEOClip},
202   {"b",   0, {tchkNone},
203           &PdfParser::opCloseFillStroke},
204   {"b*",  0, {tchkNone},
205           &PdfParser::opCloseEOFillStroke},
206   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
207               tchkNum,    tchkNum},
208           &PdfParser::opCurveTo},
209   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
210               tchkNum,    tchkNum},
211           &PdfParser::opConcat},
212   {"cs",  1, {tchkName},
213           &PdfParser::opSetFillColorSpace},
214   {"d",   2, {tchkArray,  tchkNum},
215           &PdfParser::opSetDash},
216   {"d0",  2, {tchkNum,    tchkNum},
217           &PdfParser::opSetCharWidth},
218   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
219               tchkNum,    tchkNum},
220           &PdfParser::opSetCacheDevice},
221   {"f",   0, {tchkNone},
222           &PdfParser::opFill},
223   {"f*",  0, {tchkNone},
224           &PdfParser::opEOFill},
225   {"g",   1, {tchkNum},
226           &PdfParser::opSetFillGray},
227   {"gs",  1, {tchkName},
228           &PdfParser::opSetExtGState},
229   {"h",   0, {tchkNone},
230           &PdfParser::opClosePath},
231   {"i",   1, {tchkNum},
232           &PdfParser::opSetFlat},
233   {"j",   1, {tchkInt},
234           &PdfParser::opSetLineJoin},
235   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
236           &PdfParser::opSetFillCMYKColor},
237   {"l",   2, {tchkNum,    tchkNum},
238           &PdfParser::opLineTo},
239   {"m",   2, {tchkNum,    tchkNum},
240           &PdfParser::opMoveTo},
241   {"n",   0, {tchkNone},
242           &PdfParser::opEndPath},
243   {"q",   0, {tchkNone},
244           &PdfParser::opSave},
245   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
246           &PdfParser::opRectangle},
247   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
248           &PdfParser::opSetFillRGBColor},
249   {"ri",  1, {tchkName},
250           &PdfParser::opSetRenderingIntent},
251   {"s",   0, {tchkNone},
252           &PdfParser::opCloseStroke},
253   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
254           &PdfParser::opSetFillColor},
255   {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
256                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
257                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
258                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
259                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
260                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
261                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
262                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
263                 tchkSCN},
264           &PdfParser::opSetFillColorN},
265   {"sh",  1, {tchkName},
266           &PdfParser::opShFill},
267   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
268           &PdfParser::opCurveTo1},
269   {"w",   1, {tchkNum},
270           &PdfParser::opSetLineWidth},
271   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
272           &PdfParser::opCurveTo2}
273 };
275 #ifdef WIN32 // this works around a bug in the VC7 compiler
276 #  pragma optimize("",on)
277 #endif
279 #define numOps (sizeof(opTab) / sizeof(PdfOperator))
281 //------------------------------------------------------------------------
282 // PdfParser
283 //------------------------------------------------------------------------
285 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
286                      int pageNum, int rotate, Dict *resDict, PDFRectangle *cropBox) {
288   int i;
290   xref = xrefA;
291   subPage = gFalse;
292   printCommands = false;
294   // start the resource stack
295   res = new GfxResources(xref, resDict, NULL);
297   // initialize
298   state = new GfxState(72.0, 72.0, cropBox, rotate, gTrue);
299   fontChanged = gFalse;
300   clip = clipNone;
301   ignoreUndef = 0;
302   operatorHistory = NULL;
303   builder = builderA;
304   builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
305                            state->getPageHeight()*PX_PER_PT);
307   double *ctm = state->getCTM();
308   double scaledCTM[6];
309   for (i = 0; i < 6; ++i) {
310     baseMatrix[i] = ctm[i];
311     scaledCTM[i] = PX_PER_PT * ctm[i];
312   }
313   saveState();
314   builder->setTransform((double*)&scaledCTM);
315   formDepth = 0;
317   // set crop box
318   if (cropBox) {
319     if (printCommands)
320         printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
321     // do not clip if it's not needed
322     if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
323         cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
324         
325         state->moveTo(cropBox->x1, cropBox->y1);
326         state->lineTo(cropBox->x2, cropBox->y1);
327         state->lineTo(cropBox->x2, cropBox->y2);
328         state->lineTo(cropBox->x1, cropBox->y2);
329         state->closePath();
330         state->clip();
331         builder->setClipPath(state);
332         state->clearPath();
333     }
334   }
335   pushOperator("startPage");
338 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
339                      Dict *resDict, PDFRectangle *box) {
341   int i;
343   xref = xrefA;
344   subPage = gTrue;
345   printCommands = false;
347   // start the resource stack
348   res = new GfxResources(xref, resDict, NULL);
350   // initialize
351   operatorHistory = NULL;
352   builder = builderA;
353   state = new GfxState(72, 72, box, 0, gFalse);
354   fontChanged = gFalse;
355   clip = clipNone;
356   ignoreUndef = 0;
357   for (i = 0; i < 6; ++i) {
358     baseMatrix[i] = state->getCTM()[i];
359   }
360   formDepth = 0;
363 PdfParser::~PdfParser() {
364   while (state->hasSaves()) {
365     restoreState();
366   }
367   if (!subPage) {
368     //out->endPage();
369   }
370   while (res) {
371     popResources();
372   }
373   if (state) {
374     delete state;
375   }
378 void PdfParser::parse(Object *obj, GBool topLevel) {
379   Object obj2;
380   int i;
382   if (obj->isArray()) {
383     for (i = 0; i < obj->arrayGetLength(); ++i) {
384       obj->arrayGet(i, &obj2);
385       if (!obj2.isStream()) {
386         error(-1, "Weird page contents");
387         obj2.free();
388         return;
389       }
390       obj2.free();
391     }
392   } else if (!obj->isStream()) {
393     error(-1, "Weird page contents");
394     return;
395   }
396   parser = new Parser(xref, new Lexer(xref, obj), gFalse);
397   go(topLevel);
398   delete parser;
399   parser = NULL;
402 void PdfParser::go(GBool topLevel) {
403   Object obj;
404   Object args[maxArgs];
405   int numArgs, i;
406   int lastAbortCheck;
408   // scan a sequence of objects
409   numArgs = 0;
410   parser->getObj(&obj);
411   while (!obj.isEOF()) {
413     // got a command - execute it
414     if (obj.isCmd()) {
415       if (printCommands) {
416         obj.print(stdout);
417         for (i = 0; i < numArgs; ++i) {
418           printf(" ");
419           args[i].print(stdout);
420         }
421         printf("\n");
422         fflush(stdout);
423       }
425       // Run the operation
426       execOp(&obj, args, numArgs);
428       obj.free();
429       for (i = 0; i < numArgs; ++i)
430         args[i].free();
431       numArgs = 0;
433     // got an argument - save it
434     } else if (numArgs < maxArgs) {
435       args[numArgs++] = obj;
437     // too many arguments - something is wrong
438     } else {
439       error(getPos(), "Too many args in content stream");
440       if (printCommands) {
441         printf("throwing away arg: ");
442         obj.print(stdout);
443         printf("\n");
444         fflush(stdout);
445       }
446       obj.free();
447     }
449     // grab the next object
450     parser->getObj(&obj);
451   }
452   obj.free();
454   // args at end with no command
455   if (numArgs > 0) {
456     error(getPos(), "Leftover args in content stream");
457     if (printCommands) {
458       printf("%d leftovers:", numArgs);
459       for (i = 0; i < numArgs; ++i) {
460         printf(" ");
461         args[i].print(stdout);
462       }
463       printf("\n");
464       fflush(stdout);
465     }
466     for (i = 0; i < numArgs; ++i)
467       args[i].free();
468   }
471 void PdfParser::pushOperator(const char *name) {
472     OpHistoryEntry *newEntry = new OpHistoryEntry;
473     newEntry->name = name;
474     newEntry->state = NULL;
475     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
476     newEntry->next = operatorHistory;
477     operatorHistory = newEntry;
479     // Truncate list if needed
480     if (operatorHistory->depth > maxOperatorHistoryDepth) {
481         OpHistoryEntry *curr = operatorHistory;
482         OpHistoryEntry *prev = NULL;
483         while (curr && curr->next != NULL) {
484             curr->depth--;
485             prev = curr;
486             curr = curr->next;
487         }
488         if (prev) {
489             if (curr->state != NULL)
490                 delete curr->state;
491             delete curr;
492             prev->next = NULL;
493         }
494     }
497 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
498     OpHistoryEntry *prev = NULL;
499     if (operatorHistory != NULL && look_back > 0) {
500         prev = operatorHistory->next;
501         while (--look_back > 0 && prev != NULL) {
502             prev = prev->next;
503         }
504     }
505     if (prev != NULL) {
506         return prev->name;
507     } else {
508         return "";
509     }
512 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
513   PdfOperator *op;
514   char *name;
515   Object *argPtr;
516   int i;
518   // find operator
519   name = cmd->getCmd();
520   if (!(op = findOp(name))) {
521     if (ignoreUndef == 0)
522       error(getPos(), "Unknown operator '%s'", name);
523     return;
524   }
526   // type check args
527   argPtr = args;
528   if (op->numArgs >= 0) {
529     if (numArgs < op->numArgs) {
530       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
531       return;
532     }
533     if (numArgs > op->numArgs) {
534 #if 0
535       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
536 #endif
537       argPtr += numArgs - op->numArgs;
538       numArgs = op->numArgs;
539     }
540   } else {
541     if (numArgs > -op->numArgs) {
542       error(getPos(), "Too many (%d) args to '%s' operator",
543             numArgs, name);
544       return;
545     }
546   }
547   for (i = 0; i < numArgs; ++i) {
548     if (!checkArg(&argPtr[i], op->tchk[i])) {
549       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
550             i, name, argPtr[i].getTypeName());
551       return;
552     }
553   }
555   // add to history
556   pushOperator((char*)&op->name);
558   // do it
559   (this->*op->func)(argPtr, numArgs);
562 PdfOperator *PdfParser::findOp(char *name) {
563   int a, b, m, cmp;
565   a = -1;
566   b = numOps;
567   // invariant: opTab[a] < name < opTab[b]
568   while (b - a > 1) {
569     m = (a + b) / 2;
570     cmp = strcmp(opTab[m].name, name);
571     if (cmp < 0)
572       a = m;
573     else if (cmp > 0)
574       b = m;
575     else
576       a = b = m;
577   }
578   if (cmp != 0)
579     return NULL;
580   return &opTab[a];
583 GBool PdfParser::checkArg(Object *arg, TchkType type) {
584   switch (type) {
585   case tchkBool:   return arg->isBool();
586   case tchkInt:    return arg->isInt();
587   case tchkNum:    return arg->isNum();
588   case tchkString: return arg->isString();
589   case tchkName:   return arg->isName();
590   case tchkArray:  return arg->isArray();
591   case tchkProps:  return arg->isDict() || arg->isName();
592   case tchkSCN:    return arg->isNum() || arg->isName();
593   case tchkNone:   return gFalse;
594   }
595   return gFalse;
598 int PdfParser::getPos() {
599   return parser ? parser->getPos() : -1;
602 //------------------------------------------------------------------------
603 // graphics state operators
604 //------------------------------------------------------------------------
606 void PdfParser::opSave(Object args[], int numArgs) {
607   saveState();
610 void PdfParser::opRestore(Object args[], int numArgs) {
611   restoreState();
614 void PdfParser::opConcat(Object args[], int numArgs) {
615   state->concatCTM(args[0].getNum(), args[1].getNum(),
616                    args[2].getNum(), args[3].getNum(),
617                    args[4].getNum(), args[5].getNum());
618   const char *prevOp = getPreviousOperator();
619   double a0 = args[0].getNum();
620   double a1 = args[1].getNum();
621   double a2 = args[2].getNum();
622   double a3 = args[3].getNum();
623   double a4 = args[4].getNum();
624   double a5 = args[5].getNum();
625   if (!strcmp(prevOp, "q")) {
626       builder->setTransform(a0, a1, a2, a3, a4, a5);
627   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
628       // multiply it with the previous transform
629       double otherMatrix[6];
630       if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
631           // construct identity matrix
632           otherMatrix[0] = otherMatrix[3] = 1.0;
633           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
634       }
635       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
636       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
637       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
638       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
639       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
640       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
641       builder->setTransform(c0, c1, c2, c3, c4, c5);
642   } else {
643       builder->pushGroup();
644       builder->setTransform(a0, a1, a2, a3, a4, a5);
645   }
646   fontChanged = gTrue;
649 void PdfParser::opSetDash(Object args[], int numArgs) {
650   Array *a;
651   int length;
652   Object obj;
653   double *dash;
654   int i;
656   a = args[0].getArray();
657   length = a->getLength();
658   if (length == 0) {
659     dash = NULL;
660   } else {
661     dash = (double *)gmallocn(length, sizeof(double));
662     for (i = 0; i < length; ++i) {
663       dash[i] = a->get(i, &obj)->getNum();
664       obj.free();
665     }
666   }
667   state->setLineDash(dash, length, args[1].getNum());
668   builder->updateStyle(state);
671 void PdfParser::opSetFlat(Object args[], int numArgs) {
672   state->setFlatness((int)args[0].getNum());
675 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
676   state->setLineJoin(args[0].getInt());
677   builder->updateStyle(state);
680 void PdfParser::opSetLineCap(Object args[], int numArgs) {
681   state->setLineCap(args[0].getInt());
682   builder->updateStyle(state);
685 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
686   state->setMiterLimit(args[0].getNum());
687   builder->updateStyle(state);
690 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
691   state->setLineWidth(args[0].getNum());
692   builder->updateStyle(state);
695 void PdfParser::opSetExtGState(Object args[], int numArgs) {
696   Object obj1, obj2, obj3, obj4, obj5;
697   GfxBlendMode mode;
698   GBool haveFillOP;
699   Function *funcs[4];
700   GfxColor backdropColor;
701   GBool haveBackdropColor;
702   GfxColorSpace *blendingColorSpace;
703   GBool alpha, isolated, knockout;
704   int i;
706   if (!res->lookupGState(args[0].getName(), &obj1)) {
707     return;
708   }
709   if (!obj1.isDict()) {
710     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
711     obj1.free();
712     return;
713   }
714   if (printCommands) {
715     printf("  gfx state dict: ");
716     obj1.print();
717     printf("\n");
718   }
720   // transparency support: blend mode, fill/stroke opacity
721   if (!obj1.dictLookup("BM", &obj2)->isNull()) {
722     if (state->parseBlendMode(&obj2, &mode)) {
723       state->setBlendMode(mode);
724     } else {
725       error(getPos(), "Invalid blend mode in ExtGState");
726     }
727   }
728   obj2.free();
729   if (obj1.dictLookup("ca", &obj2)->isNum()) {
730     state->setFillOpacity(obj2.getNum());
731   }
732   obj2.free();
733   if (obj1.dictLookup("CA", &obj2)->isNum()) {
734     state->setStrokeOpacity(obj2.getNum());
735   }
736   obj2.free();
738   // fill/stroke overprint
739   if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
740     state->setFillOverprint(obj2.getBool());
741   }
742   obj2.free();
743   if (obj1.dictLookup("OP", &obj2)->isBool()) {
744     state->setStrokeOverprint(obj2.getBool());
745     if (!haveFillOP) {
746       state->setFillOverprint(obj2.getBool());
747     }
748   }
749   obj2.free();
751   // stroke adjust
752   if (obj1.dictLookup("SA", &obj2)->isBool()) {
753     state->setStrokeAdjust(obj2.getBool());
754   }
755   obj2.free();
757   // transfer function
758   if (obj1.dictLookup("TR2", &obj2)->isNull()) {
759     obj2.free();
760     obj1.dictLookup("TR", &obj2);
761   }
762   if (obj2.isName("Default") ||
763       obj2.isName("Identity")) {
764     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
765     state->setTransfer(funcs);
766   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
767     for (i = 0; i < 4; ++i) {
768       obj2.arrayGet(i, &obj3);
769       funcs[i] = Function::parse(&obj3);
770       obj3.free();
771       if (!funcs[i]) {
772         break;
773       }
774     }
775     if (i == 4) {
776       state->setTransfer(funcs);
777     }
778   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
779     if ((funcs[0] = Function::parse(&obj2))) {
780       funcs[1] = funcs[2] = funcs[3] = NULL;
781       state->setTransfer(funcs);
782     }
783   } else if (!obj2.isNull()) {
784     error(getPos(), "Invalid transfer function in ExtGState");
785   }
786   obj2.free();
788   // soft mask
789   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
790     if (obj2.isName("None")) {
791       //out->clearSoftMask(state);
792     } else if (obj2.isDict()) {
793       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
794         alpha = gTrue;
795       } else { // "Luminosity"
796         alpha = gFalse;
797       }
798       obj3.free();
799       funcs[0] = NULL;
800       if (!obj2.dictLookup("TR", &obj3)->isNull()) {
801         funcs[0] = Function::parse(&obj3);
802         if (funcs[0]->getInputSize() != 1 ||
803             funcs[0]->getOutputSize() != 1) {
804           error(getPos(),
805                 "Invalid transfer function in soft mask in ExtGState");
806           delete funcs[0];
807           funcs[0] = NULL;
808         }
809       }
810       obj3.free();
811       if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
812         for (i = 0; i < gfxColorMaxComps; ++i) {
813           backdropColor.c[i] = 0;
814         }
815         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
816           obj3.arrayGet(i, &obj4);
817           if (obj4.isNum()) {
818             backdropColor.c[i] = dblToCol(obj4.getNum());
819           }
820           obj4.free();
821         }
822       }
823       obj3.free();
824       if (obj2.dictLookup("G", &obj3)->isStream()) {
825         if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
826           blendingColorSpace = NULL;
827           isolated = knockout = gFalse;
828           if (!obj4.dictLookup("CS", &obj5)->isNull()) {
829             blendingColorSpace = GfxColorSpace::parse(&obj5);
830           }
831           obj5.free();
832           if (obj4.dictLookup("I", &obj5)->isBool()) {
833             isolated = obj5.getBool();
834           }
835           obj5.free();
836           if (obj4.dictLookup("K", &obj5)->isBool()) {
837             knockout = obj5.getBool();
838           }
839           obj5.free();
840           if (!haveBackdropColor) {
841             if (blendingColorSpace) {
842               blendingColorSpace->getDefaultColor(&backdropColor);
843             } else {
844               //~ need to get the parent or default color space (?)
845               for (i = 0; i < gfxColorMaxComps; ++i) {
846                 backdropColor.c[i] = 0;
847               }
848             }
849           }
850           doSoftMask(&obj3, alpha, blendingColorSpace,
851                      isolated, knockout, funcs[0], &backdropColor);
852           if (funcs[0]) {
853             delete funcs[0];
854           }
855         } else {
856           error(getPos(), "Invalid soft mask in ExtGState - missing group");
857         }
858         obj4.free();
859       } else {
860         error(getPos(), "Invalid soft mask in ExtGState - missing group");
861       }
862       obj3.free();
863     } else if (!obj2.isNull()) {
864       error(getPos(), "Invalid soft mask in ExtGState");
865     }
866   }
867   obj2.free();
869   obj1.free();
872 void PdfParser::doSoftMask(Object *str, GBool alpha,
873                      GfxColorSpace *blendingColorSpace,
874                      GBool isolated, GBool knockout,
875                      Function *transferFunc, GfxColor *backdropColor) {
876   Dict *dict, *resDict;
877   double m[6], bbox[4];
878   Object obj1, obj2;
879   int i;
881   // check for excessive recursion
882   if (formDepth > 20) {
883     return;
884   }
886   // get stream dict
887   dict = str->streamGetDict();
889   // check form type
890   dict->lookup("FormType", &obj1);
891   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
892     error(getPos(), "Unknown form type");
893   }
894   obj1.free();
896   // get bounding box
897   dict->lookup("BBox", &obj1);
898   if (!obj1.isArray()) {
899     obj1.free();
900     error(getPos(), "Bad form bounding box");
901     return;
902   }
903   for (i = 0; i < 4; ++i) {
904     obj1.arrayGet(i, &obj2);
905     bbox[i] = obj2.getNum();
906     obj2.free();
907   }
908   obj1.free();
910   // get matrix
911   dict->lookup("Matrix", &obj1);
912   if (obj1.isArray()) {
913     for (i = 0; i < 6; ++i) {
914       obj1.arrayGet(i, &obj2);
915       m[i] = obj2.getNum();
916       obj2.free();
917     }
918   } else {
919     m[0] = 1; m[1] = 0;
920     m[2] = 0; m[3] = 1;
921     m[4] = 0; m[5] = 0;
922   }
923   obj1.free();
925   // get resources
926   dict->lookup("Resources", &obj1);
927   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
929   // draw it
930   ++formDepth;
931   doForm1(str, resDict, m, bbox, gTrue, gTrue,
932           blendingColorSpace, isolated, knockout,
933           alpha, transferFunc, backdropColor);
934   --formDepth;
936   if (blendingColorSpace) {
937     delete blendingColorSpace;
938   }
939   obj1.free();
942 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
945 //------------------------------------------------------------------------
946 // color operators
947 //------------------------------------------------------------------------
949 void PdfParser::opSetFillGray(Object args[], int numArgs) {
950   GfxColor color;
952   state->setFillPattern(NULL);
953   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
954   color.c[0] = dblToCol(args[0].getNum());
955   state->setFillColor(&color);
956   builder->updateStyle(state);
959 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
960   GfxColor color;
962   state->setStrokePattern(NULL);
963   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
964   color.c[0] = dblToCol(args[0].getNum());
965   state->setStrokeColor(&color);
966   builder->updateStyle(state);
969 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
970   GfxColor color;
971   int i;
973   state->setFillPattern(NULL);
974   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
975   for (i = 0; i < 4; ++i) {
976     color.c[i] = dblToCol(args[i].getNum());
977   }
978   state->setFillColor(&color);
979   builder->updateStyle(state);
982 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
983   GfxColor color;
984   int i;
986   state->setStrokePattern(NULL);
987   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
988   for (i = 0; i < 4; ++i) {
989     color.c[i] = dblToCol(args[i].getNum());
990   }
991   state->setStrokeColor(&color);
992   builder->updateStyle(state);
995 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
996   GfxColor color;
997   int i;
999   state->setFillPattern(NULL);
1000   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
1001   for (i = 0; i < 3; ++i) {
1002     color.c[i] = dblToCol(args[i].getNum());
1003   }
1004   state->setFillColor(&color);
1005   builder->updateStyle(state);
1008 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
1009   GfxColor color;
1010   int i;
1012   state->setStrokePattern(NULL);
1013   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1014   for (i = 0; i < 3; ++i) {
1015     color.c[i] = dblToCol(args[i].getNum());
1016   }
1017   state->setStrokeColor(&color);
1018   builder->updateStyle(state);
1021 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1022   Object obj;
1023   GfxColorSpace *colorSpace;
1024   GfxColor color;
1026   state->setFillPattern(NULL);
1027   res->lookupColorSpace(args[0].getName(), &obj);
1028   if (obj.isNull()) {
1029     colorSpace = GfxColorSpace::parse(&args[0]);
1030   } else {
1031     colorSpace = GfxColorSpace::parse(&obj);
1032   }
1033   obj.free();
1034   if (colorSpace) {
1035     state->setFillColorSpace(colorSpace);
1036     colorSpace->getDefaultColor(&color);
1037     state->setFillColor(&color);
1038     builder->updateStyle(state);
1039   } else {
1040     error(getPos(), "Bad color space (fill)");
1041   }
1044 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1045   Object obj;
1046   GfxColorSpace *colorSpace;
1047   GfxColor color;
1049   state->setStrokePattern(NULL);
1050   res->lookupColorSpace(args[0].getName(), &obj);
1051   if (obj.isNull()) {
1052     colorSpace = GfxColorSpace::parse(&args[0]);
1053   } else {
1054     colorSpace = GfxColorSpace::parse(&obj);
1055   }
1056   obj.free();
1057   if (colorSpace) {
1058     state->setStrokeColorSpace(colorSpace);
1059     colorSpace->getDefaultColor(&color);
1060     state->setStrokeColor(&color);
1061     builder->updateStyle(state);
1062   } else {
1063     error(getPos(), "Bad color space (stroke)");
1064   }
1067 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1068   GfxColor color;
1069   int i;
1071   if (numArgs != state->getFillColorSpace()->getNComps()) {
1072     error(getPos(), "Incorrect number of arguments in 'sc' command");
1073     return;
1074   }
1075   state->setFillPattern(NULL);
1076   for (i = 0; i < numArgs; ++i) {
1077     color.c[i] = dblToCol(args[i].getNum());
1078   }
1079   state->setFillColor(&color);
1080   builder->updateStyle(state);
1083 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1084   GfxColor color;
1085   int i;
1087   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1088     error(getPos(), "Incorrect number of arguments in 'SC' command");
1089     return;
1090   }
1091   state->setStrokePattern(NULL);
1092   for (i = 0; i < numArgs; ++i) {
1093     color.c[i] = dblToCol(args[i].getNum());
1094   }
1095   state->setStrokeColor(&color);
1096   builder->updateStyle(state);
1099 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1100   GfxColor color;
1101   GfxPattern *pattern;
1102   int i;
1104   if (state->getFillColorSpace()->getMode() == csPattern) {
1105     if (numArgs > 1) {
1106       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1107           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1108                              ->getUnder()->getNComps()) {
1109         error(getPos(), "Incorrect number of arguments in 'scn' command");
1110         return;
1111       }
1112       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1113         if (args[i].isNum()) {
1114           color.c[i] = dblToCol(args[i].getNum());
1115         }
1116       }
1117       state->setFillColor(&color);
1118       builder->updateStyle(state);
1119     }
1120     if (args[numArgs-1].isName() &&
1121         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1122       state->setFillPattern(pattern);
1123     }
1125   } else {
1126     if (numArgs != state->getFillColorSpace()->getNComps()) {
1127       error(getPos(), "Incorrect number of arguments in 'scn' command");
1128       return;
1129     }
1130     state->setFillPattern(NULL);
1131     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1132       if (args[i].isNum()) {
1133         color.c[i] = dblToCol(args[i].getNum());
1134       }
1135     }
1136     state->setFillColor(&color);
1137     builder->updateStyle(state);
1138   }
1141 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1142   GfxColor color;
1143   GfxPattern *pattern;
1144   int i;
1146   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1147     if (numArgs > 1) {
1148       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1149                ->getUnder() ||
1150           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1151                              ->getUnder()->getNComps()) {
1152         error(getPos(), "Incorrect number of arguments in 'SCN' command");
1153         return;
1154       }
1155       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1156         if (args[i].isNum()) {
1157           color.c[i] = dblToCol(args[i].getNum());
1158         }
1159       }
1160       state->setStrokeColor(&color);
1161       builder->updateStyle(state);
1162     }
1163     if (args[numArgs-1].isName() &&
1164         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1165       state->setStrokePattern(pattern);
1166     }
1168   } else {
1169     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1170       error(getPos(), "Incorrect number of arguments in 'SCN' command");
1171       return;
1172     }
1173     state->setStrokePattern(NULL);
1174     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1175       if (args[i].isNum()) {
1176         color.c[i] = dblToCol(args[i].getNum());
1177       }
1178     }
1179     state->setStrokeColor(&color);
1180     builder->updateStyle(state);
1181   }
1184 //------------------------------------------------------------------------
1185 // path segment operators
1186 //------------------------------------------------------------------------
1188 void PdfParser::opMoveTo(Object args[], int numArgs) {
1189   state->moveTo(args[0].getNum(), args[1].getNum());
1192 void PdfParser::opLineTo(Object args[], int numArgs) {
1193   if (!state->isCurPt()) {
1194     error(getPos(), "No current point in lineto");
1195     return;
1196   }
1197   state->lineTo(args[0].getNum(), args[1].getNum());
1200 void PdfParser::opCurveTo(Object args[], int numArgs) {
1201   double x1, y1, x2, y2, x3, y3;
1203   if (!state->isCurPt()) {
1204     error(getPos(), "No current point in curveto");
1205     return;
1206   }
1207   x1 = args[0].getNum();
1208   y1 = args[1].getNum();
1209   x2 = args[2].getNum();
1210   y2 = args[3].getNum();
1211   x3 = args[4].getNum();
1212   y3 = args[5].getNum();
1213   state->curveTo(x1, y1, x2, y2, x3, y3);
1216 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1217   double x1, y1, x2, y2, x3, y3;
1219   if (!state->isCurPt()) {
1220     error(getPos(), "No current point in curveto1");
1221     return;
1222   }
1223   x1 = state->getCurX();
1224   y1 = state->getCurY();
1225   x2 = args[0].getNum();
1226   y2 = args[1].getNum();
1227   x3 = args[2].getNum();
1228   y3 = args[3].getNum();
1229   state->curveTo(x1, y1, x2, y2, x3, y3);
1232 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1233   double x1, y1, x2, y2, x3, y3;
1235   if (!state->isCurPt()) {
1236     error(getPos(), "No current point in curveto2");
1237     return;
1238   }
1239   x1 = args[0].getNum();
1240   y1 = args[1].getNum();
1241   x2 = args[2].getNum();
1242   y2 = args[3].getNum();
1243   x3 = x2;
1244   y3 = y2;
1245   state->curveTo(x1, y1, x2, y2, x3, y3);
1248 void PdfParser::opRectangle(Object args[], int numArgs) {
1249   double x, y, w, h;
1251   x = args[0].getNum();
1252   y = args[1].getNum();
1253   w = args[2].getNum();
1254   h = args[3].getNum();
1255   state->moveTo(x, y);
1256   state->lineTo(x + w, y);
1257   state->lineTo(x + w, y + h);
1258   state->lineTo(x, y + h);
1259   state->closePath();
1262 void PdfParser::opClosePath(Object args[], int numArgs) {
1263   if (!state->isCurPt()) {
1264     error(getPos(), "No current point in closepath");
1265     return;
1266   }
1267   state->closePath();
1270 //------------------------------------------------------------------------
1271 // path painting operators
1272 //------------------------------------------------------------------------
1274 void PdfParser::opEndPath(Object args[], int numArgs) {
1275   doEndPath();
1278 void PdfParser::opStroke(Object args[], int numArgs) {
1279   if (!state->isCurPt()) {
1280     //error(getPos(), "No path in stroke");
1281     return;
1282   }
1283   if (state->isPath()) {
1284     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1285         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1286           doPatternStrokeFallback();
1287     } else {
1288       builder->addPath(state, false, true);
1289     }
1290   }
1291   doEndPath();
1294 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1295   if (!state->isCurPt()) {
1296     //error(getPos(), "No path in closepath/stroke");
1297     return;
1298   }
1299   state->closePath();
1300   if (state->isPath()) {
1301     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1302         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1303       doPatternStrokeFallback();
1304     } else {
1305       builder->addPath(state, false, true);
1306     }
1307   }
1308   doEndPath();
1311 void PdfParser::opFill(Object args[], int numArgs) {
1312   if (!state->isCurPt()) {
1313     //error(getPos(), "No path in fill");
1314     return;
1315   }
1316   if (state->isPath()) {
1317     if (state->getFillColorSpace()->getMode() == csPattern &&
1318         !builder->isPatternTypeSupported(state->getFillPattern())) {
1319       doPatternFillFallback(gFalse);
1320     } else {
1321       builder->addPath(state, true, false);
1322     }
1323   }
1324   doEndPath();
1327 void PdfParser::opEOFill(Object args[], int numArgs) {
1328   if (!state->isCurPt()) {
1329     //error(getPos(), "No path in eofill");
1330     return;
1331   }
1332   if (state->isPath()) {
1333     if (state->getFillColorSpace()->getMode() == csPattern &&
1334         !builder->isPatternTypeSupported(state->getFillPattern())) {
1335       doPatternFillFallback(gTrue);
1336     } else {
1337       builder->addPath(state, true, false, true);
1338     }
1339   }
1340   doEndPath();
1343 void PdfParser::opFillStroke(Object args[], int numArgs) {
1344   if (!state->isCurPt()) {
1345     //error(getPos(), "No path in fill/stroke");
1346     return;
1347   }
1348   if (state->isPath()) {
1349     doFillAndStroke(gFalse);
1350   } else {
1351     builder->addPath(state, true, true);
1352   }
1353   doEndPath();
1356 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1357   if (!state->isCurPt()) {
1358     //error(getPos(), "No path in closepath/fill/stroke");
1359     return;
1360   }
1361   if (state->isPath()) {
1362     state->closePath();
1363     doFillAndStroke(gFalse);
1364   }
1365   doEndPath();
1368 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1369   if (!state->isCurPt()) {
1370     //error(getPos(), "No path in eofill/stroke");
1371     return;
1372   }
1373   if (state->isPath()) {
1374     doFillAndStroke(gTrue);
1375   }
1376   doEndPath();
1379 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1380   if (!state->isCurPt()) {
1381     //error(getPos(), "No path in closepath/eofill/stroke");
1382     return;
1383   }
1384   if (state->isPath()) {
1385     state->closePath();
1386     doFillAndStroke(gTrue);
1387   }
1388   doEndPath();
1391 void PdfParser::doFillAndStroke(GBool eoFill) {
1392     GBool fillOk = gTrue, strokeOk = gTrue;
1393     if (state->getFillColorSpace()->getMode() == csPattern &&
1394         !builder->isPatternTypeSupported(state->getFillPattern())) {
1395         fillOk = gFalse;
1396     }
1397     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1398         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1399         strokeOk = gFalse;
1400     }
1401     if (fillOk && strokeOk) {
1402         builder->addPath(state, true, true, eoFill);
1403     } else {
1404         doPatternFillFallback(eoFill);
1405         doPatternStrokeFallback();
1406     }
1409 void PdfParser::doPatternFillFallback(GBool eoFill) {
1410   GfxPattern *pattern;
1412   if (!(pattern = state->getFillPattern())) {
1413     return;
1414   }
1415   switch (pattern->getType()) {
1416   case 1:
1417     break;
1418   case 2:
1419     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1420     break;
1421   default:
1422     error(getPos(), "Unimplemented pattern type (%d) in fill",
1423           pattern->getType());
1424     break;
1425   }
1428 void PdfParser::doPatternStrokeFallback() {
1429   GfxPattern *pattern;
1431   if (!(pattern = state->getStrokePattern())) {
1432     return;
1433   }
1434   switch (pattern->getType()) {
1435   case 1:
1436     break;
1437   case 2:
1438     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1439     break;
1440   default:
1441     error(getPos(), "Unimplemented pattern type (%d) in stroke",
1442           pattern->getType());
1443     break;
1444   }
1447 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1448                                              GBool stroke, GBool eoFill) {
1449   GfxShading *shading;
1450   GfxPath *savedPath;
1451   double *ctm, *btm, *ptm;
1452   double m[6], ictm[6], m1[6];
1453   double xMin, yMin, xMax, yMax;
1454   double det;
1456   shading = sPat->getShading();
1458   // save current graphics state
1459   savedPath = state->getPath()->copy();
1460   saveState();
1462   // clip to bbox
1463   if (0 ){//shading->getHasBBox()) {
1464     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1465     state->moveTo(xMin, yMin);
1466     state->lineTo(xMax, yMin);
1467     state->lineTo(xMax, yMax);
1468     state->lineTo(xMin, yMax);
1469     state->closePath();
1470     state->clip();
1471     //builder->clip(state);
1472     state->setPath(savedPath->copy());
1473   }
1475   // clip to current path
1476   if (stroke) {
1477     state->clipToStrokePath();
1478     //out->clipToStrokePath(state);
1479   } else {
1480     state->clip();
1481     if (eoFill) {
1482       builder->clip(state, true);
1483     } else {
1484       builder->clip(state);
1485     }
1486   }
1488   // set the color space
1489   state->setFillColorSpace(shading->getColorSpace()->copy());
1491   // background color fill
1492   if (shading->getHasBackground()) {
1493     state->setFillColor(shading->getBackground());
1494     builder->addPath(state, true, false);
1495   }
1496   state->clearPath();
1498   // construct a (pattern space) -> (current space) transform matrix
1499   ctm = state->getCTM();
1500   btm = baseMatrix;
1501   ptm = sPat->getMatrix();
1502   // iCTM = invert CTM
1503   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1504   ictm[0] = ctm[3] * det;
1505   ictm[1] = -ctm[1] * det;
1506   ictm[2] = -ctm[2] * det;
1507   ictm[3] = ctm[0] * det;
1508   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1509   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1510   // m1 = PTM * BTM = PTM * base transform matrix
1511   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1512   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1513   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1514   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1515   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1516   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1517   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1518   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1519   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1520   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1521   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1522   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1523   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1525   // set the new matrix
1526   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1527   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1529   // do shading type-specific operations
1530   switch (shading->getType()) {
1531   case 1:
1532     doFunctionShFill((GfxFunctionShading *)shading);
1533     break;
1534   case 2:
1535   case 3:
1536     // no need to implement these
1537     break;
1538   case 4:
1539   case 5:
1540     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1541     break;
1542   case 6:
1543   case 7:
1544     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1545     break;
1546   }
1548   // restore graphics state
1549   restoreState();
1550   state->setPath(savedPath);
1553 void PdfParser::opShFill(Object args[], int numArgs) {
1554   GfxShading *shading;
1555   GfxPath *savedPath;
1556   double xMin, yMin, xMax, yMax;
1558   if (!(shading = res->lookupShading(args[0].getName()))) {
1559     return;
1560   }
1562   // save current graphics state
1563   savedPath = state->getPath()->copy();
1564   saveState();
1566   // clip to bbox
1567   if (shading->getHasBBox()) {
1568     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1569     state->moveTo(xMin, yMin);
1570     state->lineTo(xMax, yMin);
1571     state->lineTo(xMax, yMax);
1572     state->lineTo(xMin, yMax);
1573     state->closePath();
1574     state->clip();
1575     builder->clip(state);
1576     state->clearPath();
1577   }
1579   // set the color space
1580   state->setFillColorSpace(shading->getColorSpace()->copy());
1582   // do shading type-specific operations
1583   switch (shading->getType()) {
1584   case 1:
1585     doFunctionShFill((GfxFunctionShading *)shading);
1586     break;
1587   case 2:
1588   case 3:
1589     // no need to implement these
1590     break;
1591   case 4:
1592   case 5:
1593     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1594     break;
1595   case 6:
1596   case 7:
1597     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1598     break;
1599   }
1601   // restore graphics state
1602   restoreState();
1603   state->setPath(savedPath);
1605   delete shading;
1608 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1609   double x0, y0, x1, y1;
1610   GfxColor colors[4];
1612   shading->getDomain(&x0, &y0, &x1, &y1);
1613   shading->getColor(x0, y0, &colors[0]);
1614   shading->getColor(x0, y1, &colors[1]);
1615   shading->getColor(x1, y0, &colors[2]);
1616   shading->getColor(x1, y1, &colors[3]);
1617   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1620 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1621                             double x0, double y0,
1622                             double x1, double y1,
1623                             GfxColor *colors, int depth) {
1624   GfxColor fillColor;
1625   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1626   GfxColor colors2[4];
1627   double *matrix;
1628   double xM, yM;
1629   int nComps, i, j;
1631   nComps = shading->getColorSpace()->getNComps();
1632   matrix = shading->getMatrix();
1634   // compare the four corner colors
1635   for (i = 0; i < 4; ++i) {
1636     for (j = 0; j < nComps; ++j) {
1637       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1638         break;
1639       }
1640     }
1641     if (j < nComps) {
1642       break;
1643     }
1644   }
1646   // center of the rectangle
1647   xM = 0.5 * (x0 + x1);
1648   yM = 0.5 * (y0 + y1);
1650   // the four corner colors are close (or we hit the recursive limit)
1651   // -- fill the rectangle; but require at least one subdivision
1652   // (depth==0) to avoid problems when the four outer corners of the
1653   // shaded region are the same color
1654   if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1656     // use the center color
1657     shading->getColor(xM, yM, &fillColor);
1658     state->setFillColor(&fillColor);
1660     // fill the rectangle
1661     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1662                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1663     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1664                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1665     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1666                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1667     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1668                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1669     state->closePath();
1670     builder->addPath(state, true, false);
1671     state->clearPath();
1673   // the four corner colors are not close enough -- subdivide the
1674   // rectangle
1675   } else {
1677     // colors[0]       colorM0       colors[2]
1678     //   (x0,y0)       (xM,y0)       (x1,y0)
1679     //         +----------+----------+
1680     //         |          |          |
1681     //         |    UL    |    UR    |
1682     // color0M |       colorMM       | color1M
1683     // (x0,yM) +----------+----------+ (x1,yM)
1684     //         |       (xM,yM)       |
1685     //         |    LL    |    LR    |
1686     //         |          |          |
1687     //         +----------+----------+
1688     // colors[1]       colorM1       colors[3]
1689     //   (x0,y1)       (xM,y1)       (x1,y1)
1691     shading->getColor(x0, yM, &color0M);
1692     shading->getColor(x1, yM, &color1M);
1693     shading->getColor(xM, y0, &colorM0);
1694     shading->getColor(xM, y1, &colorM1);
1695     shading->getColor(xM, yM, &colorMM);
1697     // upper-left sub-rectangle
1698     colors2[0] = colors[0];
1699     colors2[1] = color0M;
1700     colors2[2] = colorM0;
1701     colors2[3] = colorMM;
1702     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1703     
1704     // lower-left sub-rectangle
1705     colors2[0] = color0M;
1706     colors2[1] = colors[1];
1707     colors2[2] = colorMM;
1708     colors2[3] = colorM1;
1709     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1710     
1711     // upper-right sub-rectangle
1712     colors2[0] = colorM0;
1713     colors2[1] = colorMM;
1714     colors2[2] = colors[2];
1715     colors2[3] = color1M;
1716     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1718     // lower-right sub-rectangle
1719     colors2[0] = colorMM;
1720     colors2[1] = colorM1;
1721     colors2[2] = color1M;
1722     colors2[3] = colors[3];
1723     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1724   }
1727 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1728   double x0, y0, x1, y1, x2, y2;
1729   GfxColor color0, color1, color2;
1730   int i;
1732   for (i = 0; i < shading->getNTriangles(); ++i) {
1733     shading->getTriangle(i, &x0, &y0, &color0,
1734                          &x1, &y1, &color1,
1735                          &x2, &y2, &color2);
1736     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1737                         shading->getColorSpace()->getNComps(), 0);
1738   }
1741 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1742                               double x1, double y1, GfxColor *color1,
1743                               double x2, double y2, GfxColor *color2,
1744                               int nComps, int depth) {
1745   double x01, y01, x12, y12, x20, y20;
1746   GfxColor color01, color12, color20;
1747   int i;
1749   for (i = 0; i < nComps; ++i) {
1750     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1751         abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1752       break;
1753     }
1754   }
1755   if (i == nComps || depth == gouraudMaxDepth) {
1756     state->setFillColor(color0);
1757     state->moveTo(x0, y0);
1758     state->lineTo(x1, y1);
1759     state->lineTo(x2, y2);
1760     state->closePath();
1761     builder->addPath(state, true, false);
1762     state->clearPath();
1763   } else {
1764     x01 = 0.5 * (x0 + x1);
1765     y01 = 0.5 * (y0 + y1);
1766     x12 = 0.5 * (x1 + x2);
1767     y12 = 0.5 * (y1 + y2);
1768     x20 = 0.5 * (x2 + x0);
1769     y20 = 0.5 * (y2 + y0);
1770     //~ if the shading has a Function, this should interpolate on the
1771     //~ function parameter, not on the color components
1772     for (i = 0; i < nComps; ++i) {
1773       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1774       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1775       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1776     }
1777     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1778                         x20, y20, &color20, nComps, depth + 1);
1779     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1780                         x12, y12, &color12, nComps, depth + 1);
1781     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1782                         x20, y20, &color20, nComps, depth + 1);
1783     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1784                         x2, y2, color2, nComps, depth + 1);
1785   }
1788 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1789   int start, i;
1791   if (shading->getNPatches() > 128) {
1792     start = 3;
1793   } else if (shading->getNPatches() > 64) {
1794     start = 2;
1795   } else if (shading->getNPatches() > 16) {
1796     start = 1;
1797   } else {
1798     start = 0;
1799   }
1800   for (i = 0; i < shading->getNPatches(); ++i) {
1801     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1802               start);
1803   }
1806 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1807   GfxPatch patch00, patch01, patch10, patch11;
1808   double xx[4][8], yy[4][8];
1809   double xxm, yym;
1810   int i;
1812   for (i = 0; i < nComps; ++i) {
1813     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1814           > patchColorDelta ||
1815         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1816           > patchColorDelta ||
1817         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1818           > patchColorDelta ||
1819         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1820           > patchColorDelta) {
1821       break;
1822     }
1823   }
1824   if (i == nComps || depth == patchMaxDepth) {
1825     state->setFillColor(&patch->color[0][0]);
1826     state->moveTo(patch->x[0][0], patch->y[0][0]);
1827     state->curveTo(patch->x[0][1], patch->y[0][1],
1828                    patch->x[0][2], patch->y[0][2],
1829                    patch->x[0][3], patch->y[0][3]);
1830     state->curveTo(patch->x[1][3], patch->y[1][3],
1831                    patch->x[2][3], patch->y[2][3],
1832                    patch->x[3][3], patch->y[3][3]);
1833     state->curveTo(patch->x[3][2], patch->y[3][2],
1834                    patch->x[3][1], patch->y[3][1],
1835                    patch->x[3][0], patch->y[3][0]);
1836     state->curveTo(patch->x[2][0], patch->y[2][0],
1837                    patch->x[1][0], patch->y[1][0],
1838                    patch->x[0][0], patch->y[0][0]);
1839     state->closePath();
1840     builder->addPath(state, true, false);
1841     state->clearPath();
1842   } else {
1843     for (i = 0; i < 4; ++i) {
1844       xx[i][0] = patch->x[i][0];
1845       yy[i][0] = patch->y[i][0];
1846       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1847       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1848       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1849       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1850       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1851       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1852       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1853       yy[i][2] = 0.5 * (yy[i][1] + yym);
1854       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1855       yy[i][5] = 0.5 * (yym + yy[i][6]);
1856       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1857       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1858       xx[i][7] = patch->x[i][3];
1859       yy[i][7] = patch->y[i][3];
1860     }
1861     for (i = 0; i < 4; ++i) {
1862       patch00.x[0][i] = xx[0][i];
1863       patch00.y[0][i] = yy[0][i];
1864       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1865       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1866       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1867       yym = 0.5 * (yy[1][i] + yy[2][i]);
1868       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1869       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1870       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1871       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1872       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1873       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1874       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1875       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1876       patch10.x[0][i] = patch00.x[3][i];
1877       patch10.y[0][i] = patch00.y[3][i];
1878       patch10.x[3][i] = xx[3][i];
1879       patch10.y[3][i] = yy[3][i];
1880     }
1881     for (i = 4; i < 8; ++i) {
1882       patch01.x[0][i-4] = xx[0][i];
1883       patch01.y[0][i-4] = yy[0][i];
1884       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1885       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1886       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1887       yym = 0.5 * (yy[1][i] + yy[2][i]);
1888       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1889       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1890       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1891       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1892       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1893       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1894       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1895       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1896       patch11.x[0][i-4] = patch01.x[3][i-4];
1897       patch11.y[0][i-4] = patch01.y[3][i-4];
1898       patch11.x[3][i-4] = xx[3][i];
1899       patch11.y[3][i-4] = yy[3][i];
1900     }
1901     //~ if the shading has a Function, this should interpolate on the
1902     //~ function parameter, not on the color components
1903     for (i = 0; i < nComps; ++i) {
1904       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1905       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1906                                   patch->color[0][1].c[i]) / 2;
1907       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1908       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1909       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1910                                   patch->color[1][1].c[i]) / 2;
1911       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1912       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1913       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1914                                   patch->color[1][0].c[i]) / 2;
1915       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1916       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1917       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1918                                   patch->color[0][0].c[i]) / 2;
1919       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1920       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1921                                   patch01.color[1][1].c[i]) / 2;
1922       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1923       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1924       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1925     }
1926     fillPatch(&patch00, nComps, depth + 1);
1927     fillPatch(&patch10, nComps, depth + 1);
1928     fillPatch(&patch01, nComps, depth + 1);
1929     fillPatch(&patch11, nComps, depth + 1);
1930   }
1933 void PdfParser::doEndPath() {
1934   if (state->isCurPt() && clip != clipNone) {
1935     state->clip();
1936     if (clip == clipNormal) {
1937       builder->clip(state);
1938     } else {
1939       builder->clip(state, true);
1940     }
1941   }
1942   clip = clipNone;
1943   state->clearPath();
1946 //------------------------------------------------------------------------
1947 // path clipping operators
1948 //------------------------------------------------------------------------
1950 void PdfParser::opClip(Object args[], int numArgs) {
1951   clip = clipNormal;
1954 void PdfParser::opEOClip(Object args[], int numArgs) {
1955   clip = clipEO;
1958 //------------------------------------------------------------------------
1959 // text object operators
1960 //------------------------------------------------------------------------
1962 void PdfParser::opBeginText(Object args[], int numArgs) {
1963   state->setTextMat(1, 0, 0, 1, 0, 0);
1964   state->textMoveTo(0, 0);
1965   builder->updateTextPosition(0.0, 0.0);
1966   fontChanged = gTrue;
1967   builder->beginTextObject(state);
1970 void PdfParser::opEndText(Object args[], int numArgs) {
1971   builder->endTextObject(state);
1974 //------------------------------------------------------------------------
1975 // text state operators
1976 //------------------------------------------------------------------------
1978 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
1979   state->setCharSpace(args[0].getNum());
1982 void PdfParser::opSetFont(Object args[], int numArgs) {
1983   GfxFont *font;
1985   if (!(font = res->lookupFont(args[0].getName()))) {
1986     return;
1987   }
1988   if (printCommands) {
1989     printf("  font: tag=%s name='%s' %g\n",
1990            font->getTag()->getCString(),
1991            font->getName() ? font->getName()->getCString() : "???",
1992            args[1].getNum());
1993     fflush(stdout);
1994   }
1996   font->incRefCnt();
1997   state->setFont(font, args[1].getNum());
1998   fontChanged = gTrue;
2001 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2002   state->setLeading(args[0].getNum());
2005 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2006   state->setRender(args[0].getInt());
2007   builder->updateStyle(state);
2010 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2011   state->setRise(args[0].getNum());
2014 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2015   state->setWordSpace(args[0].getNum());
2018 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2019   state->setHorizScaling(args[0].getNum());
2020   fontChanged = gTrue;
2023 //------------------------------------------------------------------------
2024 // text positioning operators
2025 //------------------------------------------------------------------------
2027 void PdfParser::opTextMove(Object args[], int numArgs) {
2028   double tx, ty;
2030   tx = state->getLineX() + args[0].getNum();
2031   ty = state->getLineY() + args[1].getNum();
2032   state->textMoveTo(tx, ty);
2033   builder->updateTextPosition(tx, ty);
2036 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2037   double tx, ty;
2039   tx = state->getLineX() + args[0].getNum();
2040   ty = args[1].getNum();
2041   state->setLeading(-ty);
2042   ty += state->getLineY();
2043   state->textMoveTo(tx, ty);
2044   builder->updateTextPosition(tx, ty);
2047 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2048   state->setTextMat(args[0].getNum(), args[1].getNum(),
2049                     args[2].getNum(), args[3].getNum(),
2050                     args[4].getNum(), args[5].getNum());
2051   state->textMoveTo(0, 0);
2052   builder->updateTextMatrix(state);
2053   builder->updateTextPosition(0.0, 0.0);
2054   fontChanged = gTrue;
2057 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2058   double tx, ty;
2060   tx = state->getLineX();
2061   ty = state->getLineY() - state->getLeading();
2062   state->textMoveTo(tx, ty);
2063   builder->updateTextPosition(tx, ty);
2066 //------------------------------------------------------------------------
2067 // text string operators
2068 //------------------------------------------------------------------------
2070 void PdfParser::opShowText(Object args[], int numArgs) {
2071   if (!state->getFont()) {
2072     error(getPos(), "No font in show");
2073     return;
2074   }
2075   if (fontChanged) {
2076     builder->updateFont(state);
2077     fontChanged = gFalse;
2078   }
2079   doShowText(args[0].getString());
2082 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2083   double tx, ty;
2085   if (!state->getFont()) {
2086     error(getPos(), "No font in move/show");
2087     return;
2088   }
2089   if (fontChanged) {
2090     builder->updateFont(state);
2091     fontChanged = gFalse;
2092   }
2093   tx = state->getLineX();
2094   ty = state->getLineY() - state->getLeading();
2095   state->textMoveTo(tx, ty);
2096   builder->updateTextPosition(tx, ty);
2097   doShowText(args[0].getString());
2100 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2101   double tx, ty;
2103   if (!state->getFont()) {
2104     error(getPos(), "No font in move/set/show");
2105     return;
2106   }
2107   if (fontChanged) {
2108     builder->updateFont(state);
2109     fontChanged = gFalse;
2110   }
2111   state->setWordSpace(args[0].getNum());
2112   state->setCharSpace(args[1].getNum());
2113   tx = state->getLineX();
2114   ty = state->getLineY() - state->getLeading();
2115   state->textMoveTo(tx, ty);
2116   builder->updateTextPosition(tx, ty);
2117   doShowText(args[2].getString());
2120 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2121   Array *a;
2122   Object obj;
2123   int wMode;
2124   int i;
2126   if (!state->getFont()) {
2127     error(getPos(), "No font in show/space");
2128     return;
2129   }
2130   if (fontChanged) {
2131     builder->updateFont(state);
2132     fontChanged = gFalse;
2133   }
2134   wMode = state->getFont()->getWMode();
2135   a = args[0].getArray();
2136   for (i = 0; i < a->getLength(); ++i) {
2137     a->get(i, &obj);
2138     if (obj.isNum()) {
2139       // this uses the absolute value of the font size to match
2140       // Acrobat's behavior
2141       if (wMode) {
2142         state->textShift(0, -obj.getNum() * 0.001 *
2143                             fabs(state->getFontSize()));
2144       } else {
2145         state->textShift(-obj.getNum() * 0.001 *
2146                          fabs(state->getFontSize()), 0);
2147       }
2148       builder->updateTextShift(state, obj.getNum());
2149     } else if (obj.isString()) {
2150       doShowText(obj.getString());
2151     } else {
2152       error(getPos(), "Element of show/space array must be number or string");
2153     }
2154     obj.free();
2155   }
2158 void PdfParser::doShowText(GooString *s) {
2159   GfxFont *font;
2160   int wMode;
2161   double riseX, riseY;
2162   CharCode code;
2163   Unicode u[8];
2164   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2165   double originX, originY, tOriginX, tOriginY;
2166   double oldCTM[6], newCTM[6];
2167   double *mat;
2168   Object charProc;
2169   Dict *resDict;
2170   Parser *oldParser;
2171   char *p;
2172   int len, n, uLen, nChars, nSpaces, i;
2174   font = state->getFont();
2175   wMode = font->getWMode();
2177   builder->beginString(state, s);
2179   // handle a Type 3 char
2180   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2181     mat = state->getCTM();
2182     for (i = 0; i < 6; ++i) {
2183       oldCTM[i] = mat[i];
2184     }
2185     mat = state->getTextMat();
2186     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2187     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2188     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2189     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2190     mat = font->getFontMatrix();
2191     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2192     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2193     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2194     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2195     newCTM[0] *= state->getFontSize();
2196     newCTM[1] *= state->getFontSize();
2197     newCTM[2] *= state->getFontSize();
2198     newCTM[3] *= state->getFontSize();
2199     newCTM[0] *= state->getHorizScaling();
2200     newCTM[2] *= state->getHorizScaling();
2201     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2202     curX = state->getCurX();
2203     curY = state->getCurY();
2204     lineX = state->getLineX();
2205     lineY = state->getLineY();
2206     oldParser = parser;
2207     p = s->getCString();
2208     len = s->getLength();
2209     while (len > 0) {
2210       n = font->getNextChar(p, len, &code,
2211                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2212                             &dx, &dy, &originX, &originY);
2213       dx = dx * state->getFontSize() + state->getCharSpace();
2214       if (n == 1 && *p == ' ') {
2215         dx += state->getWordSpace();
2216       }
2217       dx *= state->getHorizScaling();
2218       dy *= state->getFontSize();
2219       state->textTransformDelta(dx, dy, &tdx, &tdy);
2220       state->transform(curX + riseX, curY + riseY, &x, &y);
2221       saveState();
2222       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2223       //~ the CTM concat values here are wrong (but never used)
2224       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2225       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2226                                code, u, uLen)) {*/
2227         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2228         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2229           pushResources(resDict);
2230         }
2231         if (charProc.isStream()) {
2232           //parse(&charProc, gFalse); // TODO: parse into SVG font
2233         } else {
2234           error(getPos(), "Missing or bad Type3 CharProc entry");
2235         }
2236         //out->endType3Char(state);
2237         if (resDict) {
2238           popResources();
2239         }
2240         charProc.free();
2241       }
2242       restoreState();
2243       // GfxState::restore() does *not* restore the current position,
2244       // so we deal with it here using (curX, curY) and (lineX, lineY)
2245       curX += tdx;
2246       curY += tdy;
2247       state->moveTo(curX, curY);
2248       state->textSetPos(lineX, lineY);
2249       p += n;
2250       len -= n;
2251     }
2252     parser = oldParser;
2254   } else {
2255     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2256     p = s->getCString();
2257     len = s->getLength();
2258     while (len > 0) {
2259       n = font->getNextChar(p, len, &code,
2260                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2261                             &dx, &dy, &originX, &originY);
2262       
2263       if (wMode) {
2264         dx *= state->getFontSize();
2265         dy = dy * state->getFontSize() + state->getCharSpace();
2266         if (n == 1 && *p == ' ') {
2267           dy += state->getWordSpace();
2268         }
2269       } else {
2270         dx = dx * state->getFontSize() + state->getCharSpace();
2271         if (n == 1 && *p == ' ') {
2272           dx += state->getWordSpace();
2273         }
2274         dx *= state->getHorizScaling();
2275         dy *= state->getFontSize();
2276       }
2277       state->textTransformDelta(dx, dy, &tdx, &tdy);
2278       originX *= state->getFontSize();
2279       originY *= state->getFontSize();
2280       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2281       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2282                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2283       state->shift(tdx, tdy);
2284       p += n;
2285       len -= n;
2286     }
2287   }
2289   builder->endString(state);
2292 //------------------------------------------------------------------------
2293 // XObject operators
2294 //------------------------------------------------------------------------
2296 void PdfParser::opXObject(Object args[], int numArgs) {
2297   char *name;
2298   Object obj1, obj2, obj3, refObj;
2300   name = args[0].getName();
2301   if (!res->lookupXObject(name, &obj1)) {
2302     return;
2303   }
2304   if (!obj1.isStream()) {
2305     error(getPos(), "XObject '%s' is wrong type", name);
2306     obj1.free();
2307     return;
2308   }
2309   obj1.streamGetDict()->lookup("Subtype", &obj2);
2310   if (obj2.isName("Image")) {
2311     res->lookupXObjectNF(name, &refObj);
2312     doImage(&refObj, obj1.getStream(), gFalse);
2313     refObj.free();
2314   } else if (obj2.isName("Form")) {
2315     doForm(&obj1);
2316   } else if (obj2.isName("PS")) {
2317     obj1.streamGetDict()->lookup("Level1", &obj3);
2318 /*    out->psXObject(obj1.getStream(),
2319                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2320   } else if (obj2.isName()) {
2321     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2322   } else {
2323     error(getPos(), "XObject subtype is missing or wrong type");
2324   }
2325   obj2.free();
2326   obj1.free();
2329 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2330   Dict *dict, *maskDict;
2331   int width, height;
2332   int bits, maskBits;
2333   StreamColorSpaceMode csMode;
2334   GBool mask;
2335   GBool invert;
2336   GfxColorSpace *colorSpace, *maskColorSpace;
2337   GfxImageColorMap *colorMap, *maskColorMap;
2338   Object maskObj, smaskObj;
2339   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2340   int maskColors[2*gfxColorMaxComps];
2341   int maskWidth, maskHeight;
2342   GBool maskInvert;
2343   Stream *maskStr;
2344   Object obj1, obj2;
2345   int i;
2347   // get info from the stream
2348   bits = 0;
2349   csMode = streamCSNone;
2350   str->getImageParams(&bits, &csMode);
2352   // get stream dict
2353   dict = str->getDict();
2355   // get size
2356   dict->lookup("Width", &obj1);
2357   if (obj1.isNull()) {
2358     obj1.free();
2359     dict->lookup("W", &obj1);
2360   }
2361   if (obj1.isInt())
2362     width = obj1.getInt();
2363   else if (obj1.isReal())
2364     width = (int)obj1.getReal();
2365   else
2366     goto err2;
2367   obj1.free();
2368   dict->lookup("Height", &obj1);
2369   if (obj1.isNull()) {
2370     obj1.free();
2371     dict->lookup("H", &obj1);
2372   }
2373   if (obj1.isInt())
2374     height = obj1.getInt();
2375   else if (obj1.isReal())
2376     height = (int)obj1.getReal();
2377   else
2378     goto err2;
2379   obj1.free();
2381   // image or mask?
2382   dict->lookup("ImageMask", &obj1);
2383   if (obj1.isNull()) {
2384     obj1.free();
2385     dict->lookup("IM", &obj1);
2386   }
2387   mask = gFalse;
2388   if (obj1.isBool())
2389     mask = obj1.getBool();
2390   else if (!obj1.isNull())
2391     goto err2;
2392   obj1.free();
2394   // bit depth
2395   if (bits == 0) {
2396     dict->lookup("BitsPerComponent", &obj1);
2397     if (obj1.isNull()) {
2398       obj1.free();
2399       dict->lookup("BPC", &obj1);
2400     }
2401     if (obj1.isInt()) {
2402       bits = obj1.getInt();
2403     } else if (mask) {
2404       bits = 1;
2405     } else {
2406       goto err2;
2407     }
2408     obj1.free();
2409   }
2411   // display a mask
2412   if (mask) {
2414     // check for inverted mask
2415     if (bits != 1)
2416       goto err1;
2417     invert = gFalse;
2418     dict->lookup("Decode", &obj1);
2419     if (obj1.isNull()) {
2420       obj1.free();
2421       dict->lookup("D", &obj1);
2422     }
2423     if (obj1.isArray()) {
2424       obj1.arrayGet(0, &obj2);
2425       if (obj2.isInt() && obj2.getInt() == 1)
2426         invert = gTrue;
2427       obj2.free();
2428     } else if (!obj1.isNull()) {
2429       goto err2;
2430     }
2431     obj1.free();
2433     // draw it
2434     //out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2436   } else {
2438     // get color space and color map
2439     dict->lookup("ColorSpace", &obj1);
2440     if (obj1.isNull()) {
2441       obj1.free();
2442       dict->lookup("CS", &obj1);
2443     }
2444     if (obj1.isName()) {
2445       res->lookupColorSpace(obj1.getName(), &obj2);
2446       if (!obj2.isNull()) {
2447         obj1.free();
2448         obj1 = obj2;
2449       } else {
2450         obj2.free();
2451       }
2452     }
2453     if (!obj1.isNull()) {
2454       colorSpace = GfxColorSpace::parse(&obj1);
2455     } else if (csMode == streamCSDeviceGray) {
2456       colorSpace = new GfxDeviceGrayColorSpace();
2457     } else if (csMode == streamCSDeviceRGB) {
2458       colorSpace = new GfxDeviceRGBColorSpace();
2459     } else if (csMode == streamCSDeviceCMYK) {
2460       colorSpace = new GfxDeviceCMYKColorSpace();
2461     } else {
2462       colorSpace = NULL;
2463     }
2464     obj1.free();
2465     if (!colorSpace) {
2466       goto err1;
2467     }
2468     dict->lookup("Decode", &obj1);
2469     if (obj1.isNull()) {
2470       obj1.free();
2471       dict->lookup("D", &obj1);
2472     }
2473     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2474     obj1.free();
2475     if (!colorMap->isOk()) {
2476       delete colorMap;
2477       goto err1;
2478     }
2480     // get the mask
2481     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2482     maskStr = NULL; // make gcc happy
2483     maskWidth = maskHeight = 0; // make gcc happy
2484     maskInvert = gFalse; // make gcc happy
2485     maskColorMap = NULL; // make gcc happy
2486     dict->lookup("Mask", &maskObj);
2487     dict->lookup("SMask", &smaskObj);
2488     if (smaskObj.isStream()) {
2489       // soft mask
2490       if (inlineImg) {
2491         goto err1;
2492       }
2493       maskStr = smaskObj.getStream();
2494       maskDict = smaskObj.streamGetDict();
2495       maskDict->lookup("Width", &obj1);
2496       if (obj1.isNull()) {
2497         obj1.free();
2498         maskDict->lookup("W", &obj1);
2499       }
2500       if (!obj1.isInt()) {
2501         goto err2;
2502       }
2503       maskWidth = obj1.getInt();
2504       obj1.free();
2505       maskDict->lookup("Height", &obj1);
2506       if (obj1.isNull()) {
2507         obj1.free();
2508         maskDict->lookup("H", &obj1);
2509       }
2510       if (!obj1.isInt()) {
2511         goto err2;
2512       }
2513       maskHeight = obj1.getInt();
2514       obj1.free();
2515       maskDict->lookup("BitsPerComponent", &obj1);
2516       if (obj1.isNull()) {
2517         obj1.free();
2518         maskDict->lookup("BPC", &obj1);
2519       }
2520       if (!obj1.isInt()) {
2521         goto err2;
2522       }
2523       maskBits = obj1.getInt();
2524       obj1.free();
2525       maskDict->lookup("ColorSpace", &obj1);
2526       if (obj1.isNull()) {
2527         obj1.free();
2528         maskDict->lookup("CS", &obj1);
2529       }
2530       if (obj1.isName()) {
2531         res->lookupColorSpace(obj1.getName(), &obj2);
2532         if (!obj2.isNull()) {
2533           obj1.free();
2534           obj1 = obj2;
2535         } else {
2536           obj2.free();
2537         }
2538       }
2539       maskColorSpace = GfxColorSpace::parse(&obj1);
2540       obj1.free();
2541       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2542         goto err1;
2543       }
2544       maskDict->lookup("Decode", &obj1);
2545       if (obj1.isNull()) {
2546         obj1.free();
2547         maskDict->lookup("D", &obj1);
2548       }
2549       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2550       obj1.free();
2551       if (!maskColorMap->isOk()) {
2552         delete maskColorMap;
2553         goto err1;
2554       }
2555       //~ handle the Matte entry
2556       haveSoftMask = gTrue;
2557     } else if (maskObj.isArray()) {
2558       // color key mask
2559       for (i = 0;
2560            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2561            ++i) {
2562         maskObj.arrayGet(i, &obj1);
2563         maskColors[i] = obj1.getInt();
2564         obj1.free();
2565       }
2566       haveColorKeyMask = gTrue;
2567     } else if (maskObj.isStream()) {
2568       // explicit mask
2569       if (inlineImg) {
2570         goto err1;
2571       }
2572       maskStr = maskObj.getStream();
2573       maskDict = maskObj.streamGetDict();
2574       maskDict->lookup("Width", &obj1);
2575       if (obj1.isNull()) {
2576         obj1.free();
2577         maskDict->lookup("W", &obj1);
2578       }
2579       if (!obj1.isInt()) {
2580         goto err2;
2581       }
2582       maskWidth = obj1.getInt();
2583       obj1.free();
2584       maskDict->lookup("Height", &obj1);
2585       if (obj1.isNull()) {
2586         obj1.free();
2587         maskDict->lookup("H", &obj1);
2588       }
2589       if (!obj1.isInt()) {
2590         goto err2;
2591       }
2592       maskHeight = obj1.getInt();
2593       obj1.free();
2594       maskDict->lookup("ImageMask", &obj1);
2595       if (obj1.isNull()) {
2596         obj1.free();
2597         maskDict->lookup("IM", &obj1);
2598       }
2599       if (!obj1.isBool() || !obj1.getBool()) {
2600         goto err2;
2601       }
2602       obj1.free();
2603       maskInvert = gFalse;
2604       maskDict->lookup("Decode", &obj1);
2605       if (obj1.isNull()) {
2606         obj1.free();
2607         maskDict->lookup("D", &obj1);
2608       }
2609       if (obj1.isArray()) {
2610         obj1.arrayGet(0, &obj2);
2611         if (obj2.isInt() && obj2.getInt() == 1) {
2612           maskInvert = gTrue;
2613         }
2614         obj2.free();
2615       } else if (!obj1.isNull()) {
2616         goto err2;
2617       }
2618       obj1.free();
2619       haveExplicitMask = gTrue;
2620     }
2622     // draw it
2623     if (haveSoftMask) {
2624 /*      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
2625                                maskStr, maskWidth, maskHeight, maskColorMap);*/
2626       delete maskColorMap;
2627     } else if (haveExplicitMask) {
2628 /*      out->drawMaskedImage(state, ref, str, width, height, colorMap,
2629                            maskStr, maskWidth, maskHeight, maskInvert);*/
2630     } else {
2631       builder->addImage(state, str, width, height, colorMap,
2632                         haveColorKeyMask ? maskColors : (int *)NULL);
2633     }
2634     delete colorMap;
2636     maskObj.free();
2637     smaskObj.free();
2638   }
2640   return;
2642  err2:
2643   obj1.free();
2644  err1:
2645   error(getPos(), "Bad image parameters");
2648 void PdfParser::doForm(Object *str) {
2649   Dict *dict;
2650   GBool transpGroup, isolated, knockout;
2651   GfxColorSpace *blendingColorSpace;
2652   Object matrixObj, bboxObj;
2653   double m[6], bbox[4];
2654   Object resObj;
2655   Dict *resDict;
2656   Object obj1, obj2, obj3;
2657   int i;
2659   // check for excessive recursion
2660   if (formDepth > 20) {
2661     return;
2662   }
2664   // get stream dict
2665   dict = str->streamGetDict();
2667   // check form type
2668   dict->lookup("FormType", &obj1);
2669   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2670     error(getPos(), "Unknown form type");
2671   }
2672   obj1.free();
2674   // get bounding box
2675   dict->lookup("BBox", &bboxObj);
2676   if (!bboxObj.isArray()) {
2677     bboxObj.free();
2678     error(getPos(), "Bad form bounding box");
2679     return;
2680   }
2681   for (i = 0; i < 4; ++i) {
2682     bboxObj.arrayGet(i, &obj1);
2683     bbox[i] = obj1.getNum();
2684     obj1.free();
2685   }
2686   bboxObj.free();
2688   // get matrix
2689   dict->lookup("Matrix", &matrixObj);
2690   if (matrixObj.isArray()) {
2691     for (i = 0; i < 6; ++i) {
2692       matrixObj.arrayGet(i, &obj1);
2693       m[i] = obj1.getNum();
2694       obj1.free();
2695     }
2696   } else {
2697     m[0] = 1; m[1] = 0;
2698     m[2] = 0; m[3] = 1;
2699     m[4] = 0; m[5] = 0;
2700   }
2701   matrixObj.free();
2703   // get resources
2704   dict->lookup("Resources", &resObj);
2705   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2707   // check for a transparency group
2708   transpGroup = isolated = knockout = gFalse;
2709   blendingColorSpace = NULL;
2710   if (dict->lookup("Group", &obj1)->isDict()) {
2711     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2712       transpGroup = gTrue;
2713       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2714         blendingColorSpace = GfxColorSpace::parse(&obj3);
2715       }
2716       obj3.free();
2717       if (obj1.dictLookup("I", &obj3)->isBool()) {
2718         isolated = obj3.getBool();
2719       }
2720       obj3.free();
2721       if (obj1.dictLookup("K", &obj3)->isBool()) {
2722         knockout = obj3.getBool();
2723       }
2724       obj3.free();
2725     }
2726     obj2.free();
2727   }
2728   obj1.free();
2730   // draw it
2731   ++formDepth;
2732   doForm1(str, resDict, m, bbox,
2733           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2734   --formDepth;
2736   if (blendingColorSpace) {
2737     delete blendingColorSpace;
2738   }
2739   resObj.free();
2742 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2743                   GBool transpGroup, GBool softMask,
2744                   GfxColorSpace *blendingColorSpace,
2745                   GBool isolated, GBool knockout,
2746                   GBool alpha, Function *transferFunc,
2747                   GfxColor *backdropColor) {
2748   Parser *oldParser;
2749   double oldBaseMatrix[6];
2750   int i;
2752   // push new resources on stack
2753   pushResources(resDict);
2755   // save current graphics state
2756   saveState();
2758   // kill any pre-existing path
2759   state->clearPath();
2761   // save current parser
2762   oldParser = parser;
2764   // set form transformation matrix
2765   state->concatCTM(matrix[0], matrix[1], matrix[2],
2766                    matrix[3], matrix[4], matrix[5]);
2767   builder->setTransform(matrix[0], matrix[1], matrix[2],
2768                         matrix[3], matrix[4], matrix[5]);
2770   // set form bounding box
2771   state->moveTo(bbox[0], bbox[1]);
2772   state->lineTo(bbox[2], bbox[1]);
2773   state->lineTo(bbox[2], bbox[3]);
2774   state->lineTo(bbox[0], bbox[3]);
2775   state->closePath();
2776   state->clip();
2777   builder->clip(state);
2778   state->clearPath();
2780   if (softMask || transpGroup) {
2781     if (state->getBlendMode() != gfxBlendNormal) {
2782       state->setBlendMode(gfxBlendNormal);
2783     }
2784     if (state->getFillOpacity() != 1) {
2785       state->setFillOpacity(1);
2786     }
2787     if (state->getStrokeOpacity() != 1) {
2788       state->setStrokeOpacity(1);
2789     }
2790     //out->clearSoftMask(state);
2791     //out->beginTransparencyGroup(state, bbox, blendingColorSpace,
2792     //                          isolated, knockout, softMask);
2793   }
2795   // set new base matrix
2796   for (i = 0; i < 6; ++i) {
2797     oldBaseMatrix[i] = baseMatrix[i];
2798     baseMatrix[i] = state->getCTM()[i];
2799   }
2801   // draw the form
2802   parse(str, gFalse);
2804   if (softMask || transpGroup) {
2805     //out->endTransparencyGroup(state);
2806   }
2808   // restore base matrix
2809   for (i = 0; i < 6; ++i) {
2810     baseMatrix[i] = oldBaseMatrix[i];
2811   }
2813   // restore parser
2814   parser = oldParser;
2816   // restore graphics state
2817   restoreState();
2819   // pop resource stack
2820   popResources();
2822   if (softMask) {
2823     //out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2824   } else if (transpGroup) {
2825     //out->paintTransparencyGroup(state, bbox);
2826   }
2828   return;
2831 //------------------------------------------------------------------------
2832 // in-line image operators
2833 //------------------------------------------------------------------------
2835 void PdfParser::opBeginImage(Object args[], int numArgs) {
2836   Stream *str;
2837   int c1, c2;
2839   // build dict/stream
2840   str = buildImageStream();
2842   // display the image
2843   if (str) {
2844     doImage(NULL, str, gTrue);
2845   
2846     // skip 'EI' tag
2847     c1 = str->getUndecodedStream()->getChar();
2848     c2 = str->getUndecodedStream()->getChar();
2849     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2850       c1 = c2;
2851       c2 = str->getUndecodedStream()->getChar();
2852     }
2853     delete str;
2854   }
2857 Stream *PdfParser::buildImageStream() {
2858   Object dict;
2859   Object obj;
2860   char *key;
2861   Stream *str;
2863   // build dictionary
2864   dict.initDict(xref);
2865   parser->getObj(&obj);
2866   while (!obj.isCmd("ID") && !obj.isEOF()) {
2867     if (!obj.isName()) {
2868       error(getPos(), "Inline image dictionary key must be a name object");
2869       obj.free();
2870     } else {
2871       key = copyString(obj.getName());
2872       obj.free();
2873       parser->getObj(&obj);
2874       if (obj.isEOF() || obj.isError()) {
2875         gfree(key);
2876         break;
2877       }
2878       dict.dictAdd(key, &obj);
2879     }
2880     parser->getObj(&obj);
2881   }
2882   if (obj.isEOF()) {
2883     error(getPos(), "End of file in inline image");
2884     obj.free();
2885     dict.free();
2886     return NULL;
2887   }
2888   obj.free();
2890   // make stream
2891   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2892   str = str->addFilters(&dict);
2894   return str;
2897 void PdfParser::opImageData(Object args[], int numArgs) {
2898   error(getPos(), "Internal: got 'ID' operator");
2901 void PdfParser::opEndImage(Object args[], int numArgs) {
2902   error(getPos(), "Internal: got 'EI' operator");
2905 //------------------------------------------------------------------------
2906 // type 3 font operators
2907 //------------------------------------------------------------------------
2909 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2912 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2915 //------------------------------------------------------------------------
2916 // compatibility operators
2917 //------------------------------------------------------------------------
2919 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2920   ++ignoreUndef;
2923 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2924   if (ignoreUndef > 0)
2925     --ignoreUndef;
2928 //------------------------------------------------------------------------
2929 // marked content operators
2930 //------------------------------------------------------------------------
2932 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2933   if (printCommands) {
2934     printf("  marked content: %s ", args[0].getName());
2935     if (numArgs == 2)
2936       args[2].print(stdout);
2937     printf("\n");
2938     fflush(stdout);
2939   }
2941   if(numArgs == 2) {
2942     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2943   } else {
2944     //out->beginMarkedContent(args[0].getName());
2945   }
2948 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2949   //out->endMarkedContent();
2952 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2953   if (printCommands) {
2954     printf("  mark point: %s ", args[0].getName());
2955     if (numArgs == 2)
2956       args[2].print(stdout);
2957     printf("\n");
2958     fflush(stdout);
2959   }
2961   if(numArgs == 2) {
2962     //out->markPoint(args[0].getName(),args[1].getDict());
2963   } else {
2964     //out->markPoint(args[0].getName());
2965   }
2969 //------------------------------------------------------------------------
2970 // misc
2971 //------------------------------------------------------------------------
2973 void PdfParser::saveState() {
2974   builder->saveState();
2975   state = state->save();
2978 void PdfParser::restoreState() {
2979   state = state->restore();
2980   builder->restoreState();
2983 void PdfParser::pushResources(Dict *resDict) {
2984   res = new GfxResources(xref, resDict, res);
2987 void PdfParser::popResources() {
2988   GfxResources *resPtr;
2990   resPtr = res->getNext();
2991   delete res;
2992   res = resPtr;
2995 #endif /* HAVE_POPPLER */