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 / 256.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 / 256.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 / 256.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 / 256.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 / 256.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 fontChanged = gFalse;
300 clip = clipNone;
301 ignoreUndef = 0;
302 operatorHistory = NULL;
303 builder = builderA;
304 builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
305 state->getPageHeight()*PX_PER_PT);
307 double *ctm = state->getCTM();
308 double scaledCTM[6];
309 for (i = 0; i < 6; ++i) {
310 baseMatrix[i] = ctm[i];
311 scaledCTM[i] = PX_PER_PT * ctm[i];
312 }
313 saveState();
314 builder->setTransform((double*)&scaledCTM);
315 formDepth = 0;
317 // set crop box
318 if (cropBox) {
319 if (printCommands)
320 printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
321 // do not clip if it's not needed
322 if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
323 cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
325 state->moveTo(cropBox->x1, cropBox->y1);
326 state->lineTo(cropBox->x2, cropBox->y1);
327 state->lineTo(cropBox->x2, cropBox->y2);
328 state->lineTo(cropBox->x1, cropBox->y2);
329 state->closePath();
330 state->clip();
331 builder->setClipPath(state);
332 state->clearPath();
333 }
334 }
335 pushOperator("startPage");
336 }
338 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
339 Dict *resDict, PDFRectangle *box) {
341 int i;
343 xref = xrefA;
344 subPage = gTrue;
345 printCommands = false;
347 // start the resource stack
348 res = new GfxResources(xref, resDict, NULL);
350 // initialize
351 operatorHistory = NULL;
352 builder = builderA;
353 state = new GfxState(72, 72, box, 0, gFalse);
354 fontChanged = gFalse;
355 clip = clipNone;
356 ignoreUndef = 0;
357 for (i = 0; i < 6; ++i) {
358 baseMatrix[i] = state->getCTM()[i];
359 }
360 formDepth = 0;
361 }
363 PdfParser::~PdfParser() {
364 while (state->hasSaves()) {
365 restoreState();
366 }
367 if (!subPage) {
368 //out->endPage();
369 }
370 while (res) {
371 popResources();
372 }
373 if (state) {
374 delete state;
375 }
376 }
378 void PdfParser::parse(Object *obj, GBool topLevel) {
379 Object obj2;
380 int i;
382 if (obj->isArray()) {
383 for (i = 0; i < obj->arrayGetLength(); ++i) {
384 obj->arrayGet(i, &obj2);
385 if (!obj2.isStream()) {
386 error(-1, "Weird page contents");
387 obj2.free();
388 return;
389 }
390 obj2.free();
391 }
392 } else if (!obj->isStream()) {
393 error(-1, "Weird page contents");
394 return;
395 }
396 parser = new Parser(xref, new Lexer(xref, obj), gFalse);
397 go(topLevel);
398 delete parser;
399 parser = NULL;
400 }
402 void PdfParser::go(GBool topLevel) {
403 Object obj;
404 Object args[maxArgs];
405 int numArgs, i;
406 int lastAbortCheck;
408 // scan a sequence of objects
409 numArgs = 0;
410 parser->getObj(&obj);
411 while (!obj.isEOF()) {
413 // got a command - execute it
414 if (obj.isCmd()) {
415 if (printCommands) {
416 obj.print(stdout);
417 for (i = 0; i < numArgs; ++i) {
418 printf(" ");
419 args[i].print(stdout);
420 }
421 printf("\n");
422 fflush(stdout);
423 }
425 // Run the operation
426 execOp(&obj, args, numArgs);
428 obj.free();
429 for (i = 0; i < numArgs; ++i)
430 args[i].free();
431 numArgs = 0;
433 // got an argument - save it
434 } else if (numArgs < maxArgs) {
435 args[numArgs++] = obj;
437 // too many arguments - something is wrong
438 } else {
439 error(getPos(), "Too many args in content stream");
440 if (printCommands) {
441 printf("throwing away arg: ");
442 obj.print(stdout);
443 printf("\n");
444 fflush(stdout);
445 }
446 obj.free();
447 }
449 // grab the next object
450 parser->getObj(&obj);
451 }
452 obj.free();
454 // args at end with no command
455 if (numArgs > 0) {
456 error(getPos(), "Leftover args in content stream");
457 if (printCommands) {
458 printf("%d leftovers:", numArgs);
459 for (i = 0; i < numArgs; ++i) {
460 printf(" ");
461 args[i].print(stdout);
462 }
463 printf("\n");
464 fflush(stdout);
465 }
466 for (i = 0; i < numArgs; ++i)
467 args[i].free();
468 }
469 }
471 void PdfParser::pushOperator(const char *name) {
472 OpHistoryEntry *newEntry = new OpHistoryEntry;
473 newEntry->name = name;
474 newEntry->state = NULL;
475 newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
476 newEntry->next = operatorHistory;
477 operatorHistory = newEntry;
479 // Truncate list if needed
480 if (operatorHistory->depth > maxOperatorHistoryDepth) {
481 OpHistoryEntry *curr = operatorHistory;
482 OpHistoryEntry *prev = NULL;
483 while (curr && curr->next != NULL) {
484 curr->depth--;
485 prev = curr;
486 curr = curr->next;
487 }
488 if (prev) {
489 if (curr->state != NULL)
490 delete curr->state;
491 delete curr;
492 prev->next = NULL;
493 }
494 }
495 }
497 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
498 OpHistoryEntry *prev = NULL;
499 if (operatorHistory != NULL && look_back > 0) {
500 prev = operatorHistory->next;
501 while (--look_back > 0 && prev != NULL) {
502 prev = prev->next;
503 }
504 }
505 if (prev != NULL) {
506 return prev->name;
507 } else {
508 return "";
509 }
510 }
512 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
513 PdfOperator *op;
514 char *name;
515 Object *argPtr;
516 int i;
518 // find operator
519 name = cmd->getCmd();
520 if (!(op = findOp(name))) {
521 if (ignoreUndef == 0)
522 error(getPos(), "Unknown operator '%s'", name);
523 return;
524 }
526 // type check args
527 argPtr = args;
528 if (op->numArgs >= 0) {
529 if (numArgs < op->numArgs) {
530 error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
531 return;
532 }
533 if (numArgs > op->numArgs) {
534 #if 0
535 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
536 #endif
537 argPtr += numArgs - op->numArgs;
538 numArgs = op->numArgs;
539 }
540 } else {
541 if (numArgs > -op->numArgs) {
542 error(getPos(), "Too many (%d) args to '%s' operator",
543 numArgs, name);
544 return;
545 }
546 }
547 for (i = 0; i < numArgs; ++i) {
548 if (!checkArg(&argPtr[i], op->tchk[i])) {
549 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
550 i, name, argPtr[i].getTypeName());
551 return;
552 }
553 }
555 // add to history
556 pushOperator((char*)&op->name);
558 // do it
559 (this->*op->func)(argPtr, numArgs);
560 }
562 PdfOperator *PdfParser::findOp(char *name) {
563 int a, b, m, cmp;
565 a = -1;
566 b = numOps;
567 // invariant: opTab[a] < name < opTab[b]
568 while (b - a > 1) {
569 m = (a + b) / 2;
570 cmp = strcmp(opTab[m].name, name);
571 if (cmp < 0)
572 a = m;
573 else if (cmp > 0)
574 b = m;
575 else
576 a = b = m;
577 }
578 if (cmp != 0)
579 return NULL;
580 return &opTab[a];
581 }
583 GBool PdfParser::checkArg(Object *arg, TchkType type) {
584 switch (type) {
585 case tchkBool: return arg->isBool();
586 case tchkInt: return arg->isInt();
587 case tchkNum: return arg->isNum();
588 case tchkString: return arg->isString();
589 case tchkName: return arg->isName();
590 case tchkArray: return arg->isArray();
591 case tchkProps: return arg->isDict() || arg->isName();
592 case tchkSCN: return arg->isNum() || arg->isName();
593 case tchkNone: return gFalse;
594 }
595 return gFalse;
596 }
598 int PdfParser::getPos() {
599 return parser ? parser->getPos() : -1;
600 }
602 //------------------------------------------------------------------------
603 // graphics state operators
604 //------------------------------------------------------------------------
606 void PdfParser::opSave(Object args[], int numArgs) {
607 saveState();
608 }
610 void PdfParser::opRestore(Object args[], int numArgs) {
611 restoreState();
612 }
614 void PdfParser::opConcat(Object args[], int numArgs) {
615 state->concatCTM(args[0].getNum(), args[1].getNum(),
616 args[2].getNum(), args[3].getNum(),
617 args[4].getNum(), args[5].getNum());
618 const char *prevOp = getPreviousOperator();
619 double a0 = args[0].getNum();
620 double a1 = args[1].getNum();
621 double a2 = args[2].getNum();
622 double a3 = args[3].getNum();
623 double a4 = args[4].getNum();
624 double a5 = args[5].getNum();
625 if (!strcmp(prevOp, "q")) {
626 builder->setTransform(a0, a1, a2, a3, a4, a5);
627 } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
628 // multiply it with the previous transform
629 double otherMatrix[6];
630 if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
631 // construct identity matrix
632 otherMatrix[0] = otherMatrix[3] = 1.0;
633 otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
634 }
635 double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
636 double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
637 double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
638 double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
639 double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
640 double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
641 builder->setTransform(c0, c1, c2, c3, c4, c5);
642 } else {
643 builder->pushGroup();
644 builder->setTransform(a0, a1, a2, a3, a4, a5);
645 }
646 fontChanged = gTrue;
647 }
649 void PdfParser::opSetDash(Object args[], int numArgs) {
650 Array *a;
651 int length;
652 Object obj;
653 double *dash;
654 int i;
656 a = args[0].getArray();
657 length = a->getLength();
658 if (length == 0) {
659 dash = NULL;
660 } else {
661 dash = (double *)gmallocn(length, sizeof(double));
662 for (i = 0; i < length; ++i) {
663 dash[i] = a->get(i, &obj)->getNum();
664 obj.free();
665 }
666 }
667 state->setLineDash(dash, length, args[1].getNum());
668 builder->updateStyle(state);
669 }
671 void PdfParser::opSetFlat(Object args[], int numArgs) {
672 state->setFlatness((int)args[0].getNum());
673 }
675 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
676 state->setLineJoin(args[0].getInt());
677 builder->updateStyle(state);
678 }
680 void PdfParser::opSetLineCap(Object args[], int numArgs) {
681 state->setLineCap(args[0].getInt());
682 builder->updateStyle(state);
683 }
685 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
686 state->setMiterLimit(args[0].getNum());
687 builder->updateStyle(state);
688 }
690 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
691 state->setLineWidth(args[0].getNum());
692 builder->updateStyle(state);
693 }
695 void PdfParser::opSetExtGState(Object args[], int numArgs) {
696 Object obj1, obj2, obj3, obj4, obj5;
697 GfxBlendMode mode;
698 GBool haveFillOP;
699 Function *funcs[4];
700 GfxColor backdropColor;
701 GBool haveBackdropColor;
702 GfxColorSpace *blendingColorSpace;
703 GBool alpha, isolated, knockout;
704 int i;
706 if (!res->lookupGState(args[0].getName(), &obj1)) {
707 return;
708 }
709 if (!obj1.isDict()) {
710 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
711 obj1.free();
712 return;
713 }
714 if (printCommands) {
715 printf(" gfx state dict: ");
716 obj1.print();
717 printf("\n");
718 }
720 // transparency support: blend mode, fill/stroke opacity
721 if (!obj1.dictLookup("BM", &obj2)->isNull()) {
722 if (state->parseBlendMode(&obj2, &mode)) {
723 state->setBlendMode(mode);
724 } else {
725 error(getPos(), "Invalid blend mode in ExtGState");
726 }
727 }
728 obj2.free();
729 if (obj1.dictLookup("ca", &obj2)->isNum()) {
730 state->setFillOpacity(obj2.getNum());
731 }
732 obj2.free();
733 if (obj1.dictLookup("CA", &obj2)->isNum()) {
734 state->setStrokeOpacity(obj2.getNum());
735 }
736 obj2.free();
738 // fill/stroke overprint
739 if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
740 state->setFillOverprint(obj2.getBool());
741 }
742 obj2.free();
743 if (obj1.dictLookup("OP", &obj2)->isBool()) {
744 state->setStrokeOverprint(obj2.getBool());
745 if (!haveFillOP) {
746 state->setFillOverprint(obj2.getBool());
747 }
748 }
749 obj2.free();
751 // stroke adjust
752 if (obj1.dictLookup("SA", &obj2)->isBool()) {
753 state->setStrokeAdjust(obj2.getBool());
754 }
755 obj2.free();
757 // transfer function
758 if (obj1.dictLookup("TR2", &obj2)->isNull()) {
759 obj2.free();
760 obj1.dictLookup("TR", &obj2);
761 }
762 if (obj2.isName("Default") ||
763 obj2.isName("Identity")) {
764 funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
765 state->setTransfer(funcs);
766 } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
767 for (i = 0; i < 4; ++i) {
768 obj2.arrayGet(i, &obj3);
769 funcs[i] = Function::parse(&obj3);
770 obj3.free();
771 if (!funcs[i]) {
772 break;
773 }
774 }
775 if (i == 4) {
776 state->setTransfer(funcs);
777 }
778 } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
779 if ((funcs[0] = Function::parse(&obj2))) {
780 funcs[1] = funcs[2] = funcs[3] = NULL;
781 state->setTransfer(funcs);
782 }
783 } else if (!obj2.isNull()) {
784 error(getPos(), "Invalid transfer function in ExtGState");
785 }
786 obj2.free();
788 // soft mask
789 if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
790 if (obj2.isName("None")) {
791 //out->clearSoftMask(state);
792 } else if (obj2.isDict()) {
793 if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
794 alpha = gTrue;
795 } else { // "Luminosity"
796 alpha = gFalse;
797 }
798 obj3.free();
799 funcs[0] = NULL;
800 if (!obj2.dictLookup("TR", &obj3)->isNull()) {
801 funcs[0] = Function::parse(&obj3);
802 if (funcs[0]->getInputSize() != 1 ||
803 funcs[0]->getOutputSize() != 1) {
804 error(getPos(),
805 "Invalid transfer function in soft mask in ExtGState");
806 delete funcs[0];
807 funcs[0] = NULL;
808 }
809 }
810 obj3.free();
811 if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
812 for (i = 0; i < gfxColorMaxComps; ++i) {
813 backdropColor.c[i] = 0;
814 }
815 for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
816 obj3.arrayGet(i, &obj4);
817 if (obj4.isNum()) {
818 backdropColor.c[i] = dblToCol(obj4.getNum());
819 }
820 obj4.free();
821 }
822 }
823 obj3.free();
824 if (obj2.dictLookup("G", &obj3)->isStream()) {
825 if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
826 blendingColorSpace = NULL;
827 isolated = knockout = gFalse;
828 if (!obj4.dictLookup("CS", &obj5)->isNull()) {
829 blendingColorSpace = GfxColorSpace::parse(&obj5);
830 }
831 obj5.free();
832 if (obj4.dictLookup("I", &obj5)->isBool()) {
833 isolated = obj5.getBool();
834 }
835 obj5.free();
836 if (obj4.dictLookup("K", &obj5)->isBool()) {
837 knockout = obj5.getBool();
838 }
839 obj5.free();
840 if (!haveBackdropColor) {
841 if (blendingColorSpace) {
842 blendingColorSpace->getDefaultColor(&backdropColor);
843 } else {
844 //~ need to get the parent or default color space (?)
845 for (i = 0; i < gfxColorMaxComps; ++i) {
846 backdropColor.c[i] = 0;
847 }
848 }
849 }
850 doSoftMask(&obj3, alpha, blendingColorSpace,
851 isolated, knockout, funcs[0], &backdropColor);
852 if (funcs[0]) {
853 delete funcs[0];
854 }
855 } else {
856 error(getPos(), "Invalid soft mask in ExtGState - missing group");
857 }
858 obj4.free();
859 } else {
860 error(getPos(), "Invalid soft mask in ExtGState - missing group");
861 }
862 obj3.free();
863 } else if (!obj2.isNull()) {
864 error(getPos(), "Invalid soft mask in ExtGState");
865 }
866 }
867 obj2.free();
869 obj1.free();
870 }
872 void PdfParser::doSoftMask(Object *str, GBool alpha,
873 GfxColorSpace *blendingColorSpace,
874 GBool isolated, GBool knockout,
875 Function *transferFunc, GfxColor *backdropColor) {
876 Dict *dict, *resDict;
877 double m[6], bbox[4];
878 Object obj1, obj2;
879 int i;
881 // check for excessive recursion
882 if (formDepth > 20) {
883 return;
884 }
886 // get stream dict
887 dict = str->streamGetDict();
889 // check form type
890 dict->lookup("FormType", &obj1);
891 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
892 error(getPos(), "Unknown form type");
893 }
894 obj1.free();
896 // get bounding box
897 dict->lookup("BBox", &obj1);
898 if (!obj1.isArray()) {
899 obj1.free();
900 error(getPos(), "Bad form bounding box");
901 return;
902 }
903 for (i = 0; i < 4; ++i) {
904 obj1.arrayGet(i, &obj2);
905 bbox[i] = obj2.getNum();
906 obj2.free();
907 }
908 obj1.free();
910 // get matrix
911 dict->lookup("Matrix", &obj1);
912 if (obj1.isArray()) {
913 for (i = 0; i < 6; ++i) {
914 obj1.arrayGet(i, &obj2);
915 m[i] = obj2.getNum();
916 obj2.free();
917 }
918 } else {
919 m[0] = 1; m[1] = 0;
920 m[2] = 0; m[3] = 1;
921 m[4] = 0; m[5] = 0;
922 }
923 obj1.free();
925 // get resources
926 dict->lookup("Resources", &obj1);
927 resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
929 // draw it
930 ++formDepth;
931 doForm1(str, resDict, m, bbox, gTrue, gTrue,
932 blendingColorSpace, isolated, knockout,
933 alpha, transferFunc, backdropColor);
934 --formDepth;
936 if (blendingColorSpace) {
937 delete blendingColorSpace;
938 }
939 obj1.free();
940 }
942 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
943 }
945 //------------------------------------------------------------------------
946 // color operators
947 //------------------------------------------------------------------------
949 void PdfParser::opSetFillGray(Object args[], int numArgs) {
950 GfxColor color;
952 state->setFillPattern(NULL);
953 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
954 color.c[0] = dblToCol(args[0].getNum());
955 state->setFillColor(&color);
956 builder->updateStyle(state);
957 }
959 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
960 GfxColor color;
962 state->setStrokePattern(NULL);
963 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
964 color.c[0] = dblToCol(args[0].getNum());
965 state->setStrokeColor(&color);
966 builder->updateStyle(state);
967 }
969 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
970 GfxColor color;
971 int i;
973 state->setFillPattern(NULL);
974 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
975 for (i = 0; i < 4; ++i) {
976 color.c[i] = dblToCol(args[i].getNum());
977 }
978 state->setFillColor(&color);
979 builder->updateStyle(state);
980 }
982 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
983 GfxColor color;
984 int i;
986 state->setStrokePattern(NULL);
987 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
988 for (i = 0; i < 4; ++i) {
989 color.c[i] = dblToCol(args[i].getNum());
990 }
991 state->setStrokeColor(&color);
992 builder->updateStyle(state);
993 }
995 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
996 GfxColor color;
997 int i;
999 state->setFillPattern(NULL);
1000 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
1001 for (i = 0; i < 3; ++i) {
1002 color.c[i] = dblToCol(args[i].getNum());
1003 }
1004 state->setFillColor(&color);
1005 builder->updateStyle(state);
1006 }
1008 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
1009 GfxColor color;
1010 int i;
1012 state->setStrokePattern(NULL);
1013 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1014 for (i = 0; i < 3; ++i) {
1015 color.c[i] = dblToCol(args[i].getNum());
1016 }
1017 state->setStrokeColor(&color);
1018 builder->updateStyle(state);
1019 }
1021 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1022 Object obj;
1023 GfxColorSpace *colorSpace;
1024 GfxColor color;
1026 state->setFillPattern(NULL);
1027 res->lookupColorSpace(args[0].getName(), &obj);
1028 if (obj.isNull()) {
1029 colorSpace = GfxColorSpace::parse(&args[0]);
1030 } else {
1031 colorSpace = GfxColorSpace::parse(&obj);
1032 }
1033 obj.free();
1034 if (colorSpace) {
1035 state->setFillColorSpace(colorSpace);
1036 colorSpace->getDefaultColor(&color);
1037 state->setFillColor(&color);
1038 builder->updateStyle(state);
1039 } else {
1040 error(getPos(), "Bad color space (fill)");
1041 }
1042 }
1044 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1045 Object obj;
1046 GfxColorSpace *colorSpace;
1047 GfxColor color;
1049 state->setStrokePattern(NULL);
1050 res->lookupColorSpace(args[0].getName(), &obj);
1051 if (obj.isNull()) {
1052 colorSpace = GfxColorSpace::parse(&args[0]);
1053 } else {
1054 colorSpace = GfxColorSpace::parse(&obj);
1055 }
1056 obj.free();
1057 if (colorSpace) {
1058 state->setStrokeColorSpace(colorSpace);
1059 colorSpace->getDefaultColor(&color);
1060 state->setStrokeColor(&color);
1061 builder->updateStyle(state);
1062 } else {
1063 error(getPos(), "Bad color space (stroke)");
1064 }
1065 }
1067 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1068 GfxColor color;
1069 int i;
1071 if (numArgs != state->getFillColorSpace()->getNComps()) {
1072 error(getPos(), "Incorrect number of arguments in 'sc' command");
1073 return;
1074 }
1075 state->setFillPattern(NULL);
1076 for (i = 0; i < numArgs; ++i) {
1077 color.c[i] = dblToCol(args[i].getNum());
1078 }
1079 state->setFillColor(&color);
1080 builder->updateStyle(state);
1081 }
1083 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1084 GfxColor color;
1085 int i;
1087 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1088 error(getPos(), "Incorrect number of arguments in 'SC' command");
1089 return;
1090 }
1091 state->setStrokePattern(NULL);
1092 for (i = 0; i < numArgs; ++i) {
1093 color.c[i] = dblToCol(args[i].getNum());
1094 }
1095 state->setStrokeColor(&color);
1096 builder->updateStyle(state);
1097 }
1099 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1100 GfxColor color;
1101 GfxPattern *pattern;
1102 int i;
1104 if (state->getFillColorSpace()->getMode() == csPattern) {
1105 if (numArgs > 1) {
1106 if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1107 numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1108 ->getUnder()->getNComps()) {
1109 error(getPos(), "Incorrect number of arguments in 'scn' command");
1110 return;
1111 }
1112 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1113 if (args[i].isNum()) {
1114 color.c[i] = dblToCol(args[i].getNum());
1115 }
1116 }
1117 state->setFillColor(&color);
1118 builder->updateStyle(state);
1119 }
1120 if (args[numArgs-1].isName() &&
1121 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1122 state->setFillPattern(pattern);
1123 }
1125 } else {
1126 if (numArgs != state->getFillColorSpace()->getNComps()) {
1127 error(getPos(), "Incorrect number of arguments in 'scn' command");
1128 return;
1129 }
1130 state->setFillPattern(NULL);
1131 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1132 if (args[i].isNum()) {
1133 color.c[i] = dblToCol(args[i].getNum());
1134 }
1135 }
1136 state->setFillColor(&color);
1137 builder->updateStyle(state);
1138 }
1139 }
1141 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1142 GfxColor color;
1143 GfxPattern *pattern;
1144 int i;
1146 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1147 if (numArgs > 1) {
1148 if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1149 ->getUnder() ||
1150 numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1151 ->getUnder()->getNComps()) {
1152 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1153 return;
1154 }
1155 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1156 if (args[i].isNum()) {
1157 color.c[i] = dblToCol(args[i].getNum());
1158 }
1159 }
1160 state->setStrokeColor(&color);
1161 builder->updateStyle(state);
1162 }
1163 if (args[numArgs-1].isName() &&
1164 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1165 state->setStrokePattern(pattern);
1166 }
1168 } else {
1169 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1170 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1171 return;
1172 }
1173 state->setStrokePattern(NULL);
1174 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1175 if (args[i].isNum()) {
1176 color.c[i] = dblToCol(args[i].getNum());
1177 }
1178 }
1179 state->setStrokeColor(&color);
1180 builder->updateStyle(state);
1181 }
1182 }
1184 //------------------------------------------------------------------------
1185 // path segment operators
1186 //------------------------------------------------------------------------
1188 void PdfParser::opMoveTo(Object args[], int numArgs) {
1189 state->moveTo(args[0].getNum(), args[1].getNum());
1190 }
1192 void PdfParser::opLineTo(Object args[], int numArgs) {
1193 if (!state->isCurPt()) {
1194 error(getPos(), "No current point in lineto");
1195 return;
1196 }
1197 state->lineTo(args[0].getNum(), args[1].getNum());
1198 }
1200 void PdfParser::opCurveTo(Object args[], int numArgs) {
1201 double x1, y1, x2, y2, x3, y3;
1203 if (!state->isCurPt()) {
1204 error(getPos(), "No current point in curveto");
1205 return;
1206 }
1207 x1 = args[0].getNum();
1208 y1 = args[1].getNum();
1209 x2 = args[2].getNum();
1210 y2 = args[3].getNum();
1211 x3 = args[4].getNum();
1212 y3 = args[5].getNum();
1213 state->curveTo(x1, y1, x2, y2, x3, y3);
1214 }
1216 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1217 double x1, y1, x2, y2, x3, y3;
1219 if (!state->isCurPt()) {
1220 error(getPos(), "No current point in curveto1");
1221 return;
1222 }
1223 x1 = state->getCurX();
1224 y1 = state->getCurY();
1225 x2 = args[0].getNum();
1226 y2 = args[1].getNum();
1227 x3 = args[2].getNum();
1228 y3 = args[3].getNum();
1229 state->curveTo(x1, y1, x2, y2, x3, y3);
1230 }
1232 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1233 double x1, y1, x2, y2, x3, y3;
1235 if (!state->isCurPt()) {
1236 error(getPos(), "No current point in curveto2");
1237 return;
1238 }
1239 x1 = args[0].getNum();
1240 y1 = args[1].getNum();
1241 x2 = args[2].getNum();
1242 y2 = args[3].getNum();
1243 x3 = x2;
1244 y3 = y2;
1245 state->curveTo(x1, y1, x2, y2, x3, y3);
1246 }
1248 void PdfParser::opRectangle(Object args[], int numArgs) {
1249 double x, y, w, h;
1251 x = args[0].getNum();
1252 y = args[1].getNum();
1253 w = args[2].getNum();
1254 h = args[3].getNum();
1255 state->moveTo(x, y);
1256 state->lineTo(x + w, y);
1257 state->lineTo(x + w, y + h);
1258 state->lineTo(x, y + h);
1259 state->closePath();
1260 }
1262 void PdfParser::opClosePath(Object args[], int numArgs) {
1263 if (!state->isCurPt()) {
1264 error(getPos(), "No current point in closepath");
1265 return;
1266 }
1267 state->closePath();
1268 }
1270 //------------------------------------------------------------------------
1271 // path painting operators
1272 //------------------------------------------------------------------------
1274 void PdfParser::opEndPath(Object args[], int numArgs) {
1275 doEndPath();
1276 }
1278 void PdfParser::opStroke(Object args[], int numArgs) {
1279 if (!state->isCurPt()) {
1280 //error(getPos(), "No path in stroke");
1281 return;
1282 }
1283 if (state->isPath()) {
1284 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1285 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1286 doPatternStrokeFallback();
1287 } else {
1288 builder->addPath(state, false, true);
1289 }
1290 }
1291 doEndPath();
1292 }
1294 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1295 if (!state->isCurPt()) {
1296 //error(getPos(), "No path in closepath/stroke");
1297 return;
1298 }
1299 state->closePath();
1300 if (state->isPath()) {
1301 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1302 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1303 doPatternStrokeFallback();
1304 } else {
1305 builder->addPath(state, false, true);
1306 }
1307 }
1308 doEndPath();
1309 }
1311 void PdfParser::opFill(Object args[], int numArgs) {
1312 if (!state->isCurPt()) {
1313 //error(getPos(), "No path in fill");
1314 return;
1315 }
1316 if (state->isPath()) {
1317 if (state->getFillColorSpace()->getMode() == csPattern &&
1318 !builder->isPatternTypeSupported(state->getFillPattern())) {
1319 doPatternFillFallback(gFalse);
1320 } else {
1321 builder->addPath(state, true, false);
1322 }
1323 }
1324 doEndPath();
1325 }
1327 void PdfParser::opEOFill(Object args[], int numArgs) {
1328 if (!state->isCurPt()) {
1329 //error(getPos(), "No path in eofill");
1330 return;
1331 }
1332 if (state->isPath()) {
1333 if (state->getFillColorSpace()->getMode() == csPattern &&
1334 !builder->isPatternTypeSupported(state->getFillPattern())) {
1335 doPatternFillFallback(gTrue);
1336 } else {
1337 builder->addPath(state, true, false, true);
1338 }
1339 }
1340 doEndPath();
1341 }
1343 void PdfParser::opFillStroke(Object args[], int numArgs) {
1344 if (!state->isCurPt()) {
1345 //error(getPos(), "No path in fill/stroke");
1346 return;
1347 }
1348 if (state->isPath()) {
1349 doFillAndStroke(gFalse);
1350 } else {
1351 builder->addPath(state, true, true);
1352 }
1353 doEndPath();
1354 }
1356 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1357 if (!state->isCurPt()) {
1358 //error(getPos(), "No path in closepath/fill/stroke");
1359 return;
1360 }
1361 if (state->isPath()) {
1362 state->closePath();
1363 doFillAndStroke(gFalse);
1364 }
1365 doEndPath();
1366 }
1368 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1369 if (!state->isCurPt()) {
1370 //error(getPos(), "No path in eofill/stroke");
1371 return;
1372 }
1373 if (state->isPath()) {
1374 doFillAndStroke(gTrue);
1375 }
1376 doEndPath();
1377 }
1379 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1380 if (!state->isCurPt()) {
1381 //error(getPos(), "No path in closepath/eofill/stroke");
1382 return;
1383 }
1384 if (state->isPath()) {
1385 state->closePath();
1386 doFillAndStroke(gTrue);
1387 }
1388 doEndPath();
1389 }
1391 void PdfParser::doFillAndStroke(GBool eoFill) {
1392 GBool fillOk = gTrue, strokeOk = gTrue;
1393 if (state->getFillColorSpace()->getMode() == csPattern &&
1394 !builder->isPatternTypeSupported(state->getFillPattern())) {
1395 fillOk = gFalse;
1396 }
1397 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1398 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1399 strokeOk = gFalse;
1400 }
1401 if (fillOk && strokeOk) {
1402 builder->addPath(state, true, true, eoFill);
1403 } else {
1404 doPatternFillFallback(eoFill);
1405 doPatternStrokeFallback();
1406 }
1407 }
1409 void PdfParser::doPatternFillFallback(GBool eoFill) {
1410 GfxPattern *pattern;
1412 if (!(pattern = state->getFillPattern())) {
1413 return;
1414 }
1415 switch (pattern->getType()) {
1416 case 1:
1417 break;
1418 case 2:
1419 doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1420 break;
1421 default:
1422 error(getPos(), "Unimplemented pattern type (%d) in fill",
1423 pattern->getType());
1424 break;
1425 }
1426 }
1428 void PdfParser::doPatternStrokeFallback() {
1429 GfxPattern *pattern;
1431 if (!(pattern = state->getStrokePattern())) {
1432 return;
1433 }
1434 switch (pattern->getType()) {
1435 case 1:
1436 break;
1437 case 2:
1438 doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1439 break;
1440 default:
1441 error(getPos(), "Unimplemented pattern type (%d) in stroke",
1442 pattern->getType());
1443 break;
1444 }
1445 }
1447 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1448 GBool stroke, GBool eoFill) {
1449 GfxShading *shading;
1450 GfxPath *savedPath;
1451 double *ctm, *btm, *ptm;
1452 double m[6], ictm[6], m1[6];
1453 double xMin, yMin, xMax, yMax;
1454 double det;
1456 shading = sPat->getShading();
1458 // save current graphics state
1459 savedPath = state->getPath()->copy();
1460 saveState();
1462 // clip to bbox
1463 if (0 ){//shading->getHasBBox()) {
1464 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1465 state->moveTo(xMin, yMin);
1466 state->lineTo(xMax, yMin);
1467 state->lineTo(xMax, yMax);
1468 state->lineTo(xMin, yMax);
1469 state->closePath();
1470 state->clip();
1471 //builder->clip(state);
1472 state->setPath(savedPath->copy());
1473 }
1475 // clip to current path
1476 if (stroke) {
1477 state->clipToStrokePath();
1478 //out->clipToStrokePath(state);
1479 } else {
1480 state->clip();
1481 if (eoFill) {
1482 builder->clip(state, true);
1483 } else {
1484 builder->clip(state);
1485 }
1486 }
1488 // set the color space
1489 state->setFillColorSpace(shading->getColorSpace()->copy());
1491 // background color fill
1492 if (shading->getHasBackground()) {
1493 state->setFillColor(shading->getBackground());
1494 builder->addPath(state, true, false);
1495 }
1496 state->clearPath();
1498 // construct a (pattern space) -> (current space) transform matrix
1499 ctm = state->getCTM();
1500 btm = baseMatrix;
1501 ptm = sPat->getMatrix();
1502 // iCTM = invert CTM
1503 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1504 ictm[0] = ctm[3] * det;
1505 ictm[1] = -ctm[1] * det;
1506 ictm[2] = -ctm[2] * det;
1507 ictm[3] = ctm[0] * det;
1508 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1509 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1510 // m1 = PTM * BTM = PTM * base transform matrix
1511 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1512 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1513 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1514 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1515 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1516 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1517 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1518 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1519 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1520 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1521 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1522 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1523 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1525 // set the new matrix
1526 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1527 builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1529 // do shading type-specific operations
1530 switch (shading->getType()) {
1531 case 1:
1532 doFunctionShFill((GfxFunctionShading *)shading);
1533 break;
1534 case 2:
1535 case 3:
1536 // no need to implement these
1537 break;
1538 case 4:
1539 case 5:
1540 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1541 break;
1542 case 6:
1543 case 7:
1544 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1545 break;
1546 }
1548 // restore graphics state
1549 restoreState();
1550 state->setPath(savedPath);
1551 }
1553 void PdfParser::opShFill(Object args[], int numArgs) {
1554 GfxShading *shading;
1555 GfxPath *savedPath;
1556 double xMin, yMin, xMax, yMax;
1558 if (!(shading = res->lookupShading(args[0].getName()))) {
1559 return;
1560 }
1562 // save current graphics state
1563 savedPath = state->getPath()->copy();
1564 saveState();
1566 // clip to bbox
1567 if (shading->getHasBBox()) {
1568 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1569 state->moveTo(xMin, yMin);
1570 state->lineTo(xMax, yMin);
1571 state->lineTo(xMax, yMax);
1572 state->lineTo(xMin, yMax);
1573 state->closePath();
1574 state->clip();
1575 builder->clip(state);
1576 state->clearPath();
1577 }
1579 // set the color space
1580 state->setFillColorSpace(shading->getColorSpace()->copy());
1582 // do shading type-specific operations
1583 switch (shading->getType()) {
1584 case 1:
1585 doFunctionShFill((GfxFunctionShading *)shading);
1586 break;
1587 case 2:
1588 case 3:
1589 // no need to implement these
1590 break;
1591 case 4:
1592 case 5:
1593 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1594 break;
1595 case 6:
1596 case 7:
1597 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1598 break;
1599 }
1601 // restore graphics state
1602 restoreState();
1603 state->setPath(savedPath);
1605 delete shading;
1606 }
1608 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1609 double x0, y0, x1, y1;
1610 GfxColor colors[4];
1612 shading->getDomain(&x0, &y0, &x1, &y1);
1613 shading->getColor(x0, y0, &colors[0]);
1614 shading->getColor(x0, y1, &colors[1]);
1615 shading->getColor(x1, y0, &colors[2]);
1616 shading->getColor(x1, y1, &colors[3]);
1617 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1618 }
1620 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1621 double x0, double y0,
1622 double x1, double y1,
1623 GfxColor *colors, int depth) {
1624 GfxColor fillColor;
1625 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1626 GfxColor colors2[4];
1627 double *matrix;
1628 double xM, yM;
1629 int nComps, i, j;
1631 nComps = shading->getColorSpace()->getNComps();
1632 matrix = shading->getMatrix();
1634 // compare the four corner colors
1635 for (i = 0; i < 4; ++i) {
1636 for (j = 0; j < nComps; ++j) {
1637 if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1638 break;
1639 }
1640 }
1641 if (j < nComps) {
1642 break;
1643 }
1644 }
1646 // center of the rectangle
1647 xM = 0.5 * (x0 + x1);
1648 yM = 0.5 * (y0 + y1);
1650 // the four corner colors are close (or we hit the recursive limit)
1651 // -- fill the rectangle; but require at least one subdivision
1652 // (depth==0) to avoid problems when the four outer corners of the
1653 // shaded region are the same color
1654 if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1656 // use the center color
1657 shading->getColor(xM, yM, &fillColor);
1658 state->setFillColor(&fillColor);
1660 // fill the rectangle
1661 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1662 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1663 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1664 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1665 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1666 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1667 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1668 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1669 state->closePath();
1670 builder->addPath(state, true, false);
1671 state->clearPath();
1673 // the four corner colors are not close enough -- subdivide the
1674 // rectangle
1675 } else {
1677 // colors[0] colorM0 colors[2]
1678 // (x0,y0) (xM,y0) (x1,y0)
1679 // +----------+----------+
1680 // | | |
1681 // | UL | UR |
1682 // color0M | colorMM | color1M
1683 // (x0,yM) +----------+----------+ (x1,yM)
1684 // | (xM,yM) |
1685 // | LL | LR |
1686 // | | |
1687 // +----------+----------+
1688 // colors[1] colorM1 colors[3]
1689 // (x0,y1) (xM,y1) (x1,y1)
1691 shading->getColor(x0, yM, &color0M);
1692 shading->getColor(x1, yM, &color1M);
1693 shading->getColor(xM, y0, &colorM0);
1694 shading->getColor(xM, y1, &colorM1);
1695 shading->getColor(xM, yM, &colorMM);
1697 // upper-left sub-rectangle
1698 colors2[0] = colors[0];
1699 colors2[1] = color0M;
1700 colors2[2] = colorM0;
1701 colors2[3] = colorMM;
1702 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1704 // lower-left sub-rectangle
1705 colors2[0] = color0M;
1706 colors2[1] = colors[1];
1707 colors2[2] = colorMM;
1708 colors2[3] = colorM1;
1709 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1711 // upper-right sub-rectangle
1712 colors2[0] = colorM0;
1713 colors2[1] = colorMM;
1714 colors2[2] = colors[2];
1715 colors2[3] = color1M;
1716 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1718 // lower-right sub-rectangle
1719 colors2[0] = colorMM;
1720 colors2[1] = colorM1;
1721 colors2[2] = color1M;
1722 colors2[3] = colors[3];
1723 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1724 }
1725 }
1727 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1728 double x0, y0, x1, y1, x2, y2;
1729 GfxColor color0, color1, color2;
1730 int i;
1732 for (i = 0; i < shading->getNTriangles(); ++i) {
1733 shading->getTriangle(i, &x0, &y0, &color0,
1734 &x1, &y1, &color1,
1735 &x2, &y2, &color2);
1736 gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1737 shading->getColorSpace()->getNComps(), 0);
1738 }
1739 }
1741 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1742 double x1, double y1, GfxColor *color1,
1743 double x2, double y2, GfxColor *color2,
1744 int nComps, int depth) {
1745 double x01, y01, x12, y12, x20, y20;
1746 GfxColor color01, color12, color20;
1747 int i;
1749 for (i = 0; i < nComps; ++i) {
1750 if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1751 abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1752 break;
1753 }
1754 }
1755 if (i == nComps || depth == gouraudMaxDepth) {
1756 state->setFillColor(color0);
1757 state->moveTo(x0, y0);
1758 state->lineTo(x1, y1);
1759 state->lineTo(x2, y2);
1760 state->closePath();
1761 builder->addPath(state, true, false);
1762 state->clearPath();
1763 } else {
1764 x01 = 0.5 * (x0 + x1);
1765 y01 = 0.5 * (y0 + y1);
1766 x12 = 0.5 * (x1 + x2);
1767 y12 = 0.5 * (y1 + y2);
1768 x20 = 0.5 * (x2 + x0);
1769 y20 = 0.5 * (y2 + y0);
1770 //~ if the shading has a Function, this should interpolate on the
1771 //~ function parameter, not on the color components
1772 for (i = 0; i < nComps; ++i) {
1773 color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1774 color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1775 color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1776 }
1777 gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1778 x20, y20, &color20, nComps, depth + 1);
1779 gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1780 x12, y12, &color12, nComps, depth + 1);
1781 gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1782 x20, y20, &color20, nComps, depth + 1);
1783 gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1784 x2, y2, color2, nComps, depth + 1);
1785 }
1786 }
1788 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1789 int start, i;
1791 if (shading->getNPatches() > 128) {
1792 start = 3;
1793 } else if (shading->getNPatches() > 64) {
1794 start = 2;
1795 } else if (shading->getNPatches() > 16) {
1796 start = 1;
1797 } else {
1798 start = 0;
1799 }
1800 for (i = 0; i < shading->getNPatches(); ++i) {
1801 fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1802 start);
1803 }
1804 }
1806 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1807 GfxPatch patch00, patch01, patch10, patch11;
1808 double xx[4][8], yy[4][8];
1809 double xxm, yym;
1810 int i;
1812 for (i = 0; i < nComps; ++i) {
1813 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1814 > patchColorDelta ||
1815 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1816 > patchColorDelta ||
1817 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1818 > patchColorDelta ||
1819 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1820 > patchColorDelta) {
1821 break;
1822 }
1823 }
1824 if (i == nComps || depth == patchMaxDepth) {
1825 state->setFillColor(&patch->color[0][0]);
1826 state->moveTo(patch->x[0][0], patch->y[0][0]);
1827 state->curveTo(patch->x[0][1], patch->y[0][1],
1828 patch->x[0][2], patch->y[0][2],
1829 patch->x[0][3], patch->y[0][3]);
1830 state->curveTo(patch->x[1][3], patch->y[1][3],
1831 patch->x[2][3], patch->y[2][3],
1832 patch->x[3][3], patch->y[3][3]);
1833 state->curveTo(patch->x[3][2], patch->y[3][2],
1834 patch->x[3][1], patch->y[3][1],
1835 patch->x[3][0], patch->y[3][0]);
1836 state->curveTo(patch->x[2][0], patch->y[2][0],
1837 patch->x[1][0], patch->y[1][0],
1838 patch->x[0][0], patch->y[0][0]);
1839 state->closePath();
1840 builder->addPath(state, true, false);
1841 state->clearPath();
1842 } else {
1843 for (i = 0; i < 4; ++i) {
1844 xx[i][0] = patch->x[i][0];
1845 yy[i][0] = patch->y[i][0];
1846 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1847 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1848 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1849 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1850 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1851 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1852 xx[i][2] = 0.5 * (xx[i][1] + xxm);
1853 yy[i][2] = 0.5 * (yy[i][1] + yym);
1854 xx[i][5] = 0.5 * (xxm + xx[i][6]);
1855 yy[i][5] = 0.5 * (yym + yy[i][6]);
1856 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1857 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1858 xx[i][7] = patch->x[i][3];
1859 yy[i][7] = patch->y[i][3];
1860 }
1861 for (i = 0; i < 4; ++i) {
1862 patch00.x[0][i] = xx[0][i];
1863 patch00.y[0][i] = yy[0][i];
1864 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1865 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1866 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1867 yym = 0.5 * (yy[1][i] + yy[2][i]);
1868 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1869 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1870 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1871 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1872 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1873 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1874 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1875 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1876 patch10.x[0][i] = patch00.x[3][i];
1877 patch10.y[0][i] = patch00.y[3][i];
1878 patch10.x[3][i] = xx[3][i];
1879 patch10.y[3][i] = yy[3][i];
1880 }
1881 for (i = 4; i < 8; ++i) {
1882 patch01.x[0][i-4] = xx[0][i];
1883 patch01.y[0][i-4] = yy[0][i];
1884 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1885 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1886 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1887 yym = 0.5 * (yy[1][i] + yy[2][i]);
1888 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1889 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1890 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1891 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1892 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1893 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1894 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1895 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1896 patch11.x[0][i-4] = patch01.x[3][i-4];
1897 patch11.y[0][i-4] = patch01.y[3][i-4];
1898 patch11.x[3][i-4] = xx[3][i];
1899 patch11.y[3][i-4] = yy[3][i];
1900 }
1901 //~ if the shading has a Function, this should interpolate on the
1902 //~ function parameter, not on the color components
1903 for (i = 0; i < nComps; ++i) {
1904 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1905 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1906 patch->color[0][1].c[i]) / 2;
1907 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1908 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1909 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1910 patch->color[1][1].c[i]) / 2;
1911 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1912 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1913 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1914 patch->color[1][0].c[i]) / 2;
1915 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1916 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1917 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1918 patch->color[0][0].c[i]) / 2;
1919 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1920 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1921 patch01.color[1][1].c[i]) / 2;
1922 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1923 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1924 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1925 }
1926 fillPatch(&patch00, nComps, depth + 1);
1927 fillPatch(&patch10, nComps, depth + 1);
1928 fillPatch(&patch01, nComps, depth + 1);
1929 fillPatch(&patch11, nComps, depth + 1);
1930 }
1931 }
1933 void PdfParser::doEndPath() {
1934 if (state->isCurPt() && clip != clipNone) {
1935 state->clip();
1936 if (clip == clipNormal) {
1937 builder->clip(state);
1938 } else {
1939 builder->clip(state, true);
1940 }
1941 }
1942 clip = clipNone;
1943 state->clearPath();
1944 }
1946 //------------------------------------------------------------------------
1947 // path clipping operators
1948 //------------------------------------------------------------------------
1950 void PdfParser::opClip(Object args[], int numArgs) {
1951 clip = clipNormal;
1952 }
1954 void PdfParser::opEOClip(Object args[], int numArgs) {
1955 clip = clipEO;
1956 }
1958 //------------------------------------------------------------------------
1959 // text object operators
1960 //------------------------------------------------------------------------
1962 void PdfParser::opBeginText(Object args[], int numArgs) {
1963 state->setTextMat(1, 0, 0, 1, 0, 0);
1964 state->textMoveTo(0, 0);
1965 builder->updateTextPosition(0.0, 0.0);
1966 fontChanged = gTrue;
1967 builder->beginTextObject(state);
1968 }
1970 void PdfParser::opEndText(Object args[], int numArgs) {
1971 builder->endTextObject(state);
1972 }
1974 //------------------------------------------------------------------------
1975 // text state operators
1976 //------------------------------------------------------------------------
1978 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
1979 state->setCharSpace(args[0].getNum());
1980 }
1982 void PdfParser::opSetFont(Object args[], int numArgs) {
1983 GfxFont *font;
1985 if (!(font = res->lookupFont(args[0].getName()))) {
1986 return;
1987 }
1988 if (printCommands) {
1989 printf(" font: tag=%s name='%s' %g\n",
1990 font->getTag()->getCString(),
1991 font->getName() ? font->getName()->getCString() : "???",
1992 args[1].getNum());
1993 fflush(stdout);
1994 }
1996 font->incRefCnt();
1997 state->setFont(font, args[1].getNum());
1998 fontChanged = gTrue;
1999 }
2001 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2002 state->setLeading(args[0].getNum());
2003 }
2005 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2006 state->setRender(args[0].getInt());
2007 builder->updateStyle(state);
2008 }
2010 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2011 state->setRise(args[0].getNum());
2012 }
2014 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2015 state->setWordSpace(args[0].getNum());
2016 }
2018 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2019 state->setHorizScaling(args[0].getNum());
2020 fontChanged = gTrue;
2021 }
2023 //------------------------------------------------------------------------
2024 // text positioning operators
2025 //------------------------------------------------------------------------
2027 void PdfParser::opTextMove(Object args[], int numArgs) {
2028 double tx, ty;
2030 tx = state->getLineX() + args[0].getNum();
2031 ty = state->getLineY() + args[1].getNum();
2032 state->textMoveTo(tx, ty);
2033 builder->updateTextPosition(tx, ty);
2034 }
2036 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2037 double tx, ty;
2039 tx = state->getLineX() + args[0].getNum();
2040 ty = args[1].getNum();
2041 state->setLeading(-ty);
2042 ty += state->getLineY();
2043 state->textMoveTo(tx, ty);
2044 builder->updateTextPosition(tx, ty);
2045 }
2047 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2048 state->setTextMat(args[0].getNum(), args[1].getNum(),
2049 args[2].getNum(), args[3].getNum(),
2050 args[4].getNum(), args[5].getNum());
2051 state->textMoveTo(0, 0);
2052 builder->updateTextMatrix(state);
2053 builder->updateTextPosition(0.0, 0.0);
2054 fontChanged = gTrue;
2055 }
2057 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2058 double tx, ty;
2060 tx = state->getLineX();
2061 ty = state->getLineY() - state->getLeading();
2062 state->textMoveTo(tx, ty);
2063 builder->updateTextPosition(tx, ty);
2064 }
2066 //------------------------------------------------------------------------
2067 // text string operators
2068 //------------------------------------------------------------------------
2070 void PdfParser::opShowText(Object args[], int numArgs) {
2071 if (!state->getFont()) {
2072 error(getPos(), "No font in show");
2073 return;
2074 }
2075 if (fontChanged) {
2076 builder->updateFont(state);
2077 fontChanged = gFalse;
2078 }
2079 doShowText(args[0].getString());
2080 }
2082 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2083 double tx, ty;
2085 if (!state->getFont()) {
2086 error(getPos(), "No font in move/show");
2087 return;
2088 }
2089 if (fontChanged) {
2090 builder->updateFont(state);
2091 fontChanged = gFalse;
2092 }
2093 tx = state->getLineX();
2094 ty = state->getLineY() - state->getLeading();
2095 state->textMoveTo(tx, ty);
2096 builder->updateTextPosition(tx, ty);
2097 doShowText(args[0].getString());
2098 }
2100 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2101 double tx, ty;
2103 if (!state->getFont()) {
2104 error(getPos(), "No font in move/set/show");
2105 return;
2106 }
2107 if (fontChanged) {
2108 builder->updateFont(state);
2109 fontChanged = gFalse;
2110 }
2111 state->setWordSpace(args[0].getNum());
2112 state->setCharSpace(args[1].getNum());
2113 tx = state->getLineX();
2114 ty = state->getLineY() - state->getLeading();
2115 state->textMoveTo(tx, ty);
2116 builder->updateTextPosition(tx, ty);
2117 doShowText(args[2].getString());
2118 }
2120 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2121 Array *a;
2122 Object obj;
2123 int wMode;
2124 int i;
2126 if (!state->getFont()) {
2127 error(getPos(), "No font in show/space");
2128 return;
2129 }
2130 if (fontChanged) {
2131 builder->updateFont(state);
2132 fontChanged = gFalse;
2133 }
2134 wMode = state->getFont()->getWMode();
2135 a = args[0].getArray();
2136 for (i = 0; i < a->getLength(); ++i) {
2137 a->get(i, &obj);
2138 if (obj.isNum()) {
2139 // this uses the absolute value of the font size to match
2140 // Acrobat's behavior
2141 if (wMode) {
2142 state->textShift(0, -obj.getNum() * 0.001 *
2143 fabs(state->getFontSize()));
2144 } else {
2145 state->textShift(-obj.getNum() * 0.001 *
2146 fabs(state->getFontSize()), 0);
2147 }
2148 builder->updateTextShift(state, obj.getNum());
2149 } else if (obj.isString()) {
2150 doShowText(obj.getString());
2151 } else {
2152 error(getPos(), "Element of show/space array must be number or string");
2153 }
2154 obj.free();
2155 }
2156 }
2158 void PdfParser::doShowText(GooString *s) {
2159 GfxFont *font;
2160 int wMode;
2161 double riseX, riseY;
2162 CharCode code;
2163 Unicode u[8];
2164 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2165 double originX, originY, tOriginX, tOriginY;
2166 double oldCTM[6], newCTM[6];
2167 double *mat;
2168 Object charProc;
2169 Dict *resDict;
2170 Parser *oldParser;
2171 char *p;
2172 int len, n, uLen, nChars, nSpaces, i;
2174 font = state->getFont();
2175 wMode = font->getWMode();
2177 builder->beginString(state, s);
2179 // handle a Type 3 char
2180 if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2181 mat = state->getCTM();
2182 for (i = 0; i < 6; ++i) {
2183 oldCTM[i] = mat[i];
2184 }
2185 mat = state->getTextMat();
2186 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2187 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2188 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2189 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2190 mat = font->getFontMatrix();
2191 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2192 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2193 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2194 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2195 newCTM[0] *= state->getFontSize();
2196 newCTM[1] *= state->getFontSize();
2197 newCTM[2] *= state->getFontSize();
2198 newCTM[3] *= state->getFontSize();
2199 newCTM[0] *= state->getHorizScaling();
2200 newCTM[2] *= state->getHorizScaling();
2201 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2202 curX = state->getCurX();
2203 curY = state->getCurY();
2204 lineX = state->getLineX();
2205 lineY = state->getLineY();
2206 oldParser = parser;
2207 p = s->getCString();
2208 len = s->getLength();
2209 while (len > 0) {
2210 n = font->getNextChar(p, len, &code,
2211 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2212 &dx, &dy, &originX, &originY);
2213 dx = dx * state->getFontSize() + state->getCharSpace();
2214 if (n == 1 && *p == ' ') {
2215 dx += state->getWordSpace();
2216 }
2217 dx *= state->getHorizScaling();
2218 dy *= state->getFontSize();
2219 state->textTransformDelta(dx, dy, &tdx, &tdy);
2220 state->transform(curX + riseX, curY + riseY, &x, &y);
2221 saveState();
2222 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2223 //~ the CTM concat values here are wrong (but never used)
2224 //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2225 if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2226 code, u, uLen)) {*/
2227 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2228 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2229 pushResources(resDict);
2230 }
2231 if (charProc.isStream()) {
2232 //parse(&charProc, gFalse); // TODO: parse into SVG font
2233 } else {
2234 error(getPos(), "Missing or bad Type3 CharProc entry");
2235 }
2236 //out->endType3Char(state);
2237 if (resDict) {
2238 popResources();
2239 }
2240 charProc.free();
2241 }
2242 restoreState();
2243 // GfxState::restore() does *not* restore the current position,
2244 // so we deal with it here using (curX, curY) and (lineX, lineY)
2245 curX += tdx;
2246 curY += tdy;
2247 state->moveTo(curX, curY);
2248 state->textSetPos(lineX, lineY);
2249 p += n;
2250 len -= n;
2251 }
2252 parser = oldParser;
2254 } else {
2255 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2256 p = s->getCString();
2257 len = s->getLength();
2258 while (len > 0) {
2259 n = font->getNextChar(p, len, &code,
2260 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2261 &dx, &dy, &originX, &originY);
2263 if (wMode) {
2264 dx *= state->getFontSize();
2265 dy = dy * state->getFontSize() + state->getCharSpace();
2266 if (n == 1 && *p == ' ') {
2267 dy += state->getWordSpace();
2268 }
2269 } else {
2270 dx = dx * state->getFontSize() + state->getCharSpace();
2271 if (n == 1 && *p == ' ') {
2272 dx += state->getWordSpace();
2273 }
2274 dx *= state->getHorizScaling();
2275 dy *= state->getFontSize();
2276 }
2277 state->textTransformDelta(dx, dy, &tdx, &tdy);
2278 originX *= state->getFontSize();
2279 originY *= state->getFontSize();
2280 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2281 builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2282 dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2283 state->shift(tdx, tdy);
2284 p += n;
2285 len -= n;
2286 }
2287 }
2289 builder->endString(state);
2290 }
2292 //------------------------------------------------------------------------
2293 // XObject operators
2294 //------------------------------------------------------------------------
2296 void PdfParser::opXObject(Object args[], int numArgs) {
2297 char *name;
2298 Object obj1, obj2, obj3, refObj;
2300 name = args[0].getName();
2301 if (!res->lookupXObject(name, &obj1)) {
2302 return;
2303 }
2304 if (!obj1.isStream()) {
2305 error(getPos(), "XObject '%s' is wrong type", name);
2306 obj1.free();
2307 return;
2308 }
2309 obj1.streamGetDict()->lookup("Subtype", &obj2);
2310 if (obj2.isName("Image")) {
2311 res->lookupXObjectNF(name, &refObj);
2312 doImage(&refObj, obj1.getStream(), gFalse);
2313 refObj.free();
2314 } else if (obj2.isName("Form")) {
2315 doForm(&obj1);
2316 } else if (obj2.isName("PS")) {
2317 obj1.streamGetDict()->lookup("Level1", &obj3);
2318 /* out->psXObject(obj1.getStream(),
2319 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2320 } else if (obj2.isName()) {
2321 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2322 } else {
2323 error(getPos(), "XObject subtype is missing or wrong type");
2324 }
2325 obj2.free();
2326 obj1.free();
2327 }
2329 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2330 Dict *dict, *maskDict;
2331 int width, height;
2332 int bits, maskBits;
2333 StreamColorSpaceMode csMode;
2334 GBool mask;
2335 GBool invert;
2336 GfxColorSpace *colorSpace, *maskColorSpace;
2337 GfxImageColorMap *colorMap, *maskColorMap;
2338 Object maskObj, smaskObj;
2339 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2340 int maskColors[2*gfxColorMaxComps];
2341 int maskWidth, maskHeight;
2342 GBool maskInvert;
2343 Stream *maskStr;
2344 Object obj1, obj2;
2345 int i;
2347 // get info from the stream
2348 bits = 0;
2349 csMode = streamCSNone;
2350 str->getImageParams(&bits, &csMode);
2352 // get stream dict
2353 dict = str->getDict();
2355 // get size
2356 dict->lookup("Width", &obj1);
2357 if (obj1.isNull()) {
2358 obj1.free();
2359 dict->lookup("W", &obj1);
2360 }
2361 if (obj1.isInt())
2362 width = obj1.getInt();
2363 else if (obj1.isReal())
2364 width = (int)obj1.getReal();
2365 else
2366 goto err2;
2367 obj1.free();
2368 dict->lookup("Height", &obj1);
2369 if (obj1.isNull()) {
2370 obj1.free();
2371 dict->lookup("H", &obj1);
2372 }
2373 if (obj1.isInt())
2374 height = obj1.getInt();
2375 else if (obj1.isReal())
2376 height = (int)obj1.getReal();
2377 else
2378 goto err2;
2379 obj1.free();
2381 // image or mask?
2382 dict->lookup("ImageMask", &obj1);
2383 if (obj1.isNull()) {
2384 obj1.free();
2385 dict->lookup("IM", &obj1);
2386 }
2387 mask = gFalse;
2388 if (obj1.isBool())
2389 mask = obj1.getBool();
2390 else if (!obj1.isNull())
2391 goto err2;
2392 obj1.free();
2394 // bit depth
2395 if (bits == 0) {
2396 dict->lookup("BitsPerComponent", &obj1);
2397 if (obj1.isNull()) {
2398 obj1.free();
2399 dict->lookup("BPC", &obj1);
2400 }
2401 if (obj1.isInt()) {
2402 bits = obj1.getInt();
2403 } else if (mask) {
2404 bits = 1;
2405 } else {
2406 goto err2;
2407 }
2408 obj1.free();
2409 }
2411 // display a mask
2412 if (mask) {
2414 // check for inverted mask
2415 if (bits != 1)
2416 goto err1;
2417 invert = gFalse;
2418 dict->lookup("Decode", &obj1);
2419 if (obj1.isNull()) {
2420 obj1.free();
2421 dict->lookup("D", &obj1);
2422 }
2423 if (obj1.isArray()) {
2424 obj1.arrayGet(0, &obj2);
2425 if (obj2.isInt() && obj2.getInt() == 1)
2426 invert = gTrue;
2427 obj2.free();
2428 } else if (!obj1.isNull()) {
2429 goto err2;
2430 }
2431 obj1.free();
2433 // draw it
2434 //out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2436 } else {
2438 // get color space and color map
2439 dict->lookup("ColorSpace", &obj1);
2440 if (obj1.isNull()) {
2441 obj1.free();
2442 dict->lookup("CS", &obj1);
2443 }
2444 if (obj1.isName()) {
2445 res->lookupColorSpace(obj1.getName(), &obj2);
2446 if (!obj2.isNull()) {
2447 obj1.free();
2448 obj1 = obj2;
2449 } else {
2450 obj2.free();
2451 }
2452 }
2453 if (!obj1.isNull()) {
2454 colorSpace = GfxColorSpace::parse(&obj1);
2455 } else if (csMode == streamCSDeviceGray) {
2456 colorSpace = new GfxDeviceGrayColorSpace();
2457 } else if (csMode == streamCSDeviceRGB) {
2458 colorSpace = new GfxDeviceRGBColorSpace();
2459 } else if (csMode == streamCSDeviceCMYK) {
2460 colorSpace = new GfxDeviceCMYKColorSpace();
2461 } else {
2462 colorSpace = NULL;
2463 }
2464 obj1.free();
2465 if (!colorSpace) {
2466 goto err1;
2467 }
2468 dict->lookup("Decode", &obj1);
2469 if (obj1.isNull()) {
2470 obj1.free();
2471 dict->lookup("D", &obj1);
2472 }
2473 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2474 obj1.free();
2475 if (!colorMap->isOk()) {
2476 delete colorMap;
2477 goto err1;
2478 }
2480 // get the mask
2481 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2482 maskStr = NULL; // make gcc happy
2483 maskWidth = maskHeight = 0; // make gcc happy
2484 maskInvert = gFalse; // make gcc happy
2485 maskColorMap = NULL; // make gcc happy
2486 dict->lookup("Mask", &maskObj);
2487 dict->lookup("SMask", &smaskObj);
2488 if (smaskObj.isStream()) {
2489 // soft mask
2490 if (inlineImg) {
2491 goto err1;
2492 }
2493 maskStr = smaskObj.getStream();
2494 maskDict = smaskObj.streamGetDict();
2495 maskDict->lookup("Width", &obj1);
2496 if (obj1.isNull()) {
2497 obj1.free();
2498 maskDict->lookup("W", &obj1);
2499 }
2500 if (!obj1.isInt()) {
2501 goto err2;
2502 }
2503 maskWidth = obj1.getInt();
2504 obj1.free();
2505 maskDict->lookup("Height", &obj1);
2506 if (obj1.isNull()) {
2507 obj1.free();
2508 maskDict->lookup("H", &obj1);
2509 }
2510 if (!obj1.isInt()) {
2511 goto err2;
2512 }
2513 maskHeight = obj1.getInt();
2514 obj1.free();
2515 maskDict->lookup("BitsPerComponent", &obj1);
2516 if (obj1.isNull()) {
2517 obj1.free();
2518 maskDict->lookup("BPC", &obj1);
2519 }
2520 if (!obj1.isInt()) {
2521 goto err2;
2522 }
2523 maskBits = obj1.getInt();
2524 obj1.free();
2525 maskDict->lookup("ColorSpace", &obj1);
2526 if (obj1.isNull()) {
2527 obj1.free();
2528 maskDict->lookup("CS", &obj1);
2529 }
2530 if (obj1.isName()) {
2531 res->lookupColorSpace(obj1.getName(), &obj2);
2532 if (!obj2.isNull()) {
2533 obj1.free();
2534 obj1 = obj2;
2535 } else {
2536 obj2.free();
2537 }
2538 }
2539 maskColorSpace = GfxColorSpace::parse(&obj1);
2540 obj1.free();
2541 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2542 goto err1;
2543 }
2544 maskDict->lookup("Decode", &obj1);
2545 if (obj1.isNull()) {
2546 obj1.free();
2547 maskDict->lookup("D", &obj1);
2548 }
2549 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2550 obj1.free();
2551 if (!maskColorMap->isOk()) {
2552 delete maskColorMap;
2553 goto err1;
2554 }
2555 //~ handle the Matte entry
2556 haveSoftMask = gTrue;
2557 } else if (maskObj.isArray()) {
2558 // color key mask
2559 for (i = 0;
2560 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2561 ++i) {
2562 maskObj.arrayGet(i, &obj1);
2563 maskColors[i] = obj1.getInt();
2564 obj1.free();
2565 }
2566 haveColorKeyMask = gTrue;
2567 } else if (maskObj.isStream()) {
2568 // explicit mask
2569 if (inlineImg) {
2570 goto err1;
2571 }
2572 maskStr = maskObj.getStream();
2573 maskDict = maskObj.streamGetDict();
2574 maskDict->lookup("Width", &obj1);
2575 if (obj1.isNull()) {
2576 obj1.free();
2577 maskDict->lookup("W", &obj1);
2578 }
2579 if (!obj1.isInt()) {
2580 goto err2;
2581 }
2582 maskWidth = obj1.getInt();
2583 obj1.free();
2584 maskDict->lookup("Height", &obj1);
2585 if (obj1.isNull()) {
2586 obj1.free();
2587 maskDict->lookup("H", &obj1);
2588 }
2589 if (!obj1.isInt()) {
2590 goto err2;
2591 }
2592 maskHeight = obj1.getInt();
2593 obj1.free();
2594 maskDict->lookup("ImageMask", &obj1);
2595 if (obj1.isNull()) {
2596 obj1.free();
2597 maskDict->lookup("IM", &obj1);
2598 }
2599 if (!obj1.isBool() || !obj1.getBool()) {
2600 goto err2;
2601 }
2602 obj1.free();
2603 maskInvert = gFalse;
2604 maskDict->lookup("Decode", &obj1);
2605 if (obj1.isNull()) {
2606 obj1.free();
2607 maskDict->lookup("D", &obj1);
2608 }
2609 if (obj1.isArray()) {
2610 obj1.arrayGet(0, &obj2);
2611 if (obj2.isInt() && obj2.getInt() == 1) {
2612 maskInvert = gTrue;
2613 }
2614 obj2.free();
2615 } else if (!obj1.isNull()) {
2616 goto err2;
2617 }
2618 obj1.free();
2619 haveExplicitMask = gTrue;
2620 }
2622 // draw it
2623 if (haveSoftMask) {
2624 /* out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
2625 maskStr, maskWidth, maskHeight, maskColorMap);*/
2626 delete maskColorMap;
2627 } else if (haveExplicitMask) {
2628 /* out->drawMaskedImage(state, ref, str, width, height, colorMap,
2629 maskStr, maskWidth, maskHeight, maskInvert);*/
2630 } else {
2631 builder->addImage(state, str, width, height, colorMap,
2632 haveColorKeyMask ? maskColors : (int *)NULL);
2633 }
2634 delete colorMap;
2636 maskObj.free();
2637 smaskObj.free();
2638 }
2640 return;
2642 err2:
2643 obj1.free();
2644 err1:
2645 error(getPos(), "Bad image parameters");
2646 }
2648 void PdfParser::doForm(Object *str) {
2649 Dict *dict;
2650 GBool transpGroup, isolated, knockout;
2651 GfxColorSpace *blendingColorSpace;
2652 Object matrixObj, bboxObj;
2653 double m[6], bbox[4];
2654 Object resObj;
2655 Dict *resDict;
2656 Object obj1, obj2, obj3;
2657 int i;
2659 // check for excessive recursion
2660 if (formDepth > 20) {
2661 return;
2662 }
2664 // get stream dict
2665 dict = str->streamGetDict();
2667 // check form type
2668 dict->lookup("FormType", &obj1);
2669 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2670 error(getPos(), "Unknown form type");
2671 }
2672 obj1.free();
2674 // get bounding box
2675 dict->lookup("BBox", &bboxObj);
2676 if (!bboxObj.isArray()) {
2677 bboxObj.free();
2678 error(getPos(), "Bad form bounding box");
2679 return;
2680 }
2681 for (i = 0; i < 4; ++i) {
2682 bboxObj.arrayGet(i, &obj1);
2683 bbox[i] = obj1.getNum();
2684 obj1.free();
2685 }
2686 bboxObj.free();
2688 // get matrix
2689 dict->lookup("Matrix", &matrixObj);
2690 if (matrixObj.isArray()) {
2691 for (i = 0; i < 6; ++i) {
2692 matrixObj.arrayGet(i, &obj1);
2693 m[i] = obj1.getNum();
2694 obj1.free();
2695 }
2696 } else {
2697 m[0] = 1; m[1] = 0;
2698 m[2] = 0; m[3] = 1;
2699 m[4] = 0; m[5] = 0;
2700 }
2701 matrixObj.free();
2703 // get resources
2704 dict->lookup("Resources", &resObj);
2705 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2707 // check for a transparency group
2708 transpGroup = isolated = knockout = gFalse;
2709 blendingColorSpace = NULL;
2710 if (dict->lookup("Group", &obj1)->isDict()) {
2711 if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2712 transpGroup = gTrue;
2713 if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2714 blendingColorSpace = GfxColorSpace::parse(&obj3);
2715 }
2716 obj3.free();
2717 if (obj1.dictLookup("I", &obj3)->isBool()) {
2718 isolated = obj3.getBool();
2719 }
2720 obj3.free();
2721 if (obj1.dictLookup("K", &obj3)->isBool()) {
2722 knockout = obj3.getBool();
2723 }
2724 obj3.free();
2725 }
2726 obj2.free();
2727 }
2728 obj1.free();
2730 // draw it
2731 ++formDepth;
2732 doForm1(str, resDict, m, bbox,
2733 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2734 --formDepth;
2736 if (blendingColorSpace) {
2737 delete blendingColorSpace;
2738 }
2739 resObj.free();
2740 }
2742 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2743 GBool transpGroup, GBool softMask,
2744 GfxColorSpace *blendingColorSpace,
2745 GBool isolated, GBool knockout,
2746 GBool alpha, Function *transferFunc,
2747 GfxColor *backdropColor) {
2748 Parser *oldParser;
2749 double oldBaseMatrix[6];
2750 int i;
2752 // push new resources on stack
2753 pushResources(resDict);
2755 // save current graphics state
2756 saveState();
2758 // kill any pre-existing path
2759 state->clearPath();
2761 // save current parser
2762 oldParser = parser;
2764 // set form transformation matrix
2765 state->concatCTM(matrix[0], matrix[1], matrix[2],
2766 matrix[3], matrix[4], matrix[5]);
2767 builder->setTransform(matrix[0], matrix[1], matrix[2],
2768 matrix[3], matrix[4], matrix[5]);
2770 // set form bounding box
2771 state->moveTo(bbox[0], bbox[1]);
2772 state->lineTo(bbox[2], bbox[1]);
2773 state->lineTo(bbox[2], bbox[3]);
2774 state->lineTo(bbox[0], bbox[3]);
2775 state->closePath();
2776 state->clip();
2777 builder->clip(state);
2778 state->clearPath();
2780 if (softMask || transpGroup) {
2781 if (state->getBlendMode() != gfxBlendNormal) {
2782 state->setBlendMode(gfxBlendNormal);
2783 }
2784 if (state->getFillOpacity() != 1) {
2785 state->setFillOpacity(1);
2786 }
2787 if (state->getStrokeOpacity() != 1) {
2788 state->setStrokeOpacity(1);
2789 }
2790 //out->clearSoftMask(state);
2791 //out->beginTransparencyGroup(state, bbox, blendingColorSpace,
2792 // isolated, knockout, softMask);
2793 }
2795 // set new base matrix
2796 for (i = 0; i < 6; ++i) {
2797 oldBaseMatrix[i] = baseMatrix[i];
2798 baseMatrix[i] = state->getCTM()[i];
2799 }
2801 // draw the form
2802 parse(str, gFalse);
2804 if (softMask || transpGroup) {
2805 //out->endTransparencyGroup(state);
2806 }
2808 // restore base matrix
2809 for (i = 0; i < 6; ++i) {
2810 baseMatrix[i] = oldBaseMatrix[i];
2811 }
2813 // restore parser
2814 parser = oldParser;
2816 // restore graphics state
2817 restoreState();
2819 // pop resource stack
2820 popResources();
2822 if (softMask) {
2823 //out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2824 } else if (transpGroup) {
2825 //out->paintTransparencyGroup(state, bbox);
2826 }
2828 return;
2829 }
2831 //------------------------------------------------------------------------
2832 // in-line image operators
2833 //------------------------------------------------------------------------
2835 void PdfParser::opBeginImage(Object args[], int numArgs) {
2836 Stream *str;
2837 int c1, c2;
2839 // build dict/stream
2840 str = buildImageStream();
2842 // display the image
2843 if (str) {
2844 doImage(NULL, str, gTrue);
2846 // skip 'EI' tag
2847 c1 = str->getUndecodedStream()->getChar();
2848 c2 = str->getUndecodedStream()->getChar();
2849 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2850 c1 = c2;
2851 c2 = str->getUndecodedStream()->getChar();
2852 }
2853 delete str;
2854 }
2855 }
2857 Stream *PdfParser::buildImageStream() {
2858 Object dict;
2859 Object obj;
2860 char *key;
2861 Stream *str;
2863 // build dictionary
2864 dict.initDict(xref);
2865 parser->getObj(&obj);
2866 while (!obj.isCmd("ID") && !obj.isEOF()) {
2867 if (!obj.isName()) {
2868 error(getPos(), "Inline image dictionary key must be a name object");
2869 obj.free();
2870 } else {
2871 key = copyString(obj.getName());
2872 obj.free();
2873 parser->getObj(&obj);
2874 if (obj.isEOF() || obj.isError()) {
2875 gfree(key);
2876 break;
2877 }
2878 dict.dictAdd(key, &obj);
2879 }
2880 parser->getObj(&obj);
2881 }
2882 if (obj.isEOF()) {
2883 error(getPos(), "End of file in inline image");
2884 obj.free();
2885 dict.free();
2886 return NULL;
2887 }
2888 obj.free();
2890 // make stream
2891 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2892 str = str->addFilters(&dict);
2894 return str;
2895 }
2897 void PdfParser::opImageData(Object args[], int numArgs) {
2898 error(getPos(), "Internal: got 'ID' operator");
2899 }
2901 void PdfParser::opEndImage(Object args[], int numArgs) {
2902 error(getPos(), "Internal: got 'EI' operator");
2903 }
2905 //------------------------------------------------------------------------
2906 // type 3 font operators
2907 //------------------------------------------------------------------------
2909 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2910 }
2912 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2913 }
2915 //------------------------------------------------------------------------
2916 // compatibility operators
2917 //------------------------------------------------------------------------
2919 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2920 ++ignoreUndef;
2921 }
2923 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2924 if (ignoreUndef > 0)
2925 --ignoreUndef;
2926 }
2928 //------------------------------------------------------------------------
2929 // marked content operators
2930 //------------------------------------------------------------------------
2932 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2933 if (printCommands) {
2934 printf(" marked content: %s ", args[0].getName());
2935 if (numArgs == 2)
2936 args[2].print(stdout);
2937 printf("\n");
2938 fflush(stdout);
2939 }
2941 if(numArgs == 2) {
2942 //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2943 } else {
2944 //out->beginMarkedContent(args[0].getName());
2945 }
2946 }
2948 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2949 //out->endMarkedContent();
2950 }
2952 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2953 if (printCommands) {
2954 printf(" mark point: %s ", args[0].getName());
2955 if (numArgs == 2)
2956 args[2].print(stdout);
2957 printf("\n");
2958 fflush(stdout);
2959 }
2961 if(numArgs == 2) {
2962 //out->markPoint(args[0].getName(),args[1].getDict());
2963 } else {
2964 //out->markPoint(args[0].getName());
2965 }
2967 }
2969 //------------------------------------------------------------------------
2970 // misc
2971 //------------------------------------------------------------------------
2973 void PdfParser::saveState() {
2974 builder->saveState();
2975 state = state->save();
2976 }
2978 void PdfParser::restoreState() {
2979 state = state->restore();
2980 builder->restoreState();
2981 }
2983 void PdfParser::pushResources(Dict *resDict) {
2984 res = new GfxResources(xref, resDict, res);
2985 }
2987 void PdfParser::popResources() {
2988 GfxResources *resPtr;
2990 resPtr = res->getNext();
2991 delete res;
2992 res = resPtr;
2993 }
2995 #endif /* HAVE_POPPLER */