]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/Gfx.cxx
The pdftops filter now minimizes print processing of PDF files
[thirdparty/cups.git] / pdftops / Gfx.cxx
1 //========================================================================
2 //
3 // Gfx.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <stddef.h>
18 #include <string.h>
19 #include <math.h>
20 #include "gmem.h"
21 #include "GlobalParams.h"
22 #include "CharTypes.h"
23 #include "Object.h"
24 #include "Array.h"
25 #include "Dict.h"
26 #include "Stream.h"
27 #include "Lexer.h"
28 #include "Parser.h"
29 #include "GfxFont.h"
30 #include "GfxState.h"
31 #include "OutputDev.h"
32 #include "Page.h"
33 #include "Error.h"
34 #include "Gfx.h"
35
36 // the MSVC math.h doesn't define this
37 #ifndef M_PI
38 #define M_PI 3.14159265358979323846
39 #endif
40
41 //------------------------------------------------------------------------
42 // constants
43 //------------------------------------------------------------------------
44
45 // Max recursive depth for a function shading fill.
46 #define functionMaxDepth 6
47
48 // Max delta allowed in any color component for a function shading fill.
49 #define functionColorDelta (dblToCol(1 / 256.0))
50
51 // Max number of splits along the t axis for an axial shading fill.
52 #define axialMaxSplits 256
53
54 // Max delta allowed in any color component for an axial shading fill.
55 #define axialColorDelta (dblToCol(1 / 256.0))
56
57 // Max number of splits along the t axis for a radial shading fill.
58 #define radialMaxSplits 256
59
60 // Max delta allowed in any color component for a radial shading fill.
61 #define radialColorDelta (dblToCol(1 / 256.0))
62
63 // Max recursive depth for a Gouraud triangle shading fill.
64 #define gouraudMaxDepth 4
65
66 // Max delta allowed in any color component for a Gouraud triangle
67 // shading fill.
68 #define gouraudColorDelta (dblToCol(1 / 256.0))
69
70 // Max recursive depth for a patch mesh shading fill.
71 #define patchMaxDepth 6
72
73 // Max delta allowed in any color component for a patch mesh shading
74 // fill.
75 #define patchColorDelta (dblToCol(1 / 256.0))
76
77 //------------------------------------------------------------------------
78 // Operator table
79 //------------------------------------------------------------------------
80
81 #ifdef WIN32 // this works around a bug in the VC7 compiler
82 # pragma optimize("",off)
83 #endif
84
85 Operator Gfx::opTab[] = {
86 {"\"", 3, {tchkNum, tchkNum, tchkString},
87 &Gfx::opMoveSetShowText},
88 {"'", 1, {tchkString},
89 &Gfx::opMoveShowText},
90 {"B", 0, {tchkNone},
91 &Gfx::opFillStroke},
92 {"B*", 0, {tchkNone},
93 &Gfx::opEOFillStroke},
94 {"BDC", 2, {tchkName, tchkProps},
95 &Gfx::opBeginMarkedContent},
96 {"BI", 0, {tchkNone},
97 &Gfx::opBeginImage},
98 {"BMC", 1, {tchkName},
99 &Gfx::opBeginMarkedContent},
100 {"BT", 0, {tchkNone},
101 &Gfx::opBeginText},
102 {"BX", 0, {tchkNone},
103 &Gfx::opBeginIgnoreUndef},
104 {"CS", 1, {tchkName},
105 &Gfx::opSetStrokeColorSpace},
106 {"DP", 2, {tchkName, tchkProps},
107 &Gfx::opMarkPoint},
108 {"Do", 1, {tchkName},
109 &Gfx::opXObject},
110 {"EI", 0, {tchkNone},
111 &Gfx::opEndImage},
112 {"EMC", 0, {tchkNone},
113 &Gfx::opEndMarkedContent},
114 {"ET", 0, {tchkNone},
115 &Gfx::opEndText},
116 {"EX", 0, {tchkNone},
117 &Gfx::opEndIgnoreUndef},
118 {"F", 0, {tchkNone},
119 &Gfx::opFill},
120 {"G", 1, {tchkNum},
121 &Gfx::opSetStrokeGray},
122 {"ID", 0, {tchkNone},
123 &Gfx::opImageData},
124 {"J", 1, {tchkInt},
125 &Gfx::opSetLineCap},
126 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
127 &Gfx::opSetStrokeCMYKColor},
128 {"M", 1, {tchkNum},
129 &Gfx::opSetMiterLimit},
130 {"MP", 1, {tchkName},
131 &Gfx::opMarkPoint},
132 {"Q", 0, {tchkNone},
133 &Gfx::opRestore},
134 {"RG", 3, {tchkNum, tchkNum, tchkNum},
135 &Gfx::opSetStrokeRGBColor},
136 {"S", 0, {tchkNone},
137 &Gfx::opStroke},
138 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
139 &Gfx::opSetStrokeColor},
140 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
141 tchkSCN},
142 &Gfx::opSetStrokeColorN},
143 {"T*", 0, {tchkNone},
144 &Gfx::opTextNextLine},
145 {"TD", 2, {tchkNum, tchkNum},
146 &Gfx::opTextMoveSet},
147 {"TJ", 1, {tchkArray},
148 &Gfx::opShowSpaceText},
149 {"TL", 1, {tchkNum},
150 &Gfx::opSetTextLeading},
151 {"Tc", 1, {tchkNum},
152 &Gfx::opSetCharSpacing},
153 {"Td", 2, {tchkNum, tchkNum},
154 &Gfx::opTextMove},
155 {"Tf", 2, {tchkName, tchkNum},
156 &Gfx::opSetFont},
157 {"Tj", 1, {tchkString},
158 &Gfx::opShowText},
159 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
160 tchkNum, tchkNum},
161 &Gfx::opSetTextMatrix},
162 {"Tr", 1, {tchkInt},
163 &Gfx::opSetTextRender},
164 {"Ts", 1, {tchkNum},
165 &Gfx::opSetTextRise},
166 {"Tw", 1, {tchkNum},
167 &Gfx::opSetWordSpacing},
168 {"Tz", 1, {tchkNum},
169 &Gfx::opSetHorizScaling},
170 {"W", 0, {tchkNone},
171 &Gfx::opClip},
172 {"W*", 0, {tchkNone},
173 &Gfx::opEOClip},
174 {"b", 0, {tchkNone},
175 &Gfx::opCloseFillStroke},
176 {"b*", 0, {tchkNone},
177 &Gfx::opCloseEOFillStroke},
178 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
179 tchkNum, tchkNum},
180 &Gfx::opCurveTo},
181 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
182 tchkNum, tchkNum},
183 &Gfx::opConcat},
184 {"cs", 1, {tchkName},
185 &Gfx::opSetFillColorSpace},
186 {"d", 2, {tchkArray, tchkNum},
187 &Gfx::opSetDash},
188 {"d0", 2, {tchkNum, tchkNum},
189 &Gfx::opSetCharWidth},
190 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
191 tchkNum, tchkNum},
192 &Gfx::opSetCacheDevice},
193 {"f", 0, {tchkNone},
194 &Gfx::opFill},
195 {"f*", 0, {tchkNone},
196 &Gfx::opEOFill},
197 {"g", 1, {tchkNum},
198 &Gfx::opSetFillGray},
199 {"gs", 1, {tchkName},
200 &Gfx::opSetExtGState},
201 {"h", 0, {tchkNone},
202 &Gfx::opClosePath},
203 {"i", 1, {tchkNum},
204 &Gfx::opSetFlat},
205 {"j", 1, {tchkInt},
206 &Gfx::opSetLineJoin},
207 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
208 &Gfx::opSetFillCMYKColor},
209 {"l", 2, {tchkNum, tchkNum},
210 &Gfx::opLineTo},
211 {"m", 2, {tchkNum, tchkNum},
212 &Gfx::opMoveTo},
213 {"n", 0, {tchkNone},
214 &Gfx::opEndPath},
215 {"q", 0, {tchkNone},
216 &Gfx::opSave},
217 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
218 &Gfx::opRectangle},
219 {"rg", 3, {tchkNum, tchkNum, tchkNum},
220 &Gfx::opSetFillRGBColor},
221 {"ri", 1, {tchkName},
222 &Gfx::opSetRenderingIntent},
223 {"s", 0, {tchkNone},
224 &Gfx::opCloseStroke},
225 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
226 &Gfx::opSetFillColor},
227 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
228 tchkSCN},
229 &Gfx::opSetFillColorN},
230 {"sh", 1, {tchkName},
231 &Gfx::opShFill},
232 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
233 &Gfx::opCurveTo1},
234 {"w", 1, {tchkNum},
235 &Gfx::opSetLineWidth},
236 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
237 &Gfx::opCurveTo2},
238 };
239
240 #ifdef WIN32 // this works around a bug in the VC7 compiler
241 # pragma optimize("",on)
242 #endif
243
244 #define numOps (sizeof(opTab) / sizeof(Operator))
245
246 //------------------------------------------------------------------------
247 // GfxResources
248 //------------------------------------------------------------------------
249
250 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
251 Object obj1, obj2;
252 Ref r;
253
254 if (resDict) {
255
256 // build font dictionary
257 fonts = NULL;
258 resDict->lookupNF("Font", &obj1);
259 if (obj1.isRef()) {
260 obj1.fetch(xref, &obj2);
261 if (obj2.isDict()) {
262 r = obj1.getRef();
263 fonts = new GfxFontDict(xref, &r, obj2.getDict());
264 }
265 obj2.free();
266 } else if (obj1.isDict()) {
267 fonts = new GfxFontDict(xref, NULL, obj1.getDict());
268 }
269 obj1.free();
270
271 // get XObject dictionary
272 resDict->lookup("XObject", &xObjDict);
273
274 // get color space dictionary
275 resDict->lookup("ColorSpace", &colorSpaceDict);
276
277 // get pattern dictionary
278 resDict->lookup("Pattern", &patternDict);
279
280 // get shading dictionary
281 resDict->lookup("Shading", &shadingDict);
282
283 // get graphics state parameter dictionary
284 resDict->lookup("ExtGState", &gStateDict);
285
286 } else {
287 fonts = NULL;
288 xObjDict.initNull();
289 colorSpaceDict.initNull();
290 patternDict.initNull();
291 shadingDict.initNull();
292 gStateDict.initNull();
293 }
294
295 next = nextA;
296 }
297
298 GfxResources::~GfxResources() {
299 if (fonts) {
300 delete fonts;
301 }
302 xObjDict.free();
303 colorSpaceDict.free();
304 patternDict.free();
305 shadingDict.free();
306 gStateDict.free();
307 }
308
309 GfxFont *GfxResources::lookupFont(char *name) {
310 GfxFont *font;
311 GfxResources *resPtr;
312
313 for (resPtr = this; resPtr; resPtr = resPtr->next) {
314 if (resPtr->fonts) {
315 if ((font = resPtr->fonts->lookup(name)))
316 return font;
317 }
318 }
319 error(-1, "Unknown font tag '%s'", name);
320 return NULL;
321 }
322
323 GBool GfxResources::lookupXObject(char *name, Object *obj) {
324 GfxResources *resPtr;
325
326 for (resPtr = this; resPtr; resPtr = resPtr->next) {
327 if (resPtr->xObjDict.isDict()) {
328 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
329 return gTrue;
330 obj->free();
331 }
332 }
333 error(-1, "XObject '%s' is unknown", name);
334 return gFalse;
335 }
336
337 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
338 GfxResources *resPtr;
339
340 for (resPtr = this; resPtr; resPtr = resPtr->next) {
341 if (resPtr->xObjDict.isDict()) {
342 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
343 return gTrue;
344 obj->free();
345 }
346 }
347 error(-1, "XObject '%s' is unknown", name);
348 return gFalse;
349 }
350
351 void GfxResources::lookupColorSpace(char *name, Object *obj) {
352 GfxResources *resPtr;
353
354 for (resPtr = this; resPtr; resPtr = resPtr->next) {
355 if (resPtr->colorSpaceDict.isDict()) {
356 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
357 return;
358 }
359 obj->free();
360 }
361 }
362 obj->initNull();
363 }
364
365 GfxPattern *GfxResources::lookupPattern(char *name) {
366 GfxResources *resPtr;
367 GfxPattern *pattern;
368 Object obj;
369
370 for (resPtr = this; resPtr; resPtr = resPtr->next) {
371 if (resPtr->patternDict.isDict()) {
372 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
373 pattern = GfxPattern::parse(&obj);
374 obj.free();
375 return pattern;
376 }
377 obj.free();
378 }
379 }
380 error(-1, "Unknown pattern '%s'", name);
381 return NULL;
382 }
383
384 GfxShading *GfxResources::lookupShading(char *name) {
385 GfxResources *resPtr;
386 GfxShading *shading;
387 Object obj;
388
389 for (resPtr = this; resPtr; resPtr = resPtr->next) {
390 if (resPtr->shadingDict.isDict()) {
391 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
392 shading = GfxShading::parse(&obj);
393 obj.free();
394 return shading;
395 }
396 obj.free();
397 }
398 }
399 error(-1, "Unknown shading '%s'", name);
400 return NULL;
401 }
402
403 GBool GfxResources::lookupGState(char *name, Object *obj) {
404 GfxResources *resPtr;
405
406 for (resPtr = this; resPtr; resPtr = resPtr->next) {
407 if (resPtr->gStateDict.isDict()) {
408 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
409 return gTrue;
410 }
411 obj->free();
412 }
413 }
414 error(-1, "ExtGState '%s' is unknown", name);
415 return gFalse;
416 }
417
418 //------------------------------------------------------------------------
419 // Gfx
420 //------------------------------------------------------------------------
421
422 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
423 double hDPI, double vDPI, PDFRectangle *box,
424 PDFRectangle *cropBox, int rotate,
425 GBool (*abortCheckCbkA)(void *data),
426 void *abortCheckCbkDataA) {
427 int i;
428
429 xref = xrefA;
430 subPage = gFalse;
431 printCommands = globalParams->getPrintCommands();
432
433 // start the resource stack
434 res = new GfxResources(xref, resDict, NULL);
435
436 // initialize
437 out = outA;
438 state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
439 fontChanged = gFalse;
440 clip = clipNone;
441 ignoreUndef = 0;
442 renderThisPage = out->startPage(pageNum, state);
443 if (!renderThisPage)
444 return;
445
446 out->setDefaultCTM(state->getCTM());
447 out->updateAll(state);
448 for (i = 0; i < 6; ++i) {
449 baseMatrix[i] = state->getCTM()[i];
450 }
451 formDepth = 0;
452 abortCheckCbk = abortCheckCbkA;
453 abortCheckCbkData = abortCheckCbkDataA;
454
455 // set crop box
456 if (cropBox) {
457 state->moveTo(cropBox->x1, cropBox->y1);
458 state->lineTo(cropBox->x2, cropBox->y1);
459 state->lineTo(cropBox->x2, cropBox->y2);
460 state->lineTo(cropBox->x1, cropBox->y2);
461 state->closePath();
462 state->clip();
463 out->clip(state);
464 state->clearPath();
465 }
466 }
467
468 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
469 PDFRectangle *box, PDFRectangle *cropBox,
470 GBool (*abortCheckCbkA)(void *data),
471 void *abortCheckCbkDataA) {
472 int i;
473
474 xref = xrefA;
475 subPage = gTrue;
476 printCommands = globalParams->getPrintCommands();
477 renderThisPage = gTrue;
478
479 // start the resource stack
480 res = new GfxResources(xref, resDict, NULL);
481
482 // initialize
483 out = outA;
484 state = new GfxState(72, 72, box, 0, gFalse);
485 fontChanged = gFalse;
486 clip = clipNone;
487 ignoreUndef = 0;
488 for (i = 0; i < 6; ++i) {
489 baseMatrix[i] = state->getCTM()[i];
490 }
491 formDepth = 0;
492 abortCheckCbk = abortCheckCbkA;
493 abortCheckCbkData = abortCheckCbkDataA;
494
495 // set crop box
496 if (cropBox) {
497 state->moveTo(cropBox->x1, cropBox->y1);
498 state->lineTo(cropBox->x2, cropBox->y1);
499 state->lineTo(cropBox->x2, cropBox->y2);
500 state->lineTo(cropBox->x1, cropBox->y2);
501 state->closePath();
502 state->clip();
503 out->clip(state);
504 state->clearPath();
505 }
506 }
507
508 Gfx::~Gfx() {
509 while (state->hasSaves()) {
510 restoreState();
511 }
512 if (!subPage) {
513 out->endPage();
514 }
515 while (res) {
516 popResources();
517 }
518 if (state) {
519 delete state;
520 }
521 }
522
523 void Gfx::display(Object *obj, GBool topLevel) {
524 Object obj2;
525 int i;
526
527 if (!renderThisPage)
528 return;
529
530 if (obj->isArray()) {
531 for (i = 0; i < obj->arrayGetLength(); ++i) {
532 obj->arrayGet(i, &obj2);
533 if (!obj2.isStream()) {
534 error(-1, "Weird page contents");
535 obj2.free();
536 return;
537 }
538 obj2.free();
539 }
540 } else if (!obj->isStream()) {
541 error(-1, "Weird page contents");
542 return;
543 }
544 parser = new Parser(xref, new Lexer(xref, obj));
545 go(topLevel);
546 delete parser;
547 parser = NULL;
548 }
549
550 void Gfx::go(GBool topLevel) {
551 Object obj;
552 Object args[maxArgs];
553 int numArgs, i;
554 int lastAbortCheck;
555
556 // scan a sequence of objects
557 updateLevel = lastAbortCheck = 0;
558 numArgs = 0;
559 parser->getObj(&obj);
560 while (!obj.isEOF()) {
561
562 // got a command - execute it
563 if (obj.isCmd()) {
564 if (printCommands) {
565 obj.print(stdout);
566 for (i = 0; i < numArgs; ++i) {
567 printf(" ");
568 args[i].print(stdout);
569 }
570 printf("\n");
571 fflush(stdout);
572 }
573 execOp(&obj, args, numArgs);
574 obj.free();
575 for (i = 0; i < numArgs; ++i)
576 args[i].free();
577 numArgs = 0;
578
579 // periodically update display
580 if (++updateLevel >= 20000) {
581 out->dump();
582 updateLevel = 0;
583 }
584
585 // check for an abort
586 if (abortCheckCbk) {
587 if (updateLevel - lastAbortCheck > 10) {
588 if ((*abortCheckCbk)(abortCheckCbkData)) {
589 break;
590 }
591 lastAbortCheck = updateLevel;
592 }
593 }
594
595 // got an argument - save it
596 } else if (numArgs < maxArgs) {
597 args[numArgs++] = obj;
598
599 // too many arguments - something is wrong
600 } else {
601 error(getPos(), "Too many args in content stream");
602 if (printCommands) {
603 printf("throwing away arg: ");
604 obj.print(stdout);
605 printf("\n");
606 fflush(stdout);
607 }
608 obj.free();
609 }
610
611 // grab the next object
612 parser->getObj(&obj);
613 }
614 obj.free();
615
616 // args at end with no command
617 if (numArgs > 0) {
618 error(getPos(), "Leftover args in content stream");
619 if (printCommands) {
620 printf("%d leftovers:", numArgs);
621 for (i = 0; i < numArgs; ++i) {
622 printf(" ");
623 args[i].print(stdout);
624 }
625 printf("\n");
626 fflush(stdout);
627 }
628 for (i = 0; i < numArgs; ++i)
629 args[i].free();
630 }
631
632 // update display
633 if (topLevel && updateLevel > 0) {
634 out->dump();
635 }
636 }
637
638 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
639 Operator *op;
640 char *name;
641 Object *argPtr;
642 int i;
643
644 // find operator
645 name = cmd->getCmd();
646 if (!(op = findOp(name))) {
647 if (ignoreUndef == 0)
648 error(getPos(), "Unknown operator '%s'", name);
649 return;
650 }
651
652 // type check args
653 argPtr = args;
654 if (op->numArgs >= 0) {
655 if (numArgs < op->numArgs) {
656 error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
657 return;
658 }
659 if (numArgs > op->numArgs) {
660 #if 0
661 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
662 #endif
663 argPtr += numArgs - op->numArgs;
664 numArgs = op->numArgs;
665 }
666 } else {
667 if (numArgs > -op->numArgs) {
668 error(getPos(), "Too many (%d) args to '%s' operator",
669 numArgs, name);
670 return;
671 }
672 }
673 for (i = 0; i < numArgs; ++i) {
674 if (!checkArg(&argPtr[i], op->tchk[i])) {
675 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
676 i, name, argPtr[i].getTypeName());
677 return;
678 }
679 }
680
681 // do it
682 (this->*op->func)(argPtr, numArgs);
683 }
684
685 Operator *Gfx::findOp(char *name) {
686 int a, b, m, cmp;
687
688 a = -1;
689 b = numOps;
690 // invariant: opTab[a] < name < opTab[b]
691 while (b - a > 1) {
692 m = (a + b) / 2;
693 cmp = strcmp(opTab[m].name, name);
694 if (cmp < 0)
695 a = m;
696 else if (cmp > 0)
697 b = m;
698 else
699 a = b = m;
700 }
701 if (cmp != 0)
702 return NULL;
703 return &opTab[a];
704 }
705
706 GBool Gfx::checkArg(Object *arg, TchkType type) {
707 switch (type) {
708 case tchkBool: return arg->isBool();
709 case tchkInt: return arg->isInt();
710 case tchkNum: return arg->isNum();
711 case tchkString: return arg->isString();
712 case tchkName: return arg->isName();
713 case tchkArray: return arg->isArray();
714 case tchkProps: return arg->isDict() || arg->isName();
715 case tchkSCN: return arg->isNum() || arg->isName();
716 case tchkNone: return gFalse;
717 }
718 return gFalse;
719 }
720
721 int Gfx::getPos() {
722 return parser ? parser->getPos() : -1;
723 }
724
725 //------------------------------------------------------------------------
726 // graphics state operators
727 //------------------------------------------------------------------------
728
729 void Gfx::opSave(Object args[], int numArgs) {
730 saveState();
731 }
732
733 void Gfx::opRestore(Object args[], int numArgs) {
734 restoreState();
735 }
736
737 void Gfx::opConcat(Object args[], int numArgs) {
738 state->concatCTM(args[0].getNum(), args[1].getNum(),
739 args[2].getNum(), args[3].getNum(),
740 args[4].getNum(), args[5].getNum());
741 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
742 args[2].getNum(), args[3].getNum(),
743 args[4].getNum(), args[5].getNum());
744 fontChanged = gTrue;
745 }
746
747 void Gfx::opSetDash(Object args[], int numArgs) {
748 Array *a;
749 int length;
750 Object obj;
751 double *dash;
752 int i;
753
754 a = args[0].getArray();
755 length = a->getLength();
756 if (length == 0) {
757 dash = NULL;
758 } else {
759 dash = (double *)gmallocn(length, sizeof(double));
760 for (i = 0; i < length; ++i) {
761 dash[i] = a->get(i, &obj)->getNum();
762 obj.free();
763 }
764 }
765 state->setLineDash(dash, length, args[1].getNum());
766 out->updateLineDash(state);
767 }
768
769 void Gfx::opSetFlat(Object args[], int numArgs) {
770 state->setFlatness((int)args[0].getNum());
771 out->updateFlatness(state);
772 }
773
774 void Gfx::opSetLineJoin(Object args[], int numArgs) {
775 state->setLineJoin(args[0].getInt());
776 out->updateLineJoin(state);
777 }
778
779 void Gfx::opSetLineCap(Object args[], int numArgs) {
780 state->setLineCap(args[0].getInt());
781 out->updateLineCap(state);
782 }
783
784 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
785 state->setMiterLimit(args[0].getNum());
786 out->updateMiterLimit(state);
787 }
788
789 void Gfx::opSetLineWidth(Object args[], int numArgs) {
790 state->setLineWidth(args[0].getNum());
791 out->updateLineWidth(state);
792 }
793
794 void Gfx::opSetExtGState(Object args[], int numArgs) {
795 Object obj1, obj2;
796 GfxBlendMode mode;
797 GBool haveFillOP;
798
799 if (!res->lookupGState(args[0].getName(), &obj1)) {
800 return;
801 }
802 if (!obj1.isDict()) {
803 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
804 obj1.free();
805 return;
806 }
807
808 // transparency support: blend mode, fill/stroke opacity
809 if (!obj1.dictLookup("BM", &obj2)->isNull()) {
810 if (state->parseBlendMode(&obj2, &mode)) {
811 state->setBlendMode(mode);
812 out->updateBlendMode(state);
813 } else {
814 error(getPos(), "Invalid blend mode in ExtGState");
815 }
816 }
817 obj2.free();
818 if (obj1.dictLookup("ca", &obj2)->isNum()) {
819 state->setFillOpacity(obj2.getNum());
820 out->updateFillOpacity(state);
821 }
822 obj2.free();
823 if (obj1.dictLookup("CA", &obj2)->isNum()) {
824 state->setStrokeOpacity(obj2.getNum());
825 out->updateStrokeOpacity(state);
826 }
827 obj2.free();
828
829 // fill/stroke overprint
830 if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
831 state->setFillOverprint(obj2.getBool());
832 out->updateFillOverprint(state);
833 }
834 obj2.free();
835 if (obj1.dictLookup("OP", &obj2)->isBool()) {
836 state->setStrokeOverprint(obj2.getBool());
837 out->updateStrokeOverprint(state);
838 if (!haveFillOP) {
839 state->setFillOverprint(obj2.getBool());
840 out->updateFillOverprint(state);
841 }
842 }
843 obj2.free();
844
845 obj1.free();
846 }
847
848 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
849 }
850
851 //------------------------------------------------------------------------
852 // color operators
853 //------------------------------------------------------------------------
854
855 void Gfx::opSetFillGray(Object args[], int numArgs) {
856 GfxColor color;
857
858 state->setFillPattern(NULL);
859 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
860 out->updateFillColorSpace(state);
861 color.c[0] = dblToCol(args[0].getNum());
862 state->setFillColor(&color);
863 out->updateFillColor(state);
864 }
865
866 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
867 GfxColor color;
868
869 state->setStrokePattern(NULL);
870 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
871 out->updateStrokeColorSpace(state);
872 color.c[0] = dblToCol(args[0].getNum());
873 state->setStrokeColor(&color);
874 out->updateStrokeColor(state);
875 }
876
877 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
878 GfxColor color;
879 int i;
880
881 state->setFillPattern(NULL);
882 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
883 out->updateFillColorSpace(state);
884 for (i = 0; i < 4; ++i) {
885 color.c[i] = dblToCol(args[i].getNum());
886 }
887 state->setFillColor(&color);
888 out->updateFillColor(state);
889 }
890
891 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
892 GfxColor color;
893 int i;
894
895 state->setStrokePattern(NULL);
896 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
897 out->updateStrokeColorSpace(state);
898 for (i = 0; i < 4; ++i) {
899 color.c[i] = dblToCol(args[i].getNum());
900 }
901 state->setStrokeColor(&color);
902 out->updateStrokeColor(state);
903 }
904
905 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
906 GfxColor color;
907 int i;
908
909 state->setFillPattern(NULL);
910 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
911 out->updateFillColorSpace(state);
912 for (i = 0; i < 3; ++i) {
913 color.c[i] = dblToCol(args[i].getNum());
914 }
915 state->setFillColor(&color);
916 out->updateFillColor(state);
917 }
918
919 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
920 GfxColor color;
921 int i;
922
923 state->setStrokePattern(NULL);
924 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
925 out->updateStrokeColorSpace(state);
926 for (i = 0; i < 3; ++i) {
927 color.c[i] = dblToCol(args[i].getNum());
928 }
929 state->setStrokeColor(&color);
930 out->updateStrokeColor(state);
931 }
932
933 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
934 Object obj;
935 GfxColorSpace *colorSpace;
936 GfxColor color;
937 int i;
938
939 state->setFillPattern(NULL);
940 res->lookupColorSpace(args[0].getName(), &obj);
941 if (obj.isNull()) {
942 colorSpace = GfxColorSpace::parse(&args[0]);
943 } else {
944 colorSpace = GfxColorSpace::parse(&obj);
945 }
946 obj.free();
947 if (colorSpace) {
948 state->setFillColorSpace(colorSpace);
949 out->updateFillColorSpace(state);
950 } else {
951 error(getPos(), "Bad color space (fill)");
952 }
953 for (i = 0; i < gfxColorMaxComps; ++i) {
954 color.c[i] = 0;
955 }
956 state->setFillColor(&color);
957 out->updateFillColor(state);
958 }
959
960 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
961 Object obj;
962 GfxColorSpace *colorSpace;
963 GfxColor color;
964 int i;
965
966 state->setStrokePattern(NULL);
967 res->lookupColorSpace(args[0].getName(), &obj);
968 if (obj.isNull()) {
969 colorSpace = GfxColorSpace::parse(&args[0]);
970 } else {
971 colorSpace = GfxColorSpace::parse(&obj);
972 }
973 obj.free();
974 if (colorSpace) {
975 state->setStrokeColorSpace(colorSpace);
976 out->updateStrokeColorSpace(state);
977 } else {
978 error(getPos(), "Bad color space (stroke)");
979 }
980 for (i = 0; i < gfxColorMaxComps; ++i) {
981 color.c[i] = 0;
982 }
983 state->setStrokeColor(&color);
984 out->updateStrokeColor(state);
985 }
986
987 void Gfx::opSetFillColor(Object args[], int numArgs) {
988 GfxColor color;
989 int i;
990
991 state->setFillPattern(NULL);
992 for (i = 0; i < numArgs; ++i) {
993 color.c[i] = dblToCol(args[i].getNum());
994 }
995 state->setFillColor(&color);
996 out->updateFillColor(state);
997 }
998
999 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
1000 GfxColor color;
1001 int i;
1002
1003 state->setStrokePattern(NULL);
1004 for (i = 0; i < numArgs; ++i) {
1005 color.c[i] = dblToCol(args[i].getNum());
1006 }
1007 state->setStrokeColor(&color);
1008 out->updateStrokeColor(state);
1009 }
1010
1011 void Gfx::opSetFillColorN(Object args[], int numArgs) {
1012 GfxColor color;
1013 GfxPattern *pattern;
1014 int i;
1015
1016 if (state->getFillColorSpace()->getMode() == csPattern) {
1017 if (numArgs > 1) {
1018 for (i = 0; i < numArgs && i < 4; ++i) {
1019 if (args[i].isNum()) {
1020 color.c[i] = dblToCol(args[i].getNum());
1021 }
1022 }
1023 state->setFillColor(&color);
1024 out->updateFillColor(state);
1025 }
1026 if (args[numArgs-1].isName() &&
1027 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1028 state->setFillPattern(pattern);
1029 }
1030
1031 } else {
1032 state->setFillPattern(NULL);
1033 for (i = 0; i < numArgs && i < 4; ++i) {
1034 if (args[i].isNum()) {
1035 color.c[i] = dblToCol(args[i].getNum());
1036 }
1037 }
1038 state->setFillColor(&color);
1039 out->updateFillColor(state);
1040 }
1041 }
1042
1043 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
1044 GfxColor color;
1045 GfxPattern *pattern;
1046 int i;
1047
1048 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1049 if (numArgs > 1) {
1050 for (i = 0; i < numArgs && i < 4; ++i) {
1051 if (args[i].isNum()) {
1052 color.c[i] = dblToCol(args[i].getNum());
1053 }
1054 }
1055 state->setStrokeColor(&color);
1056 out->updateStrokeColor(state);
1057 }
1058 if (args[numArgs-1].isName() &&
1059 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1060 state->setStrokePattern(pattern);
1061 }
1062
1063 } else {
1064 state->setStrokePattern(NULL);
1065 for (i = 0; i < numArgs && i < 4; ++i) {
1066 if (args[i].isNum()) {
1067 color.c[i] = dblToCol(args[i].getNum());
1068 }
1069 }
1070 state->setStrokeColor(&color);
1071 out->updateStrokeColor(state);
1072 }
1073 }
1074
1075 //------------------------------------------------------------------------
1076 // path segment operators
1077 //------------------------------------------------------------------------
1078
1079 void Gfx::opMoveTo(Object args[], int numArgs) {
1080 state->moveTo(args[0].getNum(), args[1].getNum());
1081 }
1082
1083 void Gfx::opLineTo(Object args[], int numArgs) {
1084 if (!state->isCurPt()) {
1085 error(getPos(), "No current point in lineto");
1086 return;
1087 }
1088 state->lineTo(args[0].getNum(), args[1].getNum());
1089 }
1090
1091 void Gfx::opCurveTo(Object args[], int numArgs) {
1092 double x1, y1, x2, y2, x3, y3;
1093
1094 if (!state->isCurPt()) {
1095 error(getPos(), "No current point in curveto");
1096 return;
1097 }
1098 x1 = args[0].getNum();
1099 y1 = args[1].getNum();
1100 x2 = args[2].getNum();
1101 y2 = args[3].getNum();
1102 x3 = args[4].getNum();
1103 y3 = args[5].getNum();
1104 state->curveTo(x1, y1, x2, y2, x3, y3);
1105 }
1106
1107 void Gfx::opCurveTo1(Object args[], int numArgs) {
1108 double x1, y1, x2, y2, x3, y3;
1109
1110 if (!state->isCurPt()) {
1111 error(getPos(), "No current point in curveto1");
1112 return;
1113 }
1114 x1 = state->getCurX();
1115 y1 = state->getCurY();
1116 x2 = args[0].getNum();
1117 y2 = args[1].getNum();
1118 x3 = args[2].getNum();
1119 y3 = args[3].getNum();
1120 state->curveTo(x1, y1, x2, y2, x3, y3);
1121 }
1122
1123 void Gfx::opCurveTo2(Object args[], int numArgs) {
1124 double x1, y1, x2, y2, x3, y3;
1125
1126 if (!state->isCurPt()) {
1127 error(getPos(), "No current point in curveto2");
1128 return;
1129 }
1130 x1 = args[0].getNum();
1131 y1 = args[1].getNum();
1132 x2 = args[2].getNum();
1133 y2 = args[3].getNum();
1134 x3 = x2;
1135 y3 = y2;
1136 state->curveTo(x1, y1, x2, y2, x3, y3);
1137 }
1138
1139 void Gfx::opRectangle(Object args[], int numArgs) {
1140 double x, y, w, h;
1141
1142 x = args[0].getNum();
1143 y = args[1].getNum();
1144 w = args[2].getNum();
1145 h = args[3].getNum();
1146 state->moveTo(x, y);
1147 state->lineTo(x + w, y);
1148 state->lineTo(x + w, y + h);
1149 state->lineTo(x, y + h);
1150 state->closePath();
1151 }
1152
1153 void Gfx::opClosePath(Object args[], int numArgs) {
1154 if (!state->isCurPt()) {
1155 error(getPos(), "No current point in closepath");
1156 return;
1157 }
1158 state->closePath();
1159 }
1160
1161 //------------------------------------------------------------------------
1162 // path painting operators
1163 //------------------------------------------------------------------------
1164
1165 void Gfx::opEndPath(Object args[], int numArgs) {
1166 doEndPath();
1167 }
1168
1169 void Gfx::opStroke(Object args[], int numArgs) {
1170 if (!state->isCurPt()) {
1171 //error(getPos(), "No path in stroke");
1172 return;
1173 }
1174 if (state->isPath())
1175 out->stroke(state);
1176 doEndPath();
1177 }
1178
1179 void Gfx::opCloseStroke(Object args[], int numArgs) {
1180 if (!state->isCurPt()) {
1181 //error(getPos(), "No path in closepath/stroke");
1182 return;
1183 }
1184 if (state->isPath()) {
1185 state->closePath();
1186 out->stroke(state);
1187 }
1188 doEndPath();
1189 }
1190
1191 void Gfx::opFill(Object args[], int numArgs) {
1192 if (!state->isCurPt()) {
1193 //error(getPos(), "No path in fill");
1194 return;
1195 }
1196 if (state->isPath()) {
1197 if (state->getFillColorSpace()->getMode() == csPattern) {
1198 doPatternFill(gFalse);
1199 } else {
1200 out->fill(state);
1201 }
1202 }
1203 doEndPath();
1204 }
1205
1206 void Gfx::opEOFill(Object args[], int numArgs) {
1207 if (!state->isCurPt()) {
1208 //error(getPos(), "No path in eofill");
1209 return;
1210 }
1211 if (state->isPath()) {
1212 if (state->getFillColorSpace()->getMode() == csPattern) {
1213 doPatternFill(gTrue);
1214 } else {
1215 out->eoFill(state);
1216 }
1217 }
1218 doEndPath();
1219 }
1220
1221 void Gfx::opFillStroke(Object args[], int numArgs) {
1222 if (!state->isCurPt()) {
1223 //error(getPos(), "No path in fill/stroke");
1224 return;
1225 }
1226 if (state->isPath()) {
1227 if (state->getFillColorSpace()->getMode() == csPattern) {
1228 doPatternFill(gFalse);
1229 } else {
1230 out->fill(state);
1231 }
1232 out->stroke(state);
1233 }
1234 doEndPath();
1235 }
1236
1237 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1238 if (!state->isCurPt()) {
1239 //error(getPos(), "No path in closepath/fill/stroke");
1240 return;
1241 }
1242 if (state->isPath()) {
1243 state->closePath();
1244 if (state->getFillColorSpace()->getMode() == csPattern) {
1245 doPatternFill(gFalse);
1246 } else {
1247 out->fill(state);
1248 }
1249 out->stroke(state);
1250 }
1251 doEndPath();
1252 }
1253
1254 void Gfx::opEOFillStroke(Object args[], int numArgs) {
1255 if (!state->isCurPt()) {
1256 //error(getPos(), "No path in eofill/stroke");
1257 return;
1258 }
1259 if (state->isPath()) {
1260 if (state->getFillColorSpace()->getMode() == csPattern) {
1261 doPatternFill(gTrue);
1262 } else {
1263 out->eoFill(state);
1264 }
1265 out->stroke(state);
1266 }
1267 doEndPath();
1268 }
1269
1270 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1271 if (!state->isCurPt()) {
1272 //error(getPos(), "No path in closepath/eofill/stroke");
1273 return;
1274 }
1275 if (state->isPath()) {
1276 state->closePath();
1277 if (state->getFillColorSpace()->getMode() == csPattern) {
1278 doPatternFill(gTrue);
1279 } else {
1280 out->eoFill(state);
1281 }
1282 out->stroke(state);
1283 }
1284 doEndPath();
1285 }
1286
1287 void Gfx::doPatternFill(GBool eoFill) {
1288 GfxPattern *pattern;
1289
1290 // this is a bit of a kludge -- patterns can be really slow, so we
1291 // skip them if we're only doing text extraction, since they almost
1292 // certainly don't contain any text
1293 if (!out->needNonText()) {
1294 return;
1295 }
1296
1297 if (!(pattern = state->getFillPattern())) {
1298 return;
1299 }
1300 switch (pattern->getType()) {
1301 case 1:
1302 doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
1303 break;
1304 case 2:
1305 doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
1306 break;
1307 default:
1308 error(getPos(), "Unimplemented pattern type (%d) in fill",
1309 pattern->getType());
1310 break;
1311 }
1312 }
1313
1314 void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
1315 GfxPatternColorSpace *patCS;
1316 GfxColorSpace *cs;
1317 GfxPath *savedPath;
1318 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1319 double cxMin, cyMin, cxMax, cyMax;
1320 int xi0, yi0, xi1, yi1, xi, yi;
1321 double *ctm, *btm, *ptm;
1322 double m[6], ictm[6], m1[6], imb[6];
1323 double det;
1324 double xstep, ystep;
1325 int i;
1326
1327 // get color space
1328 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1329
1330 // construct a (pattern space) -> (current space) transform matrix
1331 ctm = state->getCTM();
1332 btm = baseMatrix;
1333 ptm = tPat->getMatrix();
1334 // iCTM = invert CTM
1335 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1336 ictm[0] = ctm[3] * det;
1337 ictm[1] = -ctm[1] * det;
1338 ictm[2] = -ctm[2] * det;
1339 ictm[3] = ctm[0] * det;
1340 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1341 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1342 // m1 = PTM * BTM = PTM * base transform matrix
1343 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1344 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1345 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1346 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1347 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1348 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1349 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1350 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1351 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1352 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1353 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1354 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1355 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1356
1357 // construct a (device space) -> (pattern space) transform matrix
1358 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1359 imb[0] = m1[3] * det;
1360 imb[1] = -m1[1] * det;
1361 imb[2] = -m1[2] * det;
1362 imb[3] = m1[0] * det;
1363 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1364 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1365
1366 // save current graphics state
1367 savedPath = state->getPath()->copy();
1368 saveState();
1369
1370 // set underlying color space (for uncolored tiling patterns); set
1371 // various other parameters (stroke color, line width) to match
1372 // Adobe's behavior
1373 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1374 state->setFillColorSpace(cs->copy());
1375 out->updateFillColorSpace(state);
1376 state->setStrokeColorSpace(cs->copy());
1377 out->updateStrokeColorSpace(state);
1378 state->setStrokeColor(state->getFillColor());
1379 } else {
1380 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1381 out->updateFillColorSpace(state);
1382 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1383 out->updateStrokeColorSpace(state);
1384 }
1385 state->setFillPattern(NULL);
1386 out->updateFillColor(state);
1387 state->setStrokePattern(NULL);
1388 out->updateStrokeColor(state);
1389 state->setLineWidth(0);
1390 out->updateLineWidth(state);
1391
1392 // clip to current path
1393 state->clip();
1394 if (eoFill) {
1395 out->eoClip(state);
1396 } else {
1397 out->clip(state);
1398 }
1399 state->clearPath();
1400
1401 // get the clip region, check for empty
1402 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1403 if (cxMin > cxMax || cyMin > cyMax) {
1404 goto err;
1405 }
1406
1407 // transform clip region bbox to pattern space
1408 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1409 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1410 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1411 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1412 if (x1 < xMin) {
1413 xMin = x1;
1414 } else if (x1 > xMax) {
1415 xMax = x1;
1416 }
1417 if (y1 < yMin) {
1418 yMin = y1;
1419 } else if (y1 > yMax) {
1420 yMax = y1;
1421 }
1422 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1423 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1424 if (x1 < xMin) {
1425 xMin = x1;
1426 } else if (x1 > xMax) {
1427 xMax = x1;
1428 }
1429 if (y1 < yMin) {
1430 yMin = y1;
1431 } else if (y1 > yMax) {
1432 yMax = y1;
1433 }
1434 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1435 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1436 if (x1 < xMin) {
1437 xMin = x1;
1438 } else if (x1 > xMax) {
1439 xMax = x1;
1440 }
1441 if (y1 < yMin) {
1442 yMin = y1;
1443 } else if (y1 > yMax) {
1444 yMax = y1;
1445 }
1446
1447 // draw the pattern
1448 //~ this should treat negative steps differently -- start at right/top
1449 //~ edge instead of left/bottom (?)
1450 xstep = fabs(tPat->getXStep());
1451 ystep = fabs(tPat->getYStep());
1452 xi0 = (int)floor((xMin - tPat->getBBox()[0]) / xstep);
1453 xi1 = (int)ceil((xMax - tPat->getBBox()[0]) / xstep);
1454 yi0 = (int)floor((yMin - tPat->getBBox()[1]) / ystep);
1455 yi1 = (int)ceil((yMax - tPat->getBBox()[1]) / ystep);
1456 for (i = 0; i < 4; ++i) {
1457 m1[i] = m[i];
1458 }
1459 if (out->useTilingPatternFill()) {
1460 m1[4] = m[4];
1461 m1[5] = m[5];
1462 out->tilingPatternFill(state, tPat->getContentStream(),
1463 tPat->getPaintType(), tPat->getResDict(),
1464 m1, tPat->getBBox(),
1465 xi0, yi0, xi1, yi1, xstep, ystep);
1466 } else {
1467 for (yi = yi0; yi < yi1; ++yi) {
1468 for (xi = xi0; xi < xi1; ++xi) {
1469 x = xi * xstep;
1470 y = yi * ystep;
1471 m1[4] = x * m[0] + y * m[2] + m[4];
1472 m1[5] = x * m[1] + y * m[3] + m[5];
1473 doForm1(tPat->getContentStream(), tPat->getResDict(),
1474 m1, tPat->getBBox());
1475 }
1476 }
1477 }
1478
1479 // restore graphics state
1480 err:
1481 restoreState();
1482 state->setPath(savedPath);
1483 }
1484
1485 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
1486 GfxShading *shading;
1487 GfxPath *savedPath;
1488 double *ctm, *btm, *ptm;
1489 double m[6], ictm[6], m1[6];
1490 double xMin, yMin, xMax, yMax;
1491 double det;
1492
1493 shading = sPat->getShading();
1494
1495 // save current graphics state
1496 savedPath = state->getPath()->copy();
1497 saveState();
1498
1499 // clip to bbox
1500 if (shading->getHasBBox()) {
1501 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1502 state->moveTo(xMin, yMin);
1503 state->lineTo(xMax, yMin);
1504 state->lineTo(xMax, yMax);
1505 state->lineTo(xMin, yMax);
1506 state->closePath();
1507 state->clip();
1508 out->clip(state);
1509 state->setPath(savedPath->copy());
1510 }
1511
1512 // clip to current path
1513 state->clip();
1514 if (eoFill) {
1515 out->eoClip(state);
1516 } else {
1517 out->clip(state);
1518 }
1519
1520 // set the color space
1521 state->setFillColorSpace(shading->getColorSpace()->copy());
1522 out->updateFillColorSpace(state);
1523
1524 // background color fill
1525 if (shading->getHasBackground()) {
1526 state->setFillColor(shading->getBackground());
1527 out->updateFillColor(state);
1528 out->fill(state);
1529 }
1530 state->clearPath();
1531
1532 // construct a (pattern space) -> (current space) transform matrix
1533 ctm = state->getCTM();
1534 btm = baseMatrix;
1535 ptm = sPat->getMatrix();
1536 // iCTM = invert CTM
1537 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1538 ictm[0] = ctm[3] * det;
1539 ictm[1] = -ctm[1] * det;
1540 ictm[2] = -ctm[2] * det;
1541 ictm[3] = ctm[0] * det;
1542 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1543 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1544 // m1 = PTM * BTM = PTM * base transform matrix
1545 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1546 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1547 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1548 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1549 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1550 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1551 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1552 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1553 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1554 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1555 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1556 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1557 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1558
1559 // set the new matrix
1560 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1561 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1562
1563 // do shading type-specific operations
1564 switch (shading->getType()) {
1565 case 1:
1566 doFunctionShFill((GfxFunctionShading *)shading);
1567 break;
1568 case 2:
1569 doAxialShFill((GfxAxialShading *)shading);
1570 break;
1571 case 3:
1572 doRadialShFill((GfxRadialShading *)shading);
1573 break;
1574 case 4:
1575 case 5:
1576 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1577 break;
1578 case 6:
1579 case 7:
1580 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1581 break;
1582 }
1583
1584 // restore graphics state
1585 restoreState();
1586 state->setPath(savedPath);
1587 }
1588
1589 void Gfx::opShFill(Object args[], int numArgs) {
1590 GfxShading *shading;
1591 GfxPath *savedPath;
1592 double xMin, yMin, xMax, yMax;
1593
1594 if (!(shading = res->lookupShading(args[0].getName()))) {
1595 return;
1596 }
1597
1598 // save current graphics state
1599 savedPath = state->getPath()->copy();
1600 saveState();
1601
1602 // clip to bbox
1603 if (shading->getHasBBox()) {
1604 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1605 state->moveTo(xMin, yMin);
1606 state->lineTo(xMax, yMin);
1607 state->lineTo(xMax, yMax);
1608 state->lineTo(xMin, yMax);
1609 state->closePath();
1610 state->clip();
1611 out->clip(state);
1612 state->clearPath();
1613 }
1614
1615 // set the color space
1616 state->setFillColorSpace(shading->getColorSpace()->copy());
1617 out->updateFillColorSpace(state);
1618
1619 // do shading type-specific operations
1620 switch (shading->getType()) {
1621 case 1:
1622 doFunctionShFill((GfxFunctionShading *)shading);
1623 break;
1624 case 2:
1625 doAxialShFill((GfxAxialShading *)shading);
1626 break;
1627 case 3:
1628 doRadialShFill((GfxRadialShading *)shading);
1629 break;
1630 case 4:
1631 case 5:
1632 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1633 break;
1634 case 6:
1635 case 7:
1636 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1637 break;
1638 }
1639
1640 // restore graphics state
1641 restoreState();
1642 state->setPath(savedPath);
1643
1644 delete shading;
1645 }
1646
1647 void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
1648 double x0, y0, x1, y1;
1649 GfxColor colors[4];
1650
1651 if (out->useShadedFills()) {
1652 out->functionShadedFill(state, shading);
1653 } else {
1654 shading->getDomain(&x0, &y0, &x1, &y1);
1655 shading->getColor(x0, y0, &colors[0]);
1656 shading->getColor(x0, y1, &colors[1]);
1657 shading->getColor(x1, y0, &colors[2]);
1658 shading->getColor(x1, y1, &colors[3]);
1659 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1660 }
1661 }
1662
1663 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
1664 double x0, double y0,
1665 double x1, double y1,
1666 GfxColor *colors, int depth) {
1667 GfxColor fillColor;
1668 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1669 GfxColor colors2[4];
1670 double *matrix;
1671 double xM, yM;
1672 int nComps, i, j;
1673
1674 nComps = shading->getColorSpace()->getNComps();
1675 matrix = shading->getMatrix();
1676
1677 // compare the four corner colors
1678 for (i = 0; i < 4; ++i) {
1679 for (j = 0; j < nComps; ++j) {
1680 if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1681 break;
1682 }
1683 }
1684 if (j < nComps) {
1685 break;
1686 }
1687 }
1688
1689 // center of the rectangle
1690 xM = 0.5 * (x0 + x1);
1691 yM = 0.5 * (y0 + y1);
1692
1693 // the four corner colors are close (or we hit the recursive limit)
1694 // -- fill the rectangle; but require at least one subdivision
1695 // (depth==0) to avoid problems when the four outer corners of the
1696 // shaded region are the same color
1697 if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1698
1699 // use the center color
1700 shading->getColor(xM, yM, &fillColor);
1701 state->setFillColor(&fillColor);
1702 out->updateFillColor(state);
1703
1704 // fill the rectangle
1705 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1706 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1707 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1708 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1709 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1710 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1711 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1712 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1713 state->closePath();
1714 out->fill(state);
1715 state->clearPath();
1716
1717 // the four corner colors are not close enough -- subdivide the
1718 // rectangle
1719 } else {
1720
1721 // colors[0] colorM0 colors[2]
1722 // (x0,y0) (xM,y0) (x1,y0)
1723 // +----------+----------+
1724 // | | |
1725 // | UL | UR |
1726 // color0M | colorMM | color1M
1727 // (x0,yM) +----------+----------+ (x1,yM)
1728 // | (xM,yM) |
1729 // | LL | LR |
1730 // | | |
1731 // +----------+----------+
1732 // colors[1] colorM1 colors[3]
1733 // (x0,y1) (xM,y1) (x1,y1)
1734
1735 shading->getColor(x0, yM, &color0M);
1736 shading->getColor(x1, yM, &color1M);
1737 shading->getColor(xM, y0, &colorM0);
1738 shading->getColor(xM, y1, &colorM1);
1739 shading->getColor(xM, yM, &colorMM);
1740
1741 // upper-left sub-rectangle
1742 colors2[0] = colors[0];
1743 colors2[1] = color0M;
1744 colors2[2] = colorM0;
1745 colors2[3] = colorMM;
1746 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1747
1748 // lower-left sub-rectangle
1749 colors2[0] = color0M;
1750 colors2[1] = colors[1];
1751 colors2[2] = colorMM;
1752 colors2[3] = colorM1;
1753 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1754
1755 // upper-right sub-rectangle
1756 colors2[0] = colorM0;
1757 colors2[1] = colorMM;
1758 colors2[2] = colors[2];
1759 colors2[3] = color1M;
1760 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1761
1762 // lower-right sub-rectangle
1763 colors2[0] = colorMM;
1764 colors2[1] = colorM1;
1765 colors2[2] = color1M;
1766 colors2[3] = colors[3];
1767 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1768 }
1769 }
1770
1771 void Gfx::doAxialShFill(GfxAxialShading *shading) {
1772 double xMin, yMin, xMax, yMax;
1773 double x0, y0, x1, y1;
1774 double dx, dy, mul;
1775 GBool dxZero, dyZero;
1776 double tMin, tMax, t, tx, ty;
1777 double s[4], sMin, sMax, tmp;
1778 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1779 double t0, t1, tt;
1780 double ta[axialMaxSplits + 1];
1781 int next[axialMaxSplits + 1];
1782 GfxColor color0, color1;
1783 int nComps;
1784 int i, j, k, kk;
1785
1786 if (out->useShadedFills()) {
1787
1788 out->axialShadedFill(state, shading);
1789
1790 } else {
1791
1792 // get the clip region bbox
1793 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1794
1795 // compute min and max t values, based on the four corners of the
1796 // clip region bbox
1797 shading->getCoords(&x0, &y0, &x1, &y1);
1798 dx = x1 - x0;
1799 dy = y1 - y0;
1800 dxZero = fabs(dx) < 0.001;
1801 dyZero = fabs(dy) < 0.001;
1802 mul = 1 / (dx * dx + dy * dy);
1803 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1804 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1805 if (t < tMin) {
1806 tMin = t;
1807 } else if (t > tMax) {
1808 tMax = t;
1809 }
1810 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1811 if (t < tMin) {
1812 tMin = t;
1813 } else if (t > tMax) {
1814 tMax = t;
1815 }
1816 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1817 if (t < tMin) {
1818 tMin = t;
1819 } else if (t > tMax) {
1820 tMax = t;
1821 }
1822 if (tMin < 0 && !shading->getExtend0()) {
1823 tMin = 0;
1824 }
1825 if (tMax > 1 && !shading->getExtend1()) {
1826 tMax = 1;
1827 }
1828
1829 // get the function domain
1830 t0 = shading->getDomain0();
1831 t1 = shading->getDomain1();
1832
1833 // Traverse the t axis and do the shading.
1834 //
1835 // For each point (tx, ty) on the t axis, consider a line through
1836 // that point perpendicular to the t axis:
1837 //
1838 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1839 // y(s) = ty + s * dx --> s = (y - ty) / dx
1840 //
1841 // Then look at the intersection of this line with the bounding box
1842 // (xMin, yMin, xMax, yMax). In the general case, there are four
1843 // intersection points:
1844 //
1845 // s0 = (xMin - tx) / -dy
1846 // s1 = (xMax - tx) / -dy
1847 // s2 = (yMin - ty) / dx
1848 // s3 = (yMax - ty) / dx
1849 //
1850 // and we want the middle two s values.
1851 //
1852 // In the case where dx = 0, take s0 and s1; in the case where dy =
1853 // 0, take s2 and s3.
1854 //
1855 // Each filled polygon is bounded by two of these line segments
1856 // perpdendicular to the t axis.
1857 //
1858 // The t axis is bisected into smaller regions until the color
1859 // difference across a region is small enough, and then the region
1860 // is painted with a single color.
1861
1862 // set up: require at least one split to avoid problems when the two
1863 // ends of the t axis have the same color
1864 nComps = shading->getColorSpace()->getNComps();
1865 ta[0] = tMin;
1866 next[0] = axialMaxSplits / 2;
1867 ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
1868 next[axialMaxSplits / 2] = axialMaxSplits;
1869 ta[axialMaxSplits] = tMax;
1870
1871 // compute the color at t = tMin
1872 if (tMin < 0) {
1873 tt = t0;
1874 } else if (tMin > 1) {
1875 tt = t1;
1876 } else {
1877 tt = t0 + (t1 - t0) * tMin;
1878 }
1879 shading->getColor(tt, &color0);
1880
1881 // compute the coordinates of the point on the t axis at t = tMin;
1882 // then compute the intersection of the perpendicular line with the
1883 // bounding box
1884 tx = x0 + tMin * dx;
1885 ty = y0 + tMin * dy;
1886 if (dxZero && dyZero) {
1887 sMin = sMax = 0;
1888 } if (dxZero) {
1889 sMin = (xMin - tx) / -dy;
1890 sMax = (xMax - tx) / -dy;
1891 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1892 } else if (dyZero) {
1893 sMin = (yMin - ty) / dx;
1894 sMax = (yMax - ty) / dx;
1895 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1896 } else {
1897 s[0] = (yMin - ty) / dx;
1898 s[1] = (yMax - ty) / dx;
1899 s[2] = (xMin - tx) / -dy;
1900 s[3] = (xMax - tx) / -dy;
1901 for (j = 0; j < 3; ++j) {
1902 kk = j;
1903 for (k = j + 1; k < 4; ++k) {
1904 if (s[k] < s[kk]) {
1905 kk = k;
1906 }
1907 }
1908 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1909 }
1910 sMin = s[1];
1911 sMax = s[2];
1912 }
1913 ux0 = tx - sMin * dy;
1914 uy0 = ty + sMin * dx;
1915 vx0 = tx - sMax * dy;
1916 vy0 = ty + sMax * dx;
1917
1918 i = 0;
1919 while (i < axialMaxSplits) {
1920
1921 // bisect until color difference is small enough or we hit the
1922 // bisection limit
1923 j = next[i];
1924 while (j > i + 1) {
1925 if (ta[j] < 0) {
1926 tt = t0;
1927 } else if (ta[j] > 1) {
1928 tt = t1;
1929 } else {
1930 tt = t0 + (t1 - t0) * ta[j];
1931 }
1932 shading->getColor(tt, &color1);
1933 for (k = 0; k < nComps; ++k) {
1934 if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1935 break;
1936 }
1937 }
1938 if (k == nComps) {
1939 break;
1940 }
1941 k = (i + j) / 2;
1942 ta[k] = 0.5 * (ta[i] + ta[j]);
1943 next[i] = k;
1944 next[k] = j;
1945 j = k;
1946 }
1947
1948 // use the average of the colors of the two sides of the region
1949 for (k = 0; k < nComps; ++k) {
1950 color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
1951 }
1952
1953 // compute the coordinates of the point on the t axis; then
1954 // compute the intersection of the perpendicular line with the
1955 // bounding box
1956 tx = x0 + ta[j] * dx;
1957 ty = y0 + ta[j] * dy;
1958 if (dxZero && dyZero) {
1959 sMin = sMax = 0;
1960 } if (dxZero) {
1961 sMin = (xMin - tx) / -dy;
1962 sMax = (xMax - tx) / -dy;
1963 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1964 } else if (dyZero) {
1965 sMin = (yMin - ty) / dx;
1966 sMax = (yMax - ty) / dx;
1967 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1968 } else {
1969 s[0] = (yMin - ty) / dx;
1970 s[1] = (yMax - ty) / dx;
1971 s[2] = (xMin - tx) / -dy;
1972 s[3] = (xMax - tx) / -dy;
1973 for (j = 0; j < 3; ++j) {
1974 kk = j;
1975 for (k = j + 1; k < 4; ++k) {
1976 if (s[k] < s[kk]) {
1977 kk = k;
1978 }
1979 }
1980 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1981 }
1982 sMin = s[1];
1983 sMax = s[2];
1984 }
1985 ux1 = tx - sMin * dy;
1986 uy1 = ty + sMin * dx;
1987 vx1 = tx - sMax * dy;
1988 vy1 = ty + sMax * dx;
1989
1990 // set the color
1991 state->setFillColor(&color0);
1992 out->updateFillColor(state);
1993
1994 // fill the region
1995 state->moveTo(ux0, uy0);
1996 state->lineTo(vx0, vy0);
1997 state->lineTo(vx1, vy1);
1998 state->lineTo(ux1, uy1);
1999 state->closePath();
2000 out->fill(state);
2001 state->clearPath();
2002
2003 // set up for next region
2004 ux0 = ux1;
2005 uy0 = uy1;
2006 vx0 = vx1;
2007 vy0 = vy1;
2008 color0 = color1;
2009 i = next[i];
2010 }
2011 }
2012 }
2013
2014 void Gfx::doRadialShFill(GfxRadialShading *shading) {
2015 double sMin, sMax, xMin, yMin, xMax, yMax;
2016 double x0, y0, r0, x1, y1, r1, t0, t1;
2017 int nComps;
2018 GfxColor colorA, colorB;
2019 double xa, ya, xb, yb, ra, rb;
2020 double ta, tb, sa, sb;
2021 int ia, ib, k, n;
2022 double *ctm;
2023 double angle, t, d0, d1;
2024
2025 if (out->useShadedFills()) {
2026
2027 out->radialShadedFill(state, shading);
2028
2029 } else {
2030
2031 // get the shading info
2032 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
2033 t0 = shading->getDomain0();
2034 t1 = shading->getDomain1();
2035 nComps = shading->getColorSpace()->getNComps();
2036
2037 // compute the (possibly extended) s range
2038 sMin = 0;
2039 sMax = 1;
2040 if (shading->getExtend0()) {
2041 if (r0 < r1) {
2042 // extend the smaller end
2043 sMin = -r0 / (r1 - r0);
2044 } else {
2045 // extend the larger end
2046 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2047 d0 = (x0 - xMin) * (x0 - xMin);
2048 d1 = (x0 - xMax) * (x0 - xMax);
2049 sMin = d0 > d1 ? d0 : d1;
2050 d0 = (y0 - yMin) * (y0 - yMin);
2051 d1 = (y0 - yMax) * (y0 - yMax);
2052 sMin += d0 > d1 ? d0 : d1;
2053 sMin = (sqrt(sMin) - r0) / (r1 - r0);
2054 if (sMin > 0) {
2055 sMin = 0;
2056 } else if (sMin < -20) {
2057 // sanity check
2058 sMin = -20;
2059 }
2060 }
2061 }
2062 if (shading->getExtend1()) {
2063 if (r1 < r0) {
2064 // extend the smaller end
2065 sMax = -r0 / (r1 - r0);
2066 } else if (r1 > r0) {
2067 // extend the larger end
2068 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
2069 d0 = (x1 - xMin) * (x1 - xMin);
2070 d1 = (x1 - xMax) * (x1 - xMax);
2071 sMax = d0 > d1 ? d0 : d1;
2072 d0 = (y1 - yMin) * (y1 - yMin);
2073 d1 = (y1 - yMax) * (y1 - yMax);
2074 sMax += d0 > d1 ? d0 : d1;
2075 sMax = (sqrt(sMax) - r0) / (r1 - r0);
2076 if (sMax < 1) {
2077 sMax = 1;
2078 } else if (sMax > 20) {
2079 // sanity check
2080 sMax = 20;
2081 }
2082 }
2083 }
2084
2085 // compute the number of steps into which circles must be divided to
2086 // achieve a curve flatness of 0.1 pixel in device space for the
2087 // largest circle (note that "device space" is 72 dpi when generating
2088 // PostScript, hence the relatively small 0.1 pixel accuracy)
2089 ctm = state->getCTM();
2090 t = fabs(ctm[0]);
2091 if (fabs(ctm[1]) > t) {
2092 t = fabs(ctm[1]);
2093 }
2094 if (fabs(ctm[2]) > t) {
2095 t = fabs(ctm[2]);
2096 }
2097 if (fabs(ctm[3]) > t) {
2098 t = fabs(ctm[3]);
2099 }
2100 if (r0 > r1) {
2101 t *= r0;
2102 } else {
2103 t *= r1;
2104 }
2105 if (t < 1) {
2106 n = 3;
2107 } else {
2108 n = (int)(M_PI / acos(1 - 0.1 / t));
2109 if (n < 3) {
2110 n = 3;
2111 } else if (n > 200) {
2112 n = 200;
2113 }
2114 }
2115
2116 // Traverse the t axis and do the shading.
2117 //
2118 // This generates and fills a series of rings. Each ring is defined
2119 // by two circles:
2120 // sa, ta, xa, ya, ra, colorA
2121 // sb, tb, xb, yb, rb, colorB
2122 //
2123 // The s/t axis is divided into radialMaxSplits parts; these parts
2124 // are combined as much as possible while respecting the
2125 // radialColorDelta parameter.
2126
2127 // setup for the start circle
2128 ia = 0;
2129 sa = sMin;
2130 ta = t0 + sa * (t1 - t0);
2131 xa = x0 + sa * (x1 - x0);
2132 ya = y0 + sa * (y1 - y0);
2133 ra = r0 + sa * (r1 - r0);
2134 if (ta < t0) {
2135 shading->getColor(t0, &colorA);
2136 } else if (ta > t1) {
2137 shading->getColor(t1, &colorA);
2138 } else {
2139 shading->getColor(ta, &colorA);
2140 }
2141
2142 while (ia < radialMaxSplits) {
2143
2144 // go as far along the t axis (toward t1) as we can, such that the
2145 // color difference is within the tolerance (radialColorDelta) --
2146 // this uses bisection (between the current value, t, and t1),
2147 // limited to radialMaxSplits points along the t axis; require at
2148 // least one split to avoid problems when the innermost and
2149 // outermost colors are the same
2150 ib = radialMaxSplits;
2151 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2152 tb = t0 + sb * (t1 - t0);
2153 if (tb < t0) {
2154 shading->getColor(t0, &colorB);
2155 } else if (tb > t1) {
2156 shading->getColor(t1, &colorB);
2157 } else {
2158 shading->getColor(tb, &colorB);
2159 }
2160 while (ib - ia > 1) {
2161 for (k = 0; k < nComps; ++k) {
2162 if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
2163 break;
2164 }
2165 }
2166 if (k == nComps && ib < radialMaxSplits) {
2167 break;
2168 }
2169 ib = (ia + ib) / 2;
2170 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2171 tb = t0 + sb * (t1 - t0);
2172 if (tb < t0) {
2173 shading->getColor(t0, &colorB);
2174 } else if (tb > t1) {
2175 shading->getColor(t1, &colorB);
2176 } else {
2177 shading->getColor(tb, &colorB);
2178 }
2179 }
2180
2181 // compute center and radius of the circle
2182 xb = x0 + sb * (x1 - x0);
2183 yb = y0 + sb * (y1 - y0);
2184 rb = r0 + sb * (r1 - r0);
2185
2186 // use the average of the colors at the two circles
2187 for (k = 0; k < nComps; ++k) {
2188 colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
2189 }
2190 state->setFillColor(&colorA);
2191 out->updateFillColor(state);
2192
2193 // construct path for first circle
2194 state->moveTo(xa + ra, ya);
2195 for (k = 1; k < n; ++k) {
2196 angle = ((double)k / (double)n) * 2 * M_PI;
2197 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2198 }
2199 state->closePath();
2200
2201 // construct and append path for second circle
2202 state->moveTo(xb + rb, yb);
2203 for (k = 1; k < n; ++k) {
2204 angle = ((double)k / (double)n) * 2 * M_PI;
2205 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2206 }
2207 state->closePath();
2208
2209 // fill the ring
2210 out->eoFill(state);
2211 state->clearPath();
2212
2213 // step to the next value of t
2214 ia = ib;
2215 sa = sb;
2216 ta = tb;
2217 xa = xb;
2218 ya = yb;
2219 ra = rb;
2220 colorA = colorB;
2221 }
2222 }
2223 }
2224
2225 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
2226 double x0, y0, x1, y1, x2, y2;
2227 GfxColor color0, color1, color2;
2228 int i;
2229
2230 for (i = 0; i < shading->getNTriangles(); ++i) {
2231 shading->getTriangle(i, &x0, &y0, &color0,
2232 &x1, &y1, &color1,
2233 &x2, &y2, &color2);
2234 gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
2235 shading->getColorSpace()->getNComps(), 0);
2236 }
2237 }
2238
2239 void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
2240 double x1, double y1, GfxColor *color1,
2241 double x2, double y2, GfxColor *color2,
2242 int nComps, int depth) {
2243 double x01, y01, x12, y12, x20, y20;
2244 GfxColor color01, color12, color20;
2245 int i;
2246
2247 for (i = 0; i < nComps; ++i) {
2248 if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
2249 abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
2250 break;
2251 }
2252 }
2253 if (i == nComps || depth == gouraudMaxDepth) {
2254 state->setFillColor(color0);
2255 out->updateFillColor(state);
2256 state->moveTo(x0, y0);
2257 state->lineTo(x1, y1);
2258 state->lineTo(x2, y2);
2259 state->closePath();
2260 out->fill(state);
2261 state->clearPath();
2262 } else {
2263 x01 = 0.5 * (x0 + x1);
2264 y01 = 0.5 * (y0 + y1);
2265 x12 = 0.5 * (x1 + x2);
2266 y12 = 0.5 * (y1 + y2);
2267 x20 = 0.5 * (x2 + x0);
2268 y20 = 0.5 * (y2 + y0);
2269 //~ if the shading has a Function, this should interpolate on the
2270 //~ function parameter, not on the color components
2271 for (i = 0; i < nComps; ++i) {
2272 color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
2273 color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
2274 color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
2275 }
2276 gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
2277 x20, y20, &color20, nComps, depth + 1);
2278 gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
2279 x12, y12, &color12, nComps, depth + 1);
2280 gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
2281 x20, y20, &color20, nComps, depth + 1);
2282 gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
2283 x2, y2, color2, nComps, depth + 1);
2284 }
2285 }
2286
2287 void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
2288 int start, i;
2289
2290 if (shading->getNPatches() > 128) {
2291 start = 3;
2292 } else if (shading->getNPatches() > 64) {
2293 start = 2;
2294 } else if (shading->getNPatches() > 16) {
2295 start = 1;
2296 } else {
2297 start = 0;
2298 }
2299 for (i = 0; i < shading->getNPatches(); ++i) {
2300 fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
2301 start);
2302 }
2303 }
2304
2305 void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
2306 GfxPatch patch00, patch01, patch10, patch11;
2307 double xx[4][8], yy[4][8];
2308 double xxm, yym;
2309 int i;
2310
2311 for (i = 0; i < nComps; ++i) {
2312 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
2313 > patchColorDelta ||
2314 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
2315 > patchColorDelta ||
2316 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
2317 > patchColorDelta ||
2318 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
2319 > patchColorDelta) {
2320 break;
2321 }
2322 }
2323 if (i == nComps || depth == patchMaxDepth) {
2324 state->setFillColor(&patch->color[0][0]);
2325 out->updateFillColor(state);
2326 state->moveTo(patch->x[0][0], patch->y[0][0]);
2327 state->curveTo(patch->x[0][1], patch->y[0][1],
2328 patch->x[0][2], patch->y[0][2],
2329 patch->x[0][3], patch->y[0][3]);
2330 state->curveTo(patch->x[1][3], patch->y[1][3],
2331 patch->x[2][3], patch->y[2][3],
2332 patch->x[3][3], patch->y[3][3]);
2333 state->curveTo(patch->x[3][2], patch->y[3][2],
2334 patch->x[3][1], patch->y[3][1],
2335 patch->x[3][0], patch->y[3][0]);
2336 state->curveTo(patch->x[2][0], patch->y[2][0],
2337 patch->x[1][0], patch->y[1][0],
2338 patch->x[0][0], patch->y[0][0]);
2339 state->closePath();
2340 out->fill(state);
2341 state->clearPath();
2342 } else {
2343 for (i = 0; i < 4; ++i) {
2344 xx[i][0] = patch->x[i][0];
2345 yy[i][0] = patch->y[i][0];
2346 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
2347 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
2348 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
2349 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
2350 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
2351 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
2352 xx[i][2] = 0.5 * (xx[i][1] + xxm);
2353 yy[i][2] = 0.5 * (yy[i][1] + yym);
2354 xx[i][5] = 0.5 * (xxm + xx[i][6]);
2355 yy[i][5] = 0.5 * (yym + yy[i][6]);
2356 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
2357 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
2358 xx[i][7] = patch->x[i][3];
2359 yy[i][7] = patch->y[i][3];
2360 }
2361 for (i = 0; i < 4; ++i) {
2362 patch00.x[0][i] = xx[0][i];
2363 patch00.y[0][i] = yy[0][i];
2364 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
2365 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
2366 xxm = 0.5 * (xx[1][i] + xx[2][i]);
2367 yym = 0.5 * (yy[1][i] + yy[2][i]);
2368 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
2369 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
2370 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
2371 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
2372 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
2373 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
2374 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
2375 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
2376 patch10.x[0][i] = patch00.x[3][i];
2377 patch10.y[0][i] = patch00.y[3][i];
2378 patch10.x[3][i] = xx[3][i];
2379 patch10.y[3][i] = yy[3][i];
2380 }
2381 for (i = 4; i < 8; ++i) {
2382 patch01.x[0][i-4] = xx[0][i];
2383 patch01.y[0][i-4] = yy[0][i];
2384 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
2385 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
2386 xxm = 0.5 * (xx[1][i] + xx[2][i]);
2387 yym = 0.5 * (yy[1][i] + yy[2][i]);
2388 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
2389 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
2390 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
2391 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
2392 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
2393 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
2394 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
2395 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
2396 patch11.x[0][i-4] = patch01.x[3][i-4];
2397 patch11.y[0][i-4] = patch01.y[3][i-4];
2398 patch11.x[3][i-4] = xx[3][i];
2399 patch11.y[3][i-4] = yy[3][i];
2400 }
2401 //~ if the shading has a Function, this should interpolate on the
2402 //~ function parameter, not on the color components
2403 for (i = 0; i < nComps; ++i) {
2404 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
2405 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
2406 patch->color[0][1].c[i]) / 2;
2407 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
2408 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
2409 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
2410 patch->color[1][1].c[i]) / 2;
2411 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
2412 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
2413 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
2414 patch->color[1][0].c[i]) / 2;
2415 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
2416 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
2417 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
2418 patch->color[0][0].c[i]) / 2;
2419 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
2420 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
2421 patch01.color[1][1].c[i]) / 2;
2422 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
2423 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
2424 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
2425 }
2426 fillPatch(&patch00, nComps, depth + 1);
2427 fillPatch(&patch10, nComps, depth + 1);
2428 fillPatch(&patch01, nComps, depth + 1);
2429 fillPatch(&patch11, nComps, depth + 1);
2430 }
2431 }
2432
2433 void Gfx::doEndPath() {
2434 if (state->isCurPt() && clip != clipNone) {
2435 state->clip();
2436 if (clip == clipNormal) {
2437 out->clip(state);
2438 } else {
2439 out->eoClip(state);
2440 }
2441 }
2442 clip = clipNone;
2443 state->clearPath();
2444 }
2445
2446 //------------------------------------------------------------------------
2447 // path clipping operators
2448 //------------------------------------------------------------------------
2449
2450 void Gfx::opClip(Object args[], int numArgs) {
2451 clip = clipNormal;
2452 }
2453
2454 void Gfx::opEOClip(Object args[], int numArgs) {
2455 clip = clipEO;
2456 }
2457
2458 //------------------------------------------------------------------------
2459 // text object operators
2460 //------------------------------------------------------------------------
2461
2462 void Gfx::opBeginText(Object args[], int numArgs) {
2463 state->setTextMat(1, 0, 0, 1, 0, 0);
2464 state->textMoveTo(0, 0);
2465 out->updateTextMat(state);
2466 out->updateTextPos(state);
2467 fontChanged = gTrue;
2468 }
2469
2470 void Gfx::opEndText(Object args[], int numArgs) {
2471 out->endTextObject(state);
2472 }
2473
2474 //------------------------------------------------------------------------
2475 // text state operators
2476 //------------------------------------------------------------------------
2477
2478 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
2479 state->setCharSpace(args[0].getNum());
2480 out->updateCharSpace(state);
2481 }
2482
2483 void Gfx::opSetFont(Object args[], int numArgs) {
2484 GfxFont *font;
2485
2486 if (!(font = res->lookupFont(args[0].getName()))) {
2487 return;
2488 }
2489 if (printCommands) {
2490 printf(" font: tag=%s name='%s' %g\n",
2491 font->getTag()->getCString(),
2492 font->getName() ? font->getName()->getCString() : "???",
2493 args[1].getNum());
2494 fflush(stdout);
2495 }
2496 state->setFont(font, args[1].getNum());
2497 fontChanged = gTrue;
2498 }
2499
2500 void Gfx::opSetTextLeading(Object args[], int numArgs) {
2501 state->setLeading(args[0].getNum());
2502 }
2503
2504 void Gfx::opSetTextRender(Object args[], int numArgs) {
2505 state->setRender(args[0].getInt());
2506 out->updateRender(state);
2507 }
2508
2509 void Gfx::opSetTextRise(Object args[], int numArgs) {
2510 state->setRise(args[0].getNum());
2511 out->updateRise(state);
2512 }
2513
2514 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
2515 state->setWordSpace(args[0].getNum());
2516 out->updateWordSpace(state);
2517 }
2518
2519 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
2520 state->setHorizScaling(args[0].getNum());
2521 out->updateHorizScaling(state);
2522 fontChanged = gTrue;
2523 }
2524
2525 //------------------------------------------------------------------------
2526 // text positioning operators
2527 //------------------------------------------------------------------------
2528
2529 void Gfx::opTextMove(Object args[], int numArgs) {
2530 double tx, ty;
2531
2532 tx = state->getLineX() + args[0].getNum();
2533 ty = state->getLineY() + args[1].getNum();
2534 state->textMoveTo(tx, ty);
2535 out->updateTextPos(state);
2536 }
2537
2538 void Gfx::opTextMoveSet(Object args[], int numArgs) {
2539 double tx, ty;
2540
2541 tx = state->getLineX() + args[0].getNum();
2542 ty = args[1].getNum();
2543 state->setLeading(-ty);
2544 ty += state->getLineY();
2545 state->textMoveTo(tx, ty);
2546 out->updateTextPos(state);
2547 }
2548
2549 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
2550 state->setTextMat(args[0].getNum(), args[1].getNum(),
2551 args[2].getNum(), args[3].getNum(),
2552 args[4].getNum(), args[5].getNum());
2553 state->textMoveTo(0, 0);
2554 out->updateTextMat(state);
2555 out->updateTextPos(state);
2556 fontChanged = gTrue;
2557 }
2558
2559 void Gfx::opTextNextLine(Object args[], int numArgs) {
2560 double tx, ty;
2561
2562 tx = state->getLineX();
2563 ty = state->getLineY() - state->getLeading();
2564 state->textMoveTo(tx, ty);
2565 out->updateTextPos(state);
2566 }
2567
2568 //------------------------------------------------------------------------
2569 // text string operators
2570 //------------------------------------------------------------------------
2571
2572 void Gfx::opShowText(Object args[], int numArgs) {
2573 if (!state->getFont()) {
2574 error(getPos(), "No font in show");
2575 return;
2576 }
2577 if (fontChanged) {
2578 out->updateFont(state);
2579 fontChanged = gFalse;
2580 }
2581 out->beginStringOp(state);
2582 doShowText(args[0].getString());
2583 out->endStringOp(state);
2584 }
2585
2586 void Gfx::opMoveShowText(Object args[], int numArgs) {
2587 double tx, ty;
2588
2589 if (!state->getFont()) {
2590 error(getPos(), "No font in move/show");
2591 return;
2592 }
2593 if (fontChanged) {
2594 out->updateFont(state);
2595 fontChanged = gFalse;
2596 }
2597 tx = state->getLineX();
2598 ty = state->getLineY() - state->getLeading();
2599 state->textMoveTo(tx, ty);
2600 out->updateTextPos(state);
2601 out->beginStringOp(state);
2602 doShowText(args[0].getString());
2603 out->endStringOp(state);
2604 }
2605
2606 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
2607 double tx, ty;
2608
2609 if (!state->getFont()) {
2610 error(getPos(), "No font in move/set/show");
2611 return;
2612 }
2613 if (fontChanged) {
2614 out->updateFont(state);
2615 fontChanged = gFalse;
2616 }
2617 state->setWordSpace(args[0].getNum());
2618 state->setCharSpace(args[1].getNum());
2619 tx = state->getLineX();
2620 ty = state->getLineY() - state->getLeading();
2621 state->textMoveTo(tx, ty);
2622 out->updateWordSpace(state);
2623 out->updateCharSpace(state);
2624 out->updateTextPos(state);
2625 out->beginStringOp(state);
2626 doShowText(args[2].getString());
2627 out->endStringOp(state);
2628 }
2629
2630 void Gfx::opShowSpaceText(Object args[], int numArgs) {
2631 Array *a;
2632 Object obj;
2633 int wMode;
2634 int i;
2635
2636 if (!state->getFont()) {
2637 error(getPos(), "No font in show/space");
2638 return;
2639 }
2640 if (fontChanged) {
2641 out->updateFont(state);
2642 fontChanged = gFalse;
2643 }
2644 out->beginStringOp(state);
2645 wMode = state->getFont()->getWMode();
2646 a = args[0].getArray();
2647 for (i = 0; i < a->getLength(); ++i) {
2648 a->get(i, &obj);
2649 if (obj.isNum()) {
2650 // this uses the absolute value of the font size to match
2651 // Acrobat's behavior
2652 if (wMode) {
2653 state->textShift(0, -obj.getNum() * 0.001 *
2654 fabs(state->getFontSize()));
2655 } else {
2656 state->textShift(-obj.getNum() * 0.001 *
2657 fabs(state->getFontSize()), 0);
2658 }
2659 out->updateTextShift(state, obj.getNum());
2660 } else if (obj.isString()) {
2661 doShowText(obj.getString());
2662 } else {
2663 error(getPos(), "Element of show/space array must be number or string");
2664 }
2665 obj.free();
2666 }
2667 out->endStringOp(state);
2668 }
2669
2670 void Gfx::doShowText(GString *s) {
2671 GfxFont *font;
2672 int wMode;
2673 double riseX, riseY;
2674 CharCode code;
2675 Unicode u[8];
2676 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2677 double originX, originY, tOriginX, tOriginY;
2678 double oldCTM[6], newCTM[6];
2679 double *mat;
2680 Object charProc;
2681 Dict *resDict;
2682 Parser *oldParser;
2683 char *p;
2684 int len, n, uLen, nChars, nSpaces, i;
2685
2686 font = state->getFont();
2687 wMode = font->getWMode();
2688
2689 if (out->useDrawChar()) {
2690 out->beginString(state, s);
2691 }
2692
2693 // handle a Type 3 char
2694 if (font->getType() == fontType3 && out->interpretType3Chars()) {
2695 mat = state->getCTM();
2696 for (i = 0; i < 6; ++i) {
2697 oldCTM[i] = mat[i];
2698 }
2699 mat = state->getTextMat();
2700 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2701 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2702 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2703 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2704 mat = font->getFontMatrix();
2705 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2706 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2707 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2708 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2709 newCTM[0] *= state->getFontSize();
2710 newCTM[1] *= state->getFontSize();
2711 newCTM[2] *= state->getFontSize();
2712 newCTM[3] *= state->getFontSize();
2713 newCTM[0] *= state->getHorizScaling();
2714 newCTM[2] *= state->getHorizScaling();
2715 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2716 curX = state->getCurX();
2717 curY = state->getCurY();
2718 lineX = state->getLineX();
2719 lineY = state->getLineY();
2720 oldParser = parser;
2721 p = s->getCString();
2722 len = s->getLength();
2723 while (len > 0) {
2724 n = font->getNextChar(p, len, &code,
2725 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2726 &dx, &dy, &originX, &originY);
2727 dx = dx * state->getFontSize() + state->getCharSpace();
2728 if (n == 1 && *p == ' ') {
2729 dx += state->getWordSpace();
2730 }
2731 dx *= state->getHorizScaling();
2732 dy *= state->getFontSize();
2733 state->textTransformDelta(dx, dy, &tdx, &tdy);
2734 state->transform(curX + riseX, curY + riseY, &x, &y);
2735 saveState();
2736 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2737 //~ out->updateCTM(???)
2738 if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2739 code, u, uLen)) {
2740 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2741 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2742 pushResources(resDict);
2743 }
2744 if (charProc.isStream()) {
2745 display(&charProc, gFalse);
2746 } else {
2747 error(getPos(), "Missing or bad Type3 CharProc entry");
2748 }
2749 out->endType3Char(state);
2750 if (resDict) {
2751 popResources();
2752 }
2753 charProc.free();
2754 }
2755 restoreState();
2756 // GfxState::restore() does *not* restore the current position,
2757 // so we deal with it here using (curX, curY) and (lineX, lineY)
2758 curX += tdx;
2759 curY += tdy;
2760 state->moveTo(curX, curY);
2761 state->textSetPos(lineX, lineY);
2762 p += n;
2763 len -= n;
2764 }
2765 parser = oldParser;
2766
2767 } else if (out->useDrawChar()) {
2768 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2769 p = s->getCString();
2770 len = s->getLength();
2771 while (len > 0) {
2772 n = font->getNextChar(p, len, &code,
2773 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2774 &dx, &dy, &originX, &originY);
2775 if (wMode) {
2776 dx *= state->getFontSize();
2777 dy = dy * state->getFontSize() + state->getCharSpace();
2778 if (n == 1 && *p == ' ') {
2779 dy += state->getWordSpace();
2780 }
2781 } else {
2782 dx = dx * state->getFontSize() + state->getCharSpace();
2783 if (n == 1 && *p == ' ') {
2784 dx += state->getWordSpace();
2785 }
2786 dx *= state->getHorizScaling();
2787 dy *= state->getFontSize();
2788 }
2789 state->textTransformDelta(dx, dy, &tdx, &tdy);
2790 originX *= state->getFontSize();
2791 originY *= state->getFontSize();
2792 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2793 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2794 tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
2795 state->shift(tdx, tdy);
2796 p += n;
2797 len -= n;
2798 }
2799
2800 } else {
2801 dx = dy = 0;
2802 p = s->getCString();
2803 len = s->getLength();
2804 nChars = nSpaces = 0;
2805 while (len > 0) {
2806 n = font->getNextChar(p, len, &code,
2807 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2808 &dx2, &dy2, &originX, &originY);
2809 dx += dx2;
2810 dy += dy2;
2811 if (n == 1 && *p == ' ') {
2812 ++nSpaces;
2813 }
2814 ++nChars;
2815 p += n;
2816 len -= n;
2817 }
2818 if (wMode) {
2819 dx *= state->getFontSize();
2820 dy = dy * state->getFontSize()
2821 + nChars * state->getCharSpace()
2822 + nSpaces * state->getWordSpace();
2823 } else {
2824 dx = dx * state->getFontSize()
2825 + nChars * state->getCharSpace()
2826 + nSpaces * state->getWordSpace();
2827 dx *= state->getHorizScaling();
2828 dy *= state->getFontSize();
2829 }
2830 state->textTransformDelta(dx, dy, &tdx, &tdy);
2831 out->drawString(state, s);
2832 state->shift(tdx, tdy);
2833 }
2834
2835 if (out->useDrawChar()) {
2836 out->endString(state);
2837 }
2838
2839 updateLevel += 10 * s->getLength();
2840 }
2841
2842 //------------------------------------------------------------------------
2843 // XObject operators
2844 //------------------------------------------------------------------------
2845
2846 void Gfx::opXObject(Object args[], int numArgs) {
2847 Object obj1, obj2, obj3, refObj;
2848 #if OPI_SUPPORT
2849 Object opiDict;
2850 #endif
2851
2852 if (!res->lookupXObject(args[0].getName(), &obj1)) {
2853 return;
2854 }
2855 if (!obj1.isStream()) {
2856 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2857 obj1.free();
2858 return;
2859 }
2860 #if OPI_SUPPORT
2861 obj1.streamGetDict()->lookup("OPI", &opiDict);
2862 if (opiDict.isDict()) {
2863 out->opiBegin(state, opiDict.getDict());
2864 }
2865 #endif
2866 obj1.streamGetDict()->lookup("Subtype", &obj2);
2867 if (obj2.isName("Image")) {
2868 if (out->needNonText()) {
2869 res->lookupXObjectNF(args[0].getName(), &refObj);
2870 doImage(&refObj, obj1.getStream(), gFalse);
2871 refObj.free();
2872 }
2873 } else if (obj2.isName("Form")) {
2874 doForm(&obj1);
2875 } else if (obj2.isName("PS")) {
2876 obj1.streamGetDict()->lookup("Level1", &obj3);
2877 out->psXObject(obj1.getStream(),
2878 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2879 } else if (obj2.isName()) {
2880 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2881 } else {
2882 error(getPos(), "XObject subtype is missing or wrong type");
2883 }
2884 obj2.free();
2885 #if OPI_SUPPORT
2886 if (opiDict.isDict()) {
2887 out->opiEnd(state, opiDict.getDict());
2888 }
2889 opiDict.free();
2890 #endif
2891 obj1.free();
2892 }
2893
2894 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2895 Dict *dict, *maskDict;
2896 int width, height;
2897 int bits, maskBits;
2898 StreamColorSpaceMode csMode;
2899 GBool mask;
2900 GBool invert;
2901 GfxColorSpace *colorSpace, *maskColorSpace;
2902 GfxImageColorMap *colorMap, *maskColorMap;
2903 Object maskObj, smaskObj;
2904 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2905 int maskColors[2*gfxColorMaxComps];
2906 int maskWidth, maskHeight;
2907 GBool maskInvert;
2908 Stream *maskStr;
2909 Object obj1, obj2;
2910 int i;
2911
2912 // get info from the stream
2913 bits = 0;
2914 csMode = streamCSNone;
2915 str->getImageParams(&bits, &csMode);
2916
2917 // get stream dict
2918 dict = str->getDict();
2919
2920 // get size
2921 dict->lookup("Width", &obj1);
2922 if (obj1.isNull()) {
2923 obj1.free();
2924 dict->lookup("W", &obj1);
2925 }
2926 if (!obj1.isInt())
2927 goto err2;
2928 width = obj1.getInt();
2929 obj1.free();
2930 dict->lookup("Height", &obj1);
2931 if (obj1.isNull()) {
2932 obj1.free();
2933 dict->lookup("H", &obj1);
2934 }
2935 if (!obj1.isInt())
2936 goto err2;
2937 height = obj1.getInt();
2938 obj1.free();
2939
2940 // image or mask?
2941 dict->lookup("ImageMask", &obj1);
2942 if (obj1.isNull()) {
2943 obj1.free();
2944 dict->lookup("IM", &obj1);
2945 }
2946 mask = gFalse;
2947 if (obj1.isBool())
2948 mask = obj1.getBool();
2949 else if (!obj1.isNull())
2950 goto err2;
2951 obj1.free();
2952
2953 // bit depth
2954 if (bits == 0) {
2955 dict->lookup("BitsPerComponent", &obj1);
2956 if (obj1.isNull()) {
2957 obj1.free();
2958 dict->lookup("BPC", &obj1);
2959 }
2960 if (obj1.isInt()) {
2961 bits = obj1.getInt();
2962 } else if (mask) {
2963 bits = 1;
2964 } else {
2965 goto err2;
2966 }
2967 obj1.free();
2968 }
2969
2970 // display a mask
2971 if (mask) {
2972
2973 // check for inverted mask
2974 if (bits != 1)
2975 goto err1;
2976 invert = gFalse;
2977 dict->lookup("Decode", &obj1);
2978 if (obj1.isNull()) {
2979 obj1.free();
2980 dict->lookup("D", &obj1);
2981 }
2982 if (obj1.isArray()) {
2983 obj1.arrayGet(0, &obj2);
2984 if (obj2.isInt() && obj2.getInt() == 1)
2985 invert = gTrue;
2986 obj2.free();
2987 } else if (!obj1.isNull()) {
2988 goto err2;
2989 }
2990 obj1.free();
2991
2992 // draw it
2993 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2994
2995 } else {
2996
2997 // get color space and color map
2998 dict->lookup("ColorSpace", &obj1);
2999 if (obj1.isNull()) {
3000 obj1.free();
3001 dict->lookup("CS", &obj1);
3002 }
3003 if (obj1.isName()) {
3004 res->lookupColorSpace(obj1.getName(), &obj2);
3005 if (!obj2.isNull()) {
3006 obj1.free();
3007 obj1 = obj2;
3008 } else {
3009 obj2.free();
3010 }
3011 }
3012 if (!obj1.isNull()) {
3013 colorSpace = GfxColorSpace::parse(&obj1);
3014 } else if (csMode == streamCSDeviceGray) {
3015 colorSpace = new GfxDeviceGrayColorSpace();
3016 } else if (csMode == streamCSDeviceRGB) {
3017 colorSpace = new GfxDeviceRGBColorSpace();
3018 } else if (csMode == streamCSDeviceCMYK) {
3019 colorSpace = new GfxDeviceCMYKColorSpace();
3020 } else {
3021 colorSpace = NULL;
3022 }
3023 obj1.free();
3024 if (!colorSpace) {
3025 goto err1;
3026 }
3027 dict->lookup("Decode", &obj1);
3028 if (obj1.isNull()) {
3029 obj1.free();
3030 dict->lookup("D", &obj1);
3031 }
3032 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
3033 obj1.free();
3034 if (!colorMap->isOk()) {
3035 delete colorMap;
3036 goto err1;
3037 }
3038
3039 // get the mask
3040 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
3041 maskStr = NULL; // make gcc happy
3042 maskWidth = maskHeight = 0; // make gcc happy
3043 maskInvert = gFalse; // make gcc happy
3044 maskColorMap = NULL; // make gcc happy
3045 dict->lookup("Mask", &maskObj);
3046 dict->lookup("SMask", &smaskObj);
3047 if (smaskObj.isStream()) {
3048 // soft mask
3049 if (inlineImg) {
3050 goto err1;
3051 }
3052 maskStr = smaskObj.getStream();
3053 maskDict = smaskObj.streamGetDict();
3054 maskDict->lookup("Width", &obj1);
3055 if (obj1.isNull()) {
3056 obj1.free();
3057 maskDict->lookup("W", &obj1);
3058 }
3059 if (!obj1.isInt()) {
3060 goto err2;
3061 }
3062 maskWidth = obj1.getInt();
3063 obj1.free();
3064 maskDict->lookup("Height", &obj1);
3065 if (obj1.isNull()) {
3066 obj1.free();
3067 maskDict->lookup("H", &obj1);
3068 }
3069 if (!obj1.isInt()) {
3070 goto err2;
3071 }
3072 maskHeight = obj1.getInt();
3073 obj1.free();
3074 maskDict->lookup("BitsPerComponent", &obj1);
3075 if (obj1.isNull()) {
3076 obj1.free();
3077 maskDict->lookup("BPC", &obj1);
3078 }
3079 if (!obj1.isInt()) {
3080 goto err2;
3081 }
3082 maskBits = obj1.getInt();
3083 obj1.free();
3084 maskDict->lookup("ColorSpace", &obj1);
3085 if (obj1.isNull()) {
3086 obj1.free();
3087 maskDict->lookup("CS", &obj1);
3088 }
3089 if (obj1.isName()) {
3090 res->lookupColorSpace(obj1.getName(), &obj2);
3091 if (!obj2.isNull()) {
3092 obj1.free();
3093 obj1 = obj2;
3094 } else {
3095 obj2.free();
3096 }
3097 }
3098 maskColorSpace = GfxColorSpace::parse(&obj1);
3099 obj1.free();
3100 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
3101 goto err1;
3102 }
3103 maskDict->lookup("Decode", &obj1);
3104 if (obj1.isNull()) {
3105 obj1.free();
3106 maskDict->lookup("D", &obj1);
3107 }
3108 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
3109 obj1.free();
3110 if (!maskColorMap->isOk()) {
3111 delete maskColorMap;
3112 goto err1;
3113 }
3114 //~ handle the Matte entry
3115 haveSoftMask = gTrue;
3116 } else if (maskObj.isArray()) {
3117 // color key mask
3118 for (i = 0;
3119 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
3120 ++i) {
3121 maskObj.arrayGet(i, &obj1);
3122 maskColors[i] = obj1.getInt();
3123 obj1.free();
3124 }
3125 haveColorKeyMask = gTrue;
3126 } else if (maskObj.isStream()) {
3127 // explicit mask
3128 if (inlineImg) {
3129 goto err1;
3130 }
3131 maskStr = maskObj.getStream();
3132 maskDict = maskObj.streamGetDict();
3133 maskDict->lookup("Width", &obj1);
3134 if (obj1.isNull()) {
3135 obj1.free();
3136 maskDict->lookup("W", &obj1);
3137 }
3138 if (!obj1.isInt()) {
3139 goto err2;
3140 }
3141 maskWidth = obj1.getInt();
3142 obj1.free();
3143 maskDict->lookup("Height", &obj1);
3144 if (obj1.isNull()) {
3145 obj1.free();
3146 maskDict->lookup("H", &obj1);
3147 }
3148 if (!obj1.isInt()) {
3149 goto err2;
3150 }
3151 maskHeight = obj1.getInt();
3152 obj1.free();
3153 maskDict->lookup("ImageMask", &obj1);
3154 if (obj1.isNull()) {
3155 obj1.free();
3156 maskDict->lookup("IM", &obj1);
3157 }
3158 if (!obj1.isBool() || !obj1.getBool()) {
3159 goto err2;
3160 }
3161 obj1.free();
3162 maskInvert = gFalse;
3163 maskDict->lookup("Decode", &obj1);
3164 if (obj1.isNull()) {
3165 obj1.free();
3166 maskDict->lookup("D", &obj1);
3167 }
3168 if (obj1.isArray()) {
3169 obj1.arrayGet(0, &obj2);
3170 if (obj2.isInt() && obj2.getInt() == 1) {
3171 maskInvert = gTrue;
3172 }
3173 obj2.free();
3174 } else if (!obj1.isNull()) {
3175 goto err2;
3176 }
3177 obj1.free();
3178 haveExplicitMask = gTrue;
3179 }
3180
3181 // draw it
3182 if (haveSoftMask) {
3183 out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
3184 maskStr, maskWidth, maskHeight, maskColorMap);
3185 delete maskColorMap;
3186 } else if (haveExplicitMask) {
3187 out->drawMaskedImage(state, ref, str, width, height, colorMap,
3188 maskStr, maskWidth, maskHeight, maskInvert);
3189 } else {
3190 out->drawImage(state, ref, str, width, height, colorMap,
3191 haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
3192 }
3193 delete colorMap;
3194
3195 maskObj.free();
3196 smaskObj.free();
3197 }
3198
3199 if ((i = width * height) > 1000) {
3200 i = 1000;
3201 }
3202 updateLevel += i;
3203
3204 return;
3205
3206 err2:
3207 obj1.free();
3208 err1:
3209 error(getPos(), "Bad image parameters");
3210 }
3211
3212 void Gfx::doForm(Object *str) {
3213 Dict *dict;
3214 Object matrixObj, bboxObj;
3215 double m[6], bbox[6];
3216 Object resObj;
3217 Dict *resDict;
3218 Object obj1;
3219 int i;
3220
3221 // check for excessive recursion
3222 if (formDepth > 20) {
3223 return;
3224 }
3225
3226 // get stream dict
3227 dict = str->streamGetDict();
3228
3229 // check form type
3230 dict->lookup("FormType", &obj1);
3231 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
3232 error(getPos(), "Unknown form type");
3233 }
3234 obj1.free();
3235
3236 // get bounding box
3237 dict->lookup("BBox", &bboxObj);
3238 if (!bboxObj.isArray()) {
3239 matrixObj.free();
3240 bboxObj.free();
3241 error(getPos(), "Bad form bounding box");
3242 return;
3243 }
3244 for (i = 0; i < 4; ++i) {
3245 bboxObj.arrayGet(i, &obj1);
3246 bbox[i] = obj1.getNum();
3247 obj1.free();
3248 }
3249 bboxObj.free();
3250
3251 // get matrix
3252 dict->lookup("Matrix", &matrixObj);
3253 if (matrixObj.isArray()) {
3254 for (i = 0; i < 6; ++i) {
3255 matrixObj.arrayGet(i, &obj1);
3256 m[i] = obj1.getNum();
3257 obj1.free();
3258 }
3259 } else {
3260 m[0] = 1; m[1] = 0;
3261 m[2] = 0; m[3] = 1;
3262 m[4] = 0; m[5] = 0;
3263 }
3264 matrixObj.free();
3265
3266 // get resources
3267 dict->lookup("Resources", &resObj);
3268 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
3269
3270 // draw it
3271 ++formDepth;
3272 doForm1(str, resDict, m, bbox);
3273 --formDepth;
3274
3275 resObj.free();
3276 }
3277
3278 void Gfx::doAnnot(Object *str, double xMin, double yMin,
3279 double xMax, double yMax) {
3280 Dict *dict, *resDict;
3281 Object matrixObj, bboxObj, resObj, flagsObj;
3282 Object obj1;
3283 double m[6], bbox[6], ictm[6];
3284 double *ctm;
3285 double formX0, formY0, formX1, formY1;
3286 double annotX0, annotY0, annotX1, annotY1;
3287 double det, x, y, sx, sy;
3288 int i, flags;
3289
3290 // get stream dict
3291 dict = str->streamGetDict();
3292
3293 // get annotation flags and only print annotations that are hidden or
3294 // don't have the print bit set.
3295 dict->lookup("F", &flagsObj);
3296 if (flagsObj.isInt()) {
3297 flags = flagsObj.getInt();
3298 } else {
3299 // Print anything that doesn't have any flags set...
3300 flags = 4;
3301 }
3302 flagsObj.free();
3303
3304 fprintf(stderr, "DEBUG: pdftops: doAnnot found annotation with flags = %x\n",
3305 flags);
3306
3307 if ((flags & 2) == 2 || (flags & 4) == 0) {
3308 // Don't print hidden or no-print annotations...
3309 return;
3310 }
3311
3312 // get the form bounding box
3313 dict->lookup("BBox", &bboxObj);
3314 if (!bboxObj.isArray()) {
3315 bboxObj.free();
3316 error(getPos(), "Bad form bounding box");
3317 return;
3318 }
3319 for (i = 0; i < 4; ++i) {
3320 bboxObj.arrayGet(i, &obj1);
3321 bbox[i] = obj1.getNum();
3322 obj1.free();
3323 }
3324 bboxObj.free();
3325
3326 // get the form matrix
3327 dict->lookup("Matrix", &matrixObj);
3328 if (matrixObj.isArray()) {
3329 for (i = 0; i < 6; ++i) {
3330 matrixObj.arrayGet(i, &obj1);
3331 m[i] = obj1.getNum();
3332 obj1.free();
3333 }
3334 } else {
3335 m[0] = 1; m[1] = 0;
3336 m[2] = 0; m[3] = 1;
3337 m[4] = 0; m[5] = 0;
3338 }
3339 matrixObj.free();
3340
3341 // transform the form bbox from form space to user space
3342 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
3343 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
3344 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
3345 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
3346
3347 // transform the annotation bbox from default user space to user
3348 // space: (bbox * baseMatrix) * iCTM
3349 ctm = state->getCTM();
3350 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
3351 ictm[0] = ctm[3] * det;
3352 ictm[1] = -ctm[1] * det;
3353 ictm[2] = -ctm[2] * det;
3354 ictm[3] = ctm[0] * det;
3355 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
3356 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
3357 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
3358 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
3359 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
3360 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
3361 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
3362 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
3363 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
3364 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
3365
3366 // swap min/max coords
3367 if (formX0 > formX1) {
3368 x = formX0; formX0 = formX1; formX1 = x;
3369 }
3370 if (formY0 > formY1) {
3371 y = formY0; formY0 = formY1; formY1 = y;
3372 }
3373 if (annotX0 > annotX1) {
3374 x = annotX0; annotX0 = annotX1; annotX1 = x;
3375 }
3376 if (annotY0 > annotY1) {
3377 y = annotY0; annotY0 = annotY1; annotY1 = y;
3378 }
3379
3380 // scale the form to fit the annotation bbox
3381 if (formX1 == formX0) {
3382 // this shouldn't happen
3383 sx = 1;
3384 } else {
3385 sx = (annotX1 - annotX0) / (formX1 - formX0);
3386 }
3387 if (formY1 == formY0) {
3388 // this shouldn't happen
3389 sy = 1;
3390 } else {
3391 sy = (annotY1 - annotY0) / (formY1 - formY0);
3392 }
3393 m[0] *= sx;
3394 m[2] *= sx;
3395 m[4] = (m[4] - formX0) * sx + annotX0;
3396 m[1] *= sy;
3397 m[3] *= sy;
3398 m[5] = (m[5] - formY0) * sy + annotY0;
3399
3400 // get resources
3401 dict->lookup("Resources", &resObj);
3402 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
3403
3404 // draw it
3405 doForm1(str, resDict, m, bbox);
3406
3407 resObj.free();
3408 bboxObj.free();
3409 }
3410
3411 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
3412 Parser *oldParser;
3413 double oldBaseMatrix[6];
3414 int i;
3415
3416 // push new resources on stack
3417 pushResources(resDict);
3418
3419 // save current graphics state
3420 saveState();
3421
3422 // kill any pre-existing path
3423 state->clearPath();
3424
3425 // save current parser
3426 oldParser = parser;
3427
3428 // set form transformation matrix
3429 state->concatCTM(matrix[0], matrix[1], matrix[2],
3430 matrix[3], matrix[4], matrix[5]);
3431 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
3432 matrix[3], matrix[4], matrix[5]);
3433
3434 // set new base matrix
3435 for (i = 0; i < 6; ++i) {
3436 oldBaseMatrix[i] = baseMatrix[i];
3437 baseMatrix[i] = state->getCTM()[i];
3438 }
3439
3440 // set form bounding box
3441 state->moveTo(bbox[0], bbox[1]);
3442 state->lineTo(bbox[2], bbox[1]);
3443 state->lineTo(bbox[2], bbox[3]);
3444 state->lineTo(bbox[0], bbox[3]);
3445 state->closePath();
3446 state->clip();
3447 out->clip(state);
3448 state->clearPath();
3449
3450 // draw the form
3451 display(str, gFalse);
3452
3453 // restore base matrix
3454 for (i = 0; i < 6; ++i) {
3455 baseMatrix[i] = oldBaseMatrix[i];
3456 }
3457
3458 // restore parser
3459 parser = oldParser;
3460
3461 // restore graphics state
3462 restoreState();
3463
3464 // pop resource stack
3465 popResources();
3466
3467 return;
3468 }
3469
3470 //------------------------------------------------------------------------
3471 // in-line image operators
3472 //------------------------------------------------------------------------
3473
3474 void Gfx::opBeginImage(Object args[], int numArgs) {
3475 Stream *str;
3476 int c1, c2;
3477
3478 // build dict/stream
3479 str = buildImageStream();
3480
3481 // display the image
3482 if (str) {
3483 doImage(NULL, str, gTrue);
3484
3485 // skip 'EI' tag
3486 c1 = str->getBaseStream()->getChar();
3487 c2 = str->getBaseStream()->getChar();
3488 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
3489 c1 = c2;
3490 c2 = str->getBaseStream()->getChar();
3491 }
3492 delete str;
3493 }
3494 }
3495
3496 Stream *Gfx::buildImageStream() {
3497 Object dict;
3498 Object obj;
3499 char *key;
3500 Stream *str;
3501
3502 // build dictionary
3503 dict.initDict(xref);
3504 parser->getObj(&obj);
3505 while (!obj.isCmd("ID") && !obj.isEOF()) {
3506 if (!obj.isName()) {
3507 error(getPos(), "Inline image dictionary key must be a name object");
3508 obj.free();
3509 } else {
3510 key = copyString(obj.getName());
3511 obj.free();
3512 parser->getObj(&obj);
3513 if (obj.isEOF() || obj.isError()) {
3514 gfree(key);
3515 break;
3516 }
3517 dict.dictAdd(key, &obj);
3518 }
3519 parser->getObj(&obj);
3520 }
3521 if (obj.isEOF()) {
3522 error(getPos(), "End of file in inline image");
3523 obj.free();
3524 dict.free();
3525 return NULL;
3526 }
3527 obj.free();
3528
3529 // make stream
3530 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3531 str = str->addFilters(&dict);
3532
3533 return str;
3534 }
3535
3536 void Gfx::opImageData(Object args[], int numArgs) {
3537 error(getPos(), "Internal: got 'ID' operator");
3538 }
3539
3540 void Gfx::opEndImage(Object args[], int numArgs) {
3541 error(getPos(), "Internal: got 'EI' operator");
3542 }
3543
3544 //------------------------------------------------------------------------
3545 // type 3 font operators
3546 //------------------------------------------------------------------------
3547
3548 void Gfx::opSetCharWidth(Object args[], int numArgs) {
3549 out->type3D0(state, args[0].getNum(), args[1].getNum());
3550 }
3551
3552 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
3553 out->type3D1(state, args[0].getNum(), args[1].getNum(),
3554 args[2].getNum(), args[3].getNum(),
3555 args[4].getNum(), args[5].getNum());
3556 }
3557
3558 //------------------------------------------------------------------------
3559 // compatibility operators
3560 //------------------------------------------------------------------------
3561
3562 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
3563 ++ignoreUndef;
3564 }
3565
3566 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
3567 if (ignoreUndef > 0)
3568 --ignoreUndef;
3569 }
3570
3571 //------------------------------------------------------------------------
3572 // marked content operators
3573 //------------------------------------------------------------------------
3574
3575 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
3576 if (printCommands) {
3577 printf(" marked content: %s ", args[0].getName());
3578 if (numArgs == 2)
3579 args[2].print(stdout);
3580 printf("\n");
3581 fflush(stdout);
3582 }
3583 }
3584
3585 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
3586 }
3587
3588 void Gfx::opMarkPoint(Object args[], int numArgs) {
3589 if (printCommands) {
3590 printf(" mark point: %s ", args[0].getName());
3591 if (numArgs == 2)
3592 args[2].print(stdout);
3593 printf("\n");
3594 fflush(stdout);
3595 }
3596 }
3597
3598 //------------------------------------------------------------------------
3599 // misc
3600 //------------------------------------------------------------------------
3601
3602 void Gfx::saveState() {
3603 out->saveState(state);
3604 state = state->save();
3605 }
3606
3607 void Gfx::restoreState() {
3608 state = state->restore();
3609 out->restoreState(state);
3610 }
3611
3612 void Gfx::pushResources(Dict *resDict) {
3613 res = new GfxResources(xref, resDict, res);
3614 }
3615
3616 void Gfx::popResources() {
3617 GfxResources *resPtr;
3618
3619 resPtr = res->getNext();
3620 delete res;
3621 res = resPtr;
3622 }