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