filters

XOutputDev.cpp
1//========================================================================
2//
3// XOutputDev.cpp
4//
5// Copyright 1996-2002 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <aconf.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <stddef.h>
18#include <unistd.h>
19#include <string.h>
20#include <ctype.h>
21#include <math.h>
22#include "gmem.h"
23#include "gfile.h"
24#include "GString.h"
25#include "GList.h"
26#include "Object.h"
27#include "Stream.h"
28#include "Link.h"
29#include "GfxState.h"
30#include "GfxFont.h"
31#include "UnicodeMap.h"
32#include "CharCodeToUnicode.h"
33#include "FontFile.h"
34#include "Error.h"
35#include "TextOutputDev.h"
36#include "XOutputDev.h"
37#if HAVE_T1LIB_H
38#include "T1Font.h"
39#endif
40#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
41#include "FTFont.h"
42#endif
43#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
44#include "TTFont.h"
45#endif
46
47#ifdef VMS
48#if (__VMS_VER < 70000000)
49extern "C" int unlink(char *filename);
50#endif
51#endif
52
53#ifdef XlibSpecificationRelease
54#if XlibSpecificationRelease < 5
55typedef char *XPointer;
56#endif
57#else
58typedef char *XPointer;
59#endif
60
61//------------------------------------------------------------------------
62// Constants and macros
63//------------------------------------------------------------------------
64
65#define xoutRound(x) ((int)(x + 0.5))
66
67#define maxCurveSplits 6 // max number of splits when recursively
68 // drawing Bezier curves
69
70//------------------------------------------------------------------------
71// Font substitutions
72//------------------------------------------------------------------------
73
74struct XOutFontSubst {
75 char *name;
76 double mWidth;
77};
78
79// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
80static XOutFontSubst xOutSubstFonts[16] = {
81 {"Helvetica", 0.833},
82 {"Helvetica-Oblique", 0.833},
83 {"Helvetica-Bold", 0.889},
84 {"Helvetica-BoldOblique", 0.889},
85 {"Times-Roman", 0.788},
86 {"Times-Italic", 0.722},
87 {"Times-Bold", 0.833},
88 {"Times-BoldItalic", 0.778},
89 {"Courier", 0.600},
90 {"Courier-Oblique", 0.600},
91 {"Courier-Bold", 0.600},
92 {"Courier-BoldOblique", 0.600},
93 {"Symbol", 0.576},
94 {"Symbol", 0.576},
95 {"Symbol", 0.576},
96 {"Symbol", 0.576}
97};
98
99//------------------------------------------------------------------------
100
101static void outputToFile(void *stream, char *data, int len) {
102 fwrite(data, 1, len, (FILE *)stream);
103}
104
105//------------------------------------------------------------------------
106// XOutputFont
107//------------------------------------------------------------------------
108
109XOutputFont::XOutputFont(Ref *idA, double m11OrigA, double m12OrigA,
110 double m21OrigA, double m22OrigA,
111 double m11A, double m12A, double m21A, double m22A,
112 Display *displayA, XOutputDev *xOutA) {
113 id = *idA;
114 display = displayA;
115 xOut = xOutA;
116 m11Orig = m11OrigA;
117 m12Orig = m12OrigA;
118 m21Orig = m21OrigA;
119 m22Orig = m22OrigA;
120 m11 = m11A;
121 m12 = m12A;
122 m21 = m21A;
123 m22 = m22A;
124}
125
126XOutputFont::~XOutputFont() {
127}
128
129void XOutputFont::getCharPath(GfxState *state,
130 CharCode c, Unicode *u, int ulen) {
131}
132
133#if HAVE_T1LIB_H
134//------------------------------------------------------------------------
135// XOutputT1Font
136//------------------------------------------------------------------------
137
138XOutputT1Font::XOutputT1Font(Ref *idA, T1FontFile *fontFileA,
139 double m11OrigA, double m12OrigA,
140 double m21OrigA, double m22OrigA,
141 double m11A, double m12A,
142 double m21A, double m22A,
143 Display *displayA, XOutputDev *xOutA):
144 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
145 m11A, m12A, m21A, m22A, displayA, xOutA)
146{
147 double matrix[4];
148
149 fontFile = fontFileA;
150
151 // create the transformed instance
152 matrix[0] = m11;
153 matrix[1] = -m12;
154 matrix[2] = m21;
155 matrix[3] = -m22;
156 font = new T1Font(fontFile, matrix);
157}
158
159XOutputT1Font::~XOutputT1Font() {
160 if (font) {
161 delete font;
162 }
163}
164
165GBool XOutputT1Font::isOk() {
166 return font != NULL;
167}
168
169void XOutputT1Font::updateGC(GC gc) {
170}
171
172void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
173 GC gc, GfxRGB *rgb,
174 double x, double y, double dx, double dy,
175 CharCode c, Unicode *u, int uLen) {
176 font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
177 (int)(rgb->r * 65535), (int)(rgb->g * 65535),
178 (int)(rgb->b * 65535), c, u[0]);
179}
180
181void XOutputT1Font::getCharPath(GfxState *state,
182 CharCode c, Unicode *u, int uLen) {
183 font->getCharPath(c, u[0], state);
184}
185#endif // HAVE_T1LIB_H
186
187#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
188//------------------------------------------------------------------------
189// XOutputFTFont
190//------------------------------------------------------------------------
191
192XOutputFTFont::XOutputFTFont(Ref *idA, FTFontFile *fontFileA,
193 double m11OrigA, double m12OrigA,
194 double m21OrigA, double m22OrigA,
195 double m11A, double m12A,
196 double m21A, double m22A,
197 Display *displayA, XOutputDev *xOutA):
198 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
199 m11A, m12A, m21A, m22A, displayA, xOutA)
200{
201 double matrix[4];
202
203 fontFile = fontFileA;
204
205 // create the transformed instance
206 matrix[0] = m11;
207 matrix[1] = -m12;
208 matrix[2] = m21;
209 matrix[3] = -m22;
210 font = new FTFont(fontFile, matrix);
211}
212
213XOutputFTFont::~XOutputFTFont() {
214 if (font) {
215 delete font;
216 }
217}
218
219GBool XOutputFTFont::isOk() {
220 return font != NULL;
221}
222
223void XOutputFTFont::updateGC(GC gc) {
224}
225
226void XOutputFTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
227 GC gc, GfxRGB *rgb,
228 double x, double y, double dx, double dy,
229 CharCode c, Unicode *u, int uLen) {
230 font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
231 (int)(rgb->r * 65535), (int)(rgb->g * 65535),
232 (int)(rgb->b * 65535), c, uLen > 0 ? u[0] : 0);
233}
234
235void XOutputFTFont::getCharPath(GfxState *state,
236 CharCode c, Unicode *u, int uLen) {
237 font->getCharPath(c, u[0], state);
238}
239#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
240
241#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
242//------------------------------------------------------------------------
243// XOutputTTFont
244//------------------------------------------------------------------------
245
246XOutputTTFont::XOutputTTFont(Ref *idA, TTFontFile *fontFileA,
247 double m11OrigA, double m12OrigA,
248 double m21OrigA, double m22OrigA,
249 double m11A, double m12A,
250 double m21A, double m22A,
251 Display *displayA, XOutputDev *xOutA):
252 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
253 m11A, m12A, m21A, m22A, displayA, xOutA)
254{
255 double matrix[4];
256
257 fontFile = fontFileA;
258
259 // create the transformed instance
260 matrix[0] = m11;
261 matrix[1] = -m12;
262 matrix[2] = m21;
263 matrix[3] = -m22;
264 font = new TTFont(fontFile, matrix);
265}
266
267XOutputTTFont::~XOutputTTFont() {
268 if (font) {
269 delete font;
270 }
271}
272
273GBool XOutputTTFont::isOk() {
274 return font != NULL;
275}
276
277void XOutputTTFont::updateGC(GC gc) {
278}
279
280void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
281 GC gc, GfxRGB *rgb,
282 double x, double y, double dx, double dy,
283 CharCode c, Unicode *u, int uLen) {
284 font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
285 (int)(rgb->r * 65535), (int)(rgb->g * 65535),
286 (int)(rgb->b * 65535), c, u[0]);
287}
288#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
289
290//------------------------------------------------------------------------
291// XOutputServer8BitFont
292//------------------------------------------------------------------------
293
294// Copy <fmt>, substituting <val> for one occurrence of "%s", into
295// <buf>.
296static void stringSubst(char *buf, int bufSize, char *fmt, char *val) {
297 char *p, *q;
298 int i;
299
300 i = 0;
301 p = fmt;
302 while (*p) {
303 if (p[0] == '%' && p[1] == 's') {
304 q = val;
305 while (*q && i < bufSize - 1) {
306 buf[i++] = *q++;
307 }
308 p += 2;
309 } else {
310 if (i < bufSize - 1) {
311 buf[i++] = *p;
312 }
313 ++p;
314 }
315 }
316 buf[i] = '\0';
317}
318
319XOutputServer8BitFont::XOutputServer8BitFont(Ref *idA, GString *xlfdFmt,
320 UnicodeMap *xUMapA,
321 CharCodeToUnicode *fontUMap,
322 double m11OrigA, double m12OrigA,
323 double m21OrigA, double m22OrigA,
324 double m11A, double m12A,
325 double m21A, double m22A,
326 Display *displayA,
327 XOutputDev *xOutA):
328 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
329 m11A, m12A, m21A, m22A, displayA, xOutA)
330{
331 double size, ntm11, ntm12, ntm21, ntm22;
332 GBool rotated;
333 int startSize, sz;
334 char fontName[500], fontSize[100];
335 Unicode u;
336 char buf;
337 int i;
338
339 // compute size and normalized transform matrix
340 size = sqrt(m21*m21 + m22*m22);
341 ntm11 = m11 / size;
342 ntm12 = -m12 / size;
343 ntm21 = m21 / size;
344 ntm22 = -m22 / size;
345
346 // try to get a rotated font?
347 rotated = !(ntm11 > 0 && ntm22 > 0 &&
348 fabs(ntm11 / ntm22 - 1) < 0.2 &&
349 fabs(ntm12) < 0.01 &&
350 fabs(ntm21) < 0.01);
351
352 // open X font -- if font is not found (which means the server can't
353 // scale fonts), try progressively smaller and then larger sizes
354 startSize = (int)size;
355 if (rotated) {
356 sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
357 ntm11<0 ? "~" : "", fabs(ntm11 * size),
358 ntm12<0 ? "~" : "", fabs(ntm12 * size),
359 ntm21<0 ? "~" : "", fabs(ntm21 * size),
360 ntm22<0 ? "~" : "", fabs(ntm22 * size));
361 } else {
362 sprintf(fontSize, "%d", startSize);
363 }
364 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
365 xFont = XLoadQueryFont(display, fontName);
366 if (!xFont) {
367 for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
368 sprintf(fontSize, "%d", sz);
369 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
370 if ((xFont = XLoadQueryFont(display, fontName)))
371 break;
372 }
373 if (!xFont) {
374 for (sz = startSize + 1; sz < startSize + 10; ++sz) {
375 sprintf(fontSize, "%d", sz);
376 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
377 fontSize);
378 if ((xFont = XLoadQueryFont(display, fontName))) {
379 break;
380 }
381 }
382 if (!xFont) {
383 sprintf(fontSize, "%d", startSize);
384 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
385 fontSize);
386 error(-1, "Failed to open font: '%s'", fontName);
387 return;
388 }
389 }
390 }
391
392 // Construct char code map.
393 xUMap = xUMapA;
394 for (i = 0; i < 256; ++i) {
395 if (fontUMap->mapToUnicode((CID)i, &u, 1) == 1 &&
396 xUMap->mapUnicode(u, &buf, 1) == 1) {
397 map[i] = buf & 0xff;
398 } else {
399 map[i] = 0;
400 }
401 }
402}
403
404XOutputServer8BitFont::~XOutputServer8BitFont() {
405 if (xFont) {
406 XFreeFont(display, xFont);
407 }
408}
409
410GBool XOutputServer8BitFont::isOk() {
411 return xFont != NULL;
412}
413
414void XOutputServer8BitFont::updateGC(GC gc) {
415 XSetFont(display, gc, xFont->fid);
416}
417
418void XOutputServer8BitFont::drawChar(GfxState *state, Pixmap pixmap,
419 int w, int h, GC gc, GfxRGB *rgb,
420 double x, double y, double dx, double dy,
421 CharCode c, Unicode *u, int uLen) {
422 Gushort c1;
423 char buf[8];
424 double dx1, dy1;
425 int m, n, i, j, k;
426
427 c1 = map[c];
428 if (c1 > 0) {
429 buf[0] = (char)c1;
430 XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), buf, 1);
431 } else {
432 // substituted character, using more than one character
433 n = 0;
434 for (i = 0; i < uLen; ++i) {
435 n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
436 }
437 if (n > 0) {
438 dx1 = dx / n;
439 dy1 = dy / n;
440 k = 0;
441 for (i = 0; i < uLen; ++i) {
442 m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
443 for (j = 0; j < m; ++j) {
444 XDrawString(display, pixmap, gc,
445 xoutRound(x + k*dx1), xoutRound(y + k*dy1),
446 buf + j, 1);
447 ++k;
448 }
449 }
450 }
451 }
452}
453
454//------------------------------------------------------------------------
455// XOutputServer16BitFont
456//------------------------------------------------------------------------
457
458XOutputServer16BitFont::XOutputServer16BitFont(Ref *idA, GString *xlfdFmt,
459 UnicodeMap *xUMapA,
460 CharCodeToUnicode *fontUMap,
461 double m11OrigA,
462 double m12OrigA,
463 double m21OrigA,
464 double m22OrigA,
465 double m11A, double m12A,
466 double m21A, double m22A,
467 Display *displayA,
468 XOutputDev *xOutA):
469 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
470 m11A, m12A, m21A, m22A, displayA, xOutA)
471{
472 double size, ntm11, ntm12, ntm21, ntm22;
473 GBool rotated;
474 int startSize, sz;
475 char fontName[500], fontSize[100];
476
477 xUMap = xUMapA;
478 xUMap->incRefCnt();
479
480 // compute size and normalized transform matrix
481 size = sqrt(m21*m21 + m22*m22);
482 ntm11 = m11 / size;
483 ntm12 = -m12 / size;
484 ntm21 = m21 / size;
485 ntm22 = -m22 / size;
486
487 // try to get a rotated font?
488 rotated = !(ntm11 > 0 && ntm22 > 0 &&
489 fabs(ntm11 / ntm22 - 1) < 0.2 &&
490 fabs(ntm12) < 0.01 &&
491 fabs(ntm21) < 0.01);
492
493 // open X font -- if font is not found (which means the server can't
494 // scale fonts), try progressively smaller and then larger sizes
495 startSize = (int)size;
496 if (rotated) {
497 sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
498 ntm11<0 ? "~" : "", fabs(ntm11 * size),
499 ntm12<0 ? "~" : "", fabs(ntm12 * size),
500 ntm21<0 ? "~" : "", fabs(ntm21 * size),
501 ntm22<0 ? "~" : "", fabs(ntm22 * size));
502 } else {
503 sprintf(fontSize, "%d", startSize);
504 }
505 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
506 xFont = XLoadQueryFont(display, fontName);
507 if (!xFont) {
508 for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
509 sprintf(fontSize, "%d", sz);
510 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
511 if ((xFont = XLoadQueryFont(display, fontName)))
512 break;
513 }
514 if (!xFont) {
515 for (sz = startSize + 1; sz < startSize + 10; ++sz) {
516 sprintf(fontSize, "%d", sz);
517 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
518 fontSize);
519 if ((xFont = XLoadQueryFont(display, fontName))) {
520 break;
521 }
522 }
523 if (!xFont) {
524 sprintf(fontSize, "%d", startSize);
525 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
526 fontSize);
527 error(-1, "Failed to open font: '%s'", fontName);
528 return;
529 }
530 }
531 }
532}
533
534XOutputServer16BitFont::~XOutputServer16BitFont() {
535 xUMap->decRefCnt();
536 if (xFont) {
537 XFreeFont(display, xFont);
538 }
539}
540
541GBool XOutputServer16BitFont::isOk() {
542 return xFont != NULL;
543}
544
545void XOutputServer16BitFont::updateGC(GC gc) {
546 XSetFont(display, gc, xFont->fid);
547}
548
549void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap,
550 int w, int h, GC gc, GfxRGB *rgb,
551 double x, double y, double dx, double dy,
552 CharCode c, Unicode *u, int uLen) {
553 char buf[16];
554 XChar2b c1;
555 double dx1, dy1;
556 int m, n, i, j, k;
557
558 n = 0;
559 for (i = 0; i < uLen; ++i) {
560 n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
561 }
562 if (n > 0) {
563 dx1 = dx / n;
564 dy1 = dy / n;
565 k = 0;
566 for (i = 0; i < uLen; ++i) {
567 m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
568 for (j = 0; j+1 < m; j += 2) {
569 c1.byte1 = buf[j];
570 c1.byte2 = buf[j+1];
571 XDrawString16(display, pixmap, gc,
572 xoutRound(x + k*dx1), xoutRound(y + k*dy1),
573 &c1, 1);
574 ++k;
575 }
576 }
577 } else if (c != 0) {
578 // some PDF files use CID 0, which is .notdef, so just ignore it
579 error(-1, "Unknown character (CID=%d Unicode=%04x)",
580 c, uLen > 0 ? u[0] : (Unicode)0);
581 }
582}
583
584//------------------------------------------------------------------------
585// XOutputFontCache
586//------------------------------------------------------------------------
587
588#if HAVE_T1LIB_H
589XOutputT1FontFile::~XOutputT1FontFile() {
590 delete fontFile;
591 if (tmpFileName) {
592 unlink(tmpFileName->getCString());
593 delete tmpFileName;
594 }
595}
596#endif
597
598#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
599XOutputFTFontFile::~XOutputFTFontFile() {
600 delete fontFile;
601 if (tmpFileName) {
602 unlink(tmpFileName->getCString());
603 delete tmpFileName;
604 }
605}
606#endif
607
608#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
609XOutputTTFontFile::~XOutputTTFontFile() {
610 delete fontFile;
611 if (tmpFileName) {
612 unlink(tmpFileName->getCString());
613 delete tmpFileName;
614 }
615}
616#endif
617
618XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA,
619 XOutputDev *xOutA,
620 FontRastControl t1libControlA,
621 FontRastControl freetypeControlA) {
622 display = displayA;
623 depth = depthA;
624 xOut = xOutA;
625
626#if HAVE_T1LIB_H
627 t1Engine = NULL;
628 t1libControl = t1libControlA;
629#endif
630
631#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
632 ftEngine = NULL;
633#endif
634#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
635 ttEngine = NULL;
636#endif
637#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
638 freetypeControl = freetypeControlA;
639#endif
640
641 clear();
642}
643
644XOutputFontCache::~XOutputFontCache() {
645 delFonts();
646}
647
648void XOutputFontCache::startDoc(int screenNum, Visual *visual,
649 Colormap colormap, GBool trueColor,
650 int rMul, int gMul, int bMul,
651 int rShift, int gShift, int bShift,
652 Gulong *colors, int numColors) {
653 delFonts();
654 clear();
655
656#if HAVE_T1LIB_H
657 if (t1libControl != fontRastNone) {
658 t1Engine = new T1FontEngine(display, visual, depth, colormap,
659 t1libControl == fontRastAALow ||
660 t1libControl == fontRastAAHigh,
661 t1libControl == fontRastAAHigh);
662 if (t1Engine->isOk()) {
663 if (trueColor) {
664 t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
665 } else {
666 t1Engine->useColorCube(colors, numColors);
667 }
668 } else {
669 delete t1Engine;
670 t1Engine = NULL;
671 }
672 }
673#endif // HAVE_T1LIB_H
674
675#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
676 if (freetypeControl != fontRastNone) {
677 ftEngine = new FTFontEngine(display, visual, depth, colormap,
678 freetypeControl == fontRastAALow ||
679 freetypeControl == fontRastAAHigh);
680 if (ftEngine->isOk()) {
681 if (trueColor) {
682 ftEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
683 } else {
684 ftEngine->useColorCube(colors, numColors);
685 }
686 } else {
687 delete ftEngine;
688 ftEngine = NULL;
689 }
690 }
691#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
692
693#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
694 if (freetypeControl != fontRastNone) {
695 ttEngine = new TTFontEngine(display, visual, depth, colormap,
696 freetypeControl == fontRastAALow ||
697 freetypeControl == fontRastAAHigh);
698 if (ttEngine->isOk()) {
699 if (trueColor) {
700 ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
701 } else {
702 ttEngine->useColorCube(colors, numColors);
703 }
704 } else {
705 delete ttEngine;
706 ttEngine = NULL;
707 }
708 }
709#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
710}
711
712void XOutputFontCache::delFonts() {
713 int i;
714
715 for (i = 0; i < nFonts; ++i) {
716 delete fonts[i];
717 }
718
719#if HAVE_T1LIB_H
720 // delete Type 1 font files
721 deleteGList(t1FontFiles, XOutputT1FontFile);
722 if (t1Engine) {
723 delete t1Engine;
724 }
725#endif
726
727#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
728 // delete FreeType font files
729 deleteGList(ftFontFiles, XOutputFTFontFile);
730 if (ftEngine) {
731 delete ftEngine;
732 }
733#endif
734
735#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
736 // delete TrueType fonts
737 deleteGList(ttFontFiles, XOutputTTFontFile);
738 if (ttEngine) {
739 delete ttEngine;
740 }
741#endif
742}
743
744void XOutputFontCache::clear() {
745 int i;
746
747 for (i = 0; i < xOutFontCacheSize; ++i) {
748 fonts[i] = NULL;
749 }
750 nFonts = 0;
751
752#if HAVE_T1LIB_H
753 // clear Type 1 font files
754 t1FontFiles = new GList();
755#endif
756
757#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
758 // clear FreeType font cache
759 ftFontFiles = new GList();
760#endif
761
762#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
763 // clear TrueType font cache
764 ttFontFiles = new GList();
765#endif
766}
767
768XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont,
769 double m11, double m12,
770 double m21, double m22) {
771 XOutputFont *font;
772 DisplayFontParam *dfp;
773 GString *substName;
774 double m11New, m12New, m21New, m22New;
775 double w1, w2, v;
776 double *fm;
777 char *name;
778 int index;
779 int code;
780 int i, j;
781
782 // is it the most recently used font?
783 if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
784 return fonts[0];
785 }
786
787 // is it in the cache?
788 for (i = 1; i < nFonts; ++i) {
789 if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
790 font = fonts[i];
791 for (j = i; j > 0; --j) {
792 fonts[j] = fonts[j-1];
793 }
794 fonts[0] = font;
795 return font;
796 }
797 }
798
799 // try for a cached FontFile, an embedded font, or an external font
800 // file
801 font = NULL;
802 switch (gfxFont->getType()) {
803 case fontType1:
804 case fontType1C:
805#if HAVE_T1LIB_H
806 if (t1libControl != fontRastNone) {
807 font = tryGetT1Font(xref, gfxFont, m11, m12, m21, m22);
808 }
809#endif
810#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
811 if (!font) {
812 if (freetypeControl != fontRastNone) {
813 font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
814 }
815 }
816#endif
817 break;
818 case fontTrueType:
819 case fontCIDType2:
820#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
821 if (freetypeControl != fontRastNone) {
822 font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
823 }
824#endif
825#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
826 if (freetypeControl != fontRastNone) {
827 font = tryGetTTFont(xref, gfxFont, m11, m12, m21, m22);
828 }
829#endif
830 break;
831 case fontCIDType0C:
832#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
833 if (freetypeControl != fontRastNone) {
834 font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
835 }
836#endif
837 break;
838 default:
839 break;
840 }
841
842 if (!font) {
843
844 // search for a display font mapping
845 dfp = NULL;
846 if (gfxFont->isCIDFont()) {
847 if (((GfxCIDFont *)gfxFont)->getCollection()) {
848 dfp = globalParams->
849 getDisplayCIDFont(gfxFont->getName(),
850 ((GfxCIDFont *)gfxFont)->getCollection());
851 } else {
852 // this error (no CMap file) was already reported by GfxFont
853 return NULL;
854 }
855 } else {
856 if (gfxFont->getName()) {
857 dfp = globalParams->getDisplayFont(gfxFont->getName());
858 }
859 }
860 if (dfp) {
861 font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
862 m11, m12, m21, m22, gFalse);
863 }
864
865 // substitute a font (8-bit fonts only)
866 if (!font && !gfxFont->isCIDFont()) {
867
868 // choose a substitute font
869 if (gfxFont->isFixedWidth()) {
870 index = 8;
871 } else if (gfxFont->isSerif()) {
872 index = 4;
873 } else {
874 index = 0;
875 }
876 if (gfxFont->isBold()) {
877 index += 2;
878 }
879 if (gfxFont->isItalic()) {
880 index += 1;
881 }
882 substName = new GString(xOutSubstFonts[index].name);
883
884 // adjust the font matrix -- compare the width of 'm' in the
885 // original font and the substituted font
886 m11New = m11;
887 m12New = m12;
888 m21New = m21;
889 m22New = m22;
890 for (code = 0; code < 256; ++code) {
891 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
892 name[0] == 'm' && name[1] == '\0') {
893 break;
894 }
895 }
896 if (code < 256) {
897 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
898 w2 = xOutSubstFonts[index].mWidth;
899 if (gfxFont->getType() == fontType3) {
900 // This is a hack which makes it possible to substitute for some
901 // Type 3 fonts. The problem is that it's impossible to know what
902 // the base coordinate system used in the font is without actually
903 // rendering the font. This code tries to guess by looking at the
904 // width of the character 'm' (which breaks if the font is a
905 // subset that doesn't contain 'm').
906 if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
907 w1 /= w2;
908 m11New *= w1;
909 m12New *= w1;
910 m21New *= w1;
911 m22New *= w1;
912 }
913 fm = gfxFont->getFontMatrix();
914 v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
915 m21New *= v;
916 m22New *= v;
917 } else if (!gfxFont->isSymbolic()) {
918 // if real font is substantially narrower than substituted
919 // font, reduce the font size accordingly
920 if (w1 > 0.01 && w1 < 0.9 * w2) {
921 w1 /= w2;
922 m11New *= w1;
923 m21New *= w1;
924 }
925 }
926 }
927
928 // get the font
929 dfp = globalParams->getDisplayFont(substName);
930 delete substName;
931 if (!dfp) {
932 // this should never happen since GlobalParams sets up default
933 // mappings for the Base-14 fonts
934 error(-1, "Couldn't find a font for '%s'",
935 gfxFont->getName()->getCString());
936 return NULL;
937 }
938 font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
939 m11New, m12New, m21New, m22New, gTrue);
940 }
941 }
942
943 // check for error
944 if (!font) {
945 // This will happen if the user specifies a bogus font in the
946 // config file (a non-existent font file or a font that requires a
947 // rasterizer that is disabled or wasn't built in), or if a CID
948 // font has no associated font in the config file.
949 if (gfxFont->isCIDFont()) {
950 error(-1, "Couldn't find a font for the '%s' character collection",
951 ((GfxCIDFont *)gfxFont)->getCollection()->getCString());
952 } else {
953 error(-1, "Couldn't find a font for '%s'",
954 gfxFont->getName() ?
955 gfxFont->getName()->getCString() : "[unnamed]");
956 }
957 return NULL;
958 }
959
960 // insert font in cache
961 if (nFonts == xOutFontCacheSize) {
962 --nFonts;
963 delete fonts[nFonts];
964 }
965 for (j = nFonts; j > 0; --j) {
966 fonts[j] = fonts[j-1];
967 }
968 fonts[0] = font;
969 ++nFonts;
970
971 return font;
972}
973
974XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
975 GfxFont *gfxFont,
976 double m11Orig, double m12Orig,
977 double m21Orig, double m22Orig,
978 double m11, double m12,
979 double m21, double m22,
980 GBool subst) {
981 XOutputFont *font;
982
983 font = NULL;
984
985 // create the new font
986 switch (dfp->kind) {
987
988 case displayFontX:
989 font = tryGetServerFont(dfp->x.xlfd, dfp->x.encoding, gfxFont,
990 m11Orig, m12Orig, m21Orig, m22Orig,
991 m11, m12, m21, m22);
992 break;
993
994 case displayFontT1:
995#if HAVE_T1LIB_H
996 if (t1libControl != fontRastNone) {
997 font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
998 m11Orig, m12Orig, m21Orig, m22Orig,
999 m11, m12, m21, m22, subst);
1000 }
1001#endif
1002#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1003 if (!font) {
1004 if (freetypeControl != fontRastNone) {
1005 font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
1006 m11Orig, m12Orig, m21Orig, m22Orig,
1007 m11, m12, m21, m22, subst);
1008 }
1009 }
1010#endif
1011#if !((FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)) || defined(HAVE_T1LIB_H))
1012 error(-1, "Config file specifies a Type 1 font,");
1013 error(-1, "but xpdf was not built with t1lib or FreeType2 support");
1014#endif
1015 break;
1016
1017 case displayFontTT:
1018#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1019 if (freetypeControl != fontRastNone) {
1020 font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
1021 m11Orig, m12Orig, m21Orig, m22Orig,
1022 m11, m12, m21, m22, subst);
1023 }
1024#endif
1025#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1026 if (freetypeControl != fontRastNone) {
1027 font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
1028 m11Orig, m12Orig, m21Orig, m22Orig,
1029 m11, m12, m21, m22, subst);
1030 }
1031#endif
1032#if !(HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1033 error(-1, "Config file specifies a TrueType font,");
1034 error(-1, "but xpdf was not built with FreeType support");
1035 dfp = NULL;
1036#endif
1037 break;
1038 }
1039
1040 return font;
1041}
1042
1043#if HAVE_T1LIB_H
1044XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
1045 GfxFont *gfxFont,
1046 double m11, double m12,
1047 double m21, double m22) {
1048 Ref *id;
1049 XOutputT1FontFile *xFontFile;
1050 XOutputFont *font;
1051 Ref embRef;
1052 GString *fileName;
1053 FILE *f;
1054 char *fontBuf;
1055 int fontLen;
1056 Type1CFontFile *ff;
1057 Object refObj, strObj;
1058 int c;
1059 int i;
1060
1061 // check the already available font files
1062 id = gfxFont->getID();
1063 for (i = 0; i < t1FontFiles->getLength(); ++i) {
1064 xFontFile = (XOutputT1FontFile *)t1FontFiles->get(i);
1065 if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1066 !xFontFile->subst) {
1067 font = new XOutputT1Font(id, xFontFile->fontFile,
1068 m11, m12, m21, m22,
1069 m11, m12, m21, m22, display, xOut);
1070 if (!font->isOk()) {
1071 delete font;
1072 return NULL;
1073 }
1074 return font;
1075 }
1076 }
1077
1078 // check for an embedded font
1079 if (gfxFont->getEmbeddedFontID(&embRef)) {
1080
1081 // create the font file
1082 fileName = NULL;
1083 if (!openTempFile(&fileName, &f, "wb", NULL)) {
1084 error(-1, "Couldn't create temporary Type 1 font file");
1085 return NULL;
1086 }
1087 if (gfxFont->getType() == fontType1C) {
1088 if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
1089 fclose(f);
1090 return NULL;
1091 }
1092 ff = new Type1CFontFile(fontBuf, fontLen);
1093 ff->convertToType1(outputToFile, f);
1094 delete ff;
1095 gfree(fontBuf);
1096 } else { // fontType1
1097 refObj.initRef(embRef.num, embRef.gen);
1098 refObj.fetch(xref, &strObj);
1099 refObj.free();
1100 strObj.streamReset();
1101 while ((c = strObj.streamGetChar()) != EOF) {
1102 fputc(c, f);
1103 }
1104 strObj.streamClose();
1105 strObj.free();
1106 }
1107 fclose(f);
1108
1109 // create the Font
1110 font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont,
1111 m11, m12, m21, m22,
1112 m11, m12, m21, m22, gFalse);
1113
1114 // on systems with Unix hard link semantics, this will remove the
1115 // last link to the temp file
1116 unlink(fileName->getCString());
1117
1118 delete fileName;
1119
1120 // check for an external font file
1121 } else if ((fileName = gfxFont->getExtFontFile())) {
1122 font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont,
1123 m11, m12, m21, m22,
1124 m11, m12, m21, m22, gFalse);
1125
1126 } else {
1127 font = NULL;
1128 }
1129
1130 return font;
1131}
1132
1133XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
1134 GString *fileName,
1135 GBool deleteFile,
1136 GfxFont *gfxFont,
1137 double m11Orig,
1138 double m12Orig,
1139 double m21Orig,
1140 double m22Orig,
1141 double m11, double m12,
1142 double m21, double m22,
1143 GBool subst) {
1144 Ref *id;
1145 T1FontFile *fontFile;
1146 XOutputFont *font;
1147
1148 // create the t1lib font file
1149 fontFile = new T1FontFile(t1Engine, fileName->getCString(),
1150 ((Gfx8BitFont *)gfxFont)->getEncoding(),
1151 gfxFont->getFontBBox());
1152 if (!fontFile->isOk()) {
1153 error(-1, "Couldn't create t1lib font from '%s'",
1154 fileName->getCString());
1155 delete fontFile;
1156 if (deleteFile) {
1157 unlink(fileName->getCString());
1158 }
1159 return NULL;
1160 }
1161
1162 // add to list
1163 id = gfxFont->getID();
1164 t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen,
1165 subst, fontFile,
1166 deleteFile ? fileName->copy()
1167 : (GString *)NULL));
1168
1169 // create the Font
1170 font = new XOutputT1Font(gfxFont->getID(), fontFile,
1171 m11Orig, m12Orig, m21Orig, m22Orig,
1172 m11, m12, m21, m22, display, xOut);
1173 if (!font->isOk()) {
1174 delete font;
1175 return NULL;
1176 }
1177 return font;
1178}
1179#endif // HAVE_T1LIB_H
1180
1181#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1182XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
1183 GfxFont *gfxFont,
1184 double m11, double m12,
1185 double m21, double m22) {
1186 Ref *id;
1187 XOutputFTFontFile *xFontFile;
1188 XOutputFont *font;
1189 Ref embRef;
1190 GString *fileName;
1191 FILE *f;
1192#if 1 //~ need this until FT can handle fonts with missing tables
1193 char *fontBuf;
1194 int fontLen;
1195 TrueTypeFontFile *ff;
1196#endif
1197 Object refObj, strObj;
1198 int c;
1199 int i;
1200
1201 // check the already available font files
1202 id = gfxFont->getID();
1203 for (i = 0; i < ftFontFiles->getLength(); ++i) {
1204 xFontFile = (XOutputFTFontFile *)ftFontFiles->get(i);
1205 if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1206 !xFontFile->subst) {
1207 font = new XOutputFTFont(id, xFontFile->fontFile,
1208 m11, m12, m21, m22,
1209 m11, m12, m21, m22, display, xOut);
1210 if (!font->isOk()) {
1211 delete font;
1212 return NULL;
1213 }
1214 return font;
1215 }
1216 }
1217
1218 // check for an embedded font
1219 if (gfxFont->getEmbeddedFontID(&embRef)) {
1220
1221 // create the font file
1222 fileName = NULL;
1223 if (!openTempFile(&fileName, &f, "wb", NULL)) {
1224 error(-1, "Couldn't create temporary TrueType font file");
1225 return NULL;
1226 }
1227#if 1 //~ need this until FT can handle fonts with missing tables
1228 if (gfxFont->getType() == fontTrueType ||
1229 gfxFont->getType() == fontCIDType2) {
1230 if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
1231 fclose(f);
1232 return NULL;
1233 }
1234 ff = new TrueTypeFontFile(fontBuf, fontLen);
1235 ff->writeTTF(f);
1236 delete ff;
1237 gfree(fontBuf);
1238 } else {
1239 refObj.initRef(embRef.num, embRef.gen);
1240 refObj.fetch(xref, &strObj);
1241 refObj.free();
1242 strObj.streamReset();
1243 while ((c = strObj.streamGetChar()) != EOF) {
1244 fputc(c, f);
1245 }
1246 strObj.streamClose();
1247 strObj.free();
1248 }
1249#else
1250 refObj.initRef(embRef.num, embRef.gen);
1251 refObj.fetch(xref, &strObj);
1252 refObj.free();
1253 strObj.streamReset();
1254 while ((c = strObj.streamGetChar()) != EOF) {
1255 fputc(c, f);
1256 }
1257 strObj.streamClose();
1258 strObj.free();
1259#endif
1260 fclose(f);
1261
1262 // create the Font
1263 font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont,
1264 m11, m12, m21, m22,
1265 m11, m12, m21, m22, gFalse);
1266
1267 // on systems with Unix hard link semantics, this will remove the
1268 // last link to the temp file
1269 unlink(fileName->getCString());
1270
1271 delete fileName;
1272
1273 // check for an external font file
1274 } else if ((fileName = gfxFont->getExtFontFile())) {
1275 font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont,
1276 m11, m12, m21, m22,
1277 m11, m12, m21, m22, gFalse);
1278
1279 } else {
1280 font = NULL;
1281 }
1282
1283 return font;
1284}
1285
1286XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
1287 GString *fileName,
1288 GBool deleteFile,
1289 GfxFont *gfxFont,
1290 double m11Orig,
1291 double m12Orig,
1292 double m21Orig,
1293 double m22Orig,
1294 double m11, double m12,
1295 double m21, double m22,
1296 GBool subst) {
1297 Ref *id;
1298 FTFontFile *fontFile;
1299 XOutputFont *font;
1300
1301 // create the FreeType font file
1302 if (gfxFont->isCIDFont()) {
1303 if (gfxFont->getType() == fontCIDType2) {
1304 fontFile = new FTFontFile(ftEngine, fileName->getCString(),
1305 ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1306 ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
1307 } else { // fontCIDType0C
1308 fontFile = new FTFontFile(ftEngine, fileName->getCString());
1309 }
1310 } else {
1311 fontFile = new FTFontFile(ftEngine, fileName->getCString(),
1312 ((Gfx8BitFont *)gfxFont)->getEncoding(),
1313 ((Gfx8BitFont *)gfxFont)->getHasEncoding());
1314 }
1315 if (!fontFile->isOk()) {
1316 error(-1, "Couldn't create FreeType font from '%s'",
1317 fileName->getCString());
1318 delete fontFile;
1319 if (deleteFile) {
1320 unlink(fileName->getCString());
1321 }
1322 return NULL;
1323 }
1324
1325 // add to list
1326 id = gfxFont->getID();
1327 ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen,
1328 subst, fontFile,
1329 deleteFile ? fileName->copy()
1330 : (GString *)NULL));
1331
1332 // create the Font
1333 font = new XOutputFTFont(gfxFont->getID(), fontFile,
1334 m11Orig, m12Orig, m21Orig, m22Orig,
1335 m11, m12, m21, m22, display, xOut);
1336 if (!font->isOk()) {
1337 delete font;
1338 return NULL;
1339 }
1340 return font;
1341}
1342#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1343
1344#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1345XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
1346 GfxFont *gfxFont,
1347 double m11, double m12,
1348 double m21, double m22) {
1349 Ref *id;
1350 XOutputTTFontFile *xFontFile;
1351 XOutputFont *font;
1352 Ref embRef;
1353 GString *fileName;
1354 FILE *f;
1355 Object refObj, strObj;
1356 int c;
1357 int i;
1358
1359 // check the already available font files
1360 id = gfxFont->getID();
1361 xFontFile = NULL;
1362 for (i = 0; i < ttFontFiles->getLength(); ++i) {
1363 xFontFile = (XOutputTTFontFile *)ttFontFiles->get(i);
1364 if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1365 !xFontFile->subst) {
1366 font = new XOutputTTFont(id, xFontFile->fontFile,
1367 m11, m12, m21, m22,
1368 m11, m12, m21, m22, display, xOut);
1369 if (!font->isOk()) {
1370 delete font;
1371 return NULL;
1372 }
1373 return font;
1374 }
1375 }
1376
1377 // check for an embedded font
1378 if (gfxFont->getEmbeddedFontID(&embRef)) {
1379
1380 // create the font file
1381 fileName = NULL;
1382 if (!openTempFile(&fileName, &f, "wb", NULL)) {
1383 error(-1, "Couldn't create temporary TrueType font file");
1384 return NULL;
1385 }
1386 refObj.initRef(embRef.num, embRef.gen);
1387 refObj.fetch(xref, &strObj);
1388 refObj.free();
1389 strObj.streamReset();
1390 while ((c = strObj.streamGetChar()) != EOF) {
1391 fputc(c, f);
1392 }
1393 strObj.streamClose();
1394 strObj.free();
1395 fclose(f);
1396
1397 // create the Font
1398 font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont,
1399 m11, m12, m21, m22,
1400 m11, m12, m21, m22, gFalse);
1401
1402 // on systems with Unix hard link semantics, this will remove the
1403 // last link to the temp file
1404 unlink(fileName->getCString());
1405
1406 delete fileName;
1407
1408 } else if ((fileName = gfxFont->getExtFontFile())) {
1409 font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont,
1410 m11, m12, m21, m22,
1411 m11, m12, m21, m22, gFalse);
1412
1413 } else {
1414 font = NULL;
1415 }
1416
1417 return font;
1418}
1419
1420XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
1421 GString *fileName,
1422 GBool deleteFile,
1423 GfxFont *gfxFont,
1424 double m11Orig,
1425 double m12Orig,
1426 double m21Orig,
1427 double m22Orig,
1428 double m11, double m12,
1429 double m21, double m22,
1430 GBool subst) {
1431 Ref *id;
1432 TTFontFile *fontFile;
1433 XOutputFont *font;
1434
1435 // create the FreeType font file
1436 if (gfxFont->isCIDFont()) {
1437 // fontCIDType2
1438 fontFile = new TTFontFile(ttEngine, fileName->getCString(),
1439 ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1440 ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
1441 } else {
1442 fontFile = new TTFontFile(ttEngine, fileName->getCString(),
1443 ((Gfx8BitFont *)gfxFont)->getEncoding(),
1444 ((Gfx8BitFont *)gfxFont)->getHasEncoding());
1445 }
1446 if (!fontFile->isOk()) {
1447 error(-1, "Couldn't create FreeType font from '%s'",
1448 fileName->getCString());
1449 delete fontFile;
1450 if (deleteFile) {
1451 unlink(fileName->getCString());
1452 }
1453 return NULL;
1454 }
1455
1456 // add to list
1457 id = gfxFont->getID();
1458 ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen,
1459 subst, fontFile,
1460 deleteFile ? fileName->copy()
1461 : (GString *)NULL));
1462
1463 // create the Font
1464 font = new XOutputTTFont(gfxFont->getID(), fontFile,
1465 m11Orig, m12Orig, m21Orig, m22Orig,
1466 m11, m12, m21, m22, display, xOut);
1467 if (!font->isOk()) {
1468 delete font;
1469 return NULL;
1470 }
1471 return font;
1472}
1473#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1474
1475XOutputFont *XOutputFontCache::tryGetServerFont(GString *xlfd,
1476 GString *encodingName,
1477 GfxFont *gfxFont,
1478 double m11Orig, double m12Orig,
1479 double m21Orig, double m22Orig,
1480 double m11, double m12,
1481 double m21, double m22) {
1482 XOutputFont *font;
1483 UnicodeMap *uMap;
1484 CharCodeToUnicode *ctu;
1485
1486 uMap = globalParams->getUnicodeMap(encodingName);
1487 if (gfxFont->isCIDFont()) {
1488 ctu = ((GfxCIDFont *)gfxFont)->getToUnicode();
1489 font = new XOutputServer16BitFont(gfxFont->getID(), xlfd, uMap, ctu,
1490 m11Orig, m12Orig, m21Orig, m22Orig,
1491 m11, m12, m21, m22,
1492 display, xOut);
1493 ctu->decRefCnt();
1494 } else {
1495 ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode();
1496 font = new XOutputServer8BitFont(gfxFont->getID(), xlfd, uMap, ctu,
1497 m11Orig, m12Orig, m21Orig, m22Orig,
1498 m11, m12, m21, m22,
1499 display, xOut);
1500 ctu->decRefCnt();
1501 }
1502 uMap->decRefCnt();
1503 if (!font->isOk()) {
1504 delete font;
1505 return NULL;
1506 }
1507 return font;
1508}
1509
1510//------------------------------------------------------------------------
1511// T3FontCache
1512//------------------------------------------------------------------------
1513
1514struct T3FontCacheTag {
1515 Gushort code;
1516 Gushort mru; // valid bit (0x8000) and MRU index
1517 double wx, wy; // untransformed glyph metrics
1518};
1519
1520class T3FontCache {
1521public:
1522
1523 T3FontCache(Ref *fontID, double m11A, double m12A,
1524 double m21A, double m22A,
1525 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1526 Display *displayA, Visual *visual, Guint depth,
1527 Pixmap origPixmap);
1528 ~T3FontCache();
1529 GBool matches(Ref *idA, double m11A, double m12A,
1530 double m21A, double m22A)
1531 { return fontID.num == idA->num && fontID.gen == idA->gen &&
1532 m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
1533
1534 Ref fontID; // PDF font ID
1535 double m11, m12, m21, m22; // transform matrix
1536 int glyphX, glyphY; // pixel offset of glyph pixmaps
1537 int glyphW, glyphH; // size of glyph pixmaps, in pixels
1538 int glyphSize; // size of glyph pixmaps, in bytes
1539 int cacheSets; // number of sets in cache
1540 int cacheAssoc; // cache associativity (glyphs per set)
1541 Guchar *cacheData; // glyph pixmap cache
1542 T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
1543 Display *display;
1544 Pixmap pixmap;
1545 XImage *image;
1546};
1547
1548T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
1549 double m21A, double m22A,
1550 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1551 Display *displayA, Visual *visual, Guint depth,
1552 Pixmap origPixmap) {
1553 int i;
1554
1555 fontID = *fontIDA;
1556 m11 = m11A;
1557 m12 = m12A;
1558 m21 = m21A;
1559 m22 = m22A;
1560 glyphX = glyphXA;
1561 glyphY = glyphYA;
1562 glyphW = glyphWA;
1563 glyphH = glyphHA;
1564 glyphSize = glyphW * glyphH;
1565 cacheAssoc = 8;
1566 if (glyphSize <= 256) {
1567 cacheSets = 8;
1568 } else if (glyphSize <= 512) {
1569 cacheSets = 4;
1570 } else if (glyphSize <= 1024) {
1571 cacheSets = 2;
1572 } else {
1573 cacheSets = 1;
1574 }
1575 cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
1576 cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
1577 sizeof(T3FontCacheTag));
1578 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
1579 cacheTags[i].mru = i & (cacheAssoc - 1);
1580 }
1581 display = displayA;
1582 pixmap = XCreatePixmap(display, origPixmap, glyphW, glyphH, depth);
1583 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL,
1584 glyphW, glyphH, 8, 0);
1585 image->data = (char *)gmalloc(glyphH * image->bytes_per_line);
1586}
1587
1588T3FontCache::~T3FontCache() {
1589 gfree(cacheData);
1590 gfree(cacheTags);
1591 XFreePixmap(display, pixmap);
1592 gfree(image->data);
1593 image->data = NULL;
1594 XDestroyImage(image);
1595}
1596
1597struct T3GlyphStack {
1598 GBool cacheable;
1599 Gushort code;
1600 T3FontCache *cache;
1601 int cacheIdx;
1602 T3FontCacheTag *cacheTag;
1603 Guchar *cacheData;
1604 double x, y;
1605 Unicode *u;
1606 int uLen;
1607 GfxRGB color;
1608 int origPixmapW, origPixmapH;
1609 Pixmap origPixmap;
1610 GC origStrokeGC;
1611 GC origFillGC;
1612 Region origClipRegion;
1613 double origCTM4, origCTM5;
1614 double wx, wy; // untransformed glyph metrics
1615 T3GlyphStack *next;
1616};
1617
1618//------------------------------------------------------------------------
1619// XOutputDev
1620//------------------------------------------------------------------------
1621
1622XOutputDev::XOutputDev(Display *displayA, int screenNumA,
1623 Visual *visualA, Colormap colormapA,
1624 GBool reverseVideoA, unsigned long paperColorA,
1625 GBool installCmap, int rgbCubeSize,
1626 int forceDepth) {
1627 XVisualInfo visualTempl;
1628 XVisualInfo *visualList;
1629 int nVisuals;
1630 Gulong mask;
1631 XColor xcolor;
1632 XColor *xcolors;
1633 int r, g, b, n, m;
1634 GBool ok;
1635
1636 // no document yet
1637 xref = NULL;
1638
1639 // display / screen / visual / colormap
1640 display = displayA;
1641 screenNum = screenNumA;
1642 visual = visualA;
1643 colormap = colormapA;
1644
1645 // no pixmap yet
1646 pixmapW = pixmapH = 0;
1647
1648 // check for TrueColor visual
1649 if (forceDepth != 0) {
1650 depth = forceDepth;
1651 trueColor = depth >= 16;
1652 } else {
1653 visualTempl.visualid = XVisualIDFromVisual(visual);
1654 visualList = XGetVisualInfo(display, VisualIDMask,
1655 &visualTempl, &nVisuals);
1656 if (nVisuals < 1) {
1657 // this shouldn't happen
1658 XFree((XPointer)visualList);
1659 visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
1660 &nVisuals);
1661 }
1662 depth = visualList->depth;
1663 if (visualList->c_class == TrueColor) {
1664 trueColor = gTrue;
1665 for (mask = visualList->red_mask, rShift = 0;
1666 mask && !(mask & 1);
1667 mask >>= 1, ++rShift) ;
1668 rMul = (int)mask;
1669 for (mask = visualList->green_mask, gShift = 0;
1670 mask && !(mask & 1);
1671 mask >>= 1, ++gShift) ;
1672 gMul = (int)mask;
1673 for (mask = visualList->blue_mask, bShift = 0;
1674 mask && !(mask & 1);
1675 mask >>= 1, ++bShift) ;
1676 bMul = (int)mask;
1677 } else {
1678 trueColor = gFalse;
1679 }
1680 XFree((XPointer)visualList);
1681 }
1682
1683 // allocate a color cube
1684 if (!trueColor) {
1685 redMap[BlackPixel(display, screenNum) & 0xff] = 0;
1686 redMap[WhitePixel(display, screenNum) & 0xff] = 1;
1687
1688 // set colors in private colormap
1689 if (installCmap) {
1690 for (numColors = 6; numColors >= 2; --numColors) {
1691 m = numColors * numColors * numColors;
1692 if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
1693 break;
1694 }
1695 }
1696 if (numColors >= 2) {
1697 m = numColors * numColors * numColors;
1698 xcolors = (XColor *)gmalloc(m * sizeof(XColor));
1699 n = 0;
1700 for (r = 0; r < numColors; ++r) {
1701 for (g = 0; g < numColors; ++g) {
1702 for (b = 0; b < numColors; ++b) {
1703 xcolors[n].pixel = colors[n];
1704 xcolors[n].red = (r * 65535) / (numColors - 1);
1705 xcolors[n].green = (g * 65535) / (numColors - 1);
1706 xcolors[n].blue = (b * 65535) / (numColors - 1);
1707 xcolors[n].flags = DoRed | DoGreen | DoBlue;
1708 redMap[xcolors[n].pixel & 0xff] = xcolors[n].red / 65535.0;
1709 ++n;
1710 }
1711 }
1712 }
1713 XStoreColors(display, colormap, xcolors, m);
1714 gfree(xcolors);
1715 } else {
1716 numColors = 1;
1717 colors[0] = BlackPixel(display, screenNum);
1718 colors[1] = WhitePixel(display, screenNum);
1719 }
1720
1721 // allocate colors in shared colormap
1722 } else {
1723 if (rgbCubeSize > maxRGBCube) {
1724 rgbCubeSize = maxRGBCube;
1725 }
1726 ok = gFalse;
1727 for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
1728 ok = gTrue;
1729 n = 0;
1730 for (r = 0; r < numColors && ok; ++r) {
1731 for (g = 0; g < numColors && ok; ++g) {
1732 for (b = 0; b < numColors && ok; ++b) {
1733 if (n == 0) {
1734 colors[n] = BlackPixel(display, screenNum);
1735 redMap[colors[n] & 0xff] = 0;
1736 ++n;
1737 } else {
1738 xcolor.red = (r * 65535) / (numColors - 1);
1739 xcolor.green = (g * 65535) / (numColors - 1);
1740 xcolor.blue = (b * 65535) / (numColors - 1);
1741 if (XAllocColor(display, colormap, &xcolor)) {
1742 colors[n++] = xcolor.pixel;
1743 redMap[xcolor.pixel & 0xff] = xcolor.red / 65535.0;
1744 } else {
1745 ok = gFalse;
1746 }
1747 }
1748 }
1749 }
1750 }
1751 if (ok) {
1752 break;
1753 }
1754 XFreeColors(display, colormap, &colors[1], n-1, 0);
1755 }
1756 if (!ok) {
1757 numColors = 1;
1758 colors[0] = BlackPixel(display, screenNum);
1759 colors[1] = WhitePixel(display, screenNum);
1760 }
1761 }
1762 }
1763
1764 // misc parameters
1765 reverseVideo = reverseVideoA;
1766 paperColor = paperColorA;
1767
1768 // set up the font cache and fonts
1769 gfxFont = NULL;
1770 font = NULL;
1771 fontCache = new XOutputFontCache(display, depth, this,
1772 globalParams->getT1libControl(),
1773 globalParams->getFreeTypeControl());
1774 nT3Fonts = 0;
1775 t3GlyphStack = NULL;
1776
1777 // empty state stack
1778 save = NULL;
1779
1780 // create text object
1781 text = new TextPage(gFalse);
1782}
1783
1784XOutputDev::~XOutputDev() {
1785 int i;
1786
1787 delete fontCache;
1788 for (i = 0; i < nT3Fonts; ++i) {
1789 delete t3FontCache[i];
1790 }
1791 delete text;
1792}
1793
1794void XOutputDev::startDoc(XRef *xrefA) {
1795 int i;
1796
1797 xref = xrefA;
1798 fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul,
1799 rShift, gShift, bShift, colors, numColors);
1800 for (i = 0; i < nT3Fonts; ++i) {
1801 delete t3FontCache[i];
1802 }
1803 nT3Fonts = 0;
1804}
1805
1806void XOutputDev::startPage(int pageNum, GfxState *state) {
1807 XGCValues gcValues;
1808 XRectangle rect;
1809
1810 // default line flatness
1811 flatness = 0;
1812
1813 // allocate GCs
1814 gcValues.foreground = BlackPixel(display, screenNum);
1815 gcValues.background = WhitePixel(display, screenNum);
1816 gcValues.line_width = 0;
1817 gcValues.line_style = LineSolid;
1818 strokeGC = XCreateGC(display, pixmap,
1819 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1820 &gcValues);
1821 fillGC = XCreateGC(display, pixmap,
1822 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1823 &gcValues);
1824 gcValues.foreground = paperColor;
1825 paperGC = XCreateGC(display, pixmap,
1826 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1827 &gcValues);
1828
1829 // initialize clip region
1830 clipRegion = XCreateRegion();
1831 rect.x = rect.y = 0;
1832 rect.width = pixmapW;
1833 rect.height = pixmapH;
1834 XUnionRectWithRegion(&rect, clipRegion, clipRegion);
1835 XSetRegion(display, strokeGC, clipRegion);
1836 XSetRegion(display, fillGC, clipRegion);
1837
1838 // clear font
1839 gfxFont = NULL;
1840 font = NULL;
1841
1842 // clear window
1843 XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
1844
1845 // clear text object
1846 text->clear();
1847}
1848
1849void XOutputDev::endPage() {
1850 XOutputState *s;
1851
1852 text->coalesce();
1853
1854 // clear state stack, free all GCs, free the clip region
1855 while (save) {
1856 s = save;
1857 save = save->next;
1858 XFreeGC(display, s->strokeGC);
1859 XFreeGC(display, s->fillGC);
1860 XDestroyRegion(s->clipRegion);
1861 delete s;
1862 }
1863 XFreeGC(display, strokeGC);
1864 XFreeGC(display, fillGC);
1865 XFreeGC(display, paperGC);
1866 XDestroyRegion(clipRegion);
1867}
1868
1869void XOutputDev::drawLink(Link *link, Catalog *catalog) {
1870 double x1, y1, x2, y2, w;
1871 GfxRGB rgb;
1872 XPoint points[5];
1873 int x, y;
1874
1875 link->getBorder(&x1, &y1, &x2, &y2, &w);
1876 if (w > 0) {
1877 rgb.r = 0;
1878 rgb.g = 0;
1879 rgb.b = 1;
1880 XSetForeground(display, strokeGC, findColor(&rgb));
1881 XSetLineAttributes(display, strokeGC, xoutRound(w),
1882 LineSolid, CapRound, JoinRound);
1883 cvtUserToDev(x1, y1, &x, &y);
1884 points[0].x = points[4].x = x;
1885 points[0].y = points[4].y = y;
1886 cvtUserToDev(x2, y1, &x, &y);
1887 points[1].x = x;
1888 points[1].y = y;
1889 cvtUserToDev(x2, y2, &x, &y);
1890 points[2].x = x;
1891 points[2].y = y;
1892 cvtUserToDev(x1, y2, &x, &y);
1893 points[3].x = x;
1894 points[3].y = y;
1895 XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
1896 }
1897}
1898
1899void XOutputDev::saveState(GfxState *state) {
1900 XOutputState *s;
1901 XGCValues values;
1902
1903 // save current state
1904 s = new XOutputState;
1905 s->strokeGC = strokeGC;
1906 s->fillGC = fillGC;
1907 s->clipRegion = clipRegion;
1908
1909 // push onto state stack
1910 s->next = save;
1911 save = s;
1912
1913 // create a new current state by copying
1914 strokeGC = XCreateGC(display, pixmap, 0, &values);
1915 XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
1916 fillGC = XCreateGC(display, pixmap, 0, &values);
1917 XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
1918 clipRegion = XCreateRegion();
1919 XUnionRegion(s->clipRegion, clipRegion, clipRegion);
1920 XSetRegion(display, strokeGC, clipRegion);
1921 XSetRegion(display, fillGC, clipRegion);
1922}
1923
1924void XOutputDev::restoreState(GfxState *state) {
1925 XOutputState *s;
1926
1927 if (save) {
1928 // kill current state
1929 XFreeGC(display, strokeGC);
1930 XFreeGC(display, fillGC);
1931 XDestroyRegion(clipRegion);
1932
1933 // restore state
1934 flatness = state->getFlatness();
1935 strokeGC = save->strokeGC;
1936 fillGC = save->fillGC;
1937 clipRegion = save->clipRegion;
1938 XSetRegion(display, strokeGC, clipRegion);
1939 XSetRegion(display, fillGC, clipRegion);
1940
1941 // pop state stack
1942 s = save;
1943 save = save->next;
1944 delete s;
1945 }
1946}
1947
1948void XOutputDev::updateAll(GfxState *state) {
1949 updateLineAttrs(state, gTrue);
1950 updateFlatness(state);
1951 updateMiterLimit(state);
1952 updateFillColor(state);
1953 updateStrokeColor(state);
1954 updateFont(state);
1955}
1956
1957void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
1958 double m21, double m22, double m31, double m32) {
1959 updateLineAttrs(state, gTrue);
1960}
1961
1962void XOutputDev::updateLineDash(GfxState *state) {
1963 updateLineAttrs(state, gTrue);
1964}
1965
1966void XOutputDev::updateFlatness(GfxState *state) {
1967 flatness = state->getFlatness();
1968}
1969
1970void XOutputDev::updateLineJoin(GfxState *state) {
1971 updateLineAttrs(state, gFalse);
1972}
1973
1974void XOutputDev::updateLineCap(GfxState *state) {
1975 updateLineAttrs(state, gFalse);
1976}
1977
1978// unimplemented
1979void XOutputDev::updateMiterLimit(GfxState *state) {
1980}
1981
1982void XOutputDev::updateLineWidth(GfxState *state) {
1983 updateLineAttrs(state, gFalse);
1984}
1985
1986void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
1987 double width;
1988 int cap, join;
1989 double *dashPattern;
1990 int dashLength;
1991 double dashStart;
1992 char dashList[20];
1993 int i;
1994
1995 width = state->getTransformedLineWidth();
1996 switch (state->getLineCap()) {
1997 case 0: cap = CapButt; break;
1998 case 1: cap = CapRound; break;
1999 case 2: cap = CapProjecting; break;
2000 default:
2001 error(-1, "Bad line cap style (%d)", state->getLineCap());
2002 cap = CapButt;
2003 break;
2004 }
2005 switch (state->getLineJoin()) {
2006 case 0: join = JoinMiter; break;
2007 case 1: join = JoinRound; break;
2008 case 2: join = JoinBevel; break;
2009 default:
2010 error(-1, "Bad line join style (%d)", state->getLineJoin());
2011 join = JoinMiter;
2012 break;
2013 }
2014 state->getLineDash(&dashPattern, &dashLength, &dashStart);
2015#if 1 //~ work around a bug in XFree86 (???)
2016 if (dashLength > 0 && cap == CapProjecting) {
2017 cap = CapButt;
2018 }
2019#endif
2020 XSetLineAttributes(display, strokeGC, xoutRound(width),
2021 dashLength > 0 ? LineOnOffDash : LineSolid,
2022 cap, join);
2023 if (updateDash && dashLength > 0) {
2024 if (dashLength > 20)
2025 dashLength = 20;
2026 for (i = 0; i < dashLength; ++i) {
2027 dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
2028 if (dashList[i] == 0)
2029 dashList[i] = 1;
2030 }
2031 XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
2032 }
2033}
2034
2035void XOutputDev::updateFillColor(GfxState *state) {
2036 GfxRGB rgb;
2037
2038 state->getFillRGB(&rgb);
2039 if (reverseVideo) {
2040 rgb.r = 1 - rgb.r;
2041 rgb.g = 1 - rgb.g;
2042 rgb.b = 1 - rgb.b;
2043 }
2044 XSetForeground(display, fillGC, findColor(&rgb));
2045}
2046
2047void XOutputDev::updateStrokeColor(GfxState *state) {
2048 GfxRGB rgb;
2049
2050 state->getStrokeRGB(&rgb);
2051 if (reverseVideo) {
2052 rgb.r = 1 - rgb.r;
2053 rgb.g = 1 - rgb.g;
2054 rgb.b = 1 - rgb.b;
2055 }
2056 XSetForeground(display, strokeGC, findColor(&rgb));
2057}
2058
2059void XOutputDev::updateFont(GfxState *state) {
2060 double m11, m12, m21, m22;
2061
2062 text->updateFont(state);
2063
2064 if (!(gfxFont = state->getFont())) {
2065 font = NULL;
2066 return;
2067 }
2068 if (gfxFont->getType() == fontType3) {
2069 font = NULL;
2070 return;
2071 }
2072 state->getFontTransMat(&m11, &m12, &m21, &m22);
2073 m11 *= state->getHorizScaling();
2074 m12 *= state->getHorizScaling();
2075 font = fontCache->getFont(xref, gfxFont, m11, m12, m21, m22);
2076 if (font) {
2077 font->updateGC(fillGC);
2078 font->updateGC(strokeGC);
2079 }
2080}
2081
2082void XOutputDev::stroke(GfxState *state) {
2083 XPoint *points;
2084 int *lengths;
2085 int n, size, numPoints, i, j;
2086
2087 // transform points
2088 n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
2089
2090 // draw each subpath
2091 j = 0;
2092 for (i = 0; i < n; ++i) {
2093 XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
2094 CoordModeOrigin);
2095 j += lengths[i];
2096 }
2097
2098 // free points and lengths arrays
2099 if (points != tmpPoints)
2100 gfree(points);
2101 if (lengths != tmpLengths)
2102 gfree(lengths);
2103}
2104
2105void XOutputDev::fill(GfxState *state) {
2106 doFill(state, WindingRule);
2107}
2108
2109void XOutputDev::eoFill(GfxState *state) {
2110 doFill(state, EvenOddRule);
2111}
2112
2113//
2114// X doesn't color the pixels on the right-most and bottom-most
2115// borders of a polygon. This means that one-pixel-thick polygons
2116// are not colored at all. I think this is supposed to be a
2117// feature, but I can't figure out why. So after it fills a
2118// polygon, it also draws lines around the border. This is done
2119// only for single-component polygons, since it's not very
2120// compatible with the compound polygon kludge (see convertPath()).
2121//
2122void XOutputDev::doFill(GfxState *state, int rule) {
2123 XPoint *points;
2124 int *lengths;
2125 int n, size, numPoints, i, j;
2126
2127 // set fill rule
2128 XSetFillRule(display, fillGC, rule);
2129
2130 // transform points, build separate polygons
2131 n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
2132
2133 // fill them
2134 j = 0;
2135 for (i = 0; i < n; ++i) {
2136 XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
2137 Complex, CoordModeOrigin);
2138 if (state->getPath()->getNumSubpaths() == 1) {
2139 XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
2140 CoordModeOrigin);
2141 }
2142 j += lengths[i] + 1;
2143 }
2144
2145 // free points and lengths arrays
2146 if (points != tmpPoints)
2147 gfree(points);
2148 if (lengths != tmpLengths)
2149 gfree(lengths);
2150}
2151
2152void XOutputDev::clip(GfxState *state) {
2153 doClip(state, WindingRule);
2154}
2155
2156void XOutputDev::eoClip(GfxState *state) {
2157 doClip(state, EvenOddRule);
2158}
2159
2160void XOutputDev::doClip(GfxState *state, int rule) {
2161 Region region, region2;
2162 XPoint *points;
2163 int *lengths;
2164 int n, size, numPoints, i, j;
2165
2166 // transform points, build separate polygons
2167 n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
2168
2169 // construct union of subpath regions
2170 // (XPolygonRegion chokes if there aren't at least three points --
2171 // this happens if the PDF file does moveto/closepath/clip, which
2172 // sets an empty clipping region)
2173 if (lengths[0] > 2) {
2174 region = XPolygonRegion(points, lengths[0], rule);
2175 } else {
2176 region = XCreateRegion();
2177 }
2178 j = lengths[0] + 1;
2179 for (i = 1; i < n; ++i) {
2180 if (lengths[i] > 2) {
2181 region2 = XPolygonRegion(points + j, lengths[i], rule);
2182 } else {
2183 region2 = XCreateRegion();
2184 }
2185 XUnionRegion(region2, region, region);
2186 XDestroyRegion(region2);
2187 j += lengths[i] + 1;
2188 }
2189
2190 // intersect region with clipping region
2191 XIntersectRegion(region, clipRegion, clipRegion);
2192 XDestroyRegion(region);
2193 XSetRegion(display, strokeGC, clipRegion);
2194 XSetRegion(display, fillGC, clipRegion);
2195
2196 // free points and lengths arrays
2197 if (points != tmpPoints)
2198 gfree(points);
2199 if (lengths != tmpLengths)
2200 gfree(lengths);
2201}
2202
2203//
2204// Transform points in the path and convert curves to line segments.
2205// Builds a set of subpaths and returns the number of subpaths.
2206// If <fillHack> is set, close any unclosed subpaths and activate a
2207// kludge for polygon fills: First, it divides up the subpaths into
2208// non-overlapping polygons by simply comparing bounding rectangles.
2209// Then it connects subaths within a single compound polygon to a single
2210// point so that X can fill the polygon (sort of).
2211//
2212int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
2213 int *numPoints, int **lengths, GBool fillHack) {
2214 GfxPath *path;
2215 BoundingRect *rects;
2216 BoundingRect rect;
2217 int n, i, ii, j, k, k0;
2218
2219 // get path and number of subpaths
2220 path = state->getPath();
2221 n = path->getNumSubpaths();
2222
2223 // allocate lengths array
2224 if (n < numTmpSubpaths)
2225 *lengths = tmpLengths;
2226 else
2227 *lengths = (int *)gmalloc(n * sizeof(int));
2228
2229 // allocate bounding rectangles array
2230 if (fillHack) {
2231 if (n < numTmpSubpaths)
2232 rects = tmpRects;
2233 else
2234 rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
2235 } else {
2236 rects = NULL;
2237 }
2238
2239 // do each subpath
2240 *points = tmpPoints;
2241 *size = numTmpPoints;
2242 *numPoints = 0;
2243 for (i = 0; i < n; ++i) {
2244
2245 // transform the points
2246 j = *numPoints;
2247 convertSubpath(state, path->getSubpath(i), points, size, numPoints);
2248
2249 // construct bounding rectangle
2250 if (fillHack) {
2251 rects[i].xMin = rects[i].xMax = (*points)[j].x;
2252 rects[i].yMin = rects[i].yMax = (*points)[j].y;
2253 for (k = j + 1; k < *numPoints; ++k) {
2254 if ((*points)[k].x < rects[i].xMin)
2255 rects[i].xMin = (*points)[k].x;
2256 else if ((*points)[k].x > rects[i].xMax)
2257 rects[i].xMax = (*points)[k].x;
2258 if ((*points)[k].y < rects[i].yMin)
2259 rects[i].yMin = (*points)[k].y;
2260 else if ((*points)[k].y > rects[i].yMax)
2261 rects[i].yMax = (*points)[k].y;
2262 }
2263 }
2264
2265 // close subpath if necessary
2266 if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
2267 (*points)[*numPoints-1].y != (*points)[j].y)) {
2268 addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
2269 }
2270
2271 // length of this subpath
2272 (*lengths)[i] = *numPoints - j;
2273
2274 // leave an extra point for compound fill hack
2275 if (fillHack)
2276 addPoint(points, size, numPoints, 0, 0);
2277 }
2278
2279 // kludge: munge any points that are *way* out of bounds - these can
2280 // crash certain (buggy) X servers
2281 for (i = 0; i < *numPoints; ++i) {
2282 if ((*points)[i].x < -pixmapW) {
2283 (*points)[i].x = -pixmapW;
2284 } else if ((*points)[i].x > 2 * pixmapW) {
2285 (*points)[i].x = 2 * pixmapW;
2286 }
2287 if ((*points)[i].y < -pixmapH) {
2288 (*points)[i].y = -pixmapH;
2289 } else if ((*points)[i].y > 2 * pixmapH) {
2290 (*points)[i].y = 2 * pixmapH;
2291 }
2292 }
2293
2294 // combine compound polygons
2295 if (fillHack) {
2296 i = j = k = 0;
2297 while (i < n) {
2298
2299 // start with subpath i
2300 rect = rects[i];
2301 (*lengths)[j] = (*lengths)[i];
2302 k0 = k;
2303 (*points)[k + (*lengths)[i]] = (*points)[k0];
2304 k += (*lengths)[i] + 1;
2305 ++i;
2306
2307 // combine overlapping polygons
2308 do {
2309
2310 // look for the first subsequent subpath, if any, which overlaps
2311 for (ii = i; ii < n; ++ii) {
2312 if (rects[ii].xMax > rects[i].xMin &&
2313 rects[ii].xMin < rects[i].xMax &&
2314 rects[ii].yMax > rects[i].yMin &&
2315 rects[ii].yMin < rects[i].yMax) {
2316 break;
2317 }
2318 }
2319
2320 // if there is an overlap, combine the polygons
2321 if (ii < n) {
2322 for (; i <= ii; ++i) {
2323 if (rects[i].xMin < rect.xMin)
2324 rect.xMin = rects[j].xMin;
2325 if (rects[i].xMax > rect.xMax)
2326 rect.xMax = rects[j].xMax;
2327 if (rects[i].yMin < rect.yMin)
2328 rect.yMin = rects[j].yMin;
2329 if (rects[i].yMax > rect.yMax)
2330 rect.yMax = rects[j].yMax;
2331 (*lengths)[j] += (*lengths)[i] + 1;
2332 (*points)[k + (*lengths)[i]] = (*points)[k0];
2333 k += (*lengths)[i] + 1;
2334 }
2335 }
2336 } while (ii < n && i < n);
2337
2338 ++j;
2339 }
2340
2341 // free bounding rectangles
2342 if (rects != tmpRects)
2343 gfree(rects);
2344
2345 n = j;
2346 }
2347
2348 return n;
2349}
2350
2351//
2352// Transform points in a single subpath and convert curves to line
2353// segments.
2354//
2355void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
2356 XPoint **points, int *size, int *n) {
2357 double x0, y0, x1, y1, x2, y2, x3, y3;
2358 int m, i;
2359
2360 m = subpath->getNumPoints();
2361 i = 0;
2362 while (i < m) {
2363 if (i >= 1 && subpath->getCurve(i)) {
2364 state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
2365 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
2366 state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
2367 state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
2368 doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
2369 i += 3;
2370 } else {
2371 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
2372 addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
2373 ++i;
2374 }
2375 }
2376}
2377
2378//
2379// Subdivide a Bezier curve. This uses floating point to avoid
2380// propagating rounding errors. (The curves look noticeably more
2381// jagged with integer arithmetic.)
2382//
2383void XOutputDev::doCurve(XPoint **points, int *size, int *n,
2384 double x0, double y0, double x1, double y1,
2385 double x2, double y2, double x3, double y3) {
2386 double x[(1<<maxCurveSplits)+1][3];
2387 double y[(1<<maxCurveSplits)+1][3];
2388 int next[1<<maxCurveSplits];
2389 int p1, p2, p3;
2390 double xx1, yy1, xx2, yy2;
2391 double dx, dy, mx, my, d1, d2;
2392 double xl0, yl0, xl1, yl1, xl2, yl2;
2393 double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
2394 double xh, yh;
2395 double flat;
2396
2397 flat = (double)(flatness * flatness);
2398 if (flat < 1)
2399 flat = 1;
2400
2401 // initial segment
2402 p1 = 0;
2403 p2 = 1<<maxCurveSplits;
2404 x[p1][0] = x0; y[p1][0] = y0;
2405 x[p1][1] = x1; y[p1][1] = y1;
2406 x[p1][2] = x2; y[p1][2] = y2;
2407 x[p2][0] = x3; y[p2][0] = y3;
2408 next[p1] = p2;
2409
2410 while (p1 < (1<<maxCurveSplits)) {
2411
2412 // get next segment
2413 xl0 = x[p1][0]; yl0 = y[p1][0];
2414 xx1 = x[p1][1]; yy1 = y[p1][1];
2415 xx2 = x[p1][2]; yy2 = y[p1][2];
2416 p2 = next[p1];
2417 xr3 = x[p2][0]; yr3 = y[p2][0];
2418
2419 // compute distances from control points to midpoint of the
2420 // straight line (this is a bit of a hack, but it's much faster
2421 // than computing the actual distances to the line)
2422 mx = (xl0 + xr3) * 0.5;
2423 my = (yl0 + yr3) * 0.5;
2424 dx = xx1 - mx;
2425 dy = yy1 - my;
2426 d1 = dx*dx + dy*dy;
2427 dx = xx2 - mx;
2428 dy = yy2 - my;
2429 d2 = dx*dx + dy*dy;
2430
2431 // if curve is flat enough, or no more divisions allowed then
2432 // add the straight line segment
2433 if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
2434 addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
2435 p1 = p2;
2436
2437 // otherwise, subdivide the curve
2438 } else {
2439 xl1 = (xl0 + xx1) * 0.5;
2440 yl1 = (yl0 + yy1) * 0.5;
2441 xh = (xx1 + xx2) * 0.5;
2442 yh = (yy1 + yy2) * 0.5;
2443 xl2 = (xl1 + xh) * 0.5;
2444 yl2 = (yl1 + yh) * 0.5;
2445 xr2 = (xx2 + xr3) * 0.5;
2446 yr2 = (yy2 + yr3) * 0.5;
2447 xr1 = (xh + xr2) * 0.5;
2448 yr1 = (yh + yr2) * 0.5;
2449 xr0 = (xl2 + xr1) * 0.5;
2450 yr0 = (yl2 + yr1) * 0.5;
2451
2452 // add the new subdivision points
2453 p3 = (p1 + p2) / 2;
2454 x[p1][1] = xl1; y[p1][1] = yl1;
2455 x[p1][2] = xl2; y[p1][2] = yl2;
2456 next[p1] = p3;
2457 x[p3][0] = xr0; y[p3][0] = yr0;
2458 x[p3][1] = xr1; y[p3][1] = yr1;
2459 x[p3][2] = xr2; y[p3][2] = yr2;
2460 next[p3] = p2;
2461 }
2462 }
2463}
2464
2465//
2466// Add a point to the points array. (This would use a generic resizable
2467// array type if C++ supported parameterized types in some reasonable
2468// way -- templates are a disgusting kludge.)
2469//
2470void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
2471 if (*k >= *size) {
2472 *size += 32;
2473 if (*points == tmpPoints) {
2474 *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
2475 memcpy(*points, tmpPoints, *k * sizeof(XPoint));
2476 } else {
2477 *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
2478 }
2479 }
2480 (*points)[*k].x = x;
2481 (*points)[*k].y = y;
2482 ++(*k);
2483}
2484
2485void XOutputDev::beginString(GfxState *state, GString *s) {
2486 text->beginString(state, state->getCurX(), state->getCurY());
2487}
2488
2489void XOutputDev::endString(GfxState *state) {
2490 text->endString();
2491}
2492
2493void XOutputDev::drawChar(GfxState *state, double x, double y,
2494 double dx, double dy,
2495 double originX, double originY,
2496 CharCode code, Unicode *u, int uLen) {
2497 int render;
2498 double x1, y1, dx1, dy1;
2499 GfxRGB rgb;
2500 double saveCurX, saveCurY;
2501 double *ctm;
2502 double saveCTM[6];
2503
2504 text->addChar(state, x, y, dx, dy, u, uLen);
2505
2506 if (!font) {
2507 return;
2508 }
2509
2510 // check for invisible text -- this is used by Acrobat Capture
2511 render = state->getRender();
2512 if ((render & 3) == 3) {
2513 return;
2514 }
2515
2516 x -= originX;
2517 y -= originY;
2518 state->transform(x, y, &x1, &y1);
2519 state->transformDelta(dx, dy, &dx1, &dy1);
2520
2521 // fill
2522 if (!(render & 1)) {
2523 state->getFillRGB(&rgb);
2524 if (reverseVideo) {
2525 rgb.r = 1 - rgb.r;
2526 rgb.g = 1 - rgb.g;
2527 rgb.b = 1 - rgb.b;
2528 }
2529 font->drawChar(state, pixmap, pixmapW, pixmapH, fillGC, &rgb,
2530 x1, y1, dx1, dy1, code, u, uLen);
2531 }
2532
2533 // stroke
2534 if ((render & 3) == 1 || (render & 3) == 2) {
2535 if (font->hasGetCharPath()) {
2536 saveCurX = state->getCurX();
2537 saveCurY = state->getCurY();
2538 ctm = state->getCTM();
2539 memcpy(saveCTM, ctm, 6 * sizeof(double));
2540 state->setCTM(1, 0, 0, 1, x1, y1);
2541 font->getCharPath(state, code, u, uLen);
2542 stroke(state);
2543 state->clearPath();
2544 state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
2545 saveCTM[4], saveCTM[5]);
2546 state->moveTo(saveCurX, saveCurY);
2547 } else {
2548 // can't stroke the outline, so just fill it using the stroke
2549 // color
2550 state->getStrokeRGB(&rgb);
2551 if (reverseVideo) {
2552 rgb.r = 1 - rgb.r;
2553 rgb.g = 1 - rgb.g;
2554 rgb.b = 1 - rgb.b;
2555 }
2556 font->drawChar(state, pixmap, pixmapW, pixmapH, strokeGC, &rgb,
2557 x1, y1, dx1, dy1, code, u, uLen);
2558 }
2559 }
2560
2561#if 0 //~ unimplemented: clipping to char path
2562 // clip
2563 if (render & 4) {
2564 }
2565#endif
2566}
2567
2568GBool XOutputDev::beginType3Char(GfxState *state,
2569 CharCode code, Unicode *u, int uLen) {
2570 Ref *fontID;
2571 double *ctm, *bbox;
2572 GfxRGB color;
2573 T3FontCache *t3Font;
2574 T3GlyphStack *t3gs;
2575 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2576 int i, j;
2577
2578 if (!gfxFont) {
2579 return gFalse;
2580 }
2581 fontID = gfxFont->getID();
2582 ctm = state->getCTM();
2583 state->transform(0, 0, &xt, &yt);
2584
2585 // is it the first (MRU) font in the cache?
2586 if (!(nT3Fonts > 0 &&
2587 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2588
2589 // is the font elsewhere in the cache?
2590 for (i = 1; i < nT3Fonts; ++i) {
2591 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2592 t3Font = t3FontCache[i];
2593 for (j = i; j > 0; --j) {
2594 t3FontCache[j] = t3FontCache[j - 1];
2595 }
2596 t3FontCache[0] = t3Font;
2597 break;
2598 }
2599 }
2600 if (i >= nT3Fonts) {
2601
2602 // create new entry in the font cache
2603 if (nT3Fonts == xOutT3FontCacheSize) {
2604 delete t3FontCache[nT3Fonts - 1];
2605 --nT3Fonts;
2606 }
2607 for (j = nT3Fonts; j > 0; --j) {
2608 t3FontCache[j] = t3FontCache[j - 1];
2609 }
2610 ++nT3Fonts;
2611 bbox = gfxFont->getFontBBox();
2612 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2613 // broken bounding box -- just take a guess
2614 xMin = xt - 5;
2615 xMax = xMin + 30;
2616 yMax = yt + 15;
2617 yMin = yMax - 45;
2618 } else {
2619 state->transform(bbox[0], bbox[1], &x1, &y1);
2620 xMin = xMax = x1;
2621 yMin = yMax = y1;
2622 state->transform(bbox[0], bbox[3], &x1, &y1);
2623 if (x1 < xMin) {
2624 xMin = x1;
2625 } else if (x1 > xMax) {
2626 xMax = x1;
2627 }
2628 if (y1 < yMin) {
2629 yMin = y1;
2630 } else if (y1 > yMax) {
2631 yMax = y1;
2632 }
2633 state->transform(bbox[2], bbox[1], &x1, &y1);
2634 if (x1 < xMin) {
2635 xMin = x1;
2636 } else if (x1 > xMax) {
2637 xMax = x1;
2638 }
2639 if (y1 < yMin) {
2640 yMin = y1;
2641 } else if (y1 > yMax) {
2642 yMax = y1;
2643 }
2644 state->transform(bbox[2], bbox[3], &x1, &y1);
2645 if (x1 < xMin) {
2646 xMin = x1;
2647 } else if (x1 > xMax) {
2648 xMax = x1;
2649 }
2650 if (y1 < yMin) {
2651 yMin = y1;
2652 } else if (y1 > yMax) {
2653 yMax = y1;
2654 }
2655 }
2656 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2657 (int)floor(xMin - xt),
2658 (int)floor(yMin - yt),
2659 (int)ceil(xMax) - (int)floor(xMin) + 3,
2660 (int)ceil(yMax) - (int)floor(yMin) + 3,
2661 display, visual, depth, pixmap);
2662 }
2663 }
2664 t3Font = t3FontCache[0];
2665
2666 // is the glyph in the cache?
2667 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2668 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2669 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2670 t3Font->cacheTags[i+j].code == code) {
2671 state->getFillRGB(&color);
2672 if (reverseVideo) {
2673 color.r = 1 - color.r;
2674 color.g = 1 - color.g;
2675 color.b = 1 - color.b;
2676 }
2677 text->addChar(state, 0, 0,
2678 t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy,
2679 u, uLen);
2680 drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
2681 t3Font->cacheData + (i+j) * t3Font->glyphSize,
2682 xt, yt, &color);
2683 return gTrue;
2684 }
2685 }
2686
2687 // push a new Type 3 glyph record
2688 t3gs = new T3GlyphStack();
2689 t3gs->next = t3GlyphStack;
2690 t3GlyphStack = t3gs;
2691 t3GlyphStack->cacheable = gFalse;
2692 t3GlyphStack->code = code;
2693 t3GlyphStack->cache = t3Font;
2694 t3GlyphStack->cacheIdx = i;
2695 t3GlyphStack->x = xt;
2696 t3GlyphStack->y = yt;
2697 t3GlyphStack->u = u;
2698 t3GlyphStack->uLen = uLen;
2699
2700 return gFalse;
2701}
2702
2703void XOutputDev::endType3Char(GfxState *state) {
2704 XImage *image;
2705 Guchar *p;
2706 int x, y;
2707 Gulong pixel;
2708 double alpha;
2709 T3GlyphStack *t3gs;
2710 double *ctm;
2711
2712 if (t3GlyphStack->cacheable) {
2713 image = t3GlyphStack->cache->image;
2714 XGetSubImage(display, pixmap, 0, 0,
2715 t3GlyphStack->cache->glyphW, t3GlyphStack->cache->glyphH,
2716 (1 << depth) - 1, ZPixmap, image, 0, 0);
2717 p = t3GlyphStack->cacheData;
2718 for (y = 0; y < t3GlyphStack->cache->glyphH; ++y) {
2719 for (x = 0; x < t3GlyphStack->cache->glyphW; ++x) {
2720 pixel = XGetPixel(image, x, y);
2721 if (trueColor) {
2722 alpha = (double)((pixel >> rShift) & rMul) / (double)rMul;
2723 } else {
2724 alpha = redMap[pixel & 0xff];
2725 }
2726 if (alpha <= 0.2) {
2727 *p++ = 4;
2728 } else if (alpha <= 0.4) {
2729 *p++ = 3;
2730 } else if (alpha <= 0.6) {
2731 *p++ = 2;
2732 } else if (alpha <= 0.8) {
2733 *p++ = 1;
2734 } else {
2735 *p++ = 0;
2736 }
2737 }
2738 }
2739 XDestroyRegion(clipRegion);
2740 XFreeGC(display, strokeGC);
2741 XFreeGC(display, fillGC);
2742 pixmapW = t3GlyphStack->origPixmapW;
2743 pixmapH = t3GlyphStack->origPixmapH;
2744 pixmap = t3GlyphStack->origPixmap;
2745 strokeGC = t3GlyphStack->origStrokeGC;
2746 fillGC = t3GlyphStack->origFillGC;
2747 clipRegion = t3GlyphStack->origClipRegion;
2748 drawType3Glyph(t3GlyphStack->cache,
2749 t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
2750 t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color);
2751 // the CTM must be restored here in order for TextPage::addChar to
2752 // work correctly
2753 ctm = state->getCTM();
2754 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2755 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2756 }
2757 text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy,
2758 t3GlyphStack->u, t3GlyphStack->uLen);
2759 t3gs = t3GlyphStack;
2760 t3GlyphStack = t3gs->next;
2761 delete t3gs;
2762}
2763
2764void XOutputDev::drawType3Glyph(T3FontCache *t3Font,
2765 T3FontCacheTag *tag, Guchar *data,
2766 double x, double y, GfxRGB *color) {
2767 XImage *image;
2768 XColor xcolor;
2769 GfxRGB bg, rgb;
2770 Gulong map[5];
2771 Gulong pixel;
2772 Guchar *p;
2773 int x0, y0, w0, h0, x1, y1;
2774 int ix, iy;
2775
2776 // compute: (x0,y0) = position in destination pixmap
2777 // (x1,y1) = position in the XImage
2778 // (w0,h0) = size of XImage transfer
2779 x0 = xoutRound(x + t3Font->glyphX);
2780 y0 = xoutRound(y + t3Font->glyphY);
2781 x1 = 0;
2782 y1 = 0;
2783 w0 = t3Font->glyphW;
2784 h0 = t3Font->glyphH;
2785 if (x0 < 0) {
2786 x1 = -x0;
2787 w0 += x0;
2788 x0 = 0;
2789 }
2790 if (x0 + w0 > pixmapW) {
2791 w0 = pixmapW - x0;
2792 }
2793 if (w0 <= 0) {
2794 return;
2795 }
2796 if (y0 < 0) {
2797 y1 = -y0;
2798 h0 += y0;
2799 y0 = 0;
2800 }
2801 if (y0 + h0 > pixmapH) {
2802 h0 = pixmapH - y0;
2803 }
2804 if (h0 <= 0) {
2805 return;
2806 }
2807
2808 image = t3Font->image;
2809 XGetSubImage(display, pixmap, x0, y0, w0, h0,
2810 (1 << depth) - 1, ZPixmap, image, x1, y1);
2811 xcolor.pixel = XGetPixel(image, t3Font->glyphW / 2, t3Font->glyphH / 2);
2812 XQueryColor(display, colormap, &xcolor);
2813 bg.r = xcolor.red / 65535.0;
2814 bg.g = xcolor.green / 65535.0;
2815 bg.b = xcolor.blue / 65535.0;
2816 rgb.r = 0.25 * (color->r + 3 * bg.r);
2817 rgb.g = 0.25 * (color->g + 3 * bg.g);
2818 rgb.b = 0.25 * (color->b + 3 * bg.b);
2819 map[1] = findColor(&rgb);
2820 rgb.r = 0.5 * (color->r + bg.r);
2821 rgb.g = 0.5 * (color->g + bg.g);
2822 rgb.b = 0.5 * (color->b + bg.b);
2823 map[2] = findColor(&rgb);
2824 rgb.r = 0.25 * (3 * color->r + bg.r);
2825 rgb.g = 0.25 * (3 * color->g + bg.g);
2826 rgb.b = 0.25 * (3 * color->b + bg.b);
2827 map[3] = findColor(&rgb);
2828 map[4] = findColor(color);
2829 p = data;
2830 for (iy = 0; iy < t3Font->glyphH; ++iy) {
2831 for (ix = 0; ix < t3Font->glyphW; ++ix) {
2832 pixel = *p++;
2833 if (pixel > 0) {
2834 XPutPixel(image, ix, iy, map[pixel]);
2835 }
2836 }
2837 }
2838 XPutImage(display, pixmap, fillGC, image, x1, y1, x0, y0, w0, h0);
2839}
2840
2841void XOutputDev::type3D0(GfxState *state, double wx, double wy) {
2842 t3GlyphStack->wx = wx;
2843 t3GlyphStack->wy = wy;
2844}
2845
2846void XOutputDev::type3D1(GfxState *state, double wx, double wy,
2847 double llx, double lly, double urx, double ury) {
2848 GfxColor fgColor;
2849 XGCValues gcValues;
2850 XRectangle rect;
2851 double *ctm;
2852 T3FontCache *t3Font;
2853 int i, j;
2854
2855 // allocate a cache entry
2856 t3GlyphStack->cacheable = gTrue;
2857 t3Font = t3GlyphStack->cache;
2858 i = t3GlyphStack->cacheIdx;
2859 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2860 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2861 t3Font->cacheTags[i+j].mru = 0x8000;
2862 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2863 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2864 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2865 } else {
2866 ++t3Font->cacheTags[i+j].mru;
2867 }
2868 }
2869 t3GlyphStack->wx = wx;
2870 t3GlyphStack->wy = wy;
2871 t3GlyphStack->cacheTag->wx = wx;
2872 t3GlyphStack->cacheTag->wy = wy;
2873
2874 // prepare to rasterize the glyph
2875 //~ do we need to handle both fill and stroke color?
2876 state->getFillRGB(&t3GlyphStack->color);
2877 if (reverseVideo) {
2878 t3GlyphStack->color.r = 1 - t3GlyphStack->color.r;
2879 t3GlyphStack->color.g = 1 - t3GlyphStack->color.g;
2880 t3GlyphStack->color.b = 1 - t3GlyphStack->color.b;
2881 }
2882 fgColor.c[0] = reverseVideo ? 1 : 0;
2883 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
2884 state->setFillColor(&fgColor);
2885 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
2886 state->setStrokeColor(&fgColor);
2887 t3GlyphStack->origPixmapW = pixmapW;
2888 t3GlyphStack->origPixmapH = pixmapH;
2889 t3GlyphStack->origPixmap = pixmap;
2890 t3GlyphStack->origStrokeGC = strokeGC;
2891 t3GlyphStack->origFillGC = fillGC;
2892 t3GlyphStack->origClipRegion = clipRegion;
2893 pixmapW = t3GlyphStack->cache->glyphW;
2894 pixmapH = t3GlyphStack->cache->glyphH;
2895 pixmap = t3GlyphStack->cache->pixmap;
2896 gcValues.foreground = BlackPixel(display, screenNum);
2897 gcValues.background = WhitePixel(display, screenNum);
2898 gcValues.line_width = 0;
2899 gcValues.line_style = LineSolid;
2900 strokeGC = XCreateGC(display, pixmap,
2901 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
2902 &gcValues);
2903 updateLineAttrs(state, gTrue);
2904 gcValues.foreground = WhitePixel(display, screenNum);
2905 fillGC = XCreateGC(display, pixmap,
2906 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
2907 &gcValues);
2908 XFillRectangle(display, pixmap, fillGC, 0, 0, pixmapW, pixmapH);
2909 XSetForeground(display, fillGC, BlackPixel(display, screenNum));
2910 clipRegion = XCreateRegion();
2911 rect.x = rect.y = 0;
2912 rect.width = pixmapW;
2913 rect.height = pixmapH;
2914 XUnionRectWithRegion(&rect, clipRegion, clipRegion);
2915 XSetRegion(display, strokeGC, clipRegion);
2916 XSetRegion(display, fillGC, clipRegion);
2917 ctm = state->getCTM();
2918 t3GlyphStack->origCTM4 = ctm[4];
2919 t3GlyphStack->origCTM5 = ctm[5];
2920 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2921 -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
2922}
2923
2924inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) {
2925 double gray;
2926 int r, g, b;
2927 Gulong pixel;
2928
2929 if (trueColor) {
2930 r = xoutRound(x->r * rMul);
2931 g = xoutRound(x->g * gMul);
2932 b = xoutRound(x->b * bMul);
2933 pixel = ((Gulong)r << rShift) +
2934 ((Gulong)g << gShift) +
2935 ((Gulong)b << bShift);
2936 err->r = x->r - (double)r / rMul;
2937 err->g = x->g - (double)g / gMul;
2938 err->b = x->b - (double)b / bMul;
2939 } else if (numColors == 1) {
2940 gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
2941 if (gray < 0.5) {
2942 pixel = colors[0];
2943 err->r = x->r;
2944 err->g = x->g;
2945 err->b = x->b;
2946 } else {
2947 pixel = colors[1];
2948 err->r = x->r - 1;
2949 err->g = x->g - 1;
2950 err->b = x->b - 1;
2951 }
2952 } else {
2953 r = xoutRound(x->r * (numColors - 1));
2954 g = xoutRound(x->g * (numColors - 1));
2955 b = xoutRound(x->b * (numColors - 1));
2956 pixel = colors[(r * numColors + g) * numColors + b];
2957 err->r = x->r - (double)r / (numColors - 1);
2958 err->g = x->g - (double)g / (numColors - 1);
2959 err->b = x->b - (double)b / (numColors - 1);
2960 }
2961 return pixel;
2962}
2963
2964Gulong XOutputDev::findColor(GfxRGB *rgb) {
2965 int r, g, b;
2966 double gray;
2967 Gulong pixel;
2968
2969 if (trueColor) {
2970 r = xoutRound(rgb->r * rMul);
2971 g = xoutRound(rgb->g * gMul);
2972 b = xoutRound(rgb->b * bMul);
2973 pixel = ((Gulong)r << rShift) +
2974 ((Gulong)g << gShift) +
2975 ((Gulong)b << bShift);
2976 } else if (numColors == 1) {
2977 gray = 0.299 * rgb->r + 0.587 * rgb->g + 0.114 * rgb->b;
2978 if (gray < 0.5)
2979 pixel = colors[0];
2980 else
2981 pixel = colors[1];
2982 } else {
2983 r = xoutRound(rgb->r * (numColors - 1));
2984 g = xoutRound(rgb->g * (numColors - 1));
2985 b = xoutRound(rgb->b * (numColors - 1));
2986#if 0 // this makes things worse as often as better
2987 // even a very light color shouldn't map to white
2988 if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
2989 if (color->getR() < 0.95)
2990 --r;
2991 if (color->getG() < 0.95)
2992 --g;
2993 if (color->getB() < 0.95)
2994 --b;
2995 }
2996#endif
2997 pixel = colors[(r * numColors + g) * numColors + b];
2998 }
2999 return pixel;
3000}
3001
3002void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3003 int width, int height, GBool invert,
3004 GBool inlineImg) {
3005 ImageStream *imgStr;
3006 XImage *image;
3007 double *ctm;
3008 GBool rot;
3009 double xScale, yScale, xShear, yShear;
3010 int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
3011 int ulx, uly, llx, lly, urx, ury, lrx, lry;
3012 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
3013 int bx0, by0, bx1, by1, bw, bh;
3014 int cx0, cy0, cx1, cy1, cw, ch;
3015 int yp, yq, yt, yStep, lastYStep;
3016 int xp, xq, xt, xStep, xSrc;
3017 GfxRGB rgb;
3018 Guchar *pixBuf;
3019 int imgPix;
3020 double alpha;
3021 XColor xcolor;
3022 Gulong lastPixel;
3023 GfxRGB rgb2;
3024 double r0, g0, b0, r1, g1, b1;
3025 Gulong pix;
3026 Guchar *p;
3027 int x, y, x1, y1, x2, y2;
3028 int n, m, i, j;
3029
3030 // get CTM, check for singular matrix
3031 ctm = state->getCTM();
3032 if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
3033 error(-1, "Singular CTM in drawImage");
3034 if (inlineImg) {
3035 j = height * ((width + 7) / 8);
3036 str->reset();
3037 for (i = 0; i < j; ++i) {
3038 str->getChar();
3039 }
3040 str->close();
3041 }
3042 return;
3043 }
3044
3045 // compute scale, shear, rotation, translation parameters
3046 rot = fabs(ctm[1]) > fabs(ctm[0]);
3047 if (rot) {
3048 xScale = -ctm[1];
3049 yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
3050 xShear = ctm[3] / yScale;
3051 yShear = -ctm[0] / ctm[1];
3052 } else {
3053 xScale = ctm[0];
3054 yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
3055 xShear = -ctm[2] / yScale;
3056 yShear = ctm[1] / ctm[0];
3057 }
3058 tx = xoutRound(ctm[2] + ctm[4]);
3059 ty = xoutRound(ctm[3] + ctm[5]);
3060 // use ceil() to avoid gaps between "striped" images
3061 scaledWidth = (int)ceil(fabs(xScale));
3062 xSign = (xScale < 0) ? -1 : 1;
3063 scaledHeight = (int)ceil(fabs(yScale));
3064 ySign = (yScale < 0) ? -1 : 1;
3065
3066 // compute corners in device space
3067 ulx1 = 0;
3068 uly1 = 0;
3069 urx1 = xSign * (scaledWidth - 1);
3070 ury1 = xoutRound(yShear * urx1);
3071 llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
3072 lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
3073 lrx1 = xSign * (scaledWidth - 1) +
3074 xoutRound(xShear * ySign * (scaledHeight - 1));
3075 lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
3076 if (rot) {
3077 ulx = tx + uly1; uly = ty - ulx1;
3078 urx = tx + ury1; ury = ty - urx1;
3079 llx = tx + lly1; lly = ty - llx1;
3080 lrx = tx + lry1; lry = ty - lrx1;
3081 } else {
3082 ulx = tx + ulx1; uly = ty + uly1;
3083 urx = tx + urx1; ury = ty + ury1;
3084 llx = tx + llx1; lly = ty + lly1;
3085 lrx = tx + lrx1; lry = ty + lry1;
3086 }
3087
3088 // bounding box:
3089 // (bx0, by0) = upper-left corner
3090 // (bx1, by1) = lower-right corner
3091 // (bw, bh) = size
3092 bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
3093 : (llx < lrx) ? llx : lrx
3094 : (urx < llx) ? (urx < lrx) ? urx : lrx
3095 : (llx < lrx) ? llx : lrx;
3096 bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
3097 : (llx > lrx) ? llx : lrx
3098 : (urx > llx) ? (urx > lrx) ? urx : lrx
3099 : (llx > lrx) ? llx : lrx;
3100 by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
3101 : (lly < lry) ? lly : lry
3102 : (ury < lly) ? (ury < lry) ? ury : lry
3103 : (lly < lry) ? lly : lry;
3104 by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
3105 : (lly > lry) ? lly : lry
3106 : (ury > lly) ? (ury > lry) ? ury : lry
3107 : (lly > lry) ? lly : lry;
3108 bw = bx1 - bx0 + 1;
3109 bh = by1 - by0 + 1;
3110
3111 // Bounding box clipped to pixmap, i.e., "valid" rectangle:
3112 // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
3113 // (cx1, cy1) = upper-left corner of valid rectangle in XImage
3114 // (cw, ch) = size of valid rectangle
3115 // These values will be used to transfer the XImage from/to the
3116 // Pixmap.
3117 cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
3118 if (bx0 < 0) {
3119 cx0 = 0;
3120 cx1 = -bx0;
3121 cw += bx0;
3122 } else {
3123 cx0 = bx0;
3124 cx1 = 0;
3125 }
3126 ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
3127 if (by0 < 0) {
3128 cy0 = 0;
3129 cy1 = -by0;
3130 ch += by0;
3131 } else {
3132 cy0 = by0;
3133 cy1 = 0;
3134 }
3135
3136 // check for tiny (zero width or height) images
3137 // and off-page images
3138 if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
3139 if (inlineImg) {
3140 j = height * ((width + 7) / 8);
3141 str->reset();
3142 for (i = 0; i < j; ++i) {
3143 str->getChar();
3144 }
3145 str->close();
3146 }
3147 return;
3148 }
3149
3150 // compute Bresenham parameters for x and y scaling
3151 yp = height / scaledHeight;
3152 yq = height % scaledHeight;
3153 xp = width / scaledWidth;
3154 xq = width % scaledWidth;
3155
3156 // allocate pixel buffer
3157 pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3158
3159 // allocate XImage and read from page pixmap
3160 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
3161 image->data = (char *)gmalloc(bh * image->bytes_per_line);
3162 XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
3163 image, cx1, cy1);
3164
3165 // get mask color
3166 state->getFillRGB(&rgb);
3167 if (reverseVideo) {
3168 rgb.r = 1 - rgb.r;
3169 rgb.g = 1 - rgb.g;
3170 rgb.b = 1 - rgb.b;
3171 }
3172 r0 = rgb.r;
3173 g0 = rgb.g;
3174 b0 = rgb.b;
3175
3176 // initialize background color
3177 // (the specific pixel value doesn't matter here, as long as
3178 // r1,g1,b1 correspond correctly to lastPixel)
3179 xcolor.pixel = lastPixel = 0;
3180 XQueryColor(display, colormap, &xcolor);
3181 r1 = (double)xcolor.red / 65535.0;
3182 g1 = (double)xcolor.green / 65535.0;
3183 b1 = (double)xcolor.blue / 65535.0;
3184
3185 // initialize the image stream
3186 imgStr = new ImageStream(str, width, 1, 1);
3187 imgStr->reset();
3188
3189 // init y scale Bresenham
3190 yt = 0;
3191 lastYStep = 1;
3192
3193 for (y = 0; y < scaledHeight; ++y) {
3194
3195 // y scale Bresenham
3196 yStep = yp;
3197 yt += yq;
3198 if (yt >= scaledHeight) {
3199 yt -= scaledHeight;
3200 ++yStep;
3201 }
3202
3203 // read row(s) from image
3204 n = (yp > 0) ? yStep : lastYStep;
3205 if (n > 0) {
3206 p = pixBuf;
3207 for (i = 0; i < n; ++i) {
3208 memcpy(p, imgStr->getLine(), width);
3209 if (invert) {
3210 for (j = 0; j < width; ++j) {
3211 p[j] ^= 1;
3212 }
3213 }
3214 p += width;
3215 }
3216 }
3217 lastYStep = yStep;
3218
3219 // init x scale Bresenham
3220 xt = 0;
3221 xSrc = 0;
3222
3223 for (x = 0; x < scaledWidth; ++x) {
3224
3225 // x scale Bresenham
3226 xStep = xp;
3227 xt += xq;
3228 if (xt >= scaledWidth) {
3229 xt -= scaledWidth;
3230 ++xStep;
3231 }
3232
3233 // x shear
3234 x1 = xSign * x + xoutRound(xShear * ySign * y);
3235
3236 // y shear
3237 y1 = ySign * y + xoutRound(yShear * x1);
3238
3239 // rotation
3240 if (rot) {
3241 x2 = y1;
3242 y2 = -x1;
3243 } else {
3244 x2 = x1;
3245 y2 = y1;
3246 }
3247
3248 // compute the filtered pixel at (x,y) after the
3249 // x and y scaling operations
3250 n = yStep > 0 ? yStep : 1;
3251 m = xStep > 0 ? xStep : 1;
3252 p = pixBuf + xSrc;
3253 imgPix = 0;
3254 for (i = 0; i < n; ++i) {
3255 for (j = 0; j < m; ++j) {
3256 imgPix += *p++;
3257 }
3258 p += width - m;
3259 }
3260
3261 // x scale Bresenham
3262 xSrc += xStep;
3263
3264 // blend image pixel with background
3265 alpha = (double)imgPix / (double)(n * m);
3266 xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0);
3267 if (xcolor.pixel != lastPixel) {
3268 XQueryColor(display, colormap, &xcolor);
3269 r1 = (double)xcolor.red / 65535.0;
3270 g1 = (double)xcolor.green / 65535.0;
3271 b1 = (double)xcolor.blue / 65535.0;
3272 lastPixel = xcolor.pixel;
3273 }
3274 rgb2.r = r0 * (1 - alpha) + r1 * alpha;
3275 rgb2.g = g0 * (1 - alpha) + g1 * alpha;
3276 rgb2.b = b0 * (1 - alpha) + b1 * alpha;
3277 pix = findColor(&rgb2);
3278
3279 // set pixel
3280 XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
3281 }
3282 }
3283
3284 // blit the image into the pixmap
3285 XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
3286
3287 // free memory
3288 delete imgStr;
3289 gfree(pixBuf);
3290 gfree(image->data);
3291 image->data = NULL;
3292 XDestroyImage(image);
3293}
3294
3295void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3296 int width, int height, GfxImageColorMap *colorMap,
3297 int *maskColors, GBool inlineImg) {
3298 ImageStream *imgStr;
3299 XImage *image;
3300 int nComps, nVals, nBits;
3301 GBool dither;
3302 double *ctm;
3303 GBool rot;
3304 double xScale, yScale, xShear, yShear;
3305 int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
3306 int ulx, uly, llx, lly, urx, ury, lrx, lry;
3307 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
3308 int bx0, by0, bx1, by1, bw, bh;
3309 int cx0, cy0, cx1, cy1, cw, ch;
3310 int yp, yq, yt, yStep, lastYStep;
3311 int xp, xq, xt, xStep, xSrc;
3312 GfxRGB *pixBuf;
3313 Guchar *alphaBuf;
3314 Guchar pixBuf2[gfxColorMaxComps];
3315 GfxRGB color2, err, errRight;
3316 GfxRGB *errDown;
3317 double r0, g0, b0, alpha, mul;
3318 Gulong pix;
3319 GfxRGB *p;
3320 Guchar *q, *p2;
3321 GBool oneBitMode;
3322 GfxRGB oneBitRGB[2];
3323 int x, y, x1, y1, x2, y2;
3324 int n, m, i, j, k;
3325
3326 // image parameters
3327 nComps = colorMap->getNumPixelComps();
3328 nVals = width * nComps;
3329 nBits = colorMap->getBits();
3330 dither = nComps > 1 || nBits > 1;
3331
3332 // get CTM, check for singular matrix
3333 ctm = state->getCTM();
3334 if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
3335 error(-1, "Singular CTM in drawImage");
3336 if (inlineImg) {
3337 str->reset();
3338 j = height * ((nVals * nBits + 7) / 8);
3339 for (i = 0; i < j; ++i) {
3340 str->getChar();
3341 }
3342 str->close();
3343 }
3344 return;
3345 }
3346
3347 // compute scale, shear, rotation, translation parameters
3348 rot = fabs(ctm[1]) > fabs(ctm[0]);
3349 if (rot) {
3350 xScale = -ctm[1];
3351 yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
3352 xShear = ctm[3] / yScale;
3353 yShear = -ctm[0] / ctm[1];
3354 } else {
3355 xScale = ctm[0];
3356 yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
3357 xShear = -ctm[2] / yScale;
3358 yShear = ctm[1] / ctm[0];
3359 }
3360 tx = xoutRound(ctm[2] + ctm[4]);
3361 ty = xoutRound(ctm[3] + ctm[5]);
3362 if (xScale < 0) {
3363 // this is the right edge which needs to be (left + width - 1)
3364 --tx;
3365 }
3366 if (yScale < 0) {
3367 // this is the bottom edge which needs to be (top + height - 1)
3368 --ty;
3369 }
3370 // use ceil() to avoid gaps between "striped" images
3371 scaledWidth = (int)ceil(fabs(xScale));
3372 xSign = (xScale < 0) ? -1 : 1;
3373 scaledHeight = (int)ceil(fabs(yScale));
3374 ySign = (yScale < 0) ? -1 : 1;
3375
3376 // compute corners in device space
3377 ulx1 = 0;
3378 uly1 = 0;
3379 urx1 = xSign * (scaledWidth - 1);
3380 ury1 = xoutRound(yShear * urx1);
3381 llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
3382 lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
3383 lrx1 = xSign * (scaledWidth - 1) +
3384 xoutRound(xShear * ySign * (scaledHeight - 1));
3385 lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
3386 if (rot) {
3387 ulx = tx + uly1; uly = ty - ulx1;
3388 urx = tx + ury1; ury = ty - urx1;
3389 llx = tx + lly1; lly = ty - llx1;
3390 lrx = tx + lry1; lry = ty - lrx1;
3391 } else {
3392 ulx = tx + ulx1; uly = ty + uly1;
3393 urx = tx + urx1; ury = ty + ury1;
3394 llx = tx + llx1; lly = ty + lly1;
3395 lrx = tx + lrx1; lry = ty + lry1;
3396 }
3397
3398 // bounding box:
3399 // (bx0, by0) = upper-left corner
3400 // (bx1, by1) = lower-right corner
3401 // (bw, bh) = size
3402 bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
3403 : (llx < lrx) ? llx : lrx
3404 : (urx < llx) ? (urx < lrx) ? urx : lrx
3405 : (llx < lrx) ? llx : lrx;
3406 bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
3407 : (llx > lrx) ? llx : lrx
3408 : (urx > llx) ? (urx > lrx) ? urx : lrx
3409 : (llx > lrx) ? llx : lrx;
3410 by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
3411 : (lly < lry) ? lly : lry
3412 : (ury < lly) ? (ury < lry) ? ury : lry
3413 : (lly < lry) ? lly : lry;
3414 by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
3415 : (lly > lry) ? lly : lry
3416 : (ury > lly) ? (ury > lry) ? ury : lry
3417 : (lly > lry) ? lly : lry;
3418 bw = bx1 - bx0 + 1;
3419 bh = by1 - by0 + 1;
3420
3421 // Bounding box clipped to pixmap, i.e., "valid" rectangle:
3422 // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
3423 // (cx1, cy1) = upper-left corner of valid rectangle in XImage
3424 // (cw, ch) = size of valid rectangle
3425 // These values will be used to transfer the XImage from/to the
3426 // Pixmap.
3427 cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
3428 if (bx0 < 0) {
3429 cx0 = 0;
3430 cx1 = -bx0;
3431 cw += bx0;
3432 } else {
3433 cx0 = bx0;
3434 cx1 = 0;
3435 }
3436 ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
3437 if (by0 < 0) {
3438 cy0 = 0;
3439 cy1 = -by0;
3440 ch += by0;
3441 } else {
3442 cy0 = by0;
3443 cy1 = 0;
3444 }
3445
3446 // check for tiny (zero width or height) images
3447 // and off-page images
3448 if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
3449 if (inlineImg) {
3450 str->reset();
3451 j = height * ((nVals * nBits + 7) / 8);
3452 for (i = 0; i < j; ++i)
3453 str->getChar();
3454 str->close();
3455 }
3456 return;
3457 }
3458
3459 // compute Bresenham parameters for x and y scaling
3460 yp = height / scaledHeight;
3461 yq = height % scaledHeight;
3462 xp = width / scaledWidth;
3463 xq = width % scaledWidth;
3464
3465 // allocate pixel buffer
3466 pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
3467 if (maskColors) {
3468 alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3469 } else {
3470 alphaBuf = NULL;
3471 }
3472
3473 // allocate XImage
3474 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
3475 image->data = (char *)gmalloc(bh * image->bytes_per_line);
3476
3477 // if the transform is anything other than a 0/90/180/270 degree
3478 // rotation/flip, or if there is color key masking, read the
3479 // backgound pixmap to fill in the corners
3480 if (!((ulx == llx && uly == ury) ||
3481 (uly == lly && ulx == urx)) ||
3482 maskColors) {
3483 XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
3484 image, cx1, cy1);
3485 }
3486
3487 // allocate error diffusion accumulators
3488 if (dither) {
3489 errDown = (GfxRGB *)gmalloc(bw * sizeof(GfxRGB));
3490 for (j = 0; j < bw; ++j) {
3491 errDown[j].r = errDown[j].g = errDown[j].b = 0;
3492 }
3493 } else {
3494 errDown = NULL;
3495 }
3496
3497 // optimize the one-bit-deep image case
3498 if ((oneBitMode = nComps == 1 && nBits == 1)) {
3499 pixBuf2[0] = 0;
3500 colorMap->getRGB(pixBuf2, &oneBitRGB[0]);
3501 pixBuf2[0] = 1;
3502 colorMap->getRGB(pixBuf2, &oneBitRGB[1]);
3503 }
3504
3505 // initialize the image stream
3506 imgStr = new ImageStream(str, width, nComps, nBits);
3507 imgStr->reset();
3508
3509 // init y scale Bresenham
3510 yt = 0;
3511 lastYStep = 1;
3512
3513 for (y = 0; y < scaledHeight; ++y) {
3514
3515 // initialize error diffusion accumulator
3516 errRight.r = errRight.g = errRight.b = 0;
3517
3518 // y scale Bresenham
3519 yStep = yp;
3520 yt += yq;
3521 if (yt >= scaledHeight) {
3522 yt -= scaledHeight;
3523 ++yStep;
3524 }
3525
3526 // read row(s) from image
3527 n = (yp > 0) ? yStep : lastYStep;
3528 if (n > 0) {
3529 p = pixBuf;
3530 q = alphaBuf;
3531 for (i = 0; i < n; ++i) {
3532 p2 = imgStr->getLine();
3533 for (j = 0; j < width; ++j) {
3534 if (oneBitMode) {
3535 *p = oneBitRGB[*p2];
3536 } else {
3537 colorMap->getRGB(p2, p);
3538 }
3539 ++p;
3540 if (q) {
3541 *q = 1;
3542 for (k = 0; k < nComps; ++k) {
3543 if (p2[k] < maskColors[2*k] ||
3544 p2[k] > maskColors[2*k]) {
3545 *q = 0;
3546 break;
3547 }
3548 }
3549 ++q;
3550 }
3551 p2 += nComps;
3552 }
3553 }
3554 }
3555 lastYStep = yStep;
3556
3557 // init x scale Bresenham
3558 xt = 0;
3559 xSrc = 0;
3560
3561 for (x = 0; x < scaledWidth; ++x) {
3562
3563 // x scale Bresenham
3564 xStep = xp;
3565 xt += xq;
3566 if (xt >= scaledWidth) {
3567 xt -= scaledWidth;
3568 ++xStep;
3569 }
3570
3571 // x shear
3572 x1 = xSign * x + xoutRound(xShear * ySign * y);
3573
3574 // y shear
3575 y1 = ySign * y + xoutRound(yShear * x1);
3576
3577 // rotation
3578 if (rot) {
3579 x2 = y1;
3580 y2 = -x1;
3581 } else {
3582 x2 = x1;
3583 y2 = y1;
3584 }
3585
3586 // compute the filtered pixel at (x,y) after the
3587 // x and y scaling operations
3588 n = yStep > 0 ? yStep : 1;
3589 m = xStep > 0 ? xStep : 1;
3590 p = pixBuf + xSrc;
3591 r0 = g0 = b0 = 0;
3592 q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
3593 alpha = 0;
3594 for (i = 0; i < n; ++i) {
3595 for (j = 0; j < m; ++j) {
3596 r0 += p->r;
3597 g0 += p->g;
3598 b0 += p->b;
3599 ++p;
3600 if (q) {
3601 alpha += *q++;
3602 }
3603 }
3604 p += width - m;
3605 }
3606 mul = 1 / (double)(n * m);
3607 r0 *= mul;
3608 g0 *= mul;
3609 b0 *= mul;
3610 alpha *= mul;
3611
3612 // x scale Bresenham
3613 xSrc += xStep;
3614
3615 // compute pixel
3616 if (dither) {
3617 color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r;
3618 if (color2.r > 1) {
3619 color2.r = 1;
3620 } else if (color2.r < 0) {
3621 color2.r = 0;
3622 }
3623 color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g;
3624 if (color2.g > 1) {
3625 color2.g = 1;
3626 } else if (color2.g < 0) {
3627 color2.g = 0;
3628 }
3629 color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b;
3630 if (color2.b > 1) {
3631 color2.b = 1;
3632 } else if (color2.b < 0) {
3633 color2.b = 0;
3634 }
3635 pix = findColor(&color2, &err);
3636 errRight.r = errDown[tx + x2 - bx0].r = err.r / 2;
3637 errRight.g = errDown[tx + x2 - bx0].g = err.g / 2;
3638 errRight.b = errDown[tx + x2 - bx0].b = err.b / 2;
3639 } else {
3640 color2.r = r0;
3641 color2.g = g0;
3642 color2.b = b0;
3643 pix = findColor(&color2, &err);
3644 }
3645
3646 // set pixel
3647 //~ this should do a blend when 0 < alpha < 1
3648 if (alpha < 0.75) {
3649 XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
3650 }
3651 }
3652 }
3653
3654 // blit the image into the pixmap
3655 XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
3656
3657 // free memory
3658 delete imgStr;
3659 gfree(pixBuf);
3660 if (maskColors) {
3661 gfree(alphaBuf);
3662 }
3663 gfree(image->data);
3664 image->data = NULL;
3665 XDestroyImage(image);
3666 gfree(errDown);
3667}
3668
3669GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom,
3670 int *xMin, int *yMin, int *xMax, int *yMax) {
3671 double xMin1, yMin1, xMax1, yMax1;
3672
3673 xMin1 = (double)*xMin;
3674 yMin1 = (double)*yMin;
3675 xMax1 = (double)*xMax;
3676 yMax1 = (double)*yMax;
3677 if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
3678 *xMin = xoutRound(xMin1);
3679 *xMax = xoutRound(xMax1);
3680 *yMin = xoutRound(yMin1);
3681 *yMax = xoutRound(yMax1);
3682 return gTrue;
3683 }
3684 return gFalse;
3685}
3686
3687GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
3688 return text->getText((double)xMin, (double)yMin,
3689 (double)xMax, (double)yMax);
3690}