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