Gfx.cpp
1//========================================================================
2//
3// Gfx.cpp
4//
5// Copyright 1996-2002 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <aconf.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <stdio.h>
16#include <stddef.h>
17#include <string.h>
18#include <math.h>
19#include "gmem.h"
20#include "GlobalParams.h"
21#include "CharTypes.h"
22#include "Object.h"
23#include "Array.h"
24#include "Dict.h"
25#include "Stream.h"
26#include "Lexer.h"
27#include "Parser.h"
28#include "GfxFont.h"
29#include "GfxState.h"
30#include "OutputDev.h"
31#include "Page.h"
32#include "Error.h"
33#include "Gfx.h"
34
35// the MSVC math.h doesn't define this
36#ifndef M_PI
37#define M_PI 3.14159265358979323846
38#endif
39
40//------------------------------------------------------------------------
41// constants
42//------------------------------------------------------------------------
43
44// Max number of splits along the t axis for an axial shading fill.
45#define axialMaxSplits 256
46
47// Max delta allowed in any color component for an axial shading fill.
48#define axialColorDelta (1 / 256.0)
49
50// Max number of splits along the t axis for a radial shading fill.
51#define radialMaxSplits 256
52
53// Max delta allowed in any color component for a radial shading fill.
54#define radialColorDelta (1 / 256.0)
55
56//------------------------------------------------------------------------
57// Operator table
58//------------------------------------------------------------------------
59
60Operator Gfx::opTab[] = {
61 {"\"", 3, {tchkNum, tchkNum, tchkString},
62 &Gfx::opMoveSetShowText},
63 {"'", 1, {tchkString},
64 &Gfx::opMoveShowText},
65 {"B", 0, {tchkNone},
66 &Gfx::opFillStroke},
67 {"B*", 0, {tchkNone},
68 &Gfx::opEOFillStroke},
69 {"BDC", 2, {tchkName, tchkProps},
70 &Gfx::opBeginMarkedContent},
71 {"BI", 0, {tchkNone},
72 &Gfx::opBeginImage},
73 {"BMC", 1, {tchkName},
74 &Gfx::opBeginMarkedContent},
75 {"BT", 0, {tchkNone},
76 &Gfx::opBeginText},
77 {"BX", 0, {tchkNone},
78 &Gfx::opBeginIgnoreUndef},
79 {"CS", 1, {tchkName},
80 &Gfx::opSetStrokeColorSpace},
81 {"DP", 2, {tchkName, tchkProps},
82 &Gfx::opMarkPoint},
83 {"Do", 1, {tchkName},
84 &Gfx::opXObject},
85 {"EI", 0, {tchkNone},
86 &Gfx::opEndImage},
87 {"EMC", 0, {tchkNone},
88 &Gfx::opEndMarkedContent},
89 {"ET", 0, {tchkNone},
90 &Gfx::opEndText},
91 {"EX", 0, {tchkNone},
92 &Gfx::opEndIgnoreUndef},
93 {"F", 0, {tchkNone},
94 &Gfx::opFill},
95 {"G", 1, {tchkNum},
96 &Gfx::opSetStrokeGray},
97 {"ID", 0, {tchkNone},
98 &Gfx::opImageData},
99 {"J", 1, {tchkInt},
100 &Gfx::opSetLineCap},
101 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
102 &Gfx::opSetStrokeCMYKColor},
103 {"M", 1, {tchkNum},
104 &Gfx::opSetMiterLimit},
105 {"MP", 1, {tchkName},
106 &Gfx::opMarkPoint},
107 {"Q", 0, {tchkNone},
108 &Gfx::opRestore},
109 {"RG", 3, {tchkNum, tchkNum, tchkNum},
110 &Gfx::opSetStrokeRGBColor},
111 {"S", 0, {tchkNone},
112 &Gfx::opStroke},
113 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
114 &Gfx::opSetStrokeColor},
115 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
116 tchkSCN},
117 &Gfx::opSetStrokeColorN},
118 {"T*", 0, {tchkNone},
119 &Gfx::opTextNextLine},
120 {"TD", 2, {tchkNum, tchkNum},
121 &Gfx::opTextMoveSet},
122 {"TJ", 1, {tchkArray},
123 &Gfx::opShowSpaceText},
124 {"TL", 1, {tchkNum},
125 &Gfx::opSetTextLeading},
126 {"Tc", 1, {tchkNum},
127 &Gfx::opSetCharSpacing},
128 {"Td", 2, {tchkNum, tchkNum},
129 &Gfx::opTextMove},
130 {"Tf", 2, {tchkName, tchkNum},
131 &Gfx::opSetFont},
132 {"Tj", 1, {tchkString},
133 &Gfx::opShowText},
134 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
135 tchkNum, tchkNum},
136 &Gfx::opSetTextMatrix},
137 {"Tr", 1, {tchkInt},
138 &Gfx::opSetTextRender},
139 {"Ts", 1, {tchkNum},
140 &Gfx::opSetTextRise},
141 {"Tw", 1, {tchkNum},
142 &Gfx::opSetWordSpacing},
143 {"Tz", 1, {tchkNum},
144 &Gfx::opSetHorizScaling},
145 {"W", 0, {tchkNone},
146 &Gfx::opClip},
147 {"W*", 0, {tchkNone},
148 &Gfx::opEOClip},
149 {"b", 0, {tchkNone},
150 &Gfx::opCloseFillStroke},
151 {"b*", 0, {tchkNone},
152 &Gfx::opCloseEOFillStroke},
153 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
154 tchkNum, tchkNum},
155 &Gfx::opCurveTo},
156 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
157 tchkNum, tchkNum},
158 &Gfx::opConcat},
159 {"cs", 1, {tchkName},
160 &Gfx::opSetFillColorSpace},
161 {"d", 2, {tchkArray, tchkNum},
162 &Gfx::opSetDash},
163 {"d0", 2, {tchkNum, tchkNum},
164 &Gfx::opSetCharWidth},
165 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
166 tchkNum, tchkNum},
167 &Gfx::opSetCacheDevice},
168 {"f", 0, {tchkNone},
169 &Gfx::opFill},
170 {"f*", 0, {tchkNone},
171 &Gfx::opEOFill},
172 {"g", 1, {tchkNum},
173 &Gfx::opSetFillGray},
174 {"gs", 1, {tchkName},
175 &Gfx::opSetExtGState},
176 {"h", 0, {tchkNone},
177 &Gfx::opClosePath},
178 {"i", 1, {tchkNum},
179 &Gfx::opSetFlat},
180 {"j", 1, {tchkInt},
181 &Gfx::opSetLineJoin},
182 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
183 &Gfx::opSetFillCMYKColor},
184 {"l", 2, {tchkNum, tchkNum},
185 &Gfx::opLineTo},
186 {"m", 2, {tchkNum, tchkNum},
187 &Gfx::opMoveTo},
188 {"n", 0, {tchkNone},
189 &Gfx::opEndPath},
190 {"q", 0, {tchkNone},
191 &Gfx::opSave},
192 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
193 &Gfx::opRectangle},
194 {"rg", 3, {tchkNum, tchkNum, tchkNum},
195 &Gfx::opSetFillRGBColor},
196 {"ri", 1, {tchkName},
197 &Gfx::opSetRenderingIntent},
198 {"s", 0, {tchkNone},
199 &Gfx::opCloseStroke},
200 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
201 &Gfx::opSetFillColor},
202 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
203 tchkSCN},
204 &Gfx::opSetFillColorN},
205 {"sh", 1, {tchkName},
206 &Gfx::opShFill},
207 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
208 &Gfx::opCurveTo1},
209 {"w", 1, {tchkNum},
210 &Gfx::opSetLineWidth},
211 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
212 &Gfx::opCurveTo2},
213};
214
215#define numOps (sizeof(opTab) / sizeof(Operator))
216
217//------------------------------------------------------------------------
218// GfxResources
219//------------------------------------------------------------------------
220
221GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
222 Object obj1;
223
224 if (resDict) {
225
226 // build font dictionary
227 fonts = NULL;
228 resDict->lookup("Font", &obj1);
229 if (obj1.isDict()) {
230 fonts = new GfxFontDict(xref, obj1.getDict());
231 }
232 obj1.free();
233
234 // get XObject dictionary
235 resDict->lookup("XObject", &xObjDict);
236
237 // get color space dictionary
238 resDict->lookup("ColorSpace", &colorSpaceDict);
239
240 // get pattern dictionary
241 resDict->lookup("Pattern", &patternDict);
242
243 // get shading dictionary
244 resDict->lookup("Shading", &shadingDict);
245
246 // get graphics state parameter dictionary
247 resDict->lookup("ExtGState", &gStateDict);
248
249 } else {
250 fonts = NULL;
251 xObjDict.initNull();
252 colorSpaceDict.initNull();
253 patternDict.initNull();
254 gStateDict.initNull();
255 }
256
257 next = nextA;
258}
259
260GfxResources::~GfxResources() {
261 if (fonts) {
262 delete fonts;
263 }
264 xObjDict.free();
265 colorSpaceDict.free();
266 patternDict.free();
267 shadingDict.free();
268 gStateDict.free();
269}
270
271GfxFont *GfxResources::lookupFont(char *name) {
272 GfxFont *font;
273 GfxResources *resPtr;
274
275 for (resPtr = this; resPtr; resPtr = resPtr->next) {
276 if (resPtr->fonts) {
277 if ((font = resPtr->fonts->lookup(name)))
278 return font;
279 }
280 }
281 error(-1, "Unknown font tag '%s'", name);
282 return NULL;
283}
284
285GBool GfxResources::lookupXObject(char *name, Object *obj) {
286 GfxResources *resPtr;
287
288 for (resPtr = this; resPtr; resPtr = resPtr->next) {
289 if (resPtr->xObjDict.isDict()) {
290 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
291 return gTrue;
292 obj->free();
293 }
294 }
295 error(-1, "XObject '%s' is unknown", name);
296 return gFalse;
297}
298
299GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
300 GfxResources *resPtr;
301
302 for (resPtr = this; resPtr; resPtr = resPtr->next) {
303 if (resPtr->xObjDict.isDict()) {
304 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
305 return gTrue;
306 obj->free();
307 }
308 }
309 error(-1, "XObject '%s' is unknown", name);
310 return gFalse;
311}
312
313void GfxResources::lookupColorSpace(char *name, Object *obj) {
314 GfxResources *resPtr;
315
316 for (resPtr = this; resPtr; resPtr = resPtr->next) {
317 if (resPtr->colorSpaceDict.isDict()) {
318 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
319 return;
320 }
321 obj->free();
322 }
323 }
324 obj->initNull();
325}
326
327GfxPattern *GfxResources::lookupPattern(char *name) {
328 GfxResources *resPtr;
329 GfxPattern *pattern;
330 Object obj;
331
332 for (resPtr = this; resPtr; resPtr = resPtr->next) {
333 if (resPtr->patternDict.isDict()) {
334 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
335 pattern = GfxPattern::parse(&obj);
336 obj.free();
337 return pattern;
338 }
339 obj.free();
340 }
341 }
342 error(-1, "Unknown pattern '%s'", name);
343 return NULL;
344}
345
346GfxShading *GfxResources::lookupShading(char *name) {
347 GfxResources *resPtr;
348 GfxShading *shading;
349 Object obj;
350
351 for (resPtr = this; resPtr; resPtr = resPtr->next) {
352 if (resPtr->shadingDict.isDict()) {
353 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
354 shading = GfxShading::parse(&obj);
355 obj.free();
356 return shading;
357 }
358 obj.free();
359 }
360 }
361 error(-1, "Unknown shading '%s'", name);
362 return NULL;
363}
364
365GBool GfxResources::lookupGState(char *name, Object *obj) {
366 GfxResources *resPtr;
367
368 for (resPtr = this; resPtr; resPtr = resPtr->next) {
369 if (resPtr->gStateDict.isDict()) {
370 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
371 return gTrue;
372 }
373 obj->free();
374 }
375 }
376 error(-1, "ExtGState '%s' is unknown", name);
377 return gFalse;
378}
379
380//------------------------------------------------------------------------
381// Gfx
382//------------------------------------------------------------------------
383
384Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
385 PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
386 GBool (*abortCheckCbkA)(void *data),
387 void *abortCheckCbkDataA) {
388 int i;
389
390 xref = xrefA;
391 subPage = gFalse;
392 printCommands = globalParams->getPrintCommands();
393
394 // start the resource stack
395 res = new GfxResources(xref, resDict, NULL);
396
397 // initialize
398 out = outA;
399 state = new GfxState(dpi, box, rotate, out->upsideDown());
400 fontChanged = gFalse;
401 clip = clipNone;
402 ignoreUndef = 0;
403 out->startPage(pageNum, state);
404 out->setDefaultCTM(state->getCTM());
405 out->updateAll(state);
406 for (i = 0; i < 6; ++i) {
407 baseMatrix[i] = state->getCTM()[i];
408 }
409 abortCheckCbk = abortCheckCbkA;
410 abortCheckCbkData = abortCheckCbkDataA;
411
412 // set crop box
413 if (crop) {
414 state->moveTo(cropBox->x1, cropBox->y1);
415 state->lineTo(cropBox->x2, cropBox->y1);
416 state->lineTo(cropBox->x2, cropBox->y2);
417 state->lineTo(cropBox->x1, cropBox->y2);
418 state->closePath();
419 state->clip();
420 out->clip(state);
421 state->clearPath();
422 }
423}
424
425Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
426 PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
427 GBool (*abortCheckCbkA)(void *data),
428 void *abortCheckCbkDataA) {
429 int i;
430
431 xref = xrefA;
432 subPage = gTrue;
433 printCommands = globalParams->getPrintCommands();
434
435 // start the resource stack
436 res = new GfxResources(xref, resDict, NULL);
437
438 // initialize
439 out = outA;
440 state = new GfxState(72, box, 0, gFalse);
441 fontChanged = gFalse;
442 clip = clipNone;
443 ignoreUndef = 0;
444 for (i = 0; i < 6; ++i) {
445 baseMatrix[i] = state->getCTM()[i];
446 }
447 abortCheckCbk = abortCheckCbkA;
448 abortCheckCbkData = abortCheckCbkDataA;
449
450 // set crop box
451 if (crop) {
452 state->moveTo(cropBox->x1, cropBox->y1);
453 state->lineTo(cropBox->x2, cropBox->y1);
454 state->lineTo(cropBox->x2, cropBox->y2);
455 state->lineTo(cropBox->x1, cropBox->y2);
456 state->closePath();
457 state->clip();
458 out->clip(state);
459 state->clearPath();
460 }
461}
462
463Gfx::~Gfx() {
464 while (state->hasSaves()) {
465 state = state->restore();
466 out->restoreState(state);
467 }
468 if (!subPage) {
469 out->endPage();
470 }
471 while (res) {
472 popResources();
473 }
474 if (state) {
475 delete state;
476 }
477}
478
479void Gfx::display(Object *obj, GBool topLevel) {
480 Object obj2;
481 int i;
482
483 if (obj->isArray()) {
484 for (i = 0; i < obj->arrayGetLength(); ++i) {
485 obj->arrayGet(i, &obj2);
486 if (!obj2.isStream()) {
487 error(-1, "Weird page contents");
488 obj2.free();
489 return;
490 }
491 obj2.free();
492 }
493 } else if (!obj->isStream()) {
494 error(-1, "Weird page contents");
495 return;
496 }
497 parser = new Parser(xref, new Lexer(xref, obj));
498 go(topLevel);
499 delete parser;
500 parser = NULL;
501}
502
503void Gfx::go(GBool topLevel) {
504 Object obj;
505 Object args[maxArgs];
506 int numArgs, i;
507 int lastAbortCheck;
508
509 // scan a sequence of objects
510 updateLevel = lastAbortCheck = 0;
511 numArgs = 0;
512 parser->getObj(&obj);
513 while (!obj.isEOF()) {
514
515 // got a command - execute it
516 if (obj.isCmd()) {
517 if (printCommands) {
518 obj.print(stdout);
519 for (i = 0; i < numArgs; ++i) {
520 printf(" ");
521 args[i].print(stdout);
522 }
523 printf("\n");
524 fflush(stdout);
525 }
526 execOp(&obj, args, numArgs);
527 obj.free();
528 for (i = 0; i < numArgs; ++i)
529 args[i].free();
530 numArgs = 0;
531
532 // periodically update display
533 if (++updateLevel >= 20000) {
534 out->dump();
535 updateLevel = 0;
536 }
537
538 // check for an abort
539 if (abortCheckCbk) {
540 if (updateLevel - lastAbortCheck > 10) {
541 if ((*abortCheckCbk)(abortCheckCbkData)) {
542 break;
543 }
544 lastAbortCheck = updateLevel;
545 }
546 }
547
548 // got an argument - save it
549 } else if (numArgs < maxArgs) {
550 args[numArgs++] = obj;
551
552 // too many arguments - something is wrong
553 } else {
554 error(getPos(), "Too many args in content stream");
555 if (printCommands) {
556 printf("throwing away arg: ");
557 obj.print(stdout);
558 printf("\n");
559 fflush(stdout);
560 }
561 obj.free();
562 }
563
564 // grab the next object
565 parser->getObj(&obj);
566 }
567 obj.free();
568
569 // args at end with no command
570 if (numArgs > 0) {
571 error(getPos(), "Leftover args in content stream");
572 if (printCommands) {
573 printf("%d leftovers:", numArgs);
574 for (i = 0; i < numArgs; ++i) {
575 printf(" ");
576 args[i].print(stdout);
577 }
578 printf("\n");
579 fflush(stdout);
580 }
581 for (i = 0; i < numArgs; ++i)
582 args[i].free();
583 }
584
585 // update display
586 if (topLevel && updateLevel > 0) {
587 out->dump();
588 }
589}
590
591void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
592 Operator *op;
593 const char *name;
594 int i;
595
596 // find operator
597 name = cmd->getCmd();
598 if (!(op = findOp(name))) {
599 if (ignoreUndef == 0)
600 error(getPos(), "Unknown operator '%s'", name);
601 return;
602 }
603
604 // type check args
605 if (op->numArgs >= 0) {
606 if (numArgs != op->numArgs) {
607 error(getPos(), "Wrong number (%d) of args to '%s' operator",
608 numArgs, name);
609 return;
610 }
611 } else {
612 if (numArgs > -op->numArgs) {
613 error(getPos(), "Too many (%d) args to '%s' operator",
614 numArgs, name);
615 return;
616 }
617 }
618 for (i = 0; i < numArgs; ++i) {
619 if (!checkArg(&args[i], op->tchk[i])) {
620 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
621 i, name, args[i].getTypeName());
622 return;
623 }
624 }
625
626 // do it
627 (this->*op->func)(args, numArgs);
628}
629
630Operator *Gfx::findOp(const char *name) {
631 int a, b, m, cmp;
632
633 a = -1;
634 b = numOps;
635 // invariant: opTab[a] < name < opTab[b]
636 while (b - a > 1) {
637 m = (a + b) / 2;
638 cmp = strcmp(opTab[m].name, name);
639 if (cmp < 0)
640 a = m;
641 else if (cmp > 0)
642 b = m;
643 else
644 a = b = m;
645 }
646 if (cmp != 0)
647 return NULL;
648 return &opTab[a];
649}
650
651GBool Gfx::checkArg(Object *arg, TchkType type) {
652 switch (type) {
653 case tchkBool: return arg->isBool();
654 case tchkInt: return arg->isInt();
655 case tchkNum: return arg->isNum();
656 case tchkString: return arg->isString();
657 case tchkName: return arg->isName();
658 case tchkArray: return arg->isArray();
659 case tchkProps: return arg->isDict() || arg->isName();
660 case tchkSCN: return arg->isNum() || arg->isName();
661 case tchkNone: return gFalse;
662 }
663 return gFalse;
664}
665
666int Gfx::getPos() {
667 return parser ? parser->getPos() : -1;
668}
669
670//------------------------------------------------------------------------
671// graphics state operators
672//------------------------------------------------------------------------
673
674void Gfx::opSave(Object /*args*/[], int /*numArgs*/) {
675 out->saveState(state);
676 state = state->save();
677}
678
679void Gfx::opRestore(Object /*args*/[], int /*numArgs*/) {
680 state = state->restore();
681 out->restoreState(state);
682}
683
684void Gfx::opConcat(Object args[], int /*numArgs*/) {
685 state->concatCTM(args[0].getNum(), args[1].getNum(),
686 args[2].getNum(), args[3].getNum(),
687 args[4].getNum(), args[5].getNum());
688 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
689 args[2].getNum(), args[3].getNum(),
690 args[4].getNum(), args[5].getNum());
691 fontChanged = gTrue;
692}
693
694void Gfx::opSetDash(Object args[], int /*numArgs*/) {
695 Array *a;
696 int length;
697 Object obj;
698 double *dash;
699 int i;
700
701 a = args[0].getArray();
702 length = a->getLength();
703 if (length == 0) {
704 dash = NULL;
705 } else {
706 dash = (double *)gmalloc(length * sizeof(double));
707 for (i = 0; i < length; ++i) {
708 dash[i] = a->get(i, &obj)->getNum();
709 obj.free();
710 }
711 }
712 state->setLineDash(dash, length, args[1].getNum());
713 out->updateLineDash(state);
714}
715
716void Gfx::opSetFlat(Object args[], int /*numArgs*/) {
717 state->setFlatness((int)args[0].getNum());
718 out->updateFlatness(state);
719}
720
721void Gfx::opSetLineJoin(Object args[], int /*numArgs*/) {
722 state->setLineJoin(args[0].getInt());
723 out->updateLineJoin(state);
724}
725
726void Gfx::opSetLineCap(Object args[], int /*numArgs*/) {
727 state->setLineCap(args[0].getInt());
728 out->updateLineCap(state);
729}
730
731void Gfx::opSetMiterLimit(Object args[], int /*numArgs*/) {
732 state->setMiterLimit(args[0].getNum());
733 out->updateMiterLimit(state);
734}
735
736void Gfx::opSetLineWidth(Object args[], int /*numArgs*/) {
737 state->setLineWidth(args[0].getNum());
738 out->updateLineWidth(state);
739}
740
741void Gfx::opSetExtGState(Object args[], int /*numArgs*/) {
742 Object obj1, obj2;
743
744 if (!res->lookupGState(args[0].getName(), &obj1)) {
745 return;
746 }
747 if (!obj1.isDict()) {
748 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
749 obj1.free();
750 return;
751 }
752 if (obj1.dictLookup("ca", &obj2)->isNum()) {
753 state->setFillOpacity(obj2.getNum());
754 out->updateFillOpacity(state);
755 }
756 obj2.free();
757 if (obj1.dictLookup("CA", &obj2)->isNum()) {
758 state->setStrokeOpacity(obj2.getNum());
759 out->updateStrokeOpacity(state);
760 }
761 obj2.free();
762 obj1.free();
763}
764
765void Gfx::opSetRenderingIntent(Object /*args*/[], int /*numArgs*/) {
766}
767
768//------------------------------------------------------------------------
769// color operators
770//------------------------------------------------------------------------
771
772void Gfx::opSetFillGray(Object args[], int /*numArgs*/) {
773 GfxColor color;
774
775 state->setFillPattern(NULL);
776 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
777 color.c[0] = args[0].getNum();
778 state->setFillColor(&color);
779 out->updateFillColor(state);
780}
781
782void Gfx::opSetStrokeGray(Object args[], int /*numArgs*/) {
783 GfxColor color;
784
785 state->setStrokePattern(NULL);
786 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
787 color.c[0] = args[0].getNum();
788 state->setStrokeColor(&color);
789 out->updateStrokeColor(state);
790}
791
792void Gfx::opSetFillCMYKColor(Object args[], int /*numArgs*/) {
793 GfxColor color;
794 int i;
795
796 state->setFillPattern(NULL);
797 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
798 for (i = 0; i < 4; ++i) {
799 color.c[i] = args[i].getNum();
800 }
801 state->setFillColor(&color);
802 out->updateFillColor(state);
803}
804
805void Gfx::opSetStrokeCMYKColor(Object args[], int /*numArgs*/) {
806 GfxColor color;
807 int i;
808
809 state->setStrokePattern(NULL);
810 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
811 for (i = 0; i < 4; ++i) {
812 color.c[i] = args[i].getNum();
813 }
814 state->setStrokeColor(&color);
815 out->updateStrokeColor(state);
816}
817
818void Gfx::opSetFillRGBColor(Object args[], int /*numArgs*/) {
819 GfxColor color;
820 int i;
821
822 state->setFillPattern(NULL);
823 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
824 for (i = 0; i < 3; ++i) {
825 color.c[i] = args[i].getNum();
826 }
827 state->setFillColor(&color);
828 out->updateFillColor(state);
829}
830
831void Gfx::opSetStrokeRGBColor(Object args[], int /*numArgs*/) {
832 GfxColor color;
833 int i;
834
835 state->setStrokePattern(NULL);
836 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
837 for (i = 0; i < 3; ++i) {
838 color.c[i] = args[i].getNum();
839 }
840 state->setStrokeColor(&color);
841 out->updateStrokeColor(state);
842}
843
844void Gfx::opSetFillColorSpace(Object args[], int /*numArgs*/) {
845 Object obj;
846 GfxColorSpace *colorSpace;
847 GfxColor color;
848 int i;
849
850 state->setFillPattern(NULL);
851 res->lookupColorSpace(args[0].getName(), &obj);
852 if (obj.isNull()) {
853 colorSpace = GfxColorSpace::parse(&args[0]);
854 } else {
855 colorSpace = GfxColorSpace::parse(&obj);
856 }
857 obj.free();
858 if (colorSpace) {
859 state->setFillColorSpace(colorSpace);
860 } else {
861 error(getPos(), "Bad color space (fill)");
862 }
863 for (i = 0; i < gfxColorMaxComps; ++i) {
864 color.c[i] = 0;
865 }
866 state->setFillColor(&color);
867 out->updateFillColor(state);
868}
869
870void Gfx::opSetStrokeColorSpace(Object args[], int /*numArgs*/) {
871 Object obj;
872 GfxColorSpace *colorSpace;
873 GfxColor color;
874 int i;
875
876 state->setStrokePattern(NULL);
877 res->lookupColorSpace(args[0].getName(), &obj);
878 if (obj.isNull()) {
879 colorSpace = GfxColorSpace::parse(&args[0]);
880 } else {
881 colorSpace = GfxColorSpace::parse(&obj);
882 }
883 obj.free();
884 if (colorSpace) {
885 state->setStrokeColorSpace(colorSpace);
886 } else {
887 error(getPos(), "Bad color space (stroke)");
888 }
889 for (i = 0; i < gfxColorMaxComps; ++i) {
890 color.c[i] = 0;
891 }
892 state->setStrokeColor(&color);
893 out->updateStrokeColor(state);
894}
895
896void Gfx::opSetFillColor(Object args[], int numArgs) {
897 GfxColor color;
898 int i;
899
900 state->setFillPattern(NULL);
901 for (i = 0; i < numArgs; ++i) {
902 color.c[i] = args[i].getNum();
903 }
904 state->setFillColor(&color);
905 out->updateFillColor(state);
906}
907
908void Gfx::opSetStrokeColor(Object args[], int numArgs) {
909 GfxColor color;
910 int i;
911
912 state->setStrokePattern(NULL);
913 for (i = 0; i < numArgs; ++i) {
914 color.c[i] = args[i].getNum();
915 }
916 state->setStrokeColor(&color);
917 out->updateStrokeColor(state);
918}
919
920void Gfx::opSetFillColorN(Object args[], int numArgs) {
921 GfxColor color;
922 GfxPattern *pattern;
923 int i;
924
925 if (state->getFillColorSpace()->getMode() == csPattern) {
926 if (numArgs > 1) {
927 for (i = 0; i < numArgs && i < 4; ++i) {
928 if (args[i].isNum()) {
929 color.c[i] = args[i].getNum();
930 }
931 }
932 state->setFillColor(&color);
933 out->updateFillColor(state);
934 }
935 if (args[numArgs-1].isName() &&
936 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
937 state->setFillPattern(pattern);
938 }
939
940 } else {
941 state->setFillPattern(NULL);
942 for (i = 0; i < numArgs && i < 4; ++i) {
943 if (args[i].isNum()) {
944 color.c[i] = args[i].getNum();
945 }
946 }
947 state->setFillColor(&color);
948 out->updateFillColor(state);
949 }
950}
951
952void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
953 GfxColor color;
954 GfxPattern *pattern;
955 int i;
956
957 if (state->getStrokeColorSpace()->getMode() == csPattern) {
958 if (numArgs > 1) {
959 for (i = 0; i < numArgs && i < 4; ++i) {
960 if (args[i].isNum()) {
961 color.c[i] = args[i].getNum();
962 }
963 }
964 state->setStrokeColor(&color);
965 out->updateStrokeColor(state);
966 }
967 if (args[numArgs-1].isName() &&
968 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
969 state->setStrokePattern(pattern);
970 }
971
972 } else {
973 state->setStrokePattern(NULL);
974 for (i = 0; i < numArgs && i < 4; ++i) {
975 if (args[i].isNum()) {
976 color.c[i] = args[i].getNum();
977 }
978 }
979 state->setStrokeColor(&color);
980 out->updateStrokeColor(state);
981 }
982}
983
984//------------------------------------------------------------------------
985// path segment operators
986//------------------------------------------------------------------------
987
988void Gfx::opMoveTo(Object args[], int /*numArgs*/) {
989 state->moveTo(args[0].getNum(), args[1].getNum());
990}
991
992void Gfx::opLineTo(Object args[], int /*numArgs*/) {
993 if (!state->isCurPt()) {
994 error(getPos(), "No current point in lineto");
995 return;
996 }
997 state->lineTo(args[0].getNum(), args[1].getNum());
998}
999
1000void Gfx::opCurveTo(Object args[], int /*numArgs*/) {
1001 double x1, y1, x2, y2, x3, y3;
1002
1003 if (!state->isCurPt()) {
1004 error(getPos(), "No current point in curveto");
1005 return;
1006 }
1007 x1 = args[0].getNum();
1008 y1 = args[1].getNum();
1009 x2 = args[2].getNum();
1010 y2 = args[3].getNum();
1011 x3 = args[4].getNum();
1012 y3 = args[5].getNum();
1013 state->curveTo(x1, y1, x2, y2, x3, y3);
1014}
1015
1016void Gfx::opCurveTo1(Object args[], int /*numArgs*/) {
1017 double x1, y1, x2, y2, x3, y3;
1018
1019 if (!state->isCurPt()) {
1020 error(getPos(), "No current point in curveto1");
1021 return;
1022 }
1023 x1 = state->getCurX();
1024 y1 = state->getCurY();
1025 x2 = args[0].getNum();
1026 y2 = args[1].getNum();
1027 x3 = args[2].getNum();
1028 y3 = args[3].getNum();
1029 state->curveTo(x1, y1, x2, y2, x3, y3);
1030}
1031
1032void Gfx::opCurveTo2(Object args[], int /*numArgs*/) {
1033 double x1, y1, x2, y2, x3, y3;
1034
1035 if (!state->isCurPt()) {
1036 error(getPos(), "No current point in curveto2");
1037 return;
1038 }
1039 x1 = args[0].getNum();
1040 y1 = args[1].getNum();
1041 x2 = args[2].getNum();
1042 y2 = args[3].getNum();
1043 x3 = x2;
1044 y3 = y2;
1045 state->curveTo(x1, y1, x2, y2, x3, y3);
1046}
1047
1048void Gfx::opRectangle(Object args[], int /*numArgs*/) {
1049 double x, y, w, h;
1050
1051 x = args[0].getNum();
1052 y = args[1].getNum();
1053 w = args[2].getNum();
1054 h = args[3].getNum();
1055 state->moveTo(x, y);
1056 state->lineTo(x + w, y);
1057 state->lineTo(x + w, y + h);
1058 state->lineTo(x, y + h);
1059 state->closePath();
1060}
1061
1062void Gfx::opClosePath(Object /*args*/[], int /*numArgs*/) {
1063 if (!state->isCurPt()) {
1064 error(getPos(), "No current point in closepath");
1065 return;
1066 }
1067 state->closePath();
1068}
1069
1070//------------------------------------------------------------------------
1071// path painting operators
1072//------------------------------------------------------------------------
1073
1074void Gfx::opEndPath(Object /*args*/[], int /*numArgs*/) {
1075 doEndPath();
1076}
1077
1078void Gfx::opStroke(Object /*args*/[], int /*numArgs*/) {
1079 if (!state->isCurPt()) {
1080 //error(getPos(), "No path in stroke");
1081 return;
1082 }
1083 if (state->isPath())
1084 out->stroke(state);
1085 doEndPath();
1086}
1087
1088void Gfx::opCloseStroke(Object /*args*/[], int /*numArgs*/) {
1089 if (!state->isCurPt()) {
1090 //error(getPos(), "No path in closepath/stroke");
1091 return;
1092 }
1093 if (state->isPath()) {
1094 state->closePath();
1095 out->stroke(state);
1096 }
1097 doEndPath();
1098}
1099
1100void Gfx::opFill(Object /*args*/[], int /*numArgs*/) {
1101 if (!state->isCurPt()) {
1102 //error(getPos(), "No path in fill");
1103 return;
1104 }
1105 if (state->isPath()) {
1106 if (state->getFillColorSpace()->getMode() == csPattern) {
1107 doPatternFill(gFalse);
1108 } else {
1109 out->fill(state);
1110 }
1111 }
1112 doEndPath();
1113}
1114
1115void Gfx::opEOFill(Object /*args*/[], int /*numArgs*/) {
1116 if (!state->isCurPt()) {
1117 //error(getPos(), "No path in eofill");
1118 return;
1119 }
1120 if (state->isPath()) {
1121 if (state->getFillColorSpace()->getMode() == csPattern) {
1122 doPatternFill(gTrue);
1123 } else {
1124 out->eoFill(state);
1125 }
1126 }
1127 doEndPath();
1128}
1129
1130void Gfx::opFillStroke(Object /*args*/[], int /*numArgs*/) {
1131 if (!state->isCurPt()) {
1132 //error(getPos(), "No path in fill/stroke");
1133 return;
1134 }
1135 if (state->isPath()) {
1136 if (state->getFillColorSpace()->getMode() == csPattern) {
1137 doPatternFill(gFalse);
1138 } else {
1139 out->fill(state);
1140 }
1141 out->stroke(state);
1142 }
1143 doEndPath();
1144}
1145
1146void Gfx::opCloseFillStroke(Object /*args*/[], int /*numArgs*/) {
1147 if (!state->isCurPt()) {
1148 //error(getPos(), "No path in closepath/fill/stroke");
1149 return;
1150 }
1151 if (state->isPath()) {
1152 state->closePath();
1153 if (state->getFillColorSpace()->getMode() == csPattern) {
1154 doPatternFill(gFalse);
1155 } else {
1156 out->fill(state);
1157 }
1158 out->stroke(state);
1159 }
1160 doEndPath();
1161}
1162
1163void Gfx::opEOFillStroke(Object /*args*/[], int /*numArgs*/) {
1164 if (!state->isCurPt()) {
1165 //error(getPos(), "No path in eofill/stroke");
1166 return;
1167 }
1168 if (state->isPath()) {
1169 if (state->getFillColorSpace()->getMode() == csPattern) {
1170 doPatternFill(gTrue);
1171 } else {
1172 out->eoFill(state);
1173 }
1174 out->stroke(state);
1175 }
1176 doEndPath();
1177}
1178
1179void Gfx::opCloseEOFillStroke(Object /*args*/[], int /*numArgs*/) {
1180 if (!state->isCurPt()) {
1181 //error(getPos(), "No path in closepath/eofill/stroke");
1182 return;
1183 }
1184 if (state->isPath()) {
1185 state->closePath();
1186 if (state->getFillColorSpace()->getMode() == csPattern) {
1187 doPatternFill(gTrue);
1188 } else {
1189 out->eoFill(state);
1190 }
1191 out->stroke(state);
1192 }
1193 doEndPath();
1194}
1195
1196void Gfx::doPatternFill(GBool eoFill) {
1197 GfxPatternColorSpace *patCS;
1198 GfxPattern *pattern;
1199 GfxTilingPattern *tPat;
1200 const GfxColorSpace *cs;
1201 double xMin, yMin, xMax, yMax, x, y, x1, y1;
1202 double cxMin, cyMin, cxMax, cyMax;
1203 int xi0, yi0, xi1, yi1, xi, yi;
1204 const double *ctm;
1205 double *btm, *ptm;
1206 double m[6], ictm[6], m1[6], imb[6];
1207 double det;
1208 double xstep, ystep;
1209 int i;
1210
1211 // this is a bit of a kludge -- patterns can be really slow, so we
1212 // skip them if we're only doing text extraction, since they almost
1213 // certainly don't contain any text
1214 if (!out->needNonText()) {
1215 return;
1216 }
1217
1218 // get color space
1219 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1220
1221 // get pattern
1222 if (!(pattern = state->getFillPattern())) {
1223 return;
1224 }
1225 if (pattern->getType() != 1) {
1226 return;
1227 }
1228 tPat = (GfxTilingPattern *)pattern;
1229
1230 // construct a (pattern space) -> (current space) transform matrix
1231 ctm = state->getCTM();
1232 btm = baseMatrix;
1233 ptm = tPat->getMatrix();
1234 // iCTM = invert CTM
1235 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1236 ictm[0] = ctm[3] * det;
1237 ictm[1] = -ctm[1] * det;
1238 ictm[2] = -ctm[2] * det;
1239 ictm[3] = ctm[0] * det;
1240 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1241 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1242 // m1 = PTM * BTM = PTM * base transform matrix
1243 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1244 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1245 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1246 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1247 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1248 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1249 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1250 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1251 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1252 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1253 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1254 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1255 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1256
1257 // construct a (base space) -> (pattern space) transform matrix
1258 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1259 imb[0] = m1[3] * det;
1260 imb[1] = -m1[1] * det;
1261 imb[2] = -m1[2] * det;
1262 imb[3] = m1[0] * det;
1263 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1264 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1265
1266 // save current graphics state
1267 out->saveState(state);
1268 state = state->save();
1269
1270 // set underlying color space (for uncolored tiling patterns)
1271 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1272 state->setFillColorSpace(cs->copy());
1273 } else {
1274 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1275 }
1276 state->setFillPattern(NULL);
1277 out->updateFillColor(state);
1278
1279 // clip to current path
1280 state->clip();
1281 if (eoFill) {
1282 out->eoClip(state);
1283 } else {
1284 out->clip(state);
1285 }
1286 state->clearPath();
1287
1288 // transform clip region bbox to pattern space
1289 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1290 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1291 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1292 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1293 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1294 if (x1 < xMin) {
1295 xMin = x1;
1296 } else if (x1 > xMax) {
1297 xMax = x1;
1298 }
1299 if (y1 < yMin) {
1300 yMin = y1;
1301 } else if (y1 > yMax) {
1302 yMax = y1;
1303 }
1304 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1305 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1306 if (x1 < xMin) {
1307 xMin = x1;
1308 } else if (x1 > xMax) {
1309 xMax = x1;
1310 }
1311 if (y1 < yMin) {
1312 yMin = y1;
1313 } else if (y1 > yMax) {
1314 yMax = y1;
1315 }
1316 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1317 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1318 if (x1 < xMin) {
1319 xMin = x1;
1320 } else if (x1 > xMax) {
1321 xMax = x1;
1322 }
1323 if (y1 < yMin) {
1324 yMin = y1;
1325 } else if (y1 > yMax) {
1326 yMax = y1;
1327 }
1328
1329 // draw the pattern
1330 //~ this should treat negative steps differently -- start at right/top
1331 //~ edge instead of left/bottom (?)
1332 xstep = fabs(tPat->getXStep());
1333 ystep = fabs(tPat->getYStep());
1334 xi0 = (int)floor(xMin / xstep);
1335 xi1 = (int)ceil(xMax / xstep);
1336 yi0 = (int)floor(yMin / ystep);
1337 yi1 = (int)ceil(yMax / ystep);
1338 for (i = 0; i < 4; ++i) {
1339 m1[i] = m[i];
1340 }
1341 for (yi = yi0; yi < yi1; ++yi) {
1342 for (xi = xi0; xi < xi1; ++xi) {
1343 x = xi * xstep;
1344 y = yi * ystep;
1345 m1[4] = x * m[0] + y * m[2] + m[4];
1346 m1[5] = x * m[1] + y * m[3] + m[5];
1347 doForm1(tPat->getContentStream(), tPat->getResDict(),
1348 m1, tPat->getBBox());
1349 }
1350 }
1351
1352 // restore graphics state
1353 state = state->restore();
1354 out->restoreState(state);
1355}
1356
1357void Gfx::opShFill(Object args[], int /*numArgs*/) {
1358 GfxShading *shading;
1359 double xMin, yMin, xMax, yMax;
1360
1361 if (!(shading = res->lookupShading(args[0].getName()))) {
1362 return;
1363 }
1364
1365 // save current graphics state
1366 out->saveState(state);
1367 state = state->save();
1368
1369 // clip to bbox
1370 if (shading->getHasBBox()) {
1371 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1372 state->moveTo(xMin, yMin);
1373 state->lineTo(xMax, yMin);
1374 state->lineTo(xMax, yMax);
1375 state->lineTo(xMin, yMax);
1376 state->closePath();
1377 state->clip();
1378 out->clip(state);
1379 state->clearPath();
1380 }
1381
1382 // set the color space
1383 state->setFillColorSpace(shading->getColorSpace()->copy());
1384
1385 // do shading type-specific operations
1386 switch (shading->getType()) {
1387 case 2:
1388 doAxialShFill((GfxAxialShading *)shading);
1389 break;
1390 case 3:
1391 doRadialShFill((GfxRadialShading *)shading);
1392 break;
1393 }
1394
1395 // restore graphics state
1396 state = state->restore();
1397 out->restoreState(state);
1398
1399 delete shading;
1400}
1401
1402void Gfx::doAxialShFill(GfxAxialShading *shading) {
1403 double xMin, yMin, xMax, yMax;
1404 double x0, y0, x1, y1;
1405 double dx, dy, mul;
1406 double tMin, tMax, t, tx, ty;
1407 double s[4], sMin, sMax, tmp;
1408 double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1409 double t0, t1, tt;
1410 double ta[axialMaxSplits + 1];
1411 int next[axialMaxSplits + 1];
1412 GfxColor color0, color1;
1413 int nComps;
1414 int i, j, k, kk;
1415
1416 // get the clip region bbox
1417 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1418
1419 // compute min and max t values, based on the four corners of the
1420 // clip region bbox
1421 shading->getCoords(&x0, &y0, &x1, &y1);
1422 dx = x1 - x0;
1423 dy = y1 - y0;
1424 mul = 1 / (dx * dx + dy * dy);
1425 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1426 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1427 if (t < tMin) {
1428 tMin = t;
1429 } else if (t > tMax) {
1430 tMax = t;
1431 }
1432 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1433 if (t < tMin) {
1434 tMin = t;
1435 } else if (t > tMax) {
1436 tMax = t;
1437 }
1438 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1439 if (t < tMin) {
1440 tMin = t;
1441 } else if (t > tMax) {
1442 tMax = t;
1443 }
1444 if (tMin < 0 && !shading->getExtend0()) {
1445 tMin = 0;
1446 }
1447 if (tMax > 1 && !shading->getExtend1()) {
1448 tMax = 1;
1449 }
1450
1451 // get the function domain
1452 t0 = shading->getDomain0();
1453 t1 = shading->getDomain1();
1454
1455 // Traverse the t axis and do the shading.
1456 //
1457 // For each point (tx, ty) on the t axis, consider a line through
1458 // that point perpendicular to the t axis:
1459 //
1460 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1461 // y(s) = ty + s * dx --> s = (y - ty) / dx
1462 //
1463 // Then look at the intersection of this line with the bounding box
1464 // (xMin, yMin, xMax, yMax). In the general case, there are four
1465 // intersection points:
1466 //
1467 // s0 = (xMin - tx) / -dy
1468 // s1 = (xMax - tx) / -dy
1469 // s2 = (yMin - ty) / dx
1470 // s3 = (yMax - ty) / dx
1471 //
1472 // and we want the middle two s values.
1473 //
1474 // In the case where dx = 0, take s0 and s1; in the case where dy =
1475 // 0, take s2 and s3.
1476 //
1477 // Each filled polygon is bounded by two of these line segments
1478 // perpdendicular to the t axis.
1479 //
1480 // The t axis is bisected into smaller regions until the color
1481 // difference across a region is small enough, and then the region
1482 // is painted with a single color.
1483
1484 // set up
1485 nComps = shading->getColorSpace()->getNComps();
1486 ta[0] = tMin;
1487 ta[axialMaxSplits] = tMax;
1488 next[0] = axialMaxSplits;
1489
1490 // compute the color at t = tMin
1491 if (tMin < 0) {
1492 tt = t0;
1493 } else if (tMin > 1) {
1494 tt = t1;
1495 } else {
1496 tt = t0 + (t1 - t0) * tMin;
1497 }
1498 shading->getColor(tt, &color0);
1499
1500 // compute the coordinates of the point on the t axis at t = tMin;
1501 // then compute the intersection of the perpendicular line with the
1502 // bounding box
1503 tx = x0 + tMin * dx;
1504 ty = y0 + tMin * dy;
1505 if (dx == 0 && dy == 0) {
1506 sMin = sMax = 0;
1507 } if (dx == 0) {
1508 sMin = (xMin - tx) / -dy;
1509 sMax = (xMax - tx) / -dy;
1510 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1511 } else if (dy == 0) {
1512 sMin = (yMin - ty) / dx;
1513 sMax = (yMax - ty) / dx;
1514 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1515 } else {
1516 s[0] = (yMin - ty) / dx;
1517 s[1] = (yMax - ty) / dx;
1518 s[2] = (xMin - tx) / -dy;
1519 s[3] = (xMax - tx) / -dy;
1520 for (j = 0; j < 3; ++j) {
1521 kk = j;
1522 for (k = j + 1; k < 4; ++k) {
1523 if (s[k] < s[kk]) {
1524 kk = k;
1525 }
1526 }
1527 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1528 }
1529 sMin = s[1];
1530 sMax = s[2];
1531 }
1532 ux0 = tx - sMin * dy;
1533 uy0 = ty + sMin * dx;
1534 vx0 = tx - sMax * dy;
1535 vy0 = ty + sMax * dx;
1536
1537 i = 0;
1538 while (i < axialMaxSplits) {
1539
1540 // bisect until color difference is small enough or we hit the
1541 // bisection limit
1542 j = next[i];
1543 while (j > i + 1) {
1544 if (ta[j] < 0) {
1545 tt = t0;
1546 } else if (ta[j] > 1) {
1547 tt = t1;
1548 } else {
1549 tt = t0 + (t1 - t0) * ta[j];
1550 }
1551 shading->getColor(tt, &color1);
1552 for (k = 0; k < nComps; ++k) {
1553 if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1554 break;
1555 }
1556 }
1557 if (k == nComps) {
1558 break;
1559 }
1560 k = (i + j) / 2;
1561 ta[k] = 0.5 * (ta[i] + ta[j]);
1562 next[i] = k;
1563 next[k] = j;
1564 j = k;
1565 }
1566
1567 // use the average of the colors of the two sides of the region
1568 for (k = 0; k < nComps; ++k) {
1569 color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
1570 }
1571
1572 // compute the coordinates of the point on the t axis; then
1573 // compute the intersection of the perpendicular line with the
1574 // bounding box
1575 tx = x0 + ta[j] * dx;
1576 ty = y0 + ta[j] * dy;
1577 if (dx == 0 && dy == 0) {
1578 sMin = sMax = 0;
1579 } if (dx == 0) {
1580 sMin = (xMin - tx) / -dy;
1581 sMax = (xMax - tx) / -dy;
1582 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1583 } else if (dy == 0) {
1584 sMin = (yMin - ty) / dx;
1585 sMax = (yMax - ty) / dx;
1586 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1587 } else {
1588 s[0] = (yMin - ty) / dx;
1589 s[1] = (yMax - ty) / dx;
1590 s[2] = (xMin - tx) / -dy;
1591 s[3] = (xMax - tx) / -dy;
1592 for (j = 0; j < 3; ++j) {
1593 kk = j;
1594 for (k = j + 1; k < 4; ++k) {
1595 if (s[k] < s[kk]) {
1596 kk = k;
1597 }
1598 }
1599 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1600 }
1601 sMin = s[1];
1602 sMax = s[2];
1603 }
1604 ux1 = tx - sMin * dy;
1605 uy1 = ty + sMin * dx;
1606 vx1 = tx - sMax * dy;
1607 vy1 = ty + sMax * dx;
1608
1609 // set the color
1610 state->setFillColor(&color0);
1611 out->updateFillColor(state);
1612
1613 // fill the region
1614 state->moveTo(ux0, uy0);
1615 state->lineTo(vx0, vy0);
1616 state->lineTo(vx1, vy1);
1617 state->lineTo(ux1, uy1);
1618 state->closePath();
1619 out->fill(state);
1620 state->clearPath();
1621
1622 // set up for next region
1623 ux0 = ux1;
1624 uy0 = uy1;
1625 vx0 = vx1;
1626 vy0 = vy1;
1627 color0 = color1;
1628 i = next[i];
1629 }
1630}
1631
1632void Gfx::doRadialShFill(GfxRadialShading *shading) {
1633 double sMin, sMax, xMin, yMin, xMax, yMax;
1634 double x0, y0, r0, x1, y1, r1, t0, t1;
1635 int nComps;
1636 GfxColor colorA, colorB;
1637 double xa, ya, xb, yb, ra, rb;
1638 double ta, tb, sa, sb;
1639 int ia, ib, k, n;
1640 const double *ctm;
1641 double angle, t;
1642
1643 // get the shading info
1644 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1645 t0 = shading->getDomain0();
1646 t1 = shading->getDomain1();
1647 nComps = shading->getColorSpace()->getNComps();
1648
1649 // compute the (possibly extended) s range
1650 sMin = 0;
1651 sMax = 1;
1652 if (shading->getExtend0()) {
1653 if (r0 < r1) {
1654 // extend the smaller end
1655 sMin = -r0 / (r1 - r0);
1656 } else {
1657 // extend the larger end
1658 //~ this computes the diagonal of the bounding box -- we should
1659 //~ really compute the intersection of the moving/expanding
1660 //~ circles with each of the four corners and look for the max
1661 //~ radius
1662 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1663 sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
1664 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1665 if (sMin > 0) {
1666 sMin = 0;
1667 } else if (sMin < -20) {
1668 // sanity check
1669 sMin = -20;
1670 }
1671 }
1672 }
1673 if (shading->getExtend1()) {
1674 if (r1 < r0) {
1675 // extend the smaller end
1676 sMax = -r0 / (r1 - r0);
1677 } else if (r1 > r0) {
1678 // extend the larger end
1679 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1680 sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
1681 (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1682 if (sMax < 1) {
1683 sMin = 1;
1684 } else if (sMax > 20) {
1685 // sanity check
1686 sMax = 20;
1687 }
1688 }
1689 }
1690
1691 // compute the number of steps into which circles must be divided to
1692 // achieve a curve flatness of 0.1 pixel in device space for the
1693 // largest circle (note that "device space" is 72 dpi when generating
1694 // PostScript, hence the relatively small 0.1 pixel accuracy)
1695 ctm = state->getCTM();
1696 t = fabs(ctm[0]);
1697 if (fabs(ctm[1]) > t) {
1698 t = fabs(ctm[1]);
1699 }
1700 if (fabs(ctm[2]) > t) {
1701 t = fabs(ctm[2]);
1702 }
1703 if (fabs(ctm[3]) > t) {
1704 t = fabs(ctm[3]);
1705 }
1706 if (r0 > r1) {
1707 t *= r0;
1708 } else {
1709 t *= r1;
1710 }
1711 if (t < 1) {
1712 n = 3;
1713 } else {
1714 n = (int)(M_PI / acos(1 - 0.1 / t));
1715 if (n < 3) {
1716 n = 3;
1717 } else if (n > 200) {
1718 n = 200;
1719 }
1720 }
1721
1722 // Traverse the t axis and do the shading.
1723 //
1724 // This generates and fills a series of rings. Each ring is defined
1725 // by two circles:
1726 // sa, ta, xa, ya, ra, colorA
1727 // sb, tb, xb, yb, rb, colorB
1728 //
1729 // The s/t axis is divided into radialMaxSplits parts; these parts
1730 // are combined as much as possible while respecting the
1731 // radialColorDelta parameter.
1732
1733 // setup for the start circle
1734 ia = 0;
1735 sa = sMin;
1736 ta = t0 + sa * (t1 - t0);
1737 xa = x0 + sa * (x1 - x0);
1738 ya = y0 + sa * (y1 - y0);
1739 ra = r0 + sa * (r1 - r0);
1740 if (ta < t0) {
1741 shading->getColor(t0, &colorA);
1742 } else if (ta > t1) {
1743 shading->getColor(t1, &colorA);
1744 } else {
1745 shading->getColor(ta, &colorA);
1746 }
1747
1748 while (ia < radialMaxSplits) {
1749
1750 // go as far along the t axis (toward t1) as we can, such that the
1751 // color difference is within the tolerance (radialColorDelta) --
1752 // this uses bisection (between the current value, t, and t1),
1753 // limited to radialMaxSplits points along the t axis
1754 ib = radialMaxSplits;
1755 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
1756 tb = t0 + sb * (t1 - t0);
1757 if (tb < t0) {
1758 shading->getColor(t0, &colorB);
1759 } else if (tb > t1) {
1760 shading->getColor(t1, &colorB);
1761 } else {
1762 shading->getColor(tb, &colorB);
1763 }
1764 while (ib - ia > 1) {
1765 for (k = 0; k < nComps; ++k) {
1766 if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
1767 break;
1768 }
1769 }
1770 if (k == nComps) {
1771 break;
1772 }
1773 ib = (ia + ib) / 2;
1774 sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
1775 tb = t0 + sb * (t1 - t0);
1776 if (tb < t0) {
1777 shading->getColor(t0, &colorB);
1778 } else if (tb > t1) {
1779 shading->getColor(t1, &colorB);
1780 } else {
1781 shading->getColor(tb, &colorB);
1782 }
1783 }
1784
1785 // compute center and radius of the circle
1786 xb = x0 + sb * (x1 - x0);
1787 yb = y0 + sb * (y1 - y0);
1788 rb = r0 + sb * (r1 - r0);
1789
1790 // use the average of the colors at the two circles
1791 for (k = 0; k < nComps; ++k) {
1792 colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
1793 }
1794 state->setFillColor(&colorA);
1795 out->updateFillColor(state);
1796
1797 // construct path for first circle
1798 state->moveTo(xa + ra, ya);
1799 for (k = 1; k < n; ++k) {
1800 angle = ((double)k / (double)n) * 2 * M_PI;
1801 state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
1802 }
1803 state->closePath();
1804
1805 // construct and append path for second circle
1806 state->moveTo(xb + rb, yb);
1807 for (k = 1; k < n; ++k) {
1808 angle = ((double)k / (double)n) * 2 * M_PI;
1809 state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
1810 }
1811 state->closePath();
1812
1813 // fill the ring
1814 out->eoFill(state);
1815 state->clearPath();
1816
1817 // step to the next value of t
1818 ia = ib;
1819 sa = sb;
1820 ta = tb;
1821 xa = xb;
1822 ya = yb;
1823 ra = rb;
1824 colorA = colorB;
1825 }
1826}
1827
1828void Gfx::doEndPath() {
1829 if (state->isPath() && clip != clipNone) {
1830 state->clip();
1831 if (clip == clipNormal) {
1832 out->clip(state);
1833 } else {
1834 out->eoClip(state);
1835 }
1836 }
1837 clip = clipNone;
1838 state->clearPath();
1839}
1840
1841//------------------------------------------------------------------------
1842// path clipping operators
1843//------------------------------------------------------------------------
1844
1845void Gfx::opClip(Object /*args*/[], int /*numArgs*/) {
1846 clip = clipNormal;
1847}
1848
1849void Gfx::opEOClip(Object /*args*/[], int /*numArgs*/) {
1850 clip = clipEO;
1851}
1852
1853//------------------------------------------------------------------------
1854// text object operators
1855//------------------------------------------------------------------------
1856
1857void Gfx::opBeginText(Object /*args*/[], int /*numArgs*/) {
1858 state->setTextMat(1, 0, 0, 1, 0, 0);
1859 state->textMoveTo(0, 0);
1860 out->updateTextMat(state);
1861 out->updateTextPos(state);
1862 fontChanged = gTrue;
1863}
1864
1865void Gfx::opEndText(Object /*args*/[], int /*numArgs*/) {
1866}
1867
1868//------------------------------------------------------------------------
1869// text state operators
1870//------------------------------------------------------------------------
1871
1872void Gfx::opSetCharSpacing(Object args[], int /*numArgs*/) {
1873 state->setCharSpace(args[0].getNum());
1874 out->updateCharSpace(state);
1875}
1876
1877void Gfx::opSetFont(Object args[], int /*numArgs*/) {
1878 GfxFont *font;
1879
1880 if (!(font = res->lookupFont(args[0].getName()))) {
1881 return;
1882 }
1883 if (printCommands) {
1884 printf(" font: tag=%s name='%s' %g\n",
1885 font->getTag()->getCString(),
1886 font->getName() ? font->getName()->getCString() : "???",
1887 args[1].getNum());
1888 fflush(stdout);
1889 }
1890 state->setFont(font, args[1].getNum());
1891 fontChanged = gTrue;
1892}
1893
1894void Gfx::opSetTextLeading(Object args[], int /*numArgs*/) {
1895 state->setLeading(args[0].getNum());
1896}
1897
1898void Gfx::opSetTextRender(Object args[], int /*numArgs*/) {
1899 state->setRender(args[0].getInt());
1900 out->updateRender(state);
1901}
1902
1903void Gfx::opSetTextRise(Object args[], int /*numArgs*/) {
1904 state->setRise(args[0].getNum());
1905 out->updateRise(state);
1906}
1907
1908void Gfx::opSetWordSpacing(Object args[], int /*numArgs*/) {
1909 state->setWordSpace(args[0].getNum());
1910 out->updateWordSpace(state);
1911}
1912
1913void Gfx::opSetHorizScaling(Object args[], int /*numArgs*/) {
1914 state->setHorizScaling(args[0].getNum());
1915 out->updateHorizScaling(state);
1916 fontChanged = gTrue;
1917}
1918
1919//------------------------------------------------------------------------
1920// text positioning operators
1921//------------------------------------------------------------------------
1922
1923void Gfx::opTextMove(Object args[], int /*numArgs*/) {
1924 double tx, ty;
1925
1926 tx = state->getLineX() + args[0].getNum();
1927 ty = state->getLineY() + args[1].getNum();
1928 state->textMoveTo(tx, ty);
1929 out->updateTextPos(state);
1930}
1931
1932void Gfx::opTextMoveSet(Object args[], int /*numArgs*/) {
1933 double tx, ty;
1934
1935 tx = state->getLineX() + args[0].getNum();
1936 ty = args[1].getNum();
1937 state->setLeading(-ty);
1938 ty += state->getLineY();
1939 state->textMoveTo(tx, ty);
1940 out->updateTextPos(state);
1941}
1942
1943void Gfx::opSetTextMatrix(Object args[], int /*numArgs*/) {
1944 state->setTextMat(args[0].getNum(), args[1].getNum(),
1945 args[2].getNum(), args[3].getNum(),
1946 args[4].getNum(), args[5].getNum());
1947 state->textMoveTo(0, 0);
1948 out->updateTextMat(state);
1949 out->updateTextPos(state);
1950 fontChanged = gTrue;
1951}
1952
1953void Gfx::opTextNextLine(Object /*args*/[], int /*numArgs*/) {
1954 double tx, ty;
1955
1956 tx = state->getLineX();
1957 ty = state->getLineY() - state->getLeading();
1958 state->textMoveTo(tx, ty);
1959 out->updateTextPos(state);
1960}
1961
1962//------------------------------------------------------------------------
1963// text string operators
1964//------------------------------------------------------------------------
1965
1966void Gfx::opShowText(Object args[], int /*numArgs*/) {
1967 if (!state->getFont()) {
1968 error(getPos(), "No font in show");
1969 return;
1970 }
1971 doShowText(args[0].getString());
1972}
1973
1974void Gfx::opMoveShowText(Object args[], int /*numArgs*/) {
1975 double tx, ty;
1976
1977 if (!state->getFont()) {
1978 error(getPos(), "No font in move/show");
1979 return;
1980 }
1981 tx = state->getLineX();
1982 ty = state->getLineY() - state->getLeading();
1983 state->textMoveTo(tx, ty);
1984 out->updateTextPos(state);
1985 doShowText(args[0].getString());
1986}
1987
1988void Gfx::opMoveSetShowText(Object args[], int /*numArgs*/) {
1989 double tx, ty;
1990
1991 if (!state->getFont()) {
1992 error(getPos(), "No font in move/set/show");
1993 return;
1994 }
1995 state->setWordSpace(args[0].getNum());
1996 state->setCharSpace(args[1].getNum());
1997 tx = state->getLineX();
1998 ty = state->getLineY() - state->getLeading();
1999 state->textMoveTo(tx, ty);
2000 out->updateWordSpace(state);
2001 out->updateCharSpace(state);
2002 out->updateTextPos(state);
2003 doShowText(args[2].getString());
2004}
2005
2006void Gfx::opShowSpaceText(Object args[], int /*numArgs*/) {
2007 Array *a;
2008 Object obj;
2009 int wMode;
2010 int i;
2011
2012 if (!state->getFont()) {
2013 error(getPos(), "No font in show/space");
2014 return;
2015 }
2016 wMode = state->getFont()->getWMode();
2017 a = args[0].getArray();
2018 for (i = 0; i < a->getLength(); ++i) {
2019 a->get(i, &obj);
2020 if (obj.isNum()) {
2021 if (wMode) {
2022 state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
2023 } else {
2024 state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
2025 }
2026 out->updateTextShift(state, obj.getNum());
2027 } else if (obj.isString()) {
2028 doShowText(obj.getString());
2029 } else {
2030 error(getPos(), "Element of show/space array must be number or string");
2031 }
2032 obj.free();
2033 }
2034}
2035
2036void Gfx::doShowText(GString *s) {
2037 GfxFont *font;
2038 int wMode;
2039 double riseX, riseY;
2040 CharCode code;
2041 Unicode u[8];
2042 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy;
2043 double originX, originY, tOriginX, tOriginY;
2044 double oldCTM[6], newCTM[6];
2045 const double *mat;
2046 Object charProc;
2047 Dict *resDict;
2048 Parser *oldParser;
2049 char *p;
2050 int len, n, uLen, nChars, nSpaces, i;
2051
2052 if (fontChanged) {
2053 out->updateFont(state);
2054 fontChanged = gFalse;
2055 }
2056 font = state->getFont();
2057 wMode = font->getWMode();
2058
2059 if (out->useDrawChar()) {
2060 out->beginString(state, s);
2061 }
2062
2063 // handle a Type 3 char
2064 if (font->getType() == fontType3 && out->interpretType3Chars()) {
2065 mat = state->getCTM();
2066 for (i = 0; i < 6; ++i) {
2067 oldCTM[i] = mat[i];
2068 }
2069 mat = state->getTextMat();
2070 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2071 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2072 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2073 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2074 mat = font->getFontMatrix();
2075 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2076 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2077 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2078 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2079 newCTM[0] *= state->getFontSize();
2080 newCTM[3] *= state->getFontSize();
2081 newCTM[0] *= state->getHorizScaling();
2082 newCTM[2] *= state->getHorizScaling();
2083 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2084 curX = state->getCurX();
2085 curY = state->getCurY();
2086 oldParser = parser;
2087 p = s->getCString();
2088 len = s->getLength();
2089 while (len > 0) {
2090 n = font->getNextChar(p, len, &code,
2091 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2092 &dx, &dy, &originX, &originY);
2093 dx = dx * state->getFontSize() + state->getCharSpace();
2094 if (n == 1 && *p == ' ') {
2095 dx += state->getWordSpace();
2096 }
2097 dx *= state->getHorizScaling();
2098 dy *= state->getFontSize();
2099 state->textTransformDelta(dx, dy, &tdx, &tdy);
2100 state->transform(curX + riseX, curY + riseY, &x, &y);
2101 out->saveState(state);
2102 state = state->save();
2103 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2104 //~ out->updateCTM(???)
2105 if (!out->beginType3Char(state, code, u, uLen)) {
2106 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2107 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2108 pushResources(resDict);
2109 }
2110 if (charProc.isStream()) {
2111 display(&charProc, gFalse);
2112 } else {
2113 error(getPos(), "Missing or bad Type3 CharProc entry");
2114 }
2115 out->endType3Char(state);
2116 if (resDict) {
2117 popResources();
2118 }
2119 charProc.free();
2120 }
2121 state = state->restore();
2122 out->restoreState(state);
2123 // GfxState::restore() does *not* restore the current position,
2124 // so we track it here with (curX, curY)
2125 curX += tdx;
2126 curY += tdy;
2127 state->moveTo(curX, curY);
2128 p += n;
2129 len -= n;
2130 }
2131 parser = oldParser;
2132
2133 } else if (out->useDrawChar()) {
2134 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2135 p = s->getCString();
2136 len = s->getLength();
2137 while (len > 0) {
2138 n = font->getNextChar(p, len, &code,
2139 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2140 &dx, &dy, &originX, &originY);
2141 if (wMode) {
2142 dx *= state->getFontSize();
2143 dy = dy * state->getFontSize() + state->getCharSpace();
2144 if (n == 1 && *p == ' ') {
2145 dy += state->getWordSpace();
2146 }
2147 } else {
2148 dx = dx * state->getFontSize() + state->getCharSpace();
2149 if (n == 1 && *p == ' ') {
2150 dx += state->getWordSpace();
2151 }
2152 dx *= state->getHorizScaling();
2153 dy *= state->getFontSize();
2154 }
2155 state->textTransformDelta(dx, dy, &tdx, &tdy);
2156 originX *= state->getFontSize();
2157 originY *= state->getFontSize();
2158 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2159 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2160 tdx, tdy, tOriginX, tOriginY, code, u, uLen);
2161 state->shift(tdx, tdy);
2162 p += n;
2163 len -= n;
2164 }
2165
2166 } else {
2167 dx = dy = 0;
2168 p = s->getCString();
2169 len = s->getLength();
2170 nChars = nSpaces = 0;
2171 while (len > 0) {
2172 n = font->getNextChar(p, len, &code,
2173 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2174 &dx2, &dy2, &originX, &originY);
2175 dx += dx2;
2176 dy += dy2;
2177 if (n == 1 && *p == ' ') {
2178 ++nSpaces;
2179 }
2180 ++nChars;
2181 p += n;
2182 len -= n;
2183 }
2184 if (wMode) {
2185 dx *= state->getFontSize();
2186 dy = dy * state->getFontSize()
2187 + nChars * state->getCharSpace()
2188 + nSpaces * state->getWordSpace();
2189 } else {
2190 dx = dx * state->getFontSize()
2191 + nChars * state->getCharSpace()
2192 + nSpaces * state->getWordSpace();
2193 dx *= state->getHorizScaling();
2194 dy *= state->getFontSize();
2195 }
2196 state->textTransformDelta(dx, dy, &tdx, &tdy);
2197 out->drawString(state, s);
2198 state->shift(tdx, tdy);
2199 }
2200
2201 if (out->useDrawChar()) {
2202 out->endString(state);
2203 }
2204
2205 updateLevel += 10 * s->getLength();
2206}
2207
2208//------------------------------------------------------------------------
2209// XObject operators
2210//------------------------------------------------------------------------
2211
2212void Gfx::opXObject(Object args[], int /*numArgs*/) {
2213 Object obj1, obj2, obj3, refObj;
2214#ifdef OPI_SUPPORT
2215 Object opiDict;
2216#endif
2217
2218 if (!res->lookupXObject(args[0].getName(), &obj1)) {
2219 return;
2220 }
2221 if (!obj1.isStream()) {
2222 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2223 obj1.free();
2224 return;
2225 }
2226#ifdef OPI_SUPPORT
2227 obj1.streamGetDict()->lookup("OPI", &opiDict);
2228 if (opiDict.isDict()) {
2229 out->opiBegin(state, opiDict.getDict());
2230 }
2231#endif
2232 obj1.streamGetDict()->lookup("Subtype", &obj2);
2233 if (obj2.isName("Image")) {
2234 res->lookupXObjectNF(args[0].getName(), &refObj);
2235 doImage(&refObj, obj1.getStream(), gFalse);
2236 refObj.free();
2237 } else if (obj2.isName("Form")) {
2238 doForm(&obj1);
2239 } else if (obj2.isName("PS")) {
2240 obj1.streamGetDict()->lookup("Level1", &obj3);
2241 out->psXObject(obj1.getStream(),
2242 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2243 } else if (obj2.isName()) {
2244 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2245 } else {
2246 error(getPos(), "XObject subtype is missing or wrong type");
2247 }
2248 obj2.free();
2249#ifdef OPI_SUPPORT
2250 if (opiDict.isDict()) {
2251 out->opiEnd(state, opiDict.getDict());
2252 }
2253 opiDict.free();
2254#endif
2255 obj1.free();
2256}
2257
2258void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2259 Dict *dict;
2260 int width, height;
2261 int bits;
2262 GBool mask;
2263 GBool invert;
2264 GfxColorSpace *colorSpace;
2265 GfxImageColorMap *colorMap;
2266 Object maskObj;
2267 GBool haveMask;
2268 int maskColors[2*gfxColorMaxComps];
2269 Object obj1, obj2;
2270 int i;
2271
2272 // get stream dict
2273 dict = str->getDict();
2274
2275 // get size
2276 dict->lookup("Width", &obj1);
2277 if (obj1.isNull()) {
2278 obj1.free();
2279 dict->lookup("W", &obj1);
2280 }
2281 if (!obj1.isInt())
2282 goto err2;
2283 width = obj1.getInt();
2284 obj1.free();
2285 dict->lookup("Height", &obj1);
2286 if (obj1.isNull()) {
2287 obj1.free();
2288 dict->lookup("H", &obj1);
2289 }
2290 if (!obj1.isInt())
2291 goto err2;
2292 height = obj1.getInt();
2293 obj1.free();
2294
2295 // image or mask?
2296 dict->lookup("ImageMask", &obj1);
2297 if (obj1.isNull()) {
2298 obj1.free();
2299 dict->lookup("IM", &obj1);
2300 }
2301 mask = gFalse;
2302 if (obj1.isBool())
2303 mask = obj1.getBool();
2304 else if (!obj1.isNull())
2305 goto err2;
2306 obj1.free();
2307
2308 // bit depth
2309 dict->lookup("BitsPerComponent", &obj1);
2310 if (obj1.isNull()) {
2311 obj1.free();
2312 dict->lookup("BPC", &obj1);
2313 }
2314 if (!obj1.isInt())
2315 goto err2;
2316 bits = obj1.getInt();
2317 obj1.free();
2318
2319 // display a mask
2320 if (mask) {
2321
2322 // check for inverted mask
2323 if (bits != 1)
2324 goto err1;
2325 invert = gFalse;
2326 dict->lookup("Decode", &obj1);
2327 if (obj1.isNull()) {
2328 obj1.free();
2329 dict->lookup("D", &obj1);
2330 }
2331 if (obj1.isArray()) {
2332 obj1.arrayGet(0, &obj2);
2333 if (obj2.isInt() && obj2.getInt() == 1)
2334 invert = gTrue;
2335 obj2.free();
2336 } else if (!obj1.isNull()) {
2337 goto err2;
2338 }
2339 obj1.free();
2340
2341 // draw it
2342 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2343
2344 } else {
2345
2346 // get color space and color map
2347 dict->lookup("ColorSpace", &obj1);
2348 if (obj1.isNull()) {
2349 obj1.free();
2350 dict->lookup("CS", &obj1);
2351 }
2352 if (obj1.isName()) {
2353 res->lookupColorSpace(obj1.getName(), &obj2);
2354 if (!obj2.isNull()) {
2355 obj1.free();
2356 obj1 = obj2;
2357 } else {
2358 obj2.free();
2359 }
2360 }
2361 colorSpace = GfxColorSpace::parse(&obj1);
2362 obj1.free();
2363 if (!colorSpace) {
2364 goto err1;
2365 }
2366 dict->lookup("Decode", &obj1);
2367 if (obj1.isNull()) {
2368 obj1.free();
2369 dict->lookup("D", &obj1);
2370 }
2371 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2372 obj1.free();
2373 if (!colorMap->isOk()) {
2374 delete colorMap;
2375 goto err1;
2376 }
2377
2378 // get the mask
2379 haveMask = gFalse;
2380 dict->lookup("Mask", &maskObj);
2381 if (maskObj.isArray()) {
2382 for (i = 0;
2383 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2384 ++i) {
2385 maskObj.arrayGet(i, &obj1);
2386 maskColors[i] = obj1.getInt();
2387 obj1.free();
2388 }
2389 haveMask = gTrue;
2390 }
2391
2392 // draw it
2393 out->drawImage(state, ref, str, width, height, colorMap,
2394 haveMask ? maskColors : (int *)NULL, inlineImg);
2395 delete colorMap;
2396
2397 maskObj.free();
2398 }
2399
2400 if ((i = width * height) > 1000) {
2401 i = 1000;
2402 }
2403 updateLevel += i;
2404
2405 return;
2406
2407 err2:
2408 obj1.free();
2409 err1:
2410 error(getPos(), "Bad image parameters");
2411}
2412
2413void Gfx::doForm(Object *str) {
2414 Dict *dict;
2415 Object matrixObj, bboxObj;
2416 double m[6], bbox[6];
2417 Object resObj;
2418 Dict *resDict;
2419 Object obj1;
2420 int i;
2421
2422 // get stream dict
2423 dict = str->streamGetDict();
2424
2425 // check form type
2426 dict->lookup("FormType", &obj1);
2427 if (!(obj1.isInt() && obj1.getInt() == 1)) {
2428 error(getPos(), "Unknown form type");
2429 }
2430 obj1.free();
2431
2432 // get bounding box
2433 dict->lookup("BBox", &bboxObj);
2434 if (!bboxObj.isArray()) {
2435 matrixObj.free();
2436 bboxObj.free();
2437 error(getPos(), "Bad form bounding box");
2438 return;
2439 }
2440 for (i = 0; i < 4; ++i) {
2441 bboxObj.arrayGet(i, &obj1);
2442 bbox[i] = obj1.getNum();
2443 obj1.free();
2444 }
2445 bboxObj.free();
2446
2447 // get matrix
2448 dict->lookup("Matrix", &matrixObj);
2449 if (matrixObj.isArray()) {
2450 for (i = 0; i < 6; ++i) {
2451 matrixObj.arrayGet(i, &obj1);
2452 m[i] = obj1.getNum();
2453 obj1.free();
2454 }
2455 } else {
2456 m[0] = 1; m[1] = 0;
2457 m[2] = 0; m[3] = 1;
2458 m[4] = 0; m[5] = 0;
2459 }
2460 matrixObj.free();
2461
2462 // get resources
2463 dict->lookup("Resources", &resObj);
2464 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2465
2466 // draw it
2467 doForm1(str, resDict, m, bbox);
2468
2469 resObj.free();
2470}
2471
2472void Gfx::doAnnot(Object *str, double xMin, double yMin,
2473 double xMax, double yMax) {
2474 Dict *dict, *resDict;
2475 Object matrixObj, bboxObj, resObj;
2476 Object obj1;
2477 double m[6], bbox[6], ictm[6];
2478 const double *ctm;
2479 double formX0, formY0, formX1, formY1;
2480 double annotX0, annotY0, annotX1, annotY1;
2481 double det, x, y, sx, sy;
2482 int i;
2483
2484 // get stream dict
2485 dict = str->streamGetDict();
2486
2487 // get the form bounding box
2488 dict->lookup("BBox", &bboxObj);
2489 if (!bboxObj.isArray()) {
2490 bboxObj.free();
2491 error(getPos(), "Bad form bounding box");
2492 return;
2493 }
2494 for (i = 0; i < 4; ++i) {
2495 bboxObj.arrayGet(i, &obj1);
2496 bbox[i] = obj1.getNum();
2497 obj1.free();
2498 }
2499 bboxObj.free();
2500
2501 // get the form matrix
2502 dict->lookup("Matrix", &matrixObj);
2503 if (matrixObj.isArray()) {
2504 for (i = 0; i < 6; ++i) {
2505 matrixObj.arrayGet(i, &obj1);
2506 m[i] = obj1.getNum();
2507 obj1.free();
2508 }
2509 } else {
2510 m[0] = 1; m[1] = 0;
2511 m[2] = 0; m[3] = 1;
2512 m[4] = 0; m[5] = 0;
2513 }
2514 matrixObj.free();
2515
2516 // transform the form bbox from form space to user space
2517 formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
2518 formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
2519 formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
2520 formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
2521
2522 // transform the annotation bbox from default user space to user
2523 // space: (bbox * baseMatrix) * iCTM
2524 ctm = state->getCTM();
2525 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2526 ictm[0] = ctm[3] * det;
2527 ictm[1] = -ctm[1] * det;
2528 ictm[2] = -ctm[2] * det;
2529 ictm[3] = ctm[0] * det;
2530 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2531 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2532 x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
2533 y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
2534 annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
2535 annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
2536 x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
2537 y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
2538 annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
2539 annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
2540
2541 // swap min/max coords
2542 if (formX0 > formX1) {
2543 x = formX0; formX0 = formX1; formX1 = x;
2544 }
2545 if (formY0 > formY1) {
2546 y = formY0; formY0 = formY1; formY1 = y;
2547 }
2548 if (annotX0 > annotX1) {
2549 x = annotX0; annotX0 = annotX1; annotX1 = x;
2550 }
2551 if (annotY0 > annotY1) {
2552 y = annotY0; annotY0 = annotY1; annotY1 = y;
2553 }
2554
2555 // scale the form to fit the annotation bbox
2556 if (formX1 == formX0) {
2557 // this shouldn't happen
2558 sx = 1;
2559 } else {
2560 sx = (annotX1 - annotX0) / (formX1 - formX0);
2561 }
2562 if (formY1 == formY0) {
2563 // this shouldn't happen
2564 sy = 1;
2565 } else {
2566 sy = (annotY1 - annotY0) / (formY1 - formY0);
2567 }
2568 m[0] *= sx;
2569 m[2] *= sx;
2570 m[4] = (m[4] - formX0) * sx + annotX0;
2571 m[1] *= sy;
2572 m[3] *= sy;
2573 m[5] = (m[5] - formY0) * sy + annotY0;
2574
2575 // get resources
2576 dict->lookup("Resources", &resObj);
2577 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2578
2579 // draw it
2580 doForm1(str, resDict, m, bbox);
2581
2582 resObj.free();
2583 bboxObj.free();
2584}
2585
2586void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
2587 Parser *oldParser;
2588 double oldBaseMatrix[6];
2589 int i;
2590
2591 // push new resources on stack
2592 pushResources(resDict);
2593
2594 // save current graphics state
2595 out->saveState(state);
2596 state = state->save();
2597
2598 // save current parser
2599 oldParser = parser;
2600
2601 // set form transformation matrix
2602 state->concatCTM(matrix[0], matrix[1], matrix[2],
2603 matrix[3], matrix[4], matrix[5]);
2604 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2605 matrix[3], matrix[4], matrix[5]);
2606
2607 // set new base matrix
2608 for (i = 0; i < 6; ++i) {
2609 oldBaseMatrix[i] = baseMatrix[i];
2610 baseMatrix[i] = state->getCTM()[i];
2611 }
2612
2613 // set form bounding box
2614 state->moveTo(bbox[0], bbox[1]);
2615 state->lineTo(bbox[2], bbox[1]);
2616 state->lineTo(bbox[2], bbox[3]);
2617 state->lineTo(bbox[0], bbox[3]);
2618 state->closePath();
2619 state->clip();
2620 out->clip(state);
2621 state->clearPath();
2622
2623 // draw the form
2624 display(str, gFalse);
2625
2626 // restore base matrix
2627 for (i = 0; i < 6; ++i) {
2628 baseMatrix[i] = oldBaseMatrix[i];
2629 }
2630
2631 // restore parser
2632 parser = oldParser;
2633
2634 // restore graphics state
2635 state = state->restore();
2636 out->restoreState(state);
2637
2638 // pop resource stack
2639 popResources();
2640
2641 return;
2642}
2643
2644void Gfx::pushResources(Dict *resDict) {
2645 res = new GfxResources(xref, resDict, res);
2646}
2647
2648void Gfx::popResources() {
2649 GfxResources *resPtr;
2650
2651 resPtr = res->getNext();
2652 delete res;
2653 res = resPtr;
2654}
2655
2656//------------------------------------------------------------------------
2657// in-line image operators
2658//------------------------------------------------------------------------
2659
2660void Gfx::opBeginImage(Object /*args*/[], int /*numArgs*/) {
2661 Stream *str;
2662 int c1, c2;
2663
2664 // build dict/stream
2665 str = buildImageStream();
2666
2667 // display the image
2668 if (str) {
2669 doImage(NULL, str, gTrue);
2670
2671 // skip 'EI' tag
2672 c1 = str->getBaseStream()->getChar();
2673 c2 = str->getBaseStream()->getChar();
2674 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2675 c1 = c2;
2676 c2 = str->getBaseStream()->getChar();
2677 }
2678 delete str;
2679 }
2680}
2681
2682Stream *Gfx::buildImageStream() {
2683 Object dict;
2684 Object obj;
2685 char *key;
2686 Stream *str;
2687
2688 // build dictionary
2689 dict.initDict(xref);
2690 parser->getObj(&obj);
2691 while (!obj.isCmd("ID") && !obj.isEOF()) {
2692 if (!obj.isName()) {
2693 error(getPos(), "Inline image dictionary key must be a name object");
2694 obj.free();
2695 } else {
2696 key = copyString(obj.getName());
2697 obj.free();
2698 parser->getObj(&obj);
2699 if (obj.isEOF() || obj.isError()) {
2700 gfree(key);
2701 break;
2702 }
2703 dict.dictAdd(key, &obj);
2704 }
2705 parser->getObj(&obj);
2706 }
2707 if (obj.isEOF()) {
2708 error(getPos(), "End of file in inline image");
2709 obj.free();
2710 dict.free();
2711 return NULL;
2712 }
2713 obj.free();
2714
2715 // make stream
2716 str = new EmbedStream(parser->getStream(), &dict);
2717 str = str->addFilters(&dict);
2718
2719 return str;
2720}
2721
2722void Gfx::opImageData(Object /*args*/[], int /*numArgs*/) {
2723 error(getPos(), "Internal: got 'ID' operator");
2724}
2725
2726void Gfx::opEndImage(Object /*args*/[], int /*numArgs*/) {
2727 error(getPos(), "Internal: got 'EI' operator");
2728}
2729
2730//------------------------------------------------------------------------
2731// type 3 font operators
2732//------------------------------------------------------------------------
2733
2734void Gfx::opSetCharWidth(Object args[], int /*numArgs*/) {
2735 out->type3D0(state, args[0].getNum(), args[1].getNum());
2736}
2737
2738void Gfx::opSetCacheDevice(Object args[], int /*numArgs*/) {
2739 out->type3D1(state, args[0].getNum(), args[1].getNum(),
2740 args[2].getNum(), args[3].getNum(),
2741 args[4].getNum(), args[5].getNum());
2742}
2743
2744//------------------------------------------------------------------------
2745// compatibility operators
2746//------------------------------------------------------------------------
2747
2748void Gfx::opBeginIgnoreUndef(Object /*args*/[], int /*numArgs*/) {
2749 ++ignoreUndef;
2750}
2751
2752void Gfx::opEndIgnoreUndef(Object /*args*/[], int /*numArgs*/) {
2753 if (ignoreUndef > 0)
2754 --ignoreUndef;
2755}
2756
2757//------------------------------------------------------------------------
2758// marked content operators
2759//------------------------------------------------------------------------
2760
2761void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
2762 if (printCommands) {
2763 printf(" marked content: %s ", args[0].getName());
2764 if (numArgs == 2)
2765 args[2].print(stdout);
2766 printf("\n");
2767 fflush(stdout);
2768 }
2769}
2770
2771void Gfx::opEndMarkedContent(Object /*args*/[], int /*numArgs*/) {
2772}
2773
2774void Gfx::opMarkPoint(Object args[], int numArgs) {
2775 if (printCommands) {
2776 printf(" mark point: %s ", args[0].getName());
2777 if (numArgs == 2)
2778 args[2].print(stdout);
2779 printf("\n");
2780 fflush(stdout);
2781 }
2782}