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