]> git.ipfire.org Git - thirdparty/cups.git/blame - pdftops/Gfx.cxx
Get rid of compiler warnings.
[thirdparty/cups.git] / pdftops / Gfx.cxx
CommitLineData
9c72faab 1//========================================================================
2//
3// Gfx.cc
4//
5// Copyright 1996 Derek B. Noonburg
6//
7//========================================================================
8
9#ifdef __GNUC__
10#pragma implementation
11#endif
12
13#include <stdio.h>
14#include <stddef.h>
15#include <string.h>
16#include "gmem.h"
17#include "Object.h"
18#include "Array.h"
19#include "Dict.h"
20#include "Stream.h"
21#include "Lexer.h"
22#include "Parser.h"
23#include "GfxFont.h"
24#include "GfxState.h"
25#include "OutputDev.h"
26#include "Params.h"
27#include "Error.h"
28#include "Gfx.h"
29
30//------------------------------------------------------------------------
31// Operator table
32//------------------------------------------------------------------------
33
34Operator Gfx::opTab[] = {
35 {"\"", 3, {tchkNum, tchkNum, tchkString},
36 &Gfx::opMoveSetShowText},
37 {"'", 1, {tchkString},
38 &Gfx::opMoveShowText},
39 {"B", 0, {tchkNone},
40 &Gfx::opFillStroke},
41 {"B*", 0, {tchkNone},
42 &Gfx::opEOFillStroke},
43 {"BDC", 2, {tchkName, tchkProps},
44 &Gfx::opBeginMarkedContent},
45 {"BI", 0, {tchkNone},
46 &Gfx::opBeginImage},
47 {"BMC", 1, {tchkName},
48 &Gfx::opBeginMarkedContent},
49 {"BT", 0, {tchkNone},
50 &Gfx::opBeginText},
51 {"BX", 0, {tchkNone},
52 &Gfx::opBeginIgnoreUndef},
53 {"CS", 1, {tchkName},
54 &Gfx::opSetStrokeColorSpace},
55 {"DP", 2, {tchkName, tchkProps},
56 &Gfx::opMarkPoint},
57 {"Do", 1, {tchkName},
58 &Gfx::opXObject},
59 {"EI", 0, {tchkNone},
60 &Gfx::opEndImage},
61 {"EMC", 0, {tchkNone},
62 &Gfx::opEndMarkedContent},
63 {"ET", 0, {tchkNone},
64 &Gfx::opEndText},
65 {"EX", 0, {tchkNone},
66 &Gfx::opEndIgnoreUndef},
67 {"F", 0, {tchkNone},
68 &Gfx::opFill},
69 {"G", 1, {tchkNum},
70 &Gfx::opSetStrokeGray},
71 {"ID", 0, {tchkNone},
72 &Gfx::opImageData},
73 {"J", 1, {tchkInt},
74 &Gfx::opSetLineCap},
75 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
76 &Gfx::opSetStrokeCMYKColor},
77 {"M", 1, {tchkNum},
78 &Gfx::opSetMiterLimit},
79 {"MP", 1, {tchkName},
80 &Gfx::opMarkPoint},
81 {"Q", 0, {tchkNone},
82 &Gfx::opRestore},
83 {"RG", 3, {tchkNum, tchkNum, tchkNum},
84 &Gfx::opSetStrokeRGBColor},
85 {"S", 0, {tchkNone},
86 &Gfx::opStroke},
87 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
88 &Gfx::opSetStrokeColor},
89 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
90 tchkSCN},
91 &Gfx::opSetStrokeColorN},
92 {"T*", 0, {tchkNone},
93 &Gfx::opTextNextLine},
94 {"TD", 2, {tchkNum, tchkNum},
95 &Gfx::opTextMoveSet},
96 {"TJ", 1, {tchkArray},
97 &Gfx::opShowSpaceText},
98 {"TL", 1, {tchkNum},
99 &Gfx::opSetTextLeading},
100 {"Tc", 1, {tchkNum},
101 &Gfx::opSetCharSpacing},
102 {"Td", 2, {tchkNum, tchkNum},
103 &Gfx::opTextMove},
104 {"Tf", 2, {tchkName, tchkNum},
105 &Gfx::opSetFont},
106 {"Tj", 1, {tchkString},
107 &Gfx::opShowText},
108 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
109 tchkNum, tchkNum},
110 &Gfx::opSetTextMatrix},
111 {"Tr", 1, {tchkInt},
112 &Gfx::opSetTextRender},
113 {"Ts", 1, {tchkNum},
114 &Gfx::opSetTextRise},
115 {"Tw", 1, {tchkNum},
116 &Gfx::opSetWordSpacing},
117 {"Tz", 1, {tchkNum},
118 &Gfx::opSetHorizScaling},
119 {"W", 0, {tchkNone},
120 &Gfx::opClip},
121 {"W*", 0, {tchkNone},
122 &Gfx::opEOClip},
123 {"b", 0, {tchkNone},
124 &Gfx::opCloseFillStroke},
125 {"b*", 0, {tchkNone},
126 &Gfx::opCloseEOFillStroke},
127 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
128 tchkNum, tchkNum},
129 &Gfx::opCurveTo},
130 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
131 tchkNum, tchkNum},
132 &Gfx::opConcat},
133 {"cs", 1, {tchkName},
134 &Gfx::opSetFillColorSpace},
135 {"d", 2, {tchkArray, tchkNum},
136 &Gfx::opSetDash},
137 {"d0", 2, {tchkNum, tchkNum},
138 &Gfx::opSetCharWidth},
139 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
140 tchkNum, tchkNum},
141 &Gfx::opSetCacheDevice},
142 {"f", 0, {tchkNone},
143 &Gfx::opFill},
144 {"f*", 0, {tchkNone},
145 &Gfx::opEOFill},
146 {"g", 1, {tchkNum},
147 &Gfx::opSetFillGray},
148 {"gs", 1, {tchkName},
149 &Gfx::opSetExtGState},
150 {"h", 0, {tchkNone},
151 &Gfx::opClosePath},
152 {"i", 1, {tchkNum},
153 &Gfx::opSetFlat},
154 {"j", 1, {tchkInt},
155 &Gfx::opSetLineJoin},
156 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
157 &Gfx::opSetFillCMYKColor},
158 {"l", 2, {tchkNum, tchkNum},
159 &Gfx::opLineTo},
160 {"m", 2, {tchkNum, tchkNum},
161 &Gfx::opMoveTo},
162 {"n", 0, {tchkNone},
163 &Gfx::opEndPath},
164 {"q", 0, {tchkNone},
165 &Gfx::opSave},
166 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
167 &Gfx::opRectangle},
168 {"rg", 3, {tchkNum, tchkNum, tchkNum},
169 &Gfx::opSetFillRGBColor},
170 {"ri", 1, {tchkName},
171 &Gfx::opSetRenderingIntent},
172 {"s", 0, {tchkNone},
173 &Gfx::opCloseStroke},
174 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
175 &Gfx::opSetFillColor},
176 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
177 tchkSCN},
178 &Gfx::opSetFillColorN},
179 {"sh", 1, {tchkName},
180 &Gfx::opShFill},
181 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
182 &Gfx::opCurveTo1},
183 {"w", 1, {tchkNum},
184 &Gfx::opSetLineWidth},
185 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
186 &Gfx::opCurveTo2},
187};
188
189#define numOps (sizeof(opTab) / sizeof(Operator))
190
52118ca3 191//------------------------------------------------------------------------
192
193GBool printCommands = gFalse;
194
9c72faab 195//------------------------------------------------------------------------
196// Gfx
197//------------------------------------------------------------------------
198
199Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
200 int dpi, double x1, double y1, double x2, double y2, GBool crop,
201 double cropX1, double cropY1, double cropX2, double cropY2,
202 int rotate) {
203 Object obj1;
204
205 // start the resource stack
206 res = new GfxResources(NULL);
207
208 // build font dictionary
209 res->fonts = NULL;
210 if (resDict) {
211 resDict->lookup("Font", &obj1);
212 if (obj1.isDict())
213 res->fonts = new GfxFontDict(obj1.getDict());
214 obj1.free();
215 }
216
217 // get XObject dictionary
218 if (resDict)
219 resDict->lookup("XObject", &res->xObjDict);
220 else
221 res->xObjDict.initNull();
222
223 // get colorspace dictionary
224 if (resDict)
225 resDict->lookup("ColorSpace", &res->colorSpaceDict);
226 else
227 res->colorSpaceDict.initNull();
228
229 // initialize
230 out = out1;
231 state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
232 fontChanged = gFalse;
233 clip = clipNone;
234 ignoreUndef = 0;
235 out->startPage(pageNum, state);
236 out->setDefaultCTM(state->getCTM());
237 out->updateAll(state);
238
239 // set crop box
240 if (crop) {
241 state->moveTo(cropX1, cropY1);
242 state->lineTo(cropX2, cropY1);
243 state->lineTo(cropX2, cropY2);
244 state->lineTo(cropX1, cropY2);
245 state->closePath();
246 out->clip(state);
247 state->clearPath();
248 }
249}
250
251Gfx::~Gfx() {
252 GfxResources *resPtr;
253
254 while (state->hasSaves()) {
255 state = state->restore();
256 out->restoreState(state);
257 }
258 out->endPage();
259 while (res) {
260 resPtr = res->next;
261 delete res;
262 res = resPtr;
263 }
264 if (state)
265 delete state;
266}
267
268GfxResources::~GfxResources() {
269 if (fonts)
270 delete fonts;
271 xObjDict.free();
272 colorSpaceDict.free();
273}
274
275void Gfx::display(Object *obj) {
276 Object obj2;
277 int i;
278
279 if (obj->isArray()) {
280 for (i = 0; i < obj->arrayGetLength(); ++i) {
281 obj->arrayGet(i, &obj2);
282 if (!obj2.isStream()) {
283 error(-1, "Weird page contents");
284 obj2.free();
285 return;
286 }
287 obj2.free();
288 }
289 } else if (!obj->isStream()) {
290 error(-1, "Weird page contents");
291 return;
292 }
293 parser = new Parser(new Lexer(obj));
294 go();
52118ca3 295 delete parser;
296 parser = NULL;
9c72faab 297}
298
299void Gfx::go() {
300 Object obj;
301 Object args[maxArgs];
302 int numCmds, numArgs;
303 int i;
304
305 // scan a sequence of objects
306 numCmds = 0;
307 numArgs = 0;
308 parser->getObj(&obj);
309 while (!obj.isEOF()) {
310
311 // got a command - execute it
312 if (obj.isCmd()) {
313 if (printCommands) {
314 obj.print(stdout);
315 for (i = 0; i < numArgs; ++i) {
316 printf(" ");
317 args[i].print(stdout);
318 }
319 printf("\n");
320 }
321 execOp(&obj, args, numArgs);
322 obj.free();
323 for (i = 0; i < numArgs; ++i)
324 args[i].free();
325 numArgs = 0;
326
327 // periodically update display
328 if (++numCmds == 200) {
329 out->dump();
330 numCmds = 0;
331 }
332
333 // got an argument - save it
334 } else if (numArgs < maxArgs) {
335 args[numArgs++] = obj;
336
337 // too many arguments - something is wrong
338 } else {
339 error(getPos(), "Too many args in content stream");
340 if (printCommands) {
341 printf("throwing away arg: ");
342 obj.print(stdout);
343 printf("\n");
344 }
345 obj.free();
346 }
347
348 // grab the next object
349 parser->getObj(&obj);
350 }
351 obj.free();
352
353 // args at end with no command
354 if (numArgs > 0) {
355 error(getPos(), "Leftover args in content stream");
356 if (printCommands) {
357 printf("%d leftovers:", numArgs);
358 for (i = 0; i < numArgs; ++i) {
359 printf(" ");
360 args[i].print(stdout);
361 }
362 printf("\n");
363 }
364 for (i = 0; i < numArgs; ++i)
365 args[i].free();
366 }
367
368 // update display
369 if (numCmds > 0)
370 out->dump();
371
372 // clean up
9c72faab 373 if (printCommands)
374 fflush(stdout);
375}
376
377void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
378 Operator *op;
aa530ae8 379 const char *name;
9c72faab 380 int i;
381
382 // find operator
383 name = cmd->getName();
384 if (!(op = findOp(name))) {
385 if (ignoreUndef == 0)
386 error(getPos(), "Unknown operator '%s'", name);
387 return;
388 }
389
390 // type check args
391 if (op->numArgs >= 0) {
392 if (numArgs != op->numArgs) {
393 error(getPos(), "Wrong number (%d) of args to '%s' operator",
394 numArgs, name);
395 return;
396 }
397 } else {
398 if (numArgs > -op->numArgs) {
399 error(getPos(), "Too many (%d) args to '%s' operator",
400 numArgs, name);
401 return;
402 }
403 }
404 for (i = 0; i < numArgs; ++i) {
405 if (!checkArg(&args[i], op->tchk[i])) {
406 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
407 i, name, args[i].getTypeName());
408 return;
409 }
410 }
411
412 // do it
413 (this->*op->func)(args, numArgs);
414}
415
aa530ae8 416Operator *Gfx::findOp(const char *name) {
9c72faab 417 int a, b, m, cmp;
418
419 a = -1;
420 b = numOps;
421 // invariant: opTab[a] < name < opTab[b]
422 while (b - a > 1) {
423 m = (a + b) / 2;
424 cmp = strcmp(opTab[m].name, name);
425 if (cmp < 0)
426 a = m;
427 else if (cmp > 0)
428 b = m;
429 else
430 a = b = m;
431 }
432 if (cmp != 0)
433 return NULL;
434 return &opTab[a];
435}
436
437GBool Gfx::checkArg(Object *arg, TchkType type) {
438 switch (type) {
439 case tchkBool: return arg->isBool();
440 case tchkInt: return arg->isInt();
441 case tchkNum: return arg->isNum();
442 case tchkString: return arg->isString();
443 case tchkName: return arg->isName();
444 case tchkArray: return arg->isArray();
445 case tchkProps: return arg->isDict() || arg->isName();
446 case tchkSCN: return arg->isNum() || arg->isName();
447 case tchkNone: return gFalse;
448 }
449 return gFalse;
450}
451
452int Gfx::getPos() {
52118ca3 453 return parser ? parser->getPos() : -1;
9c72faab 454}
455
aa530ae8 456GfxFont *Gfx::lookupFont(const char *name) {
9c72faab 457 GfxFont *font;
458 GfxResources *resPtr;
459
460 for (resPtr = res; resPtr; resPtr = resPtr->next) {
461 if (resPtr->fonts) {
462 if ((font = resPtr->fonts->lookup(name)))
463 return font;
464 }
465 }
466 error(getPos(), "unknown font tag '%s'", name);
467 return NULL;
468}
469
aa530ae8 470GBool Gfx::lookupXObject(const char *name, Object *obj) {
9c72faab 471 GfxResources *resPtr;
472
473 for (resPtr = res; resPtr; resPtr = resPtr->next) {
474 if (resPtr->xObjDict.isDict()) {
475 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
476 return gTrue;
477 obj->free();
478 }
479 }
480 error(getPos(), "XObject '%s' is unknown", name);
481 return gFalse;
482}
483
aa530ae8 484void Gfx::lookupColorSpace(const char *name, Object *obj) {
9c72faab 485 GfxResources *resPtr;
486
487 for (resPtr = res; resPtr; resPtr = resPtr->next) {
488 if (resPtr->colorSpaceDict.isDict()) {
489 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull())
490 return;
491 obj->free();
492 }
493 }
494 obj->initNull();
495}
496
497//------------------------------------------------------------------------
498// graphics state operators
499//------------------------------------------------------------------------
500
501void Gfx::opSave(Object args[], int numArgs) {
a5f8b9e7 502 (void)args;
503 (void)numArgs;
504
9c72faab 505 out->saveState(state);
506 state = state->save();
507}
508
509void Gfx::opRestore(Object args[], int numArgs) {
a5f8b9e7 510 (void)args;
511 (void)numArgs;
512
9c72faab 513 state = state->restore();
514 out->restoreState(state);
52118ca3 515
516 // Some PDF producers (Macromedia FreeHand) generate a save (q) and
517 // restore (Q) inside a path sequence. The PDF spec seems to imply
518 // that this is illegal. Calling clearPath() here implements the
519 // behavior apparently expected by this software.
520 state->clearPath();
9c72faab 521}
522
523void Gfx::opConcat(Object args[], int numArgs) {
a5f8b9e7 524 (void)numArgs;
525
9c72faab 526 state->concatCTM(args[0].getNum(), args[1].getNum(),
527 args[2].getNum(), args[3].getNum(),
528 args[4].getNum(), args[5].getNum());
529 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
530 args[2].getNum(), args[3].getNum(),
531 args[4].getNum(), args[5].getNum());
532 fontChanged = gTrue;
533}
534
535void Gfx::opSetDash(Object args[], int numArgs) {
536 Array *a;
537 int length;
538 Object obj;
539 double *dash;
540 int i;
541
a5f8b9e7 542 (void)numArgs;
543
9c72faab 544 a = args[0].getArray();
545 length = a->getLength();
546 if (length == 0) {
547 dash = NULL;
548 } else {
549 dash = (double *)gmalloc(length * sizeof(double));
550 for (i = 0; i < length; ++i) {
551 dash[i] = a->get(i, &obj)->getNum();
552 obj.free();
553 }
554 }
555 state->setLineDash(dash, length, args[1].getNum());
556 out->updateLineDash(state);
557}
558
559void Gfx::opSetFlat(Object args[], int numArgs) {
a5f8b9e7 560 (void)numArgs;
561
9c72faab 562 state->setFlatness((int)args[0].getNum());
563 out->updateFlatness(state);
564}
565
566void Gfx::opSetLineJoin(Object args[], int numArgs) {
a5f8b9e7 567 (void)numArgs;
568
9c72faab 569 state->setLineJoin(args[0].getInt());
570 out->updateLineJoin(state);
571}
572
573void Gfx::opSetLineCap(Object args[], int numArgs) {
a5f8b9e7 574 (void)numArgs;
575
9c72faab 576 state->setLineCap(args[0].getInt());
577 out->updateLineCap(state);
578}
579
580void Gfx::opSetMiterLimit(Object args[], int numArgs) {
a5f8b9e7 581 (void)numArgs;
582
9c72faab 583 state->setMiterLimit(args[0].getNum());
584 out->updateMiterLimit(state);
585}
586
587void Gfx::opSetLineWidth(Object args[], int numArgs) {
a5f8b9e7 588 (void)numArgs;
589
9c72faab 590 state->setLineWidth(args[0].getNum());
591 out->updateLineWidth(state);
592}
593
594void Gfx::opSetExtGState(Object args[], int numArgs) {
a5f8b9e7 595 (void)args;
596 (void)numArgs;
9c72faab 597}
598
599void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
a5f8b9e7 600 (void)args;
601 (void)numArgs;
9c72faab 602}
603
604//------------------------------------------------------------------------
605// color operators
606//------------------------------------------------------------------------
607
608void Gfx::opSetFillGray(Object args[], int numArgs) {
a5f8b9e7 609 (void)numArgs;
610
9c72faab 611 state->setFillColorSpace(new GfxColorSpace(colorGray));
612 state->setFillGray(args[0].getNum());
613 out->updateFillColor(state);
614}
615
616void Gfx::opSetStrokeGray(Object args[], int numArgs) {
a5f8b9e7 617 (void)numArgs;
618
9c72faab 619 state->setStrokeColorSpace(new GfxColorSpace(colorGray));
620 state->setStrokeGray(args[0].getNum());
621 out->updateStrokeColor(state);
622}
623
624void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
a5f8b9e7 625 (void)numArgs;
626
9c72faab 627 state->setFillColorSpace(new GfxColorSpace(colorCMYK));
628 state->setFillCMYK(args[0].getNum(), args[1].getNum(),
629 args[2].getNum(), args[3].getNum());
630 out->updateFillColor(state);
631}
632
633void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
a5f8b9e7 634 (void)numArgs;
635
9c72faab 636 state->setStrokeColorSpace(new GfxColorSpace(colorCMYK));
637 state->setStrokeCMYK(args[0].getNum(), args[1].getNum(),
638 args[2].getNum(), args[3].getNum());
639 out->updateStrokeColor(state);
640}
641
642void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
a5f8b9e7 643 (void)numArgs;
644
9c72faab 645 state->setFillColorSpace(new GfxColorSpace(colorRGB));
646 state->setFillRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
647 out->updateFillColor(state);
648}
649
650void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
a5f8b9e7 651 (void)numArgs;
652
9c72faab 653 state->setStrokeColorSpace(new GfxColorSpace(colorRGB));
654 state->setStrokeRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
655 out->updateStrokeColor(state);
656}
657
658void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
659 Object obj;
660 GfxColorSpace *colorSpace;
661 double x[4];
662
a5f8b9e7 663 (void)numArgs;
664
9c72faab 665 lookupColorSpace(args[0].getName(), &obj);
666 if (obj.isNull())
667 colorSpace = new GfxColorSpace(&args[0]);
668 else
669 colorSpace = new GfxColorSpace(&obj);
670 obj.free();
671 if (colorSpace->isOk()) {
672 state->setFillColorSpace(colorSpace);
673 } else {
674 delete colorSpace;
675 error(getPos(), "Bad colorspace");
676 }
677 x[0] = x[1] = x[2] = x[3] = 0;
678 state->setFillColor(x);
679 out->updateFillColor(state);
680}
681
682void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
683 Object obj;
684 GfxColorSpace *colorSpace;
685 double x[4];
686
a5f8b9e7 687 (void)numArgs;
688
9c72faab 689 lookupColorSpace(args[0].getName(), &obj);
690 if (obj.isNull())
691 colorSpace = new GfxColorSpace(&args[0]);
692 else
693 colorSpace = new GfxColorSpace(&obj);
694 obj.free();
695 if (colorSpace->isOk()) {
696 state->setStrokeColorSpace(colorSpace);
697 } else {
698 delete colorSpace;
699 error(getPos(), "Bad colorspace");
700 }
701 x[0] = x[1] = x[2] = x[3] = 0;
702 state->setStrokeColor(x);
703 out->updateStrokeColor(state);
704}
705
706void Gfx::opSetFillColor(Object args[], int numArgs) {
707 double x[4];
708 int i;
709
710 x[0] = x[1] = x[2] = x[3] = 0;
711 for (i = 0; i < numArgs; ++i)
712 x[i] = args[i].getNum();
713 state->setFillColor(x);
714 out->updateFillColor(state);
715}
716
717void Gfx::opSetStrokeColor(Object args[], int numArgs) {
718 double x[4];
719 int i;
720
721 x[0] = x[1] = x[2] = x[3] = 0;
722 for (i = 0; i < numArgs; ++i)
723 x[i] = args[i].getNum();
724 state->setStrokeColor(x);
725 out->updateStrokeColor(state);
726}
727
728void Gfx::opSetFillColorN(Object args[], int numArgs) {
729 double x[4];
730 int i;
731
732 x[0] = x[1] = x[2] = x[3] = 0;
733 for (i = 0; i < numArgs && i < 4; ++i) {
734 if (args[i].isNum())
735 x[i] = args[i].getNum();
736 else
737 break;
738 }
739 state->setFillColor(x);
740 out->updateFillColor(state);
741}
742
743void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
744 double x[4];
745 int i;
746
747 x[0] = x[1] = x[2] = x[3] = 0;
748 for (i = 0; i < numArgs && i < 4; ++i) {
749 if (args[i].isNum())
750 x[i] = args[i].getNum();
751 else
752 break;
753 }
754 state->setStrokeColor(x);
755 out->updateStrokeColor(state);
756}
757
758//------------------------------------------------------------------------
759// path segment operators
760//------------------------------------------------------------------------
761
762void Gfx::opMoveTo(Object args[], int numArgs) {
a5f8b9e7 763 (void)numArgs;
764
9c72faab 765 state->moveTo(args[0].getNum(), args[1].getNum());
766}
767
768void Gfx::opLineTo(Object args[], int numArgs) {
a5f8b9e7 769 (void)numArgs;
770
9c72faab 771 if (!state->isCurPt()) {
772 error(getPos(), "No current point in lineto");
773 return;
774 }
775 state->lineTo(args[0].getNum(), args[1].getNum());
776}
777
778void Gfx::opCurveTo(Object args[], int numArgs) {
779 double x1, y1, x2, y2, x3, y3;
780
a5f8b9e7 781 (void)numArgs;
782
9c72faab 783 if (!state->isCurPt()) {
784 error(getPos(), "No current point in curveto");
785 return;
786 }
787 x1 = args[0].getNum();
788 y1 = args[1].getNum();
789 x2 = args[2].getNum();
790 y2 = args[3].getNum();
791 x3 = args[4].getNum();
792 y3 = args[5].getNum();
793 state->curveTo(x1, y1, x2, y2, x3, y3);
794}
795
796void Gfx::opCurveTo1(Object args[], int numArgs) {
797 double x1, y1, x2, y2, x3, y3;
798
a5f8b9e7 799 (void)numArgs;
800
9c72faab 801 if (!state->isCurPt()) {
802 error(getPos(), "No current point in curveto1");
803 return;
804 }
805 x1 = state->getCurX();
806 y1 = state->getCurY();
807 x2 = args[0].getNum();
808 y2 = args[1].getNum();
809 x3 = args[2].getNum();
810 y3 = args[3].getNum();
811 state->curveTo(x1, y1, x2, y2, x3, y3);
812}
813
814void Gfx::opCurveTo2(Object args[], int numArgs) {
815 double x1, y1, x2, y2, x3, y3;
816
a5f8b9e7 817 (void)numArgs;
818
9c72faab 819 if (!state->isCurPt()) {
820 error(getPos(), "No current point in curveto2");
821 return;
822 }
823 x1 = args[0].getNum();
824 y1 = args[1].getNum();
825 x2 = args[2].getNum();
826 y2 = args[3].getNum();
827 x3 = x2;
828 y3 = y2;
829 state->curveTo(x1, y1, x2, y2, x3, y3);
830}
831
832void Gfx::opRectangle(Object args[], int numArgs) {
833 double x, y, w, h;
834
a5f8b9e7 835 (void)numArgs;
836
9c72faab 837 x = args[0].getNum();
838 y = args[1].getNum();
839 w = args[2].getNum();
840 h = args[3].getNum();
841 state->moveTo(x, y);
842 state->lineTo(x + w, y);
843 state->lineTo(x + w, y + h);
844 state->lineTo(x, y + h);
845 state->closePath();
846}
847
848void Gfx::opClosePath(Object args[], int numArgs) {
a5f8b9e7 849 (void)args;
850 (void)numArgs;
851
9c72faab 852 if (!state->isPath()) {
853 error(getPos(), "No current point in closepath");
854 return;
855 }
856 state->closePath();
857}
858
859//------------------------------------------------------------------------
860// path painting operators
861//------------------------------------------------------------------------
862
863void Gfx::opEndPath(Object args[], int numArgs) {
a5f8b9e7 864 (void)args;
865 (void)numArgs;
866
9c72faab 867 doEndPath();
868}
869
870void Gfx::opStroke(Object args[], int numArgs) {
a5f8b9e7 871 (void)args;
872 (void)numArgs;
873
9c72faab 874 if (!state->isCurPt()) {
875 //error(getPos(), "No path in stroke");
876 return;
877 }
878 if (state->isPath())
879 out->stroke(state);
880 doEndPath();
881}
882
883void Gfx::opCloseStroke(Object args[], int numArgs) {
a5f8b9e7 884 (void)args;
885 (void)numArgs;
886
9c72faab 887 if (!state->isCurPt()) {
888 //error(getPos(), "No path in closepath/stroke");
889 return;
890 }
891 if (state->isPath()) {
892 state->closePath();
893 out->stroke(state);
894 }
895 doEndPath();
896}
897
898void Gfx::opFill(Object args[], int numArgs) {
a5f8b9e7 899 (void)args;
900 (void)numArgs;
901
9c72faab 902 if (!state->isCurPt()) {
903 //error(getPos(), "No path in fill");
904 return;
905 }
906 if (state->isPath())
907 out->fill(state);
908 doEndPath();
909}
910
911void Gfx::opEOFill(Object args[], int numArgs) {
a5f8b9e7 912 (void)args;
913 (void)numArgs;
914
9c72faab 915 if (!state->isCurPt()) {
916 //error(getPos(), "No path in eofill");
917 return;
918 }
919 if (state->isPath())
920 out->eoFill(state);
921 doEndPath();
922}
923
924void Gfx::opFillStroke(Object args[], int numArgs) {
a5f8b9e7 925 (void)args;
926 (void)numArgs;
927
9c72faab 928 if (!state->isCurPt()) {
929 //error(getPos(), "No path in fill/stroke");
930 return;
931 }
932 if (state->isPath()) {
933 out->fill(state);
934 out->stroke(state);
935 }
936 doEndPath();
937}
938
939void Gfx::opCloseFillStroke(Object args[], int numArgs) {
a5f8b9e7 940 (void)args;
941 (void)numArgs;
942
9c72faab 943 if (!state->isCurPt()) {
944 //error(getPos(), "No path in closepath/fill/stroke");
945 return;
946 }
947 if (state->isPath()) {
948 state->closePath();
949 out->fill(state);
950 out->stroke(state);
951 }
952 doEndPath();
953}
954
955void Gfx::opEOFillStroke(Object args[], int numArgs) {
a5f8b9e7 956 (void)args;
957 (void)numArgs;
958
9c72faab 959 if (!state->isCurPt()) {
960 //error(getPos(), "No path in eofill/stroke");
961 return;
962 }
963 if (state->isPath()) {
964 out->eoFill(state);
965 out->stroke(state);
966 }
967 doEndPath();
968}
969
970void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
a5f8b9e7 971 (void)args;
972 (void)numArgs;
973
9c72faab 974 if (!state->isCurPt()) {
975 //error(getPos(), "No path in closepath/eofill/stroke");
976 return;
977 }
978 if (state->isPath()) {
979 state->closePath();
980 out->eoFill(state);
981 out->stroke(state);
982 }
983 doEndPath();
984}
985
986void Gfx::opShFill(Object args[], int numArgs) {
a5f8b9e7 987 (void)args;
988 (void)numArgs;
9c72faab 989}
990
991void Gfx::doEndPath() {
992 if (state->isPath()) {
993 if (clip == clipNormal)
994 out->clip(state);
995 else if (clip == clipEO)
996 out->eoClip(state);
997 }
998 clip = clipNone;
999 state->clearPath();
1000}
1001
1002//------------------------------------------------------------------------
1003// path clipping operators
1004//------------------------------------------------------------------------
1005
1006void Gfx::opClip(Object args[], int numArgs) {
a5f8b9e7 1007 (void)args;
1008 (void)numArgs;
1009
9c72faab 1010 clip = clipNormal;
1011}
1012
1013void Gfx::opEOClip(Object args[], int numArgs) {
a5f8b9e7 1014 (void)args;
1015 (void)numArgs;
1016
9c72faab 1017 clip = clipEO;
1018}
1019
1020//------------------------------------------------------------------------
1021// text object operators
1022//------------------------------------------------------------------------
1023
1024void Gfx::opBeginText(Object args[], int numArgs) {
a5f8b9e7 1025 (void)args;
1026 (void)numArgs;
1027
9c72faab 1028 state->setTextMat(1, 0, 0, 1, 0, 0);
1029 state->textMoveTo(0, 0);
1030 out->updateTextMat(state);
1031 out->updateTextPos(state);
1032 fontChanged = gTrue;
1033}
1034
1035void Gfx::opEndText(Object args[], int numArgs) {
a5f8b9e7 1036 (void)args;
1037 (void)numArgs;
9c72faab 1038}
1039
1040//------------------------------------------------------------------------
1041// text state operators
1042//------------------------------------------------------------------------
1043
1044void Gfx::opSetCharSpacing(Object args[], int numArgs) {
a5f8b9e7 1045 (void)args;
1046 (void)numArgs;
1047
9c72faab 1048 state->setCharSpace(args[0].getNum());
1049 out->updateCharSpace(state);
1050}
1051
1052void Gfx::opSetFont(Object args[], int numArgs) {
1053 GfxFont *font;
1054
a5f8b9e7 1055 (void)numArgs;
1056
9c72faab 1057 if (!(font = lookupFont(args[0].getName())))
1058 return;
1059 if (printCommands) {
1060 printf(" font: '%s' %g\n",
1061 font->getName() ? font->getName()->getCString() : "???",
1062 args[1].getNum());
1063 }
1064 state->setFont(font, args[1].getNum());
1065 fontChanged = gTrue;
1066}
1067
1068void Gfx::opSetTextLeading(Object args[], int numArgs) {
a5f8b9e7 1069 (void)numArgs;
1070
9c72faab 1071 state->setLeading(args[0].getNum());
1072}
1073
1074void Gfx::opSetTextRender(Object args[], int numArgs) {
a5f8b9e7 1075 (void)numArgs;
1076
9c72faab 1077 state->setRender(args[0].getInt());
1078 out->updateRender(state);
1079}
1080
1081void Gfx::opSetTextRise(Object args[], int numArgs) {
a5f8b9e7 1082 (void)numArgs;
1083
9c72faab 1084 state->setRise(args[0].getNum());
1085 out->updateRise(state);
1086}
1087
1088void Gfx::opSetWordSpacing(Object args[], int numArgs) {
a5f8b9e7 1089 (void)numArgs;
1090
9c72faab 1091 state->setWordSpace(args[0].getNum());
1092 out->updateWordSpace(state);
1093}
1094
1095void Gfx::opSetHorizScaling(Object args[], int numArgs) {
a5f8b9e7 1096 (void)numArgs;
1097
9c72faab 1098 state->setHorizScaling(args[0].getNum());
1099 out->updateHorizScaling(state);
1100}
1101
1102//------------------------------------------------------------------------
1103// text positioning operators
1104//------------------------------------------------------------------------
1105
1106void Gfx::opTextMove(Object args[], int numArgs) {
1107 double tx, ty;
1108
a5f8b9e7 1109 (void)numArgs;
1110
9c72faab 1111 tx = state->getLineX() + args[0].getNum();
1112 ty = state->getLineY() + args[1].getNum();
1113 state->textMoveTo(tx, ty);
1114 out->updateTextPos(state);
1115}
1116
1117void Gfx::opTextMoveSet(Object args[], int numArgs) {
1118 double tx, ty;
1119
a5f8b9e7 1120 (void)numArgs;
1121
9c72faab 1122 tx = state->getLineX() + args[0].getNum();
1123 ty = args[1].getNum();
1124 state->setLeading(-ty);
1125 ty += state->getLineY();
1126 state->textMoveTo(tx, ty);
1127 out->updateTextPos(state);
1128}
1129
1130void Gfx::opSetTextMatrix(Object args[], int numArgs) {
a5f8b9e7 1131 (void)numArgs;
1132
9c72faab 1133 state->setTextMat(args[0].getNum(), args[1].getNum(),
1134 args[2].getNum(), args[3].getNum(),
1135 args[4].getNum(), args[5].getNum());
1136 state->textMoveTo(0, 0);
1137 out->updateTextMat(state);
1138 out->updateTextPos(state);
1139 fontChanged = gTrue;
1140}
1141
1142void Gfx::opTextNextLine(Object args[], int numArgs) {
1143 double tx, ty;
1144
a5f8b9e7 1145 (void)args;
1146 (void)numArgs;
1147
9c72faab 1148 tx = state->getLineX();
1149 ty = state->getLineY() - state->getLeading();
1150 state->textMoveTo(tx, ty);
1151 out->updateTextPos(state);
1152}
1153
1154//------------------------------------------------------------------------
1155// text string operators
1156//------------------------------------------------------------------------
1157
1158void Gfx::opShowText(Object args[], int numArgs) {
a5f8b9e7 1159 (void)numArgs;
1160
9c72faab 1161 if (!state->getFont()) {
1162 error(getPos(), "No font in show");
1163 return;
1164 }
1165 doShowText(args[0].getString());
1166}
1167
1168void Gfx::opMoveShowText(Object args[], int numArgs) {
1169 double tx, ty;
1170
a5f8b9e7 1171 (void)numArgs;
1172
9c72faab 1173 if (!state->getFont()) {
1174 error(getPos(), "No font in move/show");
1175 return;
1176 }
1177 tx = state->getLineX();
1178 ty = state->getLineY() - state->getLeading();
1179 state->textMoveTo(tx, ty);
1180 out->updateTextPos(state);
1181 doShowText(args[0].getString());
1182}
1183
1184void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1185 double tx, ty;
1186
a5f8b9e7 1187 (void)numArgs;
1188
9c72faab 1189 if (!state->getFont()) {
1190 error(getPos(), "No font in move/set/show");
1191 return;
1192 }
1193 state->setWordSpace(args[0].getNum());
1194 state->setCharSpace(args[1].getNum());
1195 tx = state->getLineX();
1196 ty = state->getLineY() - state->getLeading();
1197 state->textMoveTo(tx, ty);
1198 out->updateWordSpace(state);
1199 out->updateCharSpace(state);
1200 out->updateTextPos(state);
1201 doShowText(args[2].getString());
1202}
1203
1204void Gfx::opShowSpaceText(Object args[], int numArgs) {
1205 Array *a;
1206 Object obj;
1207 int i;
1208
a5f8b9e7 1209 (void)numArgs;
1210
9c72faab 1211 if (!state->getFont()) {
1212 error(getPos(), "No font in show/space");
1213 return;
1214 }
1215 a = args[0].getArray();
1216 for (i = 0; i < a->getLength(); ++i) {
1217 a->get(i, &obj);
1218 if (obj.isNum()) {
1219 state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
1220 out->updateTextShift(state, obj.getNum());
1221 } else if (obj.isString()) {
1222 doShowText(obj.getString());
1223 } else {
1224 error(getPos(), "Element of show/space array must be number or string");
1225 }
1226 obj.free();
1227 }
1228}
1229
1230void Gfx::doShowText(GString *s) {
1231 GfxFont *font;
1232 GfxFontEncoding16 *enc;
1233 Guchar *p;
1234 Guchar c8;
1235 int c16;
1236 GString *s16;
52118ca3 1237 char s16a[2];
9c72faab 1238 int m, n;
52118ca3 1239#if 0 //~type3
1240 double dx, dy, width, height, w, h, x, y;
1241 double oldCTM[6], newCTM[6];
1242 double *mat;
1243 Object charProc;
1244 Parser *oldParser;
1245 int i;
1246#else
1247 double dx, dy, width, height, w, h, sWidth, sHeight;
1248#endif
9c72faab 1249
1250 if (fontChanged) {
1251 out->updateFont(state);
1252 fontChanged = gFalse;
1253 }
1254 font = state->getFont();
1255
1256 //----- 16-bit font
1257 if (font->is16Bit()) {
1258 enc = font->getEncoding16();
1259 if (out->useDrawChar()) {
1260 out->beginString(state, s);
1261 s16 = NULL;
1262 } else {
52118ca3 1263 s16 = new GString();
9c72faab 1264 }
52118ca3 1265 sWidth = sHeight = 0;
9c72faab 1266 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1267 p = (Guchar *)s->getCString();
1268 n = s->getLength();
1269 while (n > 0) {
1270 m = getNextChar16(enc, p, &c16);
1271 if (enc->wMode == 0) {
1272 width = state->getFontSize() * state->getHorizScaling() *
1273 font->getWidth16(c16) +
1274 state->getCharSpace();
1275 if (c16 == ' ') {
1276 width += state->getWordSpace();
1277 }
1278 height = 0;
1279 } else {
1280 width = 0;
1281 height = state->getFontSize() * font->getHeight16(c16);
1282 }
1283 state->textTransformDelta(width, height, &w, &h);
1284 if (out->useDrawChar()) {
1285 out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
1286 w, h, c16);
52118ca3 1287 state->textShift(width, height);
9c72faab 1288 } else {
52118ca3 1289 s16a[0] = (char)(c16 >> 8);
1290 s16a[1] = (char)c16;
1291 s16->append(s16a, 2);
1292 sWidth += w;
1293 sHeight += h;
9c72faab 1294 }
9c72faab 1295 n -= m;
1296 p += m;
1297 }
52118ca3 1298 if (out->useDrawChar()) {
9c72faab 1299 out->endString(state);
52118ca3 1300 } else {
1301 out->drawString16(state, s16);
9c72faab 1302 delete s16;
52118ca3 1303 state->textShift(sWidth, sHeight);
1304 }
9c72faab 1305
1306 //----- 8-bit font
1307 } else {
52118ca3 1308#if 0 //~type3
1309 //~ also check out->renderType3()
1310 if (font->getType() == fontType3) {
1311 out->beginString(state, s);
1312 mat = state->getCTM();
1313 for (i = 0; i < 6; ++i) {
1314 oldCTM[i] = mat[i];
1315 }
1316 mat = state->getTextMat();
1317 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
1318 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
1319 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
1320 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
1321 mat = font->getFontMatrix();
1322 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
1323 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
1324 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
1325 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
1326 newCTM[0] *= state->getFontSize();
1327 newCTM[3] *= state->getFontSize();
1328 newCTM[0] *= state->getHorizScaling();
1329 newCTM[2] *= state->getHorizScaling();
1330 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1331 oldParser = parser;
1332 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1333 c8 = *p;
1334 font->getCharProc(c8, &charProc);
1335 state->transform(state->getCurX() + dx, state->getCurY() + dy, &x, &y);
1336 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
1337 //~ out->updateCTM(???)
1338 if (charProc.isStream()) {
1339 display(&charProc);
1340 } else {
1341 error(getPos(), "Missing or bad Type3 CharProc entry");
1342 }
1343 state->setCTM(oldCTM[0], oldCTM[1], oldCTM[2],
1344 oldCTM[3], oldCTM[4], oldCTM[5]);
1345 //~ out->updateCTM(???) - use gsave/grestore instead?
1346 charProc.free();
1347 width = state->getFontSize() * state->getHorizScaling() *
1348 font->getWidth(c8) +
1349 state->getCharSpace();
1350 if (c8 == ' ') {
1351 width += state->getWordSpace();
1352 }
1353 state->textShift(width);
1354 }
1355 parser = oldParser;
1356 out->endString(state);
1357 } else
1358#endif
9c72faab 1359 if (out->useDrawChar()) {
1360 out->beginString(state, s);
1361 state->textTransformDelta(0, state->getRise(), &dx, &dy);
1362 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1363 c8 = *p;
1364 width = state->getFontSize() * state->getHorizScaling() *
1365 font->getWidth(c8) +
1366 state->getCharSpace();
1367 if (c8 == ' ')
1368 width += state->getWordSpace();
1369 state->textTransformDelta(width, 0, &w, &h);
1370 out->drawChar(state, state->getCurX() + dx, state->getCurY() + dy,
1371 w, h, c8);
1372 state->textShift(width);
1373 }
1374 out->endString(state);
1375 } else {
1376 out->drawString(state, s);
1377 width = state->getFontSize() * state->getHorizScaling() *
1378 font->getWidth(s) +
1379 s->getLength() * state->getCharSpace();
1380 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1381 if (*p == ' ')
1382 width += state->getWordSpace();
1383 }
1384 state->textShift(width);
1385 }
1386 }
1387}
1388
1389int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
1390 int n;
1391 int code;
1392 int a, b, m;
1393
1394 n = enc->codeLen[*p];
1395 if (n == 1) {
1396 *c16 = enc->map1[*p];
1397 } else {
1398 code = (p[0] << 8) + p[1];
1399 a = 0;
1400 b = enc->map2Len;
1401 // invariant: map2[2*a] <= code < map2[2*b]
1402 while (b - a > 1) {
1403 m = (a + b) / 2;
1404 if (enc->map2[2*m] <= code)
1405 a = m;
1406 else if (enc->map2[2*m] > code)
1407 b = m;
1408 else
1409 break;
1410 }
1411 *c16 = enc->map2[2*a+1] + (code - enc->map2[2*a]);
1412 }
1413 return n;
1414}
1415
1416//------------------------------------------------------------------------
1417// XObject operators
1418//------------------------------------------------------------------------
1419
1420void Gfx::opXObject(Object args[], int numArgs) {
1421 Object obj1, obj2;
52118ca3 1422#if OPI_SUPPORT
1423 Object opiDict;
1424#endif
9c72faab 1425
a5f8b9e7 1426 (void)numArgs;
1427
9c72faab 1428 if (!lookupXObject(args[0].getName(), &obj1))
1429 return;
52118ca3 1430 if (!obj1.isStream()) {
9c72faab 1431 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
1432 obj1.free();
1433 return;
1434 }
52118ca3 1435#if OPI_SUPPORT
1436 obj1.streamGetDict()->lookup("OPI", &opiDict);
1437 if (opiDict.isDict()) {
1438 out->opiBegin(state, opiDict.getDict());
1439 }
1440#endif
9c72faab 1441 obj1.streamGetDict()->lookup("Subtype", &obj2);
1442 if (obj2.isName("Image"))
1443 doImage(obj1.getStream(), gFalse);
1444 else if (obj2.isName("Form"))
1445 doForm(&obj1);
1446 else if (obj2.isName())
1447 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
1448 else
1449 error(getPos(), "XObject subtype is missing or wrong type");
1450 obj2.free();
52118ca3 1451#if OPI_SUPPORT
1452 if (opiDict.isDict()) {
1453 out->opiEnd(state, opiDict.getDict());
1454 }
1455 opiDict.free();
1456#endif
9c72faab 1457 obj1.free();
1458}
1459
1460void Gfx::doImage(Stream *str, GBool inlineImg) {
1461 Dict *dict;
1462 Object obj1, obj2;
1463 int width, height;
1464 int bits;
1465 GBool mask;
1466 GfxColorSpace *colorSpace;
1467 GfxImageColorMap *colorMap;
1468 GBool invert;
1469
1470 // get stream dict
1471 dict = str->getDict();
1472
1473 // get size
1474 dict->lookup("Width", &obj1);
1475 if (obj1.isNull()) {
1476 obj1.free();
1477 dict->lookup("W", &obj1);
1478 }
1479 if (!obj1.isInt())
1480 goto err2;
1481 width = obj1.getInt();
1482 obj1.free();
1483 dict->lookup("Height", &obj1);
1484 if (obj1.isNull()) {
1485 obj1.free();
1486 dict->lookup("H", &obj1);
1487 }
1488 if (!obj1.isInt())
1489 goto err2;
1490 height = obj1.getInt();
1491 obj1.free();
1492
1493 // image or mask?
1494 dict->lookup("ImageMask", &obj1);
1495 if (obj1.isNull()) {
1496 obj1.free();
1497 dict->lookup("IM", &obj1);
1498 }
1499 mask = gFalse;
1500 if (obj1.isBool())
1501 mask = obj1.getBool();
1502 else if (!obj1.isNull())
1503 goto err2;
1504 obj1.free();
1505
1506 // bit depth
1507 dict->lookup("BitsPerComponent", &obj1);
1508 if (obj1.isNull()) {
1509 obj1.free();
1510 dict->lookup("BPC", &obj1);
1511 }
1512 if (!obj1.isInt())
1513 goto err2;
1514 bits = obj1.getInt();
1515 obj1.free();
1516
1517 // display a mask
1518 if (mask) {
1519
1520 // check for inverted mask
1521 if (bits != 1)
1522 goto err1;
1523 invert = gFalse;
1524 dict->lookup("Decode", &obj1);
1525 if (obj1.isNull()) {
1526 obj1.free();
1527 dict->lookup("D", &obj1);
1528 }
1529 if (obj1.isArray()) {
1530 obj1.arrayGet(0, &obj2);
1531 if (obj2.isInt() && obj2.getInt() == 1)
1532 invert = gTrue;
1533 obj2.free();
1534 } else if (!obj1.isNull()) {
1535 goto err2;
1536 }
1537 obj1.free();
1538
1539 // draw it
1540 out->drawImageMask(state, str, width, height, invert, inlineImg);
1541
1542 } else {
1543
1544 // get color space and color map
1545 dict->lookup("ColorSpace", &obj1);
1546 if (obj1.isNull()) {
1547 obj1.free();
1548 dict->lookup("CS", &obj1);
1549 }
1550 if (obj1.isName()) {
1551 lookupColorSpace(obj1.getName(), &obj2);
1552 if (!obj2.isNull()) {
1553 obj1.free();
1554 obj1 = obj2;
1555 } else {
1556 obj2.free();
1557 }
1558 }
1559 colorSpace = new GfxColorSpace(&obj1);
1560 obj1.free();
1561 if (!colorSpace->isOk()) {
1562 delete colorSpace;
1563 goto err1;
1564 }
1565 dict->lookup("Decode", &obj1);
1566 if (obj1.isNull()) {
1567 obj1.free();
1568 dict->lookup("D", &obj1);
1569 }
1570 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
1571 obj1.free();
1572 if (!colorMap->isOk()) {
1573 delete colorSpace;
1574 goto err1;
1575 }
1576
1577 // draw it
1578 out->drawImage(state, str, width, height, colorMap, inlineImg);
1579 delete colorMap;
1580 }
1581
1582 return;
1583
1584 err2:
1585 obj1.free();
1586 err1:
1587 error(getPos(), "Bad image parameters");
1588}
1589
1590void Gfx::doForm(Object *str) {
9c72faab 1591 Dict *dict;
9c72faab 1592 Object matrixObj, bboxObj;
52118ca3 1593 Object obj1;
9c72faab 1594
1595 // get stream dict
1596 dict = str->streamGetDict();
1597
1598 // check form type
1599 dict->lookup("FormType", &obj1);
1600 if (!(obj1.isInt() && obj1.getInt() == 1)) {
1601 error(getPos(), "Unknown form type");
1602 }
1603 obj1.free();
1604
1605 // get matrix and bounding box
1606 dict->lookup("Matrix", &matrixObj);
1607 if (!matrixObj.isArray()) {
1608 matrixObj.free();
1609 error(getPos(), "Bad form matrix");
1610 return;
1611 }
1612 dict->lookup("BBox", &bboxObj);
1613 if (!bboxObj.isArray()) {
1614 matrixObj.free();
1615 bboxObj.free();
1616 error(getPos(), "Bad form bounding box");
1617 return;
1618 }
1619
52118ca3 1620 doForm1(str, dict, &matrixObj, &bboxObj);
1621
1622 matrixObj.free();
1623 bboxObj.free();
1624}
1625
1626void Gfx::doWidgetForm(Object *str, double x, double y) {
1627 Dict *dict;
1628 Object matrixObj, bboxObj;
1629 Object obj1;
1630
1631 // get stream dict
1632 dict = str->streamGetDict();
1633
1634 // get bounding box
1635 dict->lookup("BBox", &bboxObj);
1636 if (!bboxObj.isArray()) {
1637 bboxObj.free();
1638 error(getPos(), "Bad form bounding box");
1639 return;
1640 }
1641
1642 // construct matrix
1643 matrixObj.initArray();
1644 obj1.initReal(1);
1645 matrixObj.arrayAdd(&obj1);
1646 obj1.initReal(0);
1647 matrixObj.arrayAdd(&obj1);
1648 obj1.initReal(0);
1649 matrixObj.arrayAdd(&obj1);
1650 obj1.initReal(1);
1651 matrixObj.arrayAdd(&obj1);
1652 obj1.initReal(x);
1653 matrixObj.arrayAdd(&obj1);
1654 obj1.initReal(y);
1655 matrixObj.arrayAdd(&obj1);
1656
1657 doForm1(str, dict, &matrixObj, &bboxObj);
1658
1659 matrixObj.free();
1660 bboxObj.free();
1661}
1662
1663void Gfx::doForm1(Object *str, Dict *dict,
1664 Object *matrixObj, Object *bboxObj) {
1665 Parser *oldParser;
1666 GfxResources *resPtr;
1667 Dict *resDict;
1668 double m[6];
1669 Object obj1, obj2;
1670 int i;
1671
9c72faab 1672 // push new resources on stack
1673 res = new GfxResources(res);
1674 dict->lookup("Resources", &obj1);
1675 if (obj1.isDict()) {
1676 resDict = obj1.getDict();
1677 res->fonts = NULL;
1678 resDict->lookup("Font", &obj2);
1679 if (obj2.isDict())
1680 res->fonts = new GfxFontDict(obj2.getDict());
1681 obj2.free();
1682 resDict->lookup("XObject", &res->xObjDict);
1683 resDict->lookup("ColorSpace", &res->colorSpaceDict);
1684 obj1.free();
1685 }
1686
1687 // save current graphics state
1688 out->saveState(state);
1689 state = state->save();
1690
1691 // save current parser
1692 oldParser = parser;
1693
1694 // set form transformation matrix
1695 for (i = 0; i < 6; ++i) {
52118ca3 1696 matrixObj->arrayGet(i, &obj1);
9c72faab 1697 m[i] = obj1.getNum();
1698 obj1.free();
1699 }
1700 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1701 out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1702
1703 // set form bounding box
1704 for (i = 0; i < 4; ++i) {
52118ca3 1705 bboxObj->arrayGet(i, &obj1);
9c72faab 1706 m[i] = obj1.getNum();
1707 obj1.free();
1708 }
1709 state->moveTo(m[0], m[1]);
52118ca3 1710 state->lineTo(m[2], m[1]);
1711 state->lineTo(m[2], m[3]);
1712 state->lineTo(m[0], m[3]);
9c72faab 1713 state->closePath();
1714 out->clip(state);
1715 state->clearPath();
1716
1717 // draw the form
1718 display(str);
1719
9c72faab 1720 // restore parser
1721 parser = oldParser;
1722
1723 // restore graphics state
1724 state = state->restore();
1725 out->restoreState(state);
1726
1727 // pop resource stack
1728 resPtr = res->next;
1729 delete res;
1730 res = resPtr;
1731
1732 return;
1733}
1734
1735//------------------------------------------------------------------------
1736// in-line image operators
1737//------------------------------------------------------------------------
1738
1739void Gfx::opBeginImage(Object args[], int numArgs) {
1740 Stream *str;
1741 int c1, c2;
1742
a5f8b9e7 1743 (void)args;
1744 (void)numArgs;
1745
9c72faab 1746 // build dict/stream
1747 str = buildImageStream();
1748
1749 // display the image
1750 if (str) {
1751 doImage(str, gTrue);
1752
1753 // skip 'EI' tag
1754 c1 = str->getBaseStream()->getChar();
1755 c2 = str->getBaseStream()->getChar();
1756 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
1757 c1 = c2;
1758 c2 = str->getBaseStream()->getChar();
1759 }
1760 delete str;
1761 }
1762}
1763
1764Stream *Gfx::buildImageStream() {
1765 Object dict;
1766 Object obj;
aa530ae8 1767 const char *key;
9c72faab 1768 Stream *str;
1769
1770 // build dictionary
1771 dict.initDict();
1772 parser->getObj(&obj);
1773 while (!obj.isCmd("ID") && !obj.isEOF()) {
1774 if (!obj.isName()) {
1775 error(getPos(), "Inline image dictionary key must be a name object");
1776 obj.free();
1777 parser->getObj(&obj);
1778 } else {
1779 key = copyString(obj.getName());
1780 obj.free();
1781 parser->getObj(&obj);
1782 if (obj.isEOF() || obj.isError())
1783 break;
1784 dict.dictAdd(key, &obj);
1785 }
1786 parser->getObj(&obj);
1787 }
1788 if (obj.isEOF())
1789 error(getPos(), "End of file in inline image");
1790 obj.free();
1791
1792 // make stream
52118ca3 1793 str = new EmbedStream(parser->getStream(), &dict);
9c72faab 1794 str = str->addFilters(&dict);
1795
1796 return str;
1797}
1798
1799void Gfx::opImageData(Object args[], int numArgs) {
a5f8b9e7 1800 (void)args;
1801 (void)numArgs;
1802
9c72faab 1803 error(getPos(), "Internal: got 'ID' operator");
1804}
1805
1806void Gfx::opEndImage(Object args[], int numArgs) {
a5f8b9e7 1807 (void)args;
1808 (void)numArgs;
1809
9c72faab 1810 error(getPos(), "Internal: got 'EI' operator");
1811}
1812
1813//------------------------------------------------------------------------
1814// type 3 font operators
1815//------------------------------------------------------------------------
1816
1817void Gfx::opSetCharWidth(Object args[], int numArgs) {
a5f8b9e7 1818 (void)args;
1819 (void)numArgs;
1820
9c72faab 1821 error(getPos(), "Encountered 'd0' operator in content stream");
1822}
1823
1824void Gfx::opSetCacheDevice(Object args[], int numArgs) {
a5f8b9e7 1825 (void)args;
1826 (void)numArgs;
1827
9c72faab 1828 error(getPos(), "Encountered 'd1' operator in content stream");
1829}
1830
1831//------------------------------------------------------------------------
1832// compatibility operators
1833//------------------------------------------------------------------------
1834
1835void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
a5f8b9e7 1836 (void)args;
1837 (void)numArgs;
1838
9c72faab 1839 ++ignoreUndef;
1840}
1841
1842void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
a5f8b9e7 1843 (void)args;
1844 (void)numArgs;
1845
9c72faab 1846 if (ignoreUndef > 0)
1847 --ignoreUndef;
1848}
1849
1850//------------------------------------------------------------------------
1851// marked content operators
1852//------------------------------------------------------------------------
1853
1854void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
a5f8b9e7 1855 (void)numArgs;
1856
9c72faab 1857 if (printCommands) {
1858 printf(" marked content: %s ", args[0].getName());
1859 if (numArgs == 2)
1860 args[2].print(stdout);
1861 printf("\n");
1862 }
1863}
1864
1865void Gfx::opEndMarkedContent(Object args[], int numArgs) {
a5f8b9e7 1866 (void)args;
1867 (void)numArgs;
9c72faab 1868}
1869
1870void Gfx::opMarkPoint(Object args[], int numArgs) {
a5f8b9e7 1871 (void)numArgs;
1872
9c72faab 1873 if (printCommands) {
1874 printf(" mark point: %s ", args[0].getName());
1875 if (numArgs == 2)
1876 args[2].print(stdout);
1877 printf("\n");
1878 }
1879}