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