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