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