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