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