1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
27 #include "FontEncoding.h"
30 #include "TextOutputDev.h"
31 #include "XOutputDev.h"
33 #include "XOutputFontInfo.h"
35 #ifdef XlibSpecificationRelease
36 #if XlibSpecificationRelease < 5
37 typedef char *XPointer;
40 typedef char *XPointer;
43 //------------------------------------------------------------------------
44 // Constants and macros
45 //------------------------------------------------------------------------
47 #define xoutRound(x) ((int)(x + 0.5))
49 #define maxCurveSplits 6 // max number of splits when recursively
50 // drawing Bezier curves
52 //------------------------------------------------------------------------
54 //------------------------------------------------------------------------
56 GBool installCmap = gFalse;
58 int rgbCubeSize = defaultRGBCube;
61 GString *t1libControl = NULL;
64 GString *t1Courier = NULL;
65 GString *t1CourierBold = NULL;
66 GString *t1CourierBoldOblique = NULL;
67 GString *t1CourierOblique = NULL;
68 GString *t1Helvetica = NULL;
69 GString *t1HelveticaBold = NULL;
70 GString *t1HelveticaBoldOblique = NULL;
71 GString *t1HelveticaOblique = NULL;
72 GString *t1Symbol = NULL;
73 GString *t1TimesBold = NULL;
74 GString *t1TimesBoldItalic = NULL;
75 GString *t1TimesItalic = NULL;
76 GString *t1TimesRoman = NULL;
77 GString *t1ZapfDingbats = NULL;
79 GBool useEUCJP = gFalse;
81 GString *japan12Font = NULL;
84 //------------------------------------------------------------------------
86 //------------------------------------------------------------------------
92 FontEncoding *encoding;
95 static FontMapEntry fontMap[] = {
96 {"Courier", "-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Courier, &isoLatin1Encoding},
97 {"Courier-Bold", "-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBold, &isoLatin1Encoding},
98 {"Courier-BoldOblique", "-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBoldOblique, &isoLatin1Encoding},
99 {"Courier-Oblique", "-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierOblique, &isoLatin1Encoding},
100 {"Helvetica", "-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Helvetica, &isoLatin1Encoding},
101 {"Helvetica-Bold", "-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBold, &isoLatin1Encoding},
102 {"Helvetica-BoldOblique", "-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBoldOblique, &isoLatin1Encoding},
103 {"Helvetica-Oblique", "-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaOblique, &isoLatin1Encoding},
104 {"Symbol", "-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, &symbolEncoding},
105 {"Times-Bold", "-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBold, &isoLatin1Encoding},
106 {"Times-BoldItalic", "-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBoldItalic, &isoLatin1Encoding},
107 {"Times-Italic", "-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesItalic, &isoLatin1Encoding},
108 {"Times-Roman", "-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesRoman, &isoLatin1Encoding},
109 {"ZapfDingbats", "-*-zapfdingbats-medium-r-normal-*-%s-*-*-*-*-*-*-*", &t1ZapfDingbats, &zapfDingbatsEncoding},
113 static FontMapEntry *userFontMap;
115 //------------------------------------------------------------------------
116 // Font substitutions
117 //------------------------------------------------------------------------
125 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
126 static FontSubst fontSubst[16] = {
127 {"-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Helvetica, 0.833},
128 {"-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaOblique, 0.833},
129 {"-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBold, 0.889},
130 {"-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBoldOblique, 0.889},
131 {"-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesRoman, 0.788},
132 {"-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesItalic, 0.722},
133 {"-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBold, 0.833},
134 {"-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBoldItalic, 0.778},
135 {"-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Courier, 0.600},
136 {"-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierOblique, 0.600},
137 {"-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBold, 0.600},
138 {"-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBoldOblique, 0.600},
139 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576},
140 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576},
141 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576},
142 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576}
145 //------------------------------------------------------------------------
147 //------------------------------------------------------------------------
151 static char *japan12DefFont =
152 "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-jisx0208.1983-0";
155 static Gushort japan12Map[96] = {
156 0x2120, 0x2120, 0x212a, 0x2149, 0x2174, 0x2170, 0x2173, 0x2175, // 00 .. 07
157 0x2147, 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x213e, 0x2123, // 08 .. 0f
158 0x213f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, // 10 .. 17
159 0x2337, 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, // 18 .. 1f
160 0x2129, 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, // 20 .. 27
161 0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, // 28 .. 2f
162 0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, // 30 .. 37
163 0x2357, 0x2358, 0x2359, 0x235a, 0x214e, 0x216f, 0x214f, 0x2130, // 38 .. 3f
164 0x2132, 0x2146, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, // 40 .. 47
165 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, // 48 .. 4f
166 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, // 50 .. 57
167 0x2377, 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141 // 58 .. 5f
171 static Gushort japan12KanaMap1[97] = {
172 0x2131, 0x2121, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572,
173 0x2521, 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567,
174 0x2543, 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b,
175 0x252d, 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b,
176 0x253d, 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b,
177 0x254c, 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b,
178 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568,
179 0x2569, 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b,
180 0x212c, 0x212e, 0x2570, 0x2571, 0x256e, 0x2575, 0x2576, 0x2574,
181 0x252c, 0x252e, 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a,
182 0x253c, 0x253e, 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x2550,
183 0x2551, 0x2553, 0x2554, 0x2556, 0x2557, 0x2559, 0x255a, 0x255c,
188 static Gushort japan12KanaMap2[98] = {
189 0x212d, 0x212f, 0x216d, 0x214c, 0x214d, 0x2152, 0x2153, 0x2154,
190 0x2155, 0x2158, 0x2159, 0x215a, 0x215b, 0x213d, 0x2121, 0x2472,
191 0x2421, 0x2423, 0x2425, 0x2427, 0x2429, 0x2463, 0x2465, 0x2467,
192 0x2443, 0x2422, 0x2424, 0x2426, 0x2428, 0x242a, 0x242b, 0x242d,
193 0x242f, 0x2431, 0x2433, 0x2435, 0x2437, 0x2439, 0x243b, 0x243d,
194 0x243f, 0x2441, 0x2444, 0x2446, 0x2448, 0x244a, 0x244b, 0x244c,
195 0x244d, 0x244e, 0x244f, 0x2452, 0x2455, 0x2458, 0x245b, 0x245e,
196 0x245f, 0x2460, 0x2461, 0x2462, 0x2464, 0x2466, 0x2468, 0x2469,
197 0x246a, 0x246b, 0x246c, 0x246d, 0x246f, 0x2473, 0x2470, 0x2471,
198 0x246e, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438,
199 0x243a, 0x243c, 0x243e, 0x2440, 0x2442, 0x2445, 0x2447, 0x2449,
200 0x2450, 0x2451, 0x2453, 0x2454, 0x2456, 0x2457, 0x2459, 0x245a,
204 static char *japan12Roman[10] = {
205 "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"
208 static char *japan12Abbrev1[6] = {
209 "mm", "cm", "km", "mg", "kg", "cc"
214 //------------------------------------------------------------------------
215 // Constructed characters
216 //------------------------------------------------------------------------
218 #define lastRegularChar 0x0ff
219 #define firstSubstChar 0x100
220 #define lastSubstChar 0x104
221 #define firstConstrChar 0x105
222 #define lastConstrChar 0x106
223 #define firstMultiChar 0x107
224 #define lastMultiChar 0x110
227 static Guchar substChars[] = {
228 0x27, // 100: quotesingle --> quoteright
229 0x2d, // 101: emdash --> hyphen
230 0xad, // 102: hyphen --> endash
231 0x2f, // 103: fraction --> slash
232 0xb0, // 104: ring --> degree
240 static char *multiChars[] = {
248 "...", // 10e: ellipsis
249 "``", // 10f: quotedblleft
250 "''" // 110: quotedblright
279 //------------------------------------------------------------------------
281 //------------------------------------------------------------------------
283 XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12,
284 double m21, double m22, Display *display,
285 XOutputFontCache *cache) {
289 id = gfxFont->getID();
290 this->display = display;
296 // check for hex char names
298 if (!gfxFont->is16Bit()) {
299 for (code = 0; code < 256; ++code) {
300 if ((charName = gfxFont->getCharName(code))) {
301 if ((charName[0] == 'B' || charName[0] == 'C' ||
302 charName[0] == 'G') &&
303 strlen(charName) == 3 &&
304 ((charName[1] >= 'a' && charName[1] <= 'f') ||
305 (charName[1] >= 'A' && charName[1] <= 'F') ||
306 (charName[2] >= 'a' && charName[2] <= 'f') ||
307 (charName[2] >= 'A' && charName[2] <= 'F'))) {
316 XOutputFont::~XOutputFont() {
320 //------------------------------------------------------------------------
322 //------------------------------------------------------------------------
324 XOutputT1Font::XOutputT1Font(GfxFont *gfxFont, GString *pdfBaseFont,
325 double m11, double m12, double m21, double m22,
326 double size, double ntm11, double ntm12,
327 double ntm21, double ntm22,
328 Display *display, XOutputFontCache *cache):
329 XOutputFont(gfxFont, m11, m12, m21, m22, display, cache)
335 t1libAA = cache->getT1libAA();
337 // keep size info (for drawChar())
338 this->size = (float)size;
340 // we can only handle 8-bit, Type 1/1C, with embedded font file
341 // or user-specified base fonts
342 //~ also look for external font files
344 (!gfxFont->is16Bit() &&
345 (gfxFont->getType() == fontType1 ||
346 gfxFont->getType() == fontType1C) &&
347 gfxFont->getEmbeddedFontID(&embRef)))) {
352 if ((t1ID = cache->getT1Font(gfxFont, pdfBaseFont)) < 0)
355 // transform the font
360 T1_TransformFont(t1ID, &xform);
363 XOutputT1Font::~XOutputT1Font() {
369 GBool XOutputT1Font::isOk() {
373 void XOutputT1Font::updateGC(GC gc) {
376 void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, GC gc,
377 double x, double y, int c) {
379 T1_AASetCharX(pixmap, gc, 0, xoutRound(x), xoutRound(y),
380 t1ID, c, size, NULL);
382 T1_SetCharX(pixmap, gc, 0, xoutRound(x), xoutRound(y),
383 t1ID, c, size, NULL);
386 #endif // HAVE_T1LIB_H
388 //------------------------------------------------------------------------
390 //------------------------------------------------------------------------
392 XOutputServerFont::XOutputServerFont(GfxFont *gfxFont, char *fontNameFmt,
393 FontEncoding *encoding,
394 double m11, double m12,
395 double m21, double m22,
397 double ntm11, double ntm12,
398 double ntm21, double ntm22,
400 XOutputFontCache *cache):
401 XOutputFont(gfxFont, m11, m12, m21, m22, display, cache)
403 char fontName[200], fontSize[100];
412 // Construct forward and reverse map.
413 // This tries to deal with font subset character names of the
414 // form 'Bxx', 'Cxx', 'Gxx', with decimal or hex numbering.
415 if (!gfxFont->is16Bit()) {
416 for (code = 0; code < 256; ++code)
419 for (code = 0; code < 256; ++code) {
420 if ((charName = gfxFont->getCharName(code))) {
421 if ((code2 = encoding->getCharCode(charName)) < 0) {
422 n = strlen(charName);
424 (charName[0] == 'B' || charName[0] == 'C' ||
425 charName[0] == 'G') &&
426 isxdigit(charName[1]) && isxdigit(charName[2])) {
427 sscanf(charName+1, "%x", &code2);
428 } else if (!hex && n >= 2 && n <= 3 &&
429 isdigit(charName[0]) && isdigit(charName[1])) {
430 code2 = atoi(charName);
433 } else if (!hex && n >= 3 && n <= 5 && isdigit(charName[1])) {
434 code2 = atoi(charName+1);
438 //~ this is a kludge -- is there a standard internal encoding
439 //~ used by all/most Type 1 fonts?
440 if (code2 == 262) // hyphen
442 else if (code2 == 266) // emdash
446 map[code] = (Gushort)code2;
448 revMap[code2] = (Guchar)code;
457 code2 = 0; // to make gcc happy
458 //~ this is a hack to get around the fact that X won't draw
459 //~ chars 0..31; this works when the fonts have duplicate encodings
461 for (code = 0; code < 32; ++code) {
462 if ((charName = gfxFont->getCharName(code)) &&
463 (code2 = gfxFont->getCharCode(charName)) >= 0) {
464 map[code] = (Gushort)code2;
466 revMap[code2] = (Guchar)code;
469 for (code = 32; code < 256; ++code) {
470 map[code] = (Gushort)code;
471 revMap[code] = (Guchar)code;
476 // adjust transform for the X transform convention
480 // try to get a rotated font?
481 rotated = !(ntm11 > 0 && ntm22 > 0 &&
482 fabs(ntm11 / ntm22 - 1) < 0.2 &&
483 fabs(ntm12) < 0.01 &&
486 // open X font -- if font is not found (which means the server can't
487 // scale fonts), try progressively smaller and then larger sizes
488 //~ This does a linear search -- it should get a list of fonts from
489 //~ the server and pick the closest.
490 startSize = (int)size;
492 sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
493 ntm11<0 ? "~" : "", fabs(ntm11 * size),
494 ntm12<0 ? "~" : "", fabs(ntm12 * size),
495 ntm21<0 ? "~" : "", fabs(ntm21 * size),
496 ntm22<0 ? "~" : "", fabs(ntm22 * size));
498 sprintf(fontSize, "%d", startSize);
499 sprintf(fontName, fontNameFmt, fontSize);
500 xFont = XLoadQueryFont(display, fontName);
502 for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
503 sprintf(fontSize, "%d", sz);
504 sprintf(fontName, fontNameFmt, fontSize);
505 if ((xFont = XLoadQueryFont(display, fontName)))
509 for (sz = startSize + 1; sz < startSize + 10; ++sz) {
510 sprintf(fontSize, "%d", sz);
511 sprintf(fontName, fontNameFmt, fontSize);
512 if ((xFont = XLoadQueryFont(display, fontName)))
516 sprintf(fontSize, "%d", startSize);
517 sprintf(fontName, fontNameFmt, fontSize);
518 error(-1, "Failed to open font: '%s'", fontName);
525 XOutputServerFont::~XOutputServerFont() {
527 XFreeFont(display, xFont);
530 GBool XOutputServerFont::isOk() {
531 return xFont != NULL;
534 void XOutputServerFont::updateGC(GC gc) {
535 XSetFont(display, gc, xFont->fid);
538 void XOutputServerFont::drawChar(GfxState *state, Pixmap pixmap, GC gc,
539 double x, double y, int c) {
548 if (c1 <= lastRegularChar) {
550 XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), &buf, 1);
551 } else if (c1 <= lastSubstChar) {
552 buf = (char)substChars[c1 - firstSubstChar];
553 XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), &buf, 1);
554 } else if (c1 <= lastConstrChar) {
555 gfxFont = state->getFont();
556 //~ need to deal with rotated text here
557 switch (c1 - firstConstrChar) {
559 tx = 0.25 * state->getTransformedFontSize() * gfxFont->getWidth(c);
560 XFillRectangle(display, pixmap, gc,
562 xoutRound(y - 0.4 * xFont->ascent - tx),
563 xoutRound(2 * tx), xoutRound(2 * tx));
566 //~ this should use a smaller font
567 // tx = state->getTransformedFontSize() *
568 // (gfxFont->getWidth(c) -
569 // gfxFont->getWidth(font->revMap['M']));
570 tx = 0.9 * state->getTransformedFontSize() *
571 gfxFont->getWidth(revMap['T']);
572 y -= 0.33 * (double)xFont->ascent;
574 XDrawString(display, pixmap, gc,
575 xoutRound(x), xoutRound(y), &buf, 1);
578 XDrawString(display, pixmap, gc,
579 xoutRound(x), xoutRound(y), &buf, 1);
582 } else if (c1 <= lastMultiChar) {
583 gfxFont = state->getFont();
584 p = multiChars[c1 - firstMultiChar];
586 tx = gfxFont->getWidth(c);
587 tx -= gfxFont->getWidth(revMap[p[n-1]]);
588 tx = tx * state->getTransformedFontSize() / (double)(n - 1);
589 for (i = 0; i < n; ++i) {
590 XDrawString(display, pixmap, gc,
591 xoutRound(x), xoutRound(y), p + i, 1);
597 //------------------------------------------------------------------------
599 //------------------------------------------------------------------------
601 XOutputFontCache::XOutputFontCache(Display *display) {
602 this->display = display;
606 useT1lib = t1libControl->cmp("none") != 0;
607 t1libAA = t1libControl->cmp("plain") != 0;
608 t1libAAHigh = t1libControl->cmp("high") == 0;
612 t1libAAHigh = gFalse;
618 XOutputFontCache::~XOutputFontCache() {
622 void XOutputFontCache::startDoc(int screenNum, Guint depth,
629 if (T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |
632 T1_AASetLevel(t1libAAHigh ? T1_AA_HIGH : T1_AA_LOW);
634 T1_AASetBitsPerPixel(8);
635 else if (depth <= 16)
636 T1_AASetBitsPerPixel(16);
638 T1_AASetBitsPerPixel(32);
640 T1_SetX11Params(display, DefaultVisual(display, screenNum),
647 #endif // HAVE_T1LIB_H
650 void XOutputFontCache::delFonts() {
654 // delete Type 1 fonts
655 for (i = 0; i < nT1Fonts; ++i)
657 for (i = 0; i < t1BaseFontsSize && t1BaseFonts[i].num >= 0; ++i) {
658 T1_DeleteFont(t1BaseFonts[i].t1ID);
659 gfree(t1BaseFonts[i].encStr);
660 gfree(t1BaseFonts[i].enc);
668 // delete server fonts
669 for (i = 0; i < nServerFonts; ++i)
670 delete serverFonts[i];
673 void XOutputFontCache::clear() {
677 // clear Type 1 font cache
678 for (i = 0; i < t1FontCacheSize; ++i)
685 // clear server font cache
686 for (i = 0; i < serverFontCacheSize; ++i)
687 serverFonts[i] = NULL;
691 XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont,
692 double m11, double m12,
693 double m21, double m22) {
695 XOutputT1Font *t1Font;
697 XOutputServerFont *serverFont;
701 FontEncoding *xEncoding;
703 double ntm11, ntm12, ntm21, ntm22;
710 // is it the most recently used Type 1 or server font?
712 if (useT1lib && nT1Fonts > 0 &&
713 t1Fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
717 if (nServerFonts > 0 && serverFonts[0]->matches(gfxFont->getID(),
719 return serverFonts[0];
722 // is it in the Type 1 cache?
724 for (i = 1; i < nT1Fonts; ++i) {
725 if (t1Fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
727 for (j = i; j > 0; --j)
728 t1Fonts[j] = t1Fonts[j-1];
736 // is it in the server cache?
737 for (i = 1; i < nServerFonts; ++i) {
738 if (serverFonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
739 serverFont = serverFonts[i];
740 for (j = i; j > 0; --j)
741 serverFonts[j] = serverFonts[j-1];
742 serverFonts[0] = serverFont;
747 // compute size and normalized transform matrix
748 size = sqrt(m21*m21 + m22*m22);
754 // search for a font map entry
758 if (!gfxFont->is16Bit() && gfxFont->getName()) {
759 for (fme = userFontMap; fme->pdfFont; ++fme) {
760 if (!gfxFont->getName()->cmp(fme->pdfFont)) {
765 for (fme = fontMap; fme->pdfFont; ++fme) {
766 if (!gfxFont->getName()->cmp(fme->pdfFont)) {
771 if (fme && fme->t1Font) {
772 t1FontName = *fme->t1Font;
774 if (fme && fme->xFont && fme->encoding) {
775 xFontName = fme->xFont;
776 xEncoding = fme->encoding;
780 // no font map entry found, so substitute a font
781 if (!t1FontName && !xFontName) {
782 if (gfxFont->is16Bit()) {
783 xFontName = fontSubst[0].xFont;
785 switch (gfxFont->getCharSet16()) {
786 case font16AdobeJapan12:
788 xFontName = japan12Font ? japan12Font->getCString() : japan12DefFont;
793 if (gfxFont->isFixedWidth()) {
795 } else if (gfxFont->isSerif()) {
800 if (gfxFont->isBold())
802 if (gfxFont->isItalic())
804 xFontName = fontSubst[index].xFont;
805 t1FontName = *fontSubst[index].t1Font;
806 xEncoding = &isoLatin1Encoding;
812 // get width of 'm' in real font and substituted font
813 if ((code = gfxFont->getCharCode("m")) >= 0)
814 w1 = gfxFont->getWidth(code);
817 w2 = fontSubst[index].mWidth;
818 if (gfxFont->getType() == fontType3) {
819 // This is a hack which makes it possible to substitute for some
820 // Type 3 fonts. The problem is that it's impossible to know what
821 // the base coordinate system used in the font is without actually
822 // rendering the font. This code tries to guess by looking at the
823 // width of the character 'm' (which breaks if the font is a
824 // subset that doesn't contain 'm').
825 if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
832 fm = gfxFont->getFontMatrix();
833 v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
836 } else if (!gfxFont->isSymbolic()) {
837 // if real font is substantially narrower than substituted
838 // font, reduce the font size accordingly
839 if (w1 > 0.01 && w1 < 0.9 * w2) {
850 size = sqrt(ntm21*ntm21 + ntm22*ntm22);
859 // try to create a new Type 1 font
861 t1Font = new XOutputT1Font(gfxFont, t1FontName,
863 size, ntm11, ntm12, ntm21, ntm22,
865 if (t1Font->isOk()) {
868 if (nT1Fonts == t1FontCacheSize) {
870 delete t1Fonts[nT1Fonts];
872 for (j = nT1Fonts; j > 0; --j)
873 t1Fonts[j] = t1Fonts[j-1];
883 // create a new server font
884 serverFont = new XOutputServerFont(gfxFont, xFontName, xEncoding,
886 size, ntm11, ntm12, ntm21, ntm22,
888 if (serverFont->isOk()) {
891 if (nServerFonts == serverFontCacheSize) {
893 delete serverFonts[nServerFonts];
895 for (j = nServerFonts; j > 0; --j)
896 serverFonts[j] = serverFonts[j-1];
897 serverFonts[0] = serverFont;
908 int XOutputFontCache::getT1Font(GfxFont *gfxFont, GString *pdfBaseFont) {
911 char tmpFileName[60];
915 Type1CFontConverter *cvt;
917 Object refObj, strObj;
925 id = gfxFont->getID();
927 // check available fonts
929 for (i = 0; i < t1BaseFontsSize && t1BaseFonts[i].num >= 0; ++i) {
930 if (t1BaseFonts[i].num == id.num && t1BaseFonts[i].gen == id.gen) {
931 t1ID = t1BaseFonts[i].t1ID;
935 // create a new base font
938 // resize t1BaseFonts if necessary
939 if (i == t1BaseFontsSize) {
940 t1BaseFonts = (XOutputT1BaseFont *)
941 grealloc(t1BaseFonts,
942 (t1BaseFontsSize + 16) * sizeof(XOutputT1BaseFont));
943 for (j = 0; j < 16; ++j) {
944 t1BaseFonts[t1BaseFontsSize + j].num = -1;
946 t1BaseFontsSize += 16;
949 // create the font file
950 tmpFileName[0] = '\0';
951 if (!gfxFont->is16Bit() &&
952 (gfxFont->getType() == fontType1 ||
953 gfxFont->getType() == fontType1C) &&
954 gfxFont->getEmbeddedFontID(&embRef)) {
956 if (!(f = fopen(tmpFileName, "wb"))) {
957 error(-1, "Couldn't open temporary Type 1 font file '%s'",
961 if (gfxFont->getType() == fontType1C) {
962 fontBuf = gfxFont->readEmbFontFile(&fontLen);
963 cvt = new Type1CFontConverter(fontBuf, fontLen, f);
968 gfxFont->getEmbeddedFontID(&embRef);
969 refObj.initRef(embRef.num, embRef.gen);
970 refObj.fetch(&strObj);
972 strObj.streamReset();
973 while ((c = strObj.streamGetChar()) != EOF)
978 fileName = tmpFileName;
980 fileName = pdfBaseFont->getCString();
983 // create the t1lib font
984 if ((t1ID = T1_AddFont(fileName)) < 0) {
985 error(-1, "Couldn't create t1lib font from '%s'", fileName);
989 t1BaseFonts[i].num = id.num;
990 t1BaseFonts[i].gen = id.gen;
991 t1BaseFonts[i].t1ID = t1ID;
993 // remove the font file
994 if (tmpFileName[0]) {
999 enc = gfxFont->getEncoding();
1001 for (j = 0; j < 256 && j < enc->getSize(); ++j) {
1002 if (enc->getCharName(j)) {
1003 encStrSize += strlen(enc->getCharName(j)) + 1;
1006 t1BaseFonts[i].enc = (char **)gmalloc(257 * sizeof(char *));
1007 encPtr = (char *)gmalloc(encStrSize * sizeof(char));
1008 t1BaseFonts[i].encStr = encPtr;
1009 for (j = 0; j < 256 && j < enc->getSize(); ++j) {
1010 if (enc->getCharName(j)) {
1011 strcpy(encPtr, enc->getCharName(j));
1012 t1BaseFonts[i].enc[j] = encPtr;
1013 encPtr += strlen(encPtr) + 1;
1015 t1BaseFonts[i].enc[j] = ".notdef";
1018 for (; j < 256; ++j) {
1019 t1BaseFonts[i].enc[j] = ".notdef";
1021 t1BaseFonts[i].enc[256] = "custom";
1022 T1_ReencodeFont(t1BaseFonts[i].t1ID, t1BaseFonts[i].enc);
1026 t1ID = T1_CopyFont(t1ID);
1032 //------------------------------------------------------------------------
1034 //------------------------------------------------------------------------
1036 XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth,
1037 Colormap colormap, unsigned long paperColor) {
1038 XVisualInfo visualTempl;
1039 XVisualInfo *visualList;
1045 int r, g, b, n, m, i;
1048 // get display/pixmap info
1049 this->display = display;
1050 screenNum = DefaultScreen(display);
1051 this->pixmap = pixmap;
1052 this->depth = depth;
1053 this->colormap = colormap;
1055 // check for TrueColor visual
1058 this->depth = DefaultDepth(display, screenNum);
1059 visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals);
1060 for (i = 0; i < nVisuals; ++i) {
1061 if (visualList[i].visual == DefaultVisual(display, screenNum)) {
1062 if (visualList[i].c_class == TrueColor) {
1064 mask = visualList[i].red_mask;
1066 while (mask && !(mask & 1)) {
1071 mask = visualList[i].green_mask;
1073 while (mask && !(mask & 1)) {
1078 mask = visualList[i].blue_mask;
1080 while (mask && !(mask & 1)) {
1089 XFree((XPointer)visualList);
1092 // allocate a color cube
1095 // set colors in private colormap
1097 for (numColors = 6; numColors >= 2; --numColors) {
1098 m = numColors * numColors * numColors;
1099 if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m))
1102 if (numColors >= 2) {
1103 m = numColors * numColors * numColors;
1104 xcolors = (XColor *)gmalloc(m * sizeof(XColor));
1106 for (r = 0; r < numColors; ++r) {
1107 for (g = 0; g < numColors; ++g) {
1108 for (b = 0; b < numColors; ++b) {
1109 xcolors[n].pixel = colors[n];
1110 xcolors[n].red = (r * 65535) / (numColors - 1);
1111 xcolors[n].green = (g * 65535) / (numColors - 1);
1112 xcolors[n].blue = (b * 65535) / (numColors - 1);
1113 xcolors[n].flags = DoRed | DoGreen | DoBlue;
1118 XStoreColors(display, colormap, xcolors, m);
1122 colors[0] = BlackPixel(display, screenNum);
1123 colors[1] = WhitePixel(display, screenNum);
1126 // allocate colors in shared colormap
1128 if (rgbCubeSize > maxRGBCube)
1129 rgbCubeSize = maxRGBCube;
1131 for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
1134 for (r = 0; r < numColors && ok; ++r) {
1135 for (g = 0; g < numColors && ok; ++g) {
1136 for (b = 0; b < numColors && ok; ++b) {
1138 colors[n++] = BlackPixel(display, screenNum);
1140 xcolor.red = (r * 65535) / (numColors - 1);
1141 xcolor.green = (g * 65535) / (numColors - 1);
1142 xcolor.blue = (b * 65535) / (numColors - 1);
1143 if (XAllocColor(display, colormap, &xcolor))
1144 colors[n++] = xcolor.pixel;
1153 XFreeColors(display, colormap, &colors[1], n-1, 0);
1157 colors[0] = BlackPixel(display, screenNum);
1158 colors[1] = WhitePixel(display, screenNum);
1164 gcValues.foreground = BlackPixel(display, screenNum);
1165 gcValues.background = WhitePixel(display, screenNum);
1166 gcValues.line_width = 0;
1167 gcValues.line_style = LineSolid;
1168 strokeGC = XCreateGC(display, pixmap,
1169 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1171 fillGC = XCreateGC(display, pixmap,
1172 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1174 gcValues.foreground = paperColor;
1175 paperGC = XCreateGC(display, pixmap,
1176 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1179 // no clip region yet
1182 // get user font map
1183 for (n = 0; devFontMap[n].pdfFont; ++n) ;
1184 userFontMap = (FontMapEntry *)gmalloc((n+1) * sizeof(FontMapEntry));
1185 for (i = 0; i < n; ++i) {
1186 userFontMap[i].pdfFont = devFontMap[i].pdfFont;
1187 userFontMap[i].xFont = devFontMap[i].devFont;
1188 m = strlen(userFontMap[i].xFont);
1189 if (m >= 10 && !strcmp(userFontMap[i].xFont + m - 10, "-iso8859-2"))
1190 userFontMap[i].encoding = &isoLatin2Encoding;
1191 else if (m >= 13 && !strcmp(userFontMap[i].xFont + m - 13,
1193 userFontMap[i].encoding = NULL;
1195 userFontMap[i].encoding = &isoLatin1Encoding;
1196 userFontMap[i].t1Font = NULL;
1198 userFontMap[n].pdfFont = NULL;
1200 // set up the font cache and fonts
1203 fontCache = new XOutputFontCache(display);
1205 // empty state stack
1208 // create text object
1209 text = new TextPage(useEUCJP, gFalse);
1212 XOutputDev::~XOutputDev() {
1215 XFreeGC(display, strokeGC);
1216 XFreeGC(display, fillGC);
1217 XFreeGC(display, paperGC);
1219 XDestroyRegion(clipRegion);
1223 void XOutputDev::startDoc() {
1224 fontCache->startDoc(screenNum, depth, colormap);
1227 void XOutputDev::startPage(int pageNum, GfxState *state) {
1232 // clear state stack
1236 XFreeGC(display, s->strokeGC);
1237 XFreeGC(display, s->fillGC);
1238 XDestroyRegion(s->clipRegion);
1243 // default line flatness
1247 gcValues.foreground = BlackPixel(display, screenNum);
1248 gcValues.background = WhitePixel(display, screenNum);
1249 gcValues.line_width = 0;
1250 gcValues.line_style = LineSolid;
1251 XChangeGC(display, strokeGC,
1252 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1254 XChangeGC(display, fillGC,
1255 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1258 // clear clipping region
1260 XDestroyRegion(clipRegion);
1261 clipRegion = XCreateRegion();
1262 rect.x = rect.y = 0;
1263 rect.width = pixmapW;
1264 rect.height = pixmapH;
1265 XUnionRectWithRegion(&rect, clipRegion, clipRegion);
1266 XSetRegion(display, strokeGC, clipRegion);
1267 XSetRegion(display, fillGC, clipRegion);
1274 XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
1276 // clear text object
1280 void XOutputDev::endPage() {
1284 void XOutputDev::drawLinkBorder(double x1, double y1, double x2, double y2,
1290 color.setRGB(0, 0, 1);
1291 XSetForeground(display, strokeGC, findColor(&color));
1292 XSetLineAttributes(display, strokeGC, xoutRound(w),
1293 LineSolid, CapRound, JoinRound);
1294 cvtUserToDev(x1, y1, &x, &y);
1295 points[0].x = points[4].x = x;
1296 points[0].y = points[4].y = y;
1297 cvtUserToDev(x2, y1, &x, &y);
1300 cvtUserToDev(x2, y2, &x, &y);
1303 cvtUserToDev(x1, y2, &x, &y);
1306 XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
1309 void XOutputDev::saveState(GfxState *state) {
1313 // save current state
1314 s = new XOutputState;
1315 s->strokeGC = strokeGC;
1317 s->clipRegion = clipRegion;
1319 // push onto state stack
1323 // create a new current state by copying
1324 strokeGC = XCreateGC(display, pixmap, 0, &values);
1325 XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
1326 fillGC = XCreateGC(display, pixmap, 0, &values);
1327 XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
1328 clipRegion = XCreateRegion();
1329 XUnionRegion(s->clipRegion, clipRegion, clipRegion);
1330 XSetRegion(display, strokeGC, clipRegion);
1331 XSetRegion(display, fillGC, clipRegion);
1334 void XOutputDev::restoreState(GfxState *state) {
1338 // kill current state
1339 XFreeGC(display, strokeGC);
1340 XFreeGC(display, fillGC);
1341 XDestroyRegion(clipRegion);
1344 flatness = state->getFlatness();
1345 strokeGC = save->strokeGC;
1346 fillGC = save->fillGC;
1347 clipRegion = save->clipRegion;
1348 XSetRegion(display, strokeGC, clipRegion);
1349 XSetRegion(display, fillGC, clipRegion);
1358 void XOutputDev::updateAll(GfxState *state) {
1359 updateLineAttrs(state, gTrue);
1360 updateFlatness(state);
1361 updateMiterLimit(state);
1362 updateFillColor(state);
1363 updateStrokeColor(state);
1367 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
1368 double m21, double m22, double m31, double m32) {
1369 updateLineAttrs(state, gTrue);
1372 void XOutputDev::updateLineDash(GfxState *state) {
1373 updateLineAttrs(state, gTrue);
1376 void XOutputDev::updateFlatness(GfxState *state) {
1377 flatness = state->getFlatness();
1380 void XOutputDev::updateLineJoin(GfxState *state) {
1381 updateLineAttrs(state, gFalse);
1384 void XOutputDev::updateLineCap(GfxState *state) {
1385 updateLineAttrs(state, gFalse);
1389 void XOutputDev::updateMiterLimit(GfxState *state) {
1392 void XOutputDev::updateLineWidth(GfxState *state) {
1393 updateLineAttrs(state, gFalse);
1396 void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
1399 double *dashPattern;
1405 width = state->getTransformedLineWidth();
1406 switch (state->getLineCap()) {
1407 case 0: cap = CapButt; break;
1408 case 1: cap = CapRound; break;
1409 case 2: cap = CapProjecting; break;
1411 error(-1, "Bad line cap style (%d)", state->getLineCap());
1415 #if 1 //~ work around a bug in XFree86 (???)
1416 if (dashLength > 0 && cap == CapProjecting) {
1420 switch (state->getLineJoin()) {
1421 case 0: join = JoinMiter; break;
1422 case 1: join = JoinRound; break;
1423 case 2: join = JoinBevel; break;
1425 error(-1, "Bad line join style (%d)", state->getLineJoin());
1429 state->getLineDash(&dashPattern, &dashLength, &dashStart);
1430 XSetLineAttributes(display, strokeGC, xoutRound(width),
1431 dashLength > 0 ? LineOnOffDash : LineSolid,
1433 if (updateDash && dashLength > 0) {
1434 if (dashLength > 20)
1436 for (i = 0; i < dashLength; ++i) {
1437 dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
1438 if (dashList[i] == 0)
1441 XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
1445 void XOutputDev::updateFillColor(GfxState *state) {
1446 XSetForeground(display, fillGC, findColor(state->getFillColor()));
1449 void XOutputDev::updateStrokeColor(GfxState *state) {
1450 XSetForeground(display, strokeGC, findColor(state->getStrokeColor()));
1453 void XOutputDev::updateFont(GfxState *state) {
1454 double m11, m12, m21, m22;
1456 if (!(gfxFont = state->getFont())) {
1460 state->getFontTransMat(&m11, &m12, &m21, &m22);
1461 m11 *= state->getHorizScaling();
1462 m21 *= state->getHorizScaling();
1463 font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
1465 font->updateGC(fillGC);
1466 font->updateGC(strokeGC);
1470 void XOutputDev::stroke(GfxState *state) {
1473 int n, size, numPoints, i, j;
1476 n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
1478 // draw each subpath
1480 for (i = 0; i < n; ++i) {
1481 XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
1486 // free points and lengths arrays
1487 if (points != tmpPoints)
1489 if (lengths != tmpLengths)
1493 void XOutputDev::fill(GfxState *state) {
1494 doFill(state, WindingRule);
1497 void XOutputDev::eoFill(GfxState *state) {
1498 doFill(state, EvenOddRule);
1502 // X doesn't color the pixels on the right-most and bottom-most
1503 // borders of a polygon. This means that one-pixel-thick polygons
1504 // are not colored at all. I think this is supposed to be a
1505 // feature, but I can't figure out why. So after it fills a
1506 // polygon, it also draws lines around the border. This is done
1507 // only for single-component polygons, since it's not very
1508 // compatible with the compound polygon kludge (see convertPath()).
1510 void XOutputDev::doFill(GfxState *state, int rule) {
1513 int n, size, numPoints, i, j;
1516 XSetFillRule(display, fillGC, rule);
1518 // transform points, build separate polygons
1519 n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
1523 for (i = 0; i < n; ++i) {
1524 XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
1525 Complex, CoordModeOrigin);
1526 if (state->getPath()->getNumSubpaths() == 1) {
1527 XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
1530 j += lengths[i] + 1;
1533 // free points and lengths arrays
1534 if (points != tmpPoints)
1536 if (lengths != tmpLengths)
1540 void XOutputDev::clip(GfxState *state) {
1541 doClip(state, WindingRule);
1544 void XOutputDev::eoClip(GfxState *state) {
1545 doClip(state, EvenOddRule);
1548 void XOutputDev::doClip(GfxState *state, int rule) {
1549 Region region, region2;
1552 int n, size, numPoints, i, j;
1554 // transform points, build separate polygons
1555 n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
1557 // construct union of subpath regions
1558 region = XPolygonRegion(points, lengths[0], rule);
1560 for (i = 1; i < n; ++i) {
1561 region2 = XPolygonRegion(points + j, lengths[i], rule);
1562 XUnionRegion(region2, region, region);
1563 XDestroyRegion(region2);
1564 j += lengths[i] + 1;
1567 // intersect region with clipping region
1568 XIntersectRegion(region, clipRegion, clipRegion);
1569 XDestroyRegion(region);
1570 XSetRegion(display, strokeGC, clipRegion);
1571 XSetRegion(display, fillGC, clipRegion);
1573 // free points and lengths arrays
1574 if (points != tmpPoints)
1576 if (lengths != tmpLengths)
1581 // Transform points in the path and convert curves to line segments.
1582 // Builds a set of subpaths and returns the number of subpaths.
1583 // If <fillHack> is set, close any unclosed subpaths and activate a
1584 // kludge for polygon fills: First, it divides up the subpaths into
1585 // non-overlapping polygons by simply comparing bounding rectangles.
1586 // Then it connects subaths within a single compound polygon to a single
1587 // point so that X can fill the polygon (sort of).
1589 int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
1590 int *numPoints, int **lengths, GBool fillHack) {
1592 BoundingRect *rects;
1594 int n, i, ii, j, k, k0;
1596 // get path and number of subpaths
1597 path = state->getPath();
1598 n = path->getNumSubpaths();
1600 // allocate lengths array
1601 if (n < numTmpSubpaths)
1602 *lengths = tmpLengths;
1604 *lengths = (int *)gmalloc(n * sizeof(int));
1606 // allocate bounding rectangles array
1608 if (n < numTmpSubpaths)
1611 rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
1617 *points = tmpPoints;
1618 *size = numTmpPoints;
1620 for (i = 0; i < n; ++i) {
1622 // transform the points
1624 convertSubpath(state, path->getSubpath(i), points, size, numPoints);
1626 // construct bounding rectangle
1628 rects[i].xMin = rects[i].xMax = (*points)[j].x;
1629 rects[i].yMin = rects[i].yMax = (*points)[j].y;
1630 for (k = j + 1; k < *numPoints; ++k) {
1631 if ((*points)[k].x < rects[i].xMin)
1632 rects[i].xMin = (*points)[k].x;
1633 else if ((*points)[k].x > rects[i].xMax)
1634 rects[i].xMax = (*points)[k].x;
1635 if ((*points)[k].y < rects[i].yMin)
1636 rects[i].yMin = (*points)[k].y;
1637 else if ((*points)[k].y > rects[i].yMax)
1638 rects[i].yMax = (*points)[k].y;
1642 // close subpath if necessary
1643 if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
1644 (*points)[*numPoints-1].y != (*points)[j].y)) {
1645 addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
1648 // length of this subpath
1649 (*lengths)[i] = *numPoints - j;
1651 // leave an extra point for compound fill hack
1653 addPoint(points, size, numPoints, 0, 0);
1656 // combine compound polygons
1661 // start with subpath i
1663 (*lengths)[j] = (*lengths)[i];
1665 (*points)[k + (*lengths)[i]] = (*points)[k0];
1666 k += (*lengths)[i] + 1;
1669 // combine overlapping polygons
1672 // look for the first subsequent subpath, if any, which overlaps
1673 for (ii = i; ii < n; ++ii) {
1674 if (((rects[ii].xMin > rect.xMin && rects[ii].xMin < rect.xMax) ||
1675 (rects[ii].xMax > rect.xMin && rects[ii].xMax < rect.xMax) ||
1676 (rects[ii].xMin < rect.xMin && rects[ii].xMax > rect.xMax)) &&
1677 ((rects[ii].yMin > rect.yMin && rects[ii].yMin < rect.yMax) ||
1678 (rects[ii].yMax > rect.yMin && rects[ii].yMax < rect.yMax) ||
1679 (rects[ii].yMin < rect.yMin && rects[ii].yMax > rect.yMax)))
1683 // if there is an overlap, combine the polygons
1685 for (; i <= ii; ++i) {
1686 if (rects[i].xMin < rect.xMin)
1687 rect.xMin = rects[j].xMin;
1688 if (rects[i].xMax > rect.xMax)
1689 rect.xMax = rects[j].xMax;
1690 if (rects[i].yMin < rect.yMin)
1691 rect.yMin = rects[j].yMin;
1692 if (rects[i].yMax > rect.yMax)
1693 rect.yMax = rects[j].yMax;
1694 (*lengths)[j] += (*lengths)[i] + 1;
1695 (*points)[k + (*lengths)[i]] = (*points)[k0];
1696 k += (*lengths)[i] + 1;
1699 } while (ii < n && i < n);
1704 // free bounding rectangles
1705 if (rects != tmpRects)
1715 // Transform points in a single subpath and convert curves to line
1718 void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
1719 XPoint **points, int *size, int *n) {
1720 double x0, y0, x1, y1, x2, y2, x3, y3;
1723 m = subpath->getNumPoints();
1726 if (i >= 1 && subpath->getCurve(i)) {
1727 state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
1728 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
1729 state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
1730 state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
1731 doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
1734 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
1735 addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
1742 // Subdivide a Bezier curve. This uses floating point to avoid
1743 // propagating rounding errors. (The curves look noticeably more
1744 // jagged with integer arithmetic.)
1746 void XOutputDev::doCurve(XPoint **points, int *size, int *n,
1747 double x0, double y0, double x1, double y1,
1748 double x2, double y2, double x3, double y3) {
1749 double x[(1<<maxCurveSplits)+1][3];
1750 double y[(1<<maxCurveSplits)+1][3];
1751 int next[1<<maxCurveSplits];
1753 double xx1, yy1, xx2, yy2;
1754 double dx, dy, mx, my, d1, d2;
1755 double xl0, yl0, xl1, yl1, xl2, yl2;
1756 double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
1760 flat = (double)(flatness * flatness);
1766 p2 = 1<<maxCurveSplits;
1767 x[p1][0] = x0; y[p1][0] = y0;
1768 x[p1][1] = x1; y[p1][1] = y1;
1769 x[p1][2] = x2; y[p1][2] = y2;
1770 x[p2][0] = x3; y[p2][0] = y3;
1773 while (p1 < (1<<maxCurveSplits)) {
1776 xl0 = x[p1][0]; yl0 = y[p1][0];
1777 xx1 = x[p1][1]; yy1 = y[p1][1];
1778 xx2 = x[p1][2]; yy2 = y[p1][2];
1780 xr3 = x[p2][0]; yr3 = y[p2][0];
1782 // compute distances from control points to midpoint of the
1783 // straight line (this is a bit of a hack, but it's much faster
1784 // than computing the actual distances to the line)
1785 mx = (xl0 + xr3) * 0.5;
1786 my = (yl0 + yr3) * 0.5;
1794 // if curve is flat enough, or no more divisions allowed then
1795 // add the straight line segment
1796 if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
1797 addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
1800 // otherwise, subdivide the curve
1802 xl1 = (xl0 + xx1) * 0.5;
1803 yl1 = (yl0 + yy1) * 0.5;
1804 xh = (xx1 + xx2) * 0.5;
1805 yh = (yy1 + yy2) * 0.5;
1806 xl2 = (xl1 + xh) * 0.5;
1807 yl2 = (yl1 + yh) * 0.5;
1808 xr2 = (xx2 + xr3) * 0.5;
1809 yr2 = (yy2 + yr3) * 0.5;
1810 xr1 = (xh + xr2) * 0.5;
1811 yr1 = (yh + yr2) * 0.5;
1812 xr0 = (xl2 + xr1) * 0.5;
1813 yr0 = (yl2 + yr1) * 0.5;
1815 // add the new subdivision points
1817 x[p1][1] = xl1; y[p1][1] = yl1;
1818 x[p1][2] = xl2; y[p1][2] = yl2;
1820 x[p3][0] = xr0; y[p3][0] = yr0;
1821 x[p3][1] = xr1; y[p3][1] = yr1;
1822 x[p3][2] = xr2; y[p3][2] = yr2;
1829 // Add a point to the points array. (This would use a generic resizable
1830 // array type if C++ supported parameterized types in some reasonable
1831 // way -- templates are a disgusting kludge.)
1833 void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
1836 if (*points == tmpPoints) {
1837 *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
1838 memcpy(*points, tmpPoints, *k * sizeof(XPoint));
1840 *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
1843 (*points)[*k].x = x;
1844 (*points)[*k].y = y;
1848 void XOutputDev::beginString(GfxState *state, GString *s) {
1849 text->beginString(state, s, font ? font->isHex() : gFalse);
1852 void XOutputDev::endString(GfxState *state) {
1856 void XOutputDev::drawChar(GfxState *state, double x, double y,
1857 double dx, double dy, Guchar c) {
1860 text->addChar(state, x, y, dx, dy, c);
1865 // check for invisible text -- this is used by Acrobat Capture
1866 if ((state->getRender() & 3) == 3)
1869 state->transform(x, y, &x1, &y1);
1871 font->drawChar(state, pixmap, (state->getRender() & 1) ? strokeGC : fillGC,
1872 xoutRound(x1), xoutRound(y1), c);
1875 void XOutputDev::drawChar16(GfxState *state, double x, double y,
1876 double dx, double dy, int c) {
1880 #if JAPANESE_SUPPORT
1888 text->addChar16(state, x, y, dx, dy, c, gfxFont->getCharSet16());
1891 //~ assumes font is an XOutputServerFont
1896 // check for invisible text -- this is used by Acrobat Capture
1897 if ((state->getRender() & 3) == 3)
1900 // handle origin offset for vertical fonts
1901 if (gfxFont->getWMode16() == 1) {
1902 x -= gfxFont->getOriginX16(c) * state->getFontSize();
1903 y -= gfxFont->getOriginY16(c) * state->getFontSize();
1906 state->transform(x, y, &x1, &y1);
1909 switch (gfxFont->getCharSet16()) {
1911 // convert Adobe-Japan1-2 to JIS X 0208-1983
1912 case font16AdobeJapan12:
1913 #if JAPANESE_SUPPORT
1916 } else if (c <= 632) {
1920 c1 = japan12Map[c - 230];
1922 c1 = japan12KanaMap1[c - 325];
1926 c1 = japan12KanaMap2[c - 501];
1929 } else if (c <= 1124) {
1932 c1 = 0x2121 + (c - 633);
1934 c1 = 0x2221 + (c - 727);
1936 c1 = 0x223a + (c - 741);
1938 c1 = 0x224a + (c - 749);
1940 c1 = 0x225c + (c - 756);
1942 c1 = 0x2272 + (c - 771);
1945 } else if (c <= 841) {
1947 c1 = 0x2330 + (c - 780);
1949 c1 = 0x2341 + (c - 790);
1951 c1 = 0x2361 + (c - 816);
1952 } else if (c <= 1010) {
1954 c1 = 0x2421 + (c - 842);
1956 c1 = 0x2521 + (c - 925);
1959 c1 = 0x2621 + (c - 1011);
1961 c1 = 0x2641 + (c - 1035);
1963 c1 = 0x2721 + (c - 1059);
1965 c1 = 0x2751 + (c - 1092);
1967 } else if (c <= 4089) {
1968 t1 = (c - 1125) / 94;
1969 t2 = (c - 1125) % 94;
1970 c1 = 0x3021 + (t1 << 8) + t2;
1971 } else if (c <= 7477) {
1972 t1 = (c - 4090) / 94;
1973 t2 = (c - 4090) % 94;
1974 c1 = 0x5021 + (t1 << 8) + t2;
1975 } else if (c <= 7554) {
1977 } else if (c <= 7563) { // circled Arabic numbers 1..9
1978 c1 = 0x2331 + (c - 7555);
1979 c2[0].byte1 = c1 >> 8;
1980 c2[0].byte2 = c1 & 0xff;
1981 XDrawString16(display, pixmap,
1982 (state->getRender() & 1) ? strokeGC : fillGC,
1983 xoutRound(x1), xoutRound(y1), c2, 1);
1985 c2[0].byte1 = c1 >> 8;
1986 c2[0].byte2 = c1 & 0xff;
1987 XDrawString16(display, pixmap,
1988 (state->getRender() & 1) ? strokeGC : fillGC,
1989 xoutRound(x1), xoutRound(y1), c2, 1);
1991 } else if (c <= 7574) { // circled Arabic numbers 10..20
1994 for (i = 0; i < 2; ++i) {
1995 c1 = 0x2330 + (i == 0 ? (n / 10) : (n % 10));
1996 c2[0].byte1 = c1 >> 8;
1997 c2[0].byte2 = c1 & 0xff;
1998 XDrawString16(display, pixmap,
1999 (state->getRender() & 1) ? strokeGC : fillGC,
2000 xoutRound(x2), xoutRound(y1), c2, 1);
2001 x2 += 0.5 * state->getTransformedFontSize();
2004 c2[0].byte1 = c1 >> 8;
2005 c2[0].byte2 = c1 & 0xff;
2006 XDrawString16(display, pixmap,
2007 (state->getRender() & 1) ? strokeGC : fillGC,
2008 xoutRound(x1), xoutRound(y1), c2, 1);
2010 } else if (c <= 7584) { // Roman numbers I..X
2011 p = japan12Roman[c - 7575];
2020 c2[0].byte1 = c1 >> 8;
2021 c2[0].byte2 = c1 & 0xff;
2022 XDrawString16(display, pixmap,
2023 (state->getRender() & 1) ? strokeGC : fillGC,
2024 xoutRound(x1), xoutRound(y1), c2, 1);
2026 x1 += 0.2 * state->getTransformedFontSize();
2028 x1 += 0.5 * state->getTransformedFontSize();
2031 } else if (c <= 7632) {
2034 } else if (c <= 7606) {
2035 p = japan12Abbrev1[c - 7601];
2039 c2[0].byte1 = c1 >> 8;
2040 c2[0].byte2 = c1 & 0xff;
2041 XDrawString16(display, pixmap,
2042 (state->getRender() & 1) ? strokeGC : fillGC,
2043 xoutRound(x1), xoutRound(y1), c2, 1);
2044 x1 += 0.5 * state->getTransformedFontSize();
2055 error(-1, "Unsupported Adobe-Japan1-2 character: %d", c);
2057 #endif // JAPANESE_SUPPORT
2062 c2[0].byte1 = c1 >> 8;
2063 c2[0].byte2 = c1 & 0xff;
2064 XDrawString16(display, pixmap,
2065 (state->getRender() & 1) ? strokeGC : fillGC,
2066 xoutRound(x1), xoutRound(y1), c2, 1);
2070 void XOutputDev::drawImageMask(GfxState *state, Stream *str,
2071 int width, int height, GBool invert,
2073 ImageStream *imgStr;
2075 int x0, y0; // top left corner of image
2076 int w0, h0, w1, h1; // size of image
2079 double xt, yt, wt, ht;
2080 GBool rotate, xFlip, yFlip;
2083 int px1, px2, qx, dx;
2084 int py1, py2, qy, dy;
2089 // get image position and size
2090 state->transform(0, 0, &xt, &yt);
2091 state->transformDelta(1, 1, &wt, &ht);
2096 x0 = xoutRound(xt + wt);
2097 w0 = xoutRound(-wt);
2103 y0 = xoutRound(yt + ht);
2104 h0 = xoutRound(-ht);
2106 state->transformDelta(1, 0, &xt, &yt);
2107 rotate = fabs(xt) < fabs(yt);
2121 color = findColor(state->getFillColor());
2123 // check for tiny (zero width or height) images
2124 if (w0 == 0 || h0 == 0) {
2125 j = height * ((width + 7) / 8);
2127 for (i = 0; i < j; ++i)
2132 // Bresenham parameters
2134 px2 = w1 - px1 * width;
2136 py2 = h1 - py1 * height;
2139 image = XCreateImage(display, DefaultVisual(display, screenNum),
2140 depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
2141 image->data = (char *)gmalloc(h0 * image->bytes_per_line);
2142 if (x0 + w0 > pixmapW)
2153 if (y0 + h0 > pixmapH)
2164 XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap,
2167 // initialize the image stream
2168 imgStr = new ImageStream(str, width, 1, 1);
2171 // first line (column)
2172 y = yFlip ? h1 - 1 : 0;
2176 for (i = 0; i < height; ++i) {
2178 // vertical (horizontal) Bresenham
2180 if ((qy += py2) >= height) {
2185 // drop a line (column)
2191 // first column (line)
2192 x = xFlip ? w1 - 1 : 0;
2195 // for each column (line)...
2196 for (j = 0; j < width; ++j) {
2198 // horizontal (vertical) Bresenham
2200 if ((qx += px2) >= width) {
2206 imgStr->getPixel(&pixBuf);
2211 if (dx > 0 && pixBuf == 0) {
2212 if (dx == 1 && dy == 1) {
2214 XPutPixel(image, y, x, color);
2216 XPutPixel(image, x, y, color);
2218 for (ix = 0; ix < dx; ++ix) {
2219 for (iy = 0; iy < dy; ++iy) {
2221 XPutPixel(image, yFlip ? y - iy : y + iy,
2222 xFlip ? x - ix : x + ix, color);
2224 XPutPixel(image, xFlip ? x - ix : x + ix,
2225 yFlip ? y - iy : y + iy, color);
2231 // next column (line)
2239 // next line (column)
2246 // blit the image into the pixmap
2247 XPutImage(display, pixmap, fillGC, image, x2, y2, x0, y0, w2, h2);
2253 XDestroyImage(image);
2256 inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *err) {
2262 r = xoutRound(x->r * rMul);
2263 g = xoutRound(x->g * gMul);
2264 b = xoutRound(x->b * bMul);
2265 pixel = ((Gulong)r << rShift) +
2266 ((Gulong)g << gShift) +
2267 ((Gulong)b << bShift);
2268 err->r = x->r - (double)r / rMul;
2269 err->g = x->g - (double)g / gMul;
2270 err->b = x->b - (double)b / bMul;
2271 } else if (numColors == 1) {
2272 gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
2285 r = xoutRound(x->r * (numColors - 1));
2286 g = xoutRound(x->g * (numColors - 1));
2287 b = xoutRound(x->b * (numColors - 1));
2288 pixel = colors[(r * numColors + g) * numColors + b];
2289 err->r = x->r - (double)r / (numColors - 1);
2290 err->g = x->g - (double)g / (numColors - 1);
2291 err->b = x->b - (double)b / (numColors - 1);
2296 void XOutputDev::drawImage(GfxState *state, Stream *str, int width,
2297 int height, GfxImageColorMap *colorMap,
2299 ImageStream *imgStr;
2301 int x0, y0; // top left corner of image
2302 int w0, h0, w1, h1; // size of image
2303 double xt, yt, wt, ht;
2304 GBool rotate, xFlip, yFlip;
2308 int px1, px2, qx, dx;
2309 int py1, py2, qy, dy;
2312 int nComps, nVals, nBits;
2315 RGBColor color2, err;
2316 RGBColor *errRight, *errDown;
2319 // get image position and size
2320 state->transform(0, 0, &xt, &yt);
2321 state->transformDelta(1, 1, &wt, &ht);
2326 x0 = xoutRound(xt + wt);
2327 w0 = xoutRound(-wt);
2333 y0 = xoutRound(yt + ht);
2334 h0 = xoutRound(-ht);
2336 state->transformDelta(1, 0, &xt, &yt);
2337 rotate = fabs(xt) < fabs(yt);
2351 nComps = colorMap->getNumPixelComps();
2352 nVals = width * nComps;
2353 nBits = colorMap->getBits();
2354 dither = nComps > 1 || nBits > 1;
2356 // check for tiny (zero width or height) images
2357 if (w0 == 0 || h0 == 0) {
2358 j = height * ((nVals * nBits + 7) / 8);
2360 for (i = 0; i < j; ++i)
2365 // Bresenham parameters
2367 px2 = w1 - px1 * width;
2369 py2 = h1 - py1 * height;
2372 image = XCreateImage(display, DefaultVisual(display, screenNum),
2373 depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
2374 image->data = (char *)gmalloc(h0 * image->bytes_per_line);
2376 // allocate error diffusion accumulators
2378 errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor));
2379 errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor));
2380 for (j = 0; j < w1; ++j)
2381 errDown[j].r = errDown[j].g = errDown[j].b = 0;
2387 // initialize the image stream
2388 imgStr = new ImageStream(str, width, nComps, nBits);
2391 // first line (column)
2392 y = yFlip ? h1 - 1 : 0;
2396 for (i = 0; i < height; ++i) {
2398 // vertical (horizontal) Bresenham
2400 if ((qy += py2) >= height) {
2405 // drop a line (column)
2411 // first column (line)
2412 x = xFlip ? w1 - 1 : 0;
2415 // clear error accumulator
2417 for (j = 0; j <= py1; ++j)
2418 errRight[j].r = errRight[j].g = errRight[j].b = 0;
2421 // for each column (line)...
2422 for (j = 0; j < width; ++j) {
2424 // horizontal (vertical) Bresenham
2426 if ((qx += px2) >= width) {
2432 imgStr->getPixel(pixBuf);
2436 colorMap->getColor(pixBuf, &color);
2446 pixel = findColor(&color2, &err);
2448 if (dx == 1 && dy == 1) {
2450 color2.r = r1 + errRight[0].r + errDown[x].r;
2453 else if (color2.r < 0)
2455 color2.g = g1 + errRight[0].g + errDown[x].g;
2458 else if (color2.g < 0)
2460 color2.b = b1 + errRight[0].b + errDown[x].b;
2463 else if (color2.b < 0)
2465 pixel = findColor(&color2, &err);
2466 errRight[0].r = errDown[x].r = err.r / 2;
2467 errRight[0].g = errDown[x].g = err.g / 2;
2468 errRight[0].b = errDown[x].b = err.b / 2;
2471 XPutPixel(image, y, x, pixel);
2473 XPutPixel(image, x, y, pixel);
2475 for (iy = 0; iy < dy; ++iy) {
2476 for (ix = 0; ix < dx; ++ix) {
2478 color2.r = r1 + errRight[iy].r +
2479 errDown[xFlip ? x - ix : x + ix].r;
2482 else if (color2.r < 0)
2484 color2.g = g1 + errRight[iy].g +
2485 errDown[xFlip ? x - ix : x + ix].g;
2488 else if (color2.g < 0)
2490 color2.b = b1 + errRight[iy].b +
2491 errDown[xFlip ? x - ix : x + ix].b;
2494 else if (color2.b < 0)
2496 pixel = findColor(&color2, &err);
2497 errRight[iy].r = errDown[xFlip ? x - ix : x + ix].r =
2499 errRight[iy].g = errDown[xFlip ? x - ix : x + ix].g =
2501 errRight[iy].b = errDown[xFlip ? x - ix : x + ix].b =
2505 XPutPixel(image, yFlip ? y - iy : y + iy,
2506 xFlip ? x - ix : x + ix, pixel);
2508 XPutPixel(image, xFlip ? x - ix : x + ix,
2509 yFlip ? y - iy : y + iy, pixel);
2515 // next column (line)
2523 // next line (column)
2530 // blit the image into the pixmap
2531 XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0);
2537 XDestroyImage(image);
2542 Gulong XOutputDev::findColor(GfxColor *color) {
2548 r = xoutRound(color->getR() * rMul);
2549 g = xoutRound(color->getG() * gMul);
2550 b = xoutRound(color->getB() * bMul);
2551 pixel = ((Gulong)r << rShift) +
2552 ((Gulong)g << gShift) +
2553 ((Gulong)b << bShift);
2554 } else if (numColors == 1) {
2555 gray = color->getGray();
2561 r = xoutRound(color->getR() * (numColors - 1));
2562 g = xoutRound(color->getG() * (numColors - 1));
2563 b = xoutRound(color->getB() * (numColors - 1));
2564 #if 0 //~ this makes things worse as often as better
2565 // even a very light color shouldn't map to white
2566 if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
2567 if (color->getR() < 0.95)
2569 if (color->getG() < 0.95)
2571 if (color->getB() < 0.95)
2575 pixel = colors[(r * numColors + g) * numColors + b];
2580 GBool XOutputDev::findText(char *s, GBool top, GBool bottom,
2581 int *xMin, int *yMin, int *xMax, int *yMax) {
2582 double xMin1, yMin1, xMax1, yMax1;
2584 xMin1 = (double)*xMin;
2585 yMin1 = (double)*yMin;
2586 xMax1 = (double)*xMax;
2587 yMax1 = (double)*yMax;
2588 if (text->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
2589 *xMin = xoutRound(xMin1);
2590 *xMax = xoutRound(xMax1);
2591 *yMin = xoutRound(yMin1);
2592 *yMax = xoutRound(yMax1);
2598 GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
2599 return text->getText((double)xMin, (double)yMin,
2600 (double)xMax, (double)yMax);