1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
27 #include "TextOutputDev.h"
28 #include "XOutputDev.h"
30 #include "XOutputFontInfo.h"
32 #ifdef XlibSpecificationRelease
33 #if XlibSpecificationRelease < 5
34 typedef char *XPointer;
37 typedef char *XPointer;
40 //------------------------------------------------------------------------
41 // Constants and macros
42 //------------------------------------------------------------------------
44 #define xoutRound(x) ((int)(x + 0.5))
46 #define maxCurveSplits 6 // max number of splits when recursively
47 // drawing Bezier curves
49 //------------------------------------------------------------------------
51 //------------------------------------------------------------------------
57 //------------------------------------------------------------------------
59 //------------------------------------------------------------------------
64 GfxFontEncoding *encoding;
67 static FontMapEntry fontMap[] = {
69 "-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",
72 "-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",
74 {"Courier-BoldOblique",
75 "-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",
78 "-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",
81 "-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",
84 "-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",
86 {"Helvetica-BoldOblique",
87 "-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1",
90 "-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1",
93 "-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific",
96 "-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1",
99 "-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1",
102 "-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1",
105 "-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1",
108 "-*-zapfdingbats-medium-r-normal-*-%s-*-*-*-*-*-*-*",
109 &zapfDingbatsEncoding},
113 static FontMapEntry *userFontMap;
115 //------------------------------------------------------------------------
116 // Font substitutions
117 //------------------------------------------------------------------------
124 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
125 static FontSubst fontSubst[16] = {
126 {"-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.833},
127 {"-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.833},
128 {"-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.889},
129 {"-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.889},
130 {"-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.788},
131 {"-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1", 0.722},
132 {"-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.833},
133 {"-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1", 0.778},
134 {"-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
135 {"-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
136 {"-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
137 {"-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", 0.600},
138 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
139 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
140 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
141 {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576}
144 //------------------------------------------------------------------------
146 //------------------------------------------------------------------------
150 static char *japan12Font = "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-jisx0208.1983-0";
153 static Gushort japan12Map[96] = {
154 0x2120, 0x2120, 0x212a, 0x2149, 0x2174, 0x2170, 0x2173, 0x2175, // 00 .. 07
155 0x2147, 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x213e, 0x2123, // 08 .. 0f
156 0x213f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, // 10 .. 17
157 0x2337, 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, // 18 .. 1f
158 0x2129, 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, // 20 .. 27
159 0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, // 28 .. 2f
160 0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, // 30 .. 37
161 0x2357, 0x2358, 0x2359, 0x235a, 0x214e, 0x216f, 0x214f, 0x2130, // 38 .. 3f
162 0x2132, 0x2146, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, // 40 .. 47
163 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, // 48 .. 4f
164 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, // 50 .. 57
165 0x2377, 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141 // 58 .. 5f
169 static Gushort japan12KanaMap1[97] = {
170 0x2131, 0x2121, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572,
171 0x2521, 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567,
172 0x2543, 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b,
173 0x252d, 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b,
174 0x253d, 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b,
175 0x254c, 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b,
176 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568,
177 0x2569, 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b,
178 0x212c, 0x212e, 0x2570, 0x2571, 0x256e, 0x2575, 0x2576, 0x2574,
179 0x252c, 0x252e, 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a,
180 0x253c, 0x253e, 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x2550,
181 0x2551, 0x2553, 0x2554, 0x2556, 0x2557, 0x2559, 0x255a, 0x255c,
186 static Gushort japan12KanaMap2[98] = {
187 0x212d, 0x212f, 0x216d, 0x214c, 0x214d, 0x2152, 0x2153, 0x2154,
188 0x2155, 0x2158, 0x2159, 0x215a, 0x215b, 0x213d, 0x2121, 0x2472,
189 0x2421, 0x2423, 0x2425, 0x2427, 0x2429, 0x2463, 0x2465, 0x2467,
190 0x2443, 0x2422, 0x2424, 0x2426, 0x2428, 0x242a, 0x242b, 0x242d,
191 0x242f, 0x2431, 0x2433, 0x2435, 0x2437, 0x2439, 0x243b, 0x243d,
192 0x243f, 0x2441, 0x2444, 0x2446, 0x2448, 0x244a, 0x244b, 0x244c,
193 0x244d, 0x244e, 0x244f, 0x2452, 0x2455, 0x2458, 0x245b, 0x245e,
194 0x245f, 0x2460, 0x2461, 0x2462, 0x2464, 0x2466, 0x2468, 0x2469,
195 0x246a, 0x246b, 0x246c, 0x246d, 0x246f, 0x2473, 0x2470, 0x2471,
196 0x246e, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438,
197 0x243a, 0x243c, 0x243e, 0x2440, 0x2442, 0x2445, 0x2447, 0x2449,
198 0x2450, 0x2451, 0x2453, 0x2454, 0x2456, 0x2457, 0x2459, 0x245a,
202 static char *japan12Roman[10] = {
203 "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"
206 static char *japan12Abbrev1[6] = {
207 "mm", "cm", "km", "mg", "kg", "cc"
212 //------------------------------------------------------------------------
213 // Constructed characters
214 //------------------------------------------------------------------------
216 #define lastRegularChar 0x0ff
217 #define firstSubstChar 0x100
218 #define lastSubstChar 0x104
219 #define firstConstrChar 0x105
220 #define lastConstrChar 0x106
221 #define firstMultiChar 0x107
222 #define lastMultiChar 0x10d
225 static Guchar substChars[] = {
226 0x27, // 100: quotesingle --> quoteright
227 0x2d, // 101: emdash --> hyphen
228 0xad, // 102: hyphen --> endash
229 0x2f, // 103: fraction --> slash
230 0xb0, // 104: ring --> degree
238 static char *multiChars[] = {
243 "...", // 10b: ellipsis
244 "``", // 10c: quotedblleft
245 "''" // 10d: quotedblright
262 // 119: guilsinglleft
263 // 11a: guilsinglright
269 // 120: quotesinglbase
274 //------------------------------------------------------------------------
276 //------------------------------------------------------------------------
278 // Note: if real font is substantially narrower than substituted
279 // font, the size is reduced accordingly.
280 XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12,
281 double m21, double m22, Display *display1) {
284 GfxFontEncoding *encoding;
286 char fontName[200], fontSize[100];
298 id = gfxFont->getID();
307 // construct X font name
308 if (gfxFont->is16Bit()) {
309 fontNameFmt = fontSubst[0].xFont;
310 switch (gfxFont->getCharSet16()) {
311 case font16AdobeJapan12:
313 fontNameFmt = japan12Font;
318 pdfFont = gfxFont->getName();
320 for (p = userFontMap; p->pdfFont; ++p) {
321 if (!pdfFont->cmp(p->pdfFont))
325 for (p = fontMap; p->pdfFont; ++p) {
326 if (!pdfFont->cmp(p->pdfFont))
333 if (p && p->pdfFont) {
334 fontNameFmt = p->xFont;
335 encoding = p->encoding;
337 encoding = &isoLatin1Encoding;
338 //~ Some non-symbolic fonts are tagged as symbolic.
339 // if (gfxFont->isSymbolic()) {
341 // encoding = symbolEncoding;
343 if (gfxFont->isFixedWidth()) {
345 } else if (gfxFont->isSerif()) {
350 if (gfxFont->isBold())
352 if (gfxFont->isItalic())
354 if ((code = gfxFont->getCharCode("m")) >= 0)
355 w1 = gfxFont->getWidth(code);
358 w2 = fontSubst[index].mWidth;
359 if (gfxFont->getType() == fontType3) {
360 // This is a hack which makes it possible to substitute for some
361 // Type 3 fonts. The problem is that it's impossible to know what
362 // the base coordinate system used in the font is without actually
363 // rendering the font. This code tries to guess by looking at the
364 // width of the character 'm' (which breaks if the font is a
365 // subset that doesn't contain 'm').
366 if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
373 fm = gfxFont->getFontMatrix();
374 v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
377 } else if (!gfxFont->isSymbolic()) {
378 if (w1 > 0.01 && w1 < 0.9 * w2) {
388 fontNameFmt = fontSubst[index].xFont;
391 // Construct forward and reverse map.
392 // This tries to deal with font subset character names of the
393 // form 'Bxx', 'Cxx', 'Gxx', with decimal or hex numbering.
394 for (code = 0; code < 256; ++code)
397 for (code = 0; code < 256; ++code) {
398 if ((charName = gfxFont->getCharName(code))) {
399 if ((charName[0] == 'B' || charName[0] == 'C' ||
400 charName[0] == 'G') &&
401 strlen(charName) == 3 &&
402 ((charName[1] >= 'a' && charName[1] <= 'f') ||
403 (charName[1] >= 'A' && charName[1] <= 'F') ||
404 (charName[2] >= 'a' && charName[2] <= 'f') ||
405 (charName[2] >= 'A' && charName[2] <= 'F'))) {
411 for (code = 0; code < 256; ++code) {
412 if ((charName = gfxFont->getCharName(code))) {
413 if ((code2 = encoding->getCharCode(charName)) < 0) {
414 n = strlen(charName);
416 (charName[0] == 'B' || charName[0] == 'C' ||
417 charName[0] == 'G') &&
418 isxdigit(charName[1]) && isxdigit(charName[2])) {
419 sscanf(charName+1, "%x", &code2);
420 } else if (!hex && n >= 2 && n <= 3 &&
421 isdigit(charName[0]) && isdigit(charName[1])) {
422 code2 = atoi(charName);
425 } else if (!hex && n >= 3 && n <= 5 && isdigit(charName[1])) {
426 code2 = atoi(charName+1);
430 //~ this is a kludge -- is there a standard internal encoding
431 //~ used by all/most Type 1 fonts?
432 if (code2 == 262) // hyphen
434 else if (code2 == 266) // emdash
438 map[code] = (Gushort)code2;
440 revMap[code2] = (Guchar)code;
449 code2 = 0; // to make gcc happy
450 //~ this is a hack to get around the fact that X won't draw
451 //~ chars 0..31; this works when the fonts have duplicate encodings
453 for (code = 0; code < 32; ++code) {
454 if ((charName = gfxFont->getCharName(code)) &&
455 (code2 = gfxFont->getCharCode(charName)) >= 0) {
456 map[code] = (Gushort)code2;
458 revMap[code2] = (Guchar)code;
461 for (code = 32; code < 256; ++code) {
462 map[code] = (Gushort)code;
463 revMap[code] = (Guchar)code;
468 // compute size, normalize matrix
469 size = sqrt(mat21*mat21 + mat22*mat22);
470 mat11 = mat11 / size;
471 mat12 = -mat12 / size;
472 mat21 = mat21 / size;
473 mat22 = -mat22 / size;
474 startSize = (int)size;
476 // try to get a rotated font?
477 rotated = !(mat11 > 0 && mat22 > 0 && fabs(mat11/mat22 - 1) < 0.2 &&
478 fabs(mat12) < 0.01 && fabs(mat21) < 0.01);
480 // open X font -- if font is not found (which means the server can't
481 // scale fonts), try progressively smaller and then larger sizes
482 //~ This does a linear search -- it should get a list of fonts from
483 //~ the server and pick the closest.
485 sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
486 mat11<0 ? "~" : "", fabs(mat11 * startSize),
487 mat12<0 ? "~" : "", fabs(mat12 * startSize),
488 mat21<0 ? "~" : "", fabs(mat21 * startSize),
489 mat22<0 ? "~" : "", fabs(mat22 * startSize));
491 sprintf(fontSize, "%d", startSize);
492 sprintf(fontName, fontNameFmt, fontSize);
493 xFont = XLoadQueryFont(display, fontName);
495 for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
496 sprintf(fontSize, "%d", sz);
497 sprintf(fontName, fontNameFmt, fontSize);
498 if ((xFont = XLoadQueryFont(display, fontName)))
502 for (sz = startSize + 1; sz < startSize + 10; ++sz) {
503 sprintf(fontSize, "%d", sz);
504 sprintf(fontName, fontNameFmt, fontSize);
505 if ((xFont = XLoadQueryFont(display, fontName)))
509 sprintf(fontSize, "%d", startSize);
510 sprintf(fontName, fontNameFmt, fontSize);
511 error(-1, "Failed to open font: '%s'", fontName);
518 XOutputFont::~XOutputFont() {
520 XFreeFont(display, xFont);
523 //------------------------------------------------------------------------
525 //------------------------------------------------------------------------
527 XOutputFontCache::XOutputFontCache(Display *display1) {
531 for (i = 0; i < fontCacheSize; ++i)
536 XOutputFontCache::~XOutputFontCache() {
539 for (i = 0; i < numFonts; ++i)
543 XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont,
544 double m11, double m12,
545 double m21, double m22) {
549 // is it the most recently used font?
550 if (numFonts > 0 && fonts[0]->matches(gfxFont->getID(),
554 // is it in the cache?
555 for (i = 1; i < numFonts; ++i) {
556 if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
558 for (j = i; j > 0; --j)
559 fonts[j] = fonts[j-1];
566 font = new XOutputFont(gfxFont, m11, m12, m21, m22, display);
567 if (!font->getXFont()) {
572 // insert font in cache
573 if (numFonts == fontCacheSize) {
575 delete fonts[numFonts];
577 for (j = numFonts; j > 0; --j)
578 fonts[j] = fonts[j-1];
586 //------------------------------------------------------------------------
588 //------------------------------------------------------------------------
590 XOutputDev::XOutputDev(Display *display1, Pixmap pixmap1, Guint depth1,
591 Colormap colormap, unsigned long paperColor) {
592 XVisualInfo visualTempl;
593 XVisualInfo *visualList;
599 int r, g, b, n, m, i;
602 // get display/pixmap info
604 screenNum = DefaultScreen(display);
608 // check for TrueColor visual
611 depth = DefaultDepth(display, screenNum);
612 visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals);
613 for (i = 0; i < nVisuals; ++i) {
614 if (visualList[i].visual == DefaultVisual(display, screenNum)) {
615 if (visualList[i].c_class == TrueColor) {
617 mask = visualList[i].red_mask;
619 while (mask && !(mask & 1)) {
624 mask = visualList[i].green_mask;
626 while (mask && !(mask & 1)) {
631 mask = visualList[i].blue_mask;
633 while (mask && !(mask & 1)) {
642 XFree((XPointer)visualList);
645 // allocate a color cube
648 // set colors in private colormap
650 for (numColors = 6; numColors >= 2; --numColors) {
651 m = numColors * numColors * numColors;
652 if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m))
655 if (numColors >= 2) {
656 m = numColors * numColors * numColors;
657 xcolors = (XColor *)gmalloc(m * sizeof(XColor));
659 for (r = 0; r < numColors; ++r) {
660 for (g = 0; g < numColors; ++g) {
661 for (b = 0; b < numColors; ++b) {
662 xcolors[n].pixel = colors[n];
663 xcolors[n].red = (r * 65535) / (numColors - 1);
664 xcolors[n].green = (g * 65535) / (numColors - 1);
665 xcolors[n].blue = (b * 65535) / (numColors - 1);
666 xcolors[n].flags = DoRed | DoGreen | DoBlue;
671 XStoreColors(display, colormap, xcolors, m);
675 colors[0] = BlackPixel(display, screenNum);
676 colors[1] = WhitePixel(display, screenNum);
679 // allocate colors in shared colormap
681 if (rgbCubeSize > maxRGBCube)
682 rgbCubeSize = maxRGBCube;
684 for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
687 for (r = 0; r < numColors && ok; ++r) {
688 for (g = 0; g < numColors && ok; ++g) {
689 for (b = 0; b < numColors && ok; ++b) {
691 colors[n++] = BlackPixel(display, screenNum);
693 xcolor.red = (r * 65535) / (numColors - 1);
694 xcolor.green = (g * 65535) / (numColors - 1);
695 xcolor.blue = (b * 65535) / (numColors - 1);
696 if (XAllocColor(display, colormap, &xcolor))
697 colors[n++] = xcolor.pixel;
706 XFreeColors(display, colormap, &colors[1], n-1, 0);
710 colors[0] = BlackPixel(display, screenNum);
711 colors[1] = WhitePixel(display, screenNum);
717 gcValues.foreground = BlackPixel(display, screenNum);
718 gcValues.background = WhitePixel(display, screenNum);
719 gcValues.line_width = 0;
720 gcValues.line_style = LineSolid;
721 strokeGC = XCreateGC(display, pixmap,
722 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
724 fillGC = XCreateGC(display, pixmap,
725 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
727 gcValues.foreground = paperColor;
728 paperGC = XCreateGC(display, pixmap,
729 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
732 // no clip region yet
736 for (n = 0; devFontMap[n].pdfFont; ++n) ;
737 userFontMap = (FontMapEntry *)gmalloc((n+1) * sizeof(FontMapEntry));
738 for (i = 0; i < n; ++i) {
739 userFontMap[i].pdfFont = devFontMap[i].pdfFont;
740 userFontMap[i].xFont = devFontMap[i].devFont;
741 m = strlen(userFontMap[i].xFont);
742 if (m >= 10 && !strcmp(userFontMap[i].xFont + m - 10, "-iso8859-2"))
743 userFontMap[i].encoding = &isoLatin2Encoding;
744 else if (m >= 13 && !strcmp(userFontMap[i].xFont + m - 13,
746 userFontMap[i].encoding = NULL;
748 userFontMap[i].encoding = &isoLatin1Encoding;
750 userFontMap[n].pdfFont = NULL;
752 // set up the font cache and fonts
755 fontCache = new XOutputFontCache(display);
760 // create text object
761 text = new TextPage(gFalse);
764 XOutputDev::~XOutputDev() {
767 XFreeGC(display, strokeGC);
768 XFreeGC(display, fillGC);
769 XFreeGC(display, paperGC);
771 XDestroyRegion(clipRegion);
775 void XOutputDev::startPage(int pageNum, GfxState *state) {
784 XFreeGC(display, s->strokeGC);
785 XFreeGC(display, s->fillGC);
786 XDestroyRegion(s->clipRegion);
791 // default line flatness
795 gcValues.foreground = BlackPixel(display, screenNum);
796 gcValues.background = WhitePixel(display, screenNum);
797 gcValues.line_width = 0;
798 gcValues.line_style = LineSolid;
799 XChangeGC(display, strokeGC,
800 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
802 XChangeGC(display, fillGC,
803 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
806 // clear clipping region
808 XDestroyRegion(clipRegion);
809 clipRegion = XCreateRegion();
811 rect.width = pixmapW;
812 rect.height = pixmapH;
813 XUnionRectWithRegion(&rect, clipRegion, clipRegion);
814 XSetRegion(display, strokeGC, clipRegion);
815 XSetRegion(display, fillGC, clipRegion);
822 XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
828 void XOutputDev::endPage() {
832 void XOutputDev::drawLinkBorder(double x1, double y1, double x2, double y2,
838 color.setRGB(0, 0, 1);
839 XSetForeground(display, strokeGC, findColor(&color));
840 XSetLineAttributes(display, strokeGC, xoutRound(w),
841 LineSolid, CapRound, JoinRound);
842 cvtUserToDev(x1, y1, &x, &y);
843 points[0].x = points[4].x = x;
844 points[0].y = points[4].y = y;
845 cvtUserToDev(x2, y1, &x, &y);
848 cvtUserToDev(x2, y2, &x, &y);
851 cvtUserToDev(x1, y2, &x, &y);
854 XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
857 void XOutputDev::saveState(GfxState *state) {
861 // save current state
862 s = new XOutputState;
863 s->strokeGC = strokeGC;
865 s->clipRegion = clipRegion;
867 // push onto state stack
871 // create a new current state by copying
872 strokeGC = XCreateGC(display, pixmap, 0, &values);
873 XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
874 fillGC = XCreateGC(display, pixmap, 0, &values);
875 XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
876 clipRegion = XCreateRegion();
877 XUnionRegion(s->clipRegion, clipRegion, clipRegion);
878 XSetRegion(display, strokeGC, clipRegion);
879 XSetRegion(display, fillGC, clipRegion);
882 void XOutputDev::restoreState(GfxState *state) {
886 // kill current state
887 XFreeGC(display, strokeGC);
888 XFreeGC(display, fillGC);
889 XDestroyRegion(clipRegion);
892 flatness = state->getFlatness();
893 strokeGC = save->strokeGC;
894 fillGC = save->fillGC;
895 clipRegion = save->clipRegion;
896 XSetRegion(display, strokeGC, clipRegion);
897 XSetRegion(display, fillGC, clipRegion);
906 void XOutputDev::updateAll(GfxState *state) {
907 updateLineAttrs(state, gTrue);
908 updateFlatness(state);
909 updateMiterLimit(state);
910 updateFillColor(state);
911 updateStrokeColor(state);
915 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
916 double m21, double m22, double m31, double m32) {
917 updateLineAttrs(state, gTrue);
920 void XOutputDev::updateLineDash(GfxState *state) {
921 updateLineAttrs(state, gTrue);
924 void XOutputDev::updateFlatness(GfxState *state) {
925 flatness = state->getFlatness();
928 void XOutputDev::updateLineJoin(GfxState *state) {
929 updateLineAttrs(state, gFalse);
932 void XOutputDev::updateLineCap(GfxState *state) {
933 updateLineAttrs(state, gFalse);
937 void XOutputDev::updateMiterLimit(GfxState *state) {
940 void XOutputDev::updateLineWidth(GfxState *state) {
941 updateLineAttrs(state, gFalse);
944 void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
953 width = state->getTransformedLineWidth();
954 switch (state->getLineCap()) {
955 case 0: cap = CapButt; break;
956 case 1: cap = CapRound; break;
957 case 2: cap = CapProjecting; break;
959 error(-1, "Bad line cap style (%d)", state->getLineCap());
963 switch (state->getLineJoin()) {
964 case 0: join = JoinMiter; break;
965 case 1: join = JoinRound; break;
966 case 2: join = JoinBevel; break;
968 error(-1, "Bad line join style (%d)", state->getLineJoin());
972 state->getLineDash(&dashPattern, &dashLength, &dashStart);
973 XSetLineAttributes(display, strokeGC, xoutRound(width),
974 dashLength > 0 ? LineOnOffDash : LineSolid,
976 if (updateDash && dashLength > 0) {
979 for (i = 0; i < dashLength; ++i) {
980 dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
981 if (dashList[i] == 0)
984 XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
988 void XOutputDev::updateFillColor(GfxState *state) {
989 XSetForeground(display, fillGC, findColor(state->getFillColor()));
992 void XOutputDev::updateStrokeColor(GfxState *state) {
993 XSetForeground(display, strokeGC, findColor(state->getStrokeColor()));
996 void XOutputDev::updateFont(GfxState *state) {
997 double m11, m12, m21, m22;
999 if (!(gfxFont = state->getFont())) {
1003 state->getFontTransMat(&m11, &m12, &m21, &m22);
1004 m11 *= state->getHorizScaling();
1005 m21 *= state->getHorizScaling();
1006 font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
1008 XSetFont(display, fillGC, font->getXFont()->fid);
1009 XSetFont(display, strokeGC, font->getXFont()->fid);
1013 void XOutputDev::stroke(GfxState *state) {
1016 int n, size, numPoints, i, j;
1019 n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse);
1021 // draw each subpath
1023 for (i = 0; i < n; ++i) {
1024 XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
1029 // free points and lengths arrays
1030 if (points != tmpPoints)
1032 if (lengths != tmpLengths)
1036 void XOutputDev::fill(GfxState *state) {
1037 doFill(state, WindingRule);
1040 void XOutputDev::eoFill(GfxState *state) {
1041 doFill(state, EvenOddRule);
1045 // X doesn't color the pixels on the right-most and bottom-most
1046 // borders of a polygon. This means that one-pixel-thick polygons
1047 // are not colored at all. I think this is supposed to be a
1048 // feature, but I can't figure out why. So after it fills a
1049 // polygon, it also draws lines around the border. This is done
1050 // only for single-component polygons, since it's not very
1051 // compatible with the compound polygon kludge (see convertPath()).
1053 void XOutputDev::doFill(GfxState *state, int rule) {
1056 int n, size, numPoints, i, j;
1059 XSetFillRule(display, fillGC, rule);
1061 // transform points, build separate polygons
1062 n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
1066 for (i = 0; i < n; ++i) {
1067 XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
1068 Complex, CoordModeOrigin);
1069 if (state->getPath()->getNumSubpaths() == 1) {
1070 XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
1073 j += lengths[i] + 1;
1076 // free points and lengths arrays
1077 if (points != tmpPoints)
1079 if (lengths != tmpLengths)
1083 void XOutputDev::clip(GfxState *state) {
1084 doClip(state, WindingRule);
1087 void XOutputDev::eoClip(GfxState *state) {
1088 doClip(state, EvenOddRule);
1091 void XOutputDev::doClip(GfxState *state, int rule) {
1092 Region region, region2;
1095 int n, size, numPoints, i, j;
1097 // transform points, build separate polygons
1098 n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue);
1100 // construct union of subpath regions
1101 region = XPolygonRegion(points, lengths[0], rule);
1103 for (i = 1; i < n; ++i) {
1104 region2 = XPolygonRegion(points + j, lengths[i], rule);
1105 XUnionRegion(region2, region, region);
1106 XDestroyRegion(region2);
1107 j += lengths[i] + 1;
1110 // intersect region with clipping region
1111 XIntersectRegion(region, clipRegion, clipRegion);
1112 XDestroyRegion(region);
1113 XSetRegion(display, strokeGC, clipRegion);
1114 XSetRegion(display, fillGC, clipRegion);
1116 // free points and lengths arrays
1117 if (points != tmpPoints)
1119 if (lengths != tmpLengths)
1124 // Transform points in the path and convert curves to line segments.
1125 // Builds a set of subpaths and returns the number of subpaths.
1126 // If <fillHack> is set, close any unclosed subpaths and activate a
1127 // kludge for polygon fills: First, it divides up the subpaths into
1128 // non-overlapping polygons by simply comparing bounding rectangles.
1129 // Then it connects subaths within a single compound polygon to a single
1130 // point so that X can fill the polygon (sort of).
1132 int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size,
1133 int *numPoints, int **lengths, GBool fillHack) {
1135 BoundingRect *rects;
1137 int n, i, ii, j, k, k0;
1139 // get path and number of subpaths
1140 path = state->getPath();
1141 n = path->getNumSubpaths();
1143 // allocate lengths array
1144 if (n < numTmpSubpaths)
1145 *lengths = tmpLengths;
1147 *lengths = (int *)gmalloc(n * sizeof(int));
1149 // allocate bounding rectangles array
1151 if (n < numTmpSubpaths)
1154 rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
1160 *points = tmpPoints;
1161 *size = numTmpPoints;
1163 for (i = 0; i < n; ++i) {
1165 // transform the points
1167 convertSubpath(state, path->getSubpath(i), points, size, numPoints);
1169 // construct bounding rectangle
1171 rects[i].xMin = rects[i].xMax = (*points)[j].x;
1172 rects[i].yMin = rects[i].yMax = (*points)[j].y;
1173 for (k = j + 1; k < *numPoints; ++k) {
1174 if ((*points)[k].x < rects[i].xMin)
1175 rects[i].xMin = (*points)[k].x;
1176 else if ((*points)[k].x > rects[i].xMax)
1177 rects[i].xMax = (*points)[k].x;
1178 if ((*points)[k].y < rects[i].yMin)
1179 rects[i].yMin = (*points)[k].y;
1180 else if ((*points)[k].y > rects[i].yMax)
1181 rects[i].yMax = (*points)[k].y;
1185 // close subpath if necessary
1186 if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
1187 (*points)[*numPoints-1].y != (*points)[j].y)) {
1188 addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
1191 // length of this subpath
1192 (*lengths)[i] = *numPoints - j;
1194 // leave an extra point for compound fill hack
1196 addPoint(points, size, numPoints, 0, 0);
1199 // combine compound polygons
1204 // start with subpath i
1206 (*lengths)[j] = (*lengths)[i];
1208 (*points)[k + (*lengths)[i]] = (*points)[k0];
1209 k += (*lengths)[i] + 1;
1212 // combine overlapping polygons
1215 // look for the first subsequent subpath, if any, which overlaps
1216 for (ii = i; ii < n; ++ii) {
1217 if (((rects[ii].xMin > rect.xMin && rects[ii].xMin < rect.xMax) ||
1218 (rects[ii].xMax > rect.xMin && rects[ii].xMax < rect.xMax) ||
1219 (rects[ii].xMin < rect.xMin && rects[ii].xMax > rect.xMax)) &&
1220 ((rects[ii].yMin > rect.yMin && rects[ii].yMin < rect.yMax) ||
1221 (rects[ii].yMax > rect.yMin && rects[ii].yMax < rect.yMax) ||
1222 (rects[ii].yMin < rect.yMin && rects[ii].yMax > rect.yMax)))
1226 // if there is an overlap, combine the polygons
1228 for (; i <= ii; ++i) {
1229 if (rects[i].xMin < rect.xMin)
1230 rect.xMin = rects[j].xMin;
1231 if (rects[i].xMax > rect.xMax)
1232 rect.xMax = rects[j].xMax;
1233 if (rects[i].yMin < rect.yMin)
1234 rect.yMin = rects[j].yMin;
1235 if (rects[i].yMax > rect.yMax)
1236 rect.yMax = rects[j].yMax;
1237 (*lengths)[j] += (*lengths)[i] + 1;
1238 (*points)[k + (*lengths)[i]] = (*points)[k0];
1239 k += (*lengths)[i] + 1;
1242 } while (ii < n && i < n);
1247 // free bounding rectangles
1248 if (rects != tmpRects)
1258 // Transform points in a single subpath and convert curves to line
1261 void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
1262 XPoint **points, int *size, int *n) {
1263 double x0, y0, x1, y1, x2, y2, x3, y3;
1266 m = subpath->getNumPoints();
1269 if (i >= 1 && subpath->getCurve(i)) {
1270 state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
1271 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
1272 state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
1273 state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
1274 doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
1277 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
1278 addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
1285 // Subdivide a Bezier curve. This uses floating point to avoid
1286 // propagating rounding errors. (The curves look noticeably more
1287 // jagged with integer arithmetic.)
1289 void XOutputDev::doCurve(XPoint **points, int *size, int *n,
1290 double x0, double y0, double x1, double y1,
1291 double x2, double y2, double x3, double y3) {
1292 double x[(1<<maxCurveSplits)+1][3];
1293 double y[(1<<maxCurveSplits)+1][3];
1294 int next[1<<maxCurveSplits];
1296 double xx1, yy1, xx2, yy2;
1297 double dx, dy, mx, my, d1, d2;
1298 double xl0, yl0, xl1, yl1, xl2, yl2;
1299 double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
1303 flat = (double)(flatness * flatness);
1309 p2 = 1<<maxCurveSplits;
1310 x[p1][0] = x0; y[p1][0] = y0;
1311 x[p1][1] = x1; y[p1][1] = y1;
1312 x[p1][2] = x2; y[p1][2] = y2;
1313 x[p2][0] = x3; y[p2][0] = y3;
1316 while (p1 < (1<<maxCurveSplits)) {
1319 xl0 = x[p1][0]; yl0 = y[p1][0];
1320 xx1 = x[p1][1]; yy1 = y[p1][1];
1321 xx2 = x[p1][2]; yy2 = y[p1][2];
1323 xr3 = x[p2][0]; yr3 = y[p2][0];
1325 // compute distances from control points to midpoint of the
1326 // straight line (this is a bit of a hack, but it's much faster
1327 // than computing the actual distances to the line)
1328 mx = (xl0 + xr3) * 0.5;
1329 my = (yl0 + yr3) * 0.5;
1337 // if curve is flat enough, or no more divisions allowed then
1338 // add the straight line segment
1339 if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
1340 addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
1343 // otherwise, subdivide the curve
1345 xl1 = (xl0 + xx1) * 0.5;
1346 yl1 = (yl0 + yy1) * 0.5;
1347 xh = (xx1 + xx2) * 0.5;
1348 yh = (yy1 + yy2) * 0.5;
1349 xl2 = (xl1 + xh) * 0.5;
1350 yl2 = (yl1 + yh) * 0.5;
1351 xr2 = (xx2 + xr3) * 0.5;
1352 yr2 = (yy2 + yr3) * 0.5;
1353 xr1 = (xh + xr2) * 0.5;
1354 yr1 = (yh + yr2) * 0.5;
1355 xr0 = (xl2 + xr1) * 0.5;
1356 yr0 = (yl2 + yr1) * 0.5;
1358 // add the new subdivision points
1360 x[p1][1] = xl1; y[p1][1] = yl1;
1361 x[p1][2] = xl2; y[p1][2] = yl2;
1363 x[p3][0] = xr0; y[p3][0] = yr0;
1364 x[p3][1] = xr1; y[p3][1] = yr1;
1365 x[p3][2] = xr2; y[p3][2] = yr2;
1372 // Add a point to the points array. (This would use a generic resizable
1373 // array type if C++ supported parameterized types in some reasonable
1374 // way -- templates are a disgusting kludge.)
1376 void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
1379 if (*points == tmpPoints) {
1380 *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
1381 memcpy(*points, tmpPoints, *k * sizeof(XPoint));
1383 *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
1386 (*points)[*k].x = x;
1387 (*points)[*k].y = y;
1391 void XOutputDev::beginString(GfxState *state, GString *s) {
1392 text->beginString(state, s, font ? font->isHex() : gFalse);
1395 void XOutputDev::endString(GfxState *state) {
1399 void XOutputDev::drawChar(GfxState *state, double x, double y,
1400 double dx, double dy, Guchar c) {
1408 text->addChar(state, x, y, dx, dy, c);
1413 // check for invisible text -- this is used by Acrobat Capture
1414 if ((state->getRender() & 3) == 3)
1417 state->transform(x, y, &x1, &y1);
1418 c1 = font->mapChar(c);
1419 if (c1 <= lastRegularChar) {
1421 XDrawString(display, pixmap,
1422 (state->getRender() & 1) ? strokeGC : fillGC,
1423 xoutRound(x1), xoutRound(y1), &buf, 1);
1424 } else if (c1 <= lastSubstChar) {
1425 buf = (char)substChars[c1 - firstSubstChar];
1426 XDrawString(display, pixmap,
1427 (state->getRender() & 1) ? strokeGC : fillGC,
1428 xoutRound(x1), xoutRound(y1), &buf, 1);
1429 } else if (c1 <= lastConstrChar) {
1430 //~ need to deal with rotated text here
1431 switch (c1 - firstConstrChar) {
1433 tx = 0.25 * state->getTransformedFontSize() *
1434 gfxFont->getWidth(c);
1435 XFillRectangle(display, pixmap,
1436 (state->getRender() & 1) ? strokeGC : fillGC,
1438 xoutRound(y1 - 0.4 * font->getXFont()->ascent - tx),
1439 xoutRound(2 * tx), xoutRound(2 * tx));
1441 case 1: // trademark
1442 //~ this should use a smaller font
1443 // tx = state->getTransformedFontSize() *
1444 // (gfxFont->getWidth(c) -
1445 // gfxFont->getWidth(font->revCharMap('M')));
1446 tx = 0.9 * state->getTransformedFontSize() *
1447 gfxFont->getWidth(font->revMapChar('T'));
1448 y1 -= 0.33 * (double)font->getXFont()->ascent;
1450 XDrawString(display, pixmap,
1451 (state->getRender() & 1) ? strokeGC : fillGC,
1452 xoutRound(x1), xoutRound(y1), &buf, 1);
1455 XDrawString(display, pixmap,
1456 (state->getRender() & 1) ? strokeGC : fillGC,
1457 xoutRound(x1), xoutRound(y1), &buf, 1);
1460 } else if (c1 <= lastMultiChar) {
1461 p = multiChars[c1 - firstMultiChar];
1463 tx = gfxFont->getWidth(c);
1464 tx -= gfxFont->getWidth(font->revMapChar(p[n-1]));
1465 tx = tx * state->getTransformedFontSize() / (double)(n - 1);
1466 for (i = 0; i < n; ++i) {
1467 XDrawString(display, pixmap,
1468 (state->getRender() & 1) ? strokeGC : fillGC,
1469 xoutRound(x1), xoutRound(y1), p + i, 1);
1475 void XOutputDev::drawChar16(GfxState *state, double x, double y,
1476 double dx, double dy, int c) {
1480 #if JAPANESE_SUPPORT
1490 // check for invisible text -- this is used by Acrobat Capture
1491 if ((state->getRender() & 3) == 3)
1494 state->transform(x, y, &x1, &y1);
1497 switch (gfxFont->getCharSet16()) {
1499 // convert Adobe-Japan1-2 to JIS X 0208-1983
1500 case font16AdobeJapan12:
1501 #if JAPANESE_SUPPORT
1504 } else if (c <= 632) {
1508 c1 = japan12Map[c - 230];
1510 c1 = japan12KanaMap1[c - 325];
1514 c1 = japan12KanaMap2[c - 501];
1517 } else if (c <= 1124) {
1520 c1 = 0x2121 + (c - 633);
1522 c1 = 0x2221 + (c - 727);
1524 c1 = 0x223a + (c - 741);
1526 c1 = 0x224a + (c - 749);
1528 c1 = 0x225c + (c - 756);
1530 c1 = 0x2272 + (c - 771);
1533 } else if (c <= 841) {
1535 c1 = 0x2330 + (c - 780);
1537 c1 = 0x2341 + (c - 790);
1539 c1 = 0x2361 + (c - 816);
1540 } else if (c <= 1010) {
1542 c1 = 0x2421 + (c - 842);
1544 c1 = 0x2521 + (c - 925);
1547 c1 = 0x2621 + (c - 1011);
1549 c1 = 0x2641 + (c - 1035);
1551 c1 = 0x2721 + (c - 1059);
1553 c1 = 0x2751 + (c - 1092);
1555 } else if (c <= 4089) {
1556 t1 = (c - 1125) / 94;
1557 t2 = (c - 1125) % 94;
1558 c1 = 0x3021 + (t1 << 8) + t2;
1559 } else if (c <= 7477) {
1560 t1 = (c - 4090) / 94;
1561 t2 = (c - 4090) % 94;
1562 c1 = 0x5021 + (t1 << 8) + t2;
1563 } else if (c <= 7554) {
1565 } else if (c <= 7563) { // circled Arabic numbers 1..9
1566 c1 = 0x2331 + (c - 7555);
1567 c2[0].byte1 = c1 >> 8;
1568 c2[0].byte2 = c1 & 0xff;
1569 XDrawString16(display, pixmap,
1570 (state->getRender() & 1) ? strokeGC : fillGC,
1571 xoutRound(x1), xoutRound(y1), c2, 1);
1573 c2[0].byte1 = c1 >> 8;
1574 c2[0].byte2 = c1 & 0xff;
1575 XDrawString16(display, pixmap,
1576 (state->getRender() & 1) ? strokeGC : fillGC,
1577 xoutRound(x1), xoutRound(y1), c2, 1);
1579 } else if (c <= 7574) { // circled Arabic numbers 10..20
1582 for (i = 0; i < 2; ++i) {
1583 c1 = 0x2330 + (i == 0 ? (n / 10) : (n % 10));
1584 c2[0].byte1 = c1 >> 8;
1585 c2[0].byte2 = c1 & 0xff;
1586 XDrawString16(display, pixmap,
1587 (state->getRender() & 1) ? strokeGC : fillGC,
1588 xoutRound(x2), xoutRound(y1), c2, 1);
1589 x2 += 0.5 * state->getTransformedFontSize();
1592 c2[0].byte1 = c1 >> 8;
1593 c2[0].byte2 = c1 & 0xff;
1594 XDrawString16(display, pixmap,
1595 (state->getRender() & 1) ? strokeGC : fillGC,
1596 xoutRound(x1), xoutRound(y1), c2, 1);
1598 } else if (c <= 7584) { // Roman numbers I..X
1599 p = japan12Roman[c - 7575];
1608 c2[0].byte1 = c1 >> 8;
1609 c2[0].byte2 = c1 & 0xff;
1610 XDrawString16(display, pixmap,
1611 (state->getRender() & 1) ? strokeGC : fillGC,
1612 xoutRound(x1), xoutRound(y1), c2, 1);
1614 x1 += 0.2 * state->getTransformedFontSize();
1616 x1 += 0.5 * state->getTransformedFontSize();
1619 } else if (c <= 7632) {
1622 } else if (c <= 7606) {
1623 p = japan12Abbrev1[c - 7601];
1627 c2[0].byte1 = c1 >> 8;
1628 c2[0].byte2 = c1 & 0xff;
1629 XDrawString16(display, pixmap,
1630 (state->getRender() & 1) ? strokeGC : fillGC,
1631 xoutRound(x1), xoutRound(y1), c2, 1);
1632 x1 += 0.5 * state->getTransformedFontSize();
1643 error(-1, "Unsupported Adobe-Japan1-2 character: %d", c);
1645 #endif // JAPANESE_SUPPORT
1650 c2[0].byte1 = c1 >> 8;
1651 c2[0].byte2 = c1 & 0xff;
1652 XDrawString16(display, pixmap,
1653 (state->getRender() & 1) ? strokeGC : fillGC,
1654 xoutRound(x1), xoutRound(y1), c2, 1);
1658 void XOutputDev::drawImageMask(GfxState *state, Stream *str,
1659 int width, int height, GBool invert,
1662 int x0, y0; // top left corner of image
1663 int w0, h0, w1, h1; // size of image
1666 double xt, yt, wt, ht;
1667 GBool rotate, xFlip, yFlip;
1670 int px1, px2, qx, dx;
1671 int py1, py2, qy, dy;
1676 // get image position and size
1677 state->transform(0, 0, &xt, &yt);
1678 state->transformDelta(1, 1, &wt, &ht);
1683 x0 = xoutRound(xt + wt);
1684 w0 = xoutRound(-wt);
1690 y0 = xoutRound(yt + ht);
1691 h0 = xoutRound(-ht);
1693 state->transformDelta(1, 0, &xt, &yt);
1694 rotate = fabs(xt) < fabs(yt);
1708 color = findColor(state->getFillColor());
1710 // check for tiny (zero width or height) images
1711 if (w0 == 0 || h0 == 0) {
1712 j = height * ((width + 7) / 8);
1714 for (i = 0; i < j; ++i)
1719 // Bresenham parameters
1721 px2 = w1 - px1 * width;
1723 py2 = h1 - py1 * height;
1726 image = XCreateImage(display, DefaultVisual(display, screenNum),
1727 depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
1728 image->data = (char *)gmalloc(h0 * image->bytes_per_line);
1729 if (x0 + w0 > pixmapW)
1740 if (y0 + h0 > pixmapH)
1751 XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap,
1754 // initialize the image stream
1755 str->resetImage(width, 1, 1);
1757 // first line (column)
1758 y = yFlip ? h1 - 1 : 0;
1762 for (i = 0; i < height; ++i) {
1764 // vertical (horizontal) Bresenham
1766 if ((qy += py2) >= height) {
1771 // drop a line (column)
1773 str->skipImageLine();
1777 // first column (line)
1778 x = xFlip ? w1 - 1 : 0;
1781 // for each column (line)...
1782 for (j = 0; j < width; ++j) {
1784 // horizontal (vertical) Bresenham
1786 if ((qx += px2) >= width) {
1792 str->getImagePixel(&pixBuf);
1797 if (dx > 0 && pixBuf == 0) {
1798 if (dx == 1 && dy == 1) {
1800 XPutPixel(image, y, x, color);
1802 XPutPixel(image, x, y, color);
1804 for (ix = 0; ix < dx; ++ix) {
1805 for (iy = 0; iy < dy; ++iy) {
1807 XPutPixel(image, yFlip ? y - iy : y + iy,
1808 xFlip ? x - ix : x + ix, color);
1810 XPutPixel(image, xFlip ? x - ix : x + ix,
1811 yFlip ? y - iy : y + iy, color);
1817 // next column (line)
1825 // next line (column)
1832 // blit the image into the pixmap
1833 XPutImage(display, pixmap, fillGC, image, x2, y2, x0, y0, w2, h2);
1838 XDestroyImage(image);
1841 inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *err) {
1847 r = xoutRound(x->r * rMul);
1848 g = xoutRound(x->g * gMul);
1849 b = xoutRound(x->b * bMul);
1850 pixel = ((Gulong)r << rShift) +
1851 ((Gulong)g << gShift) +
1852 ((Gulong)b << bShift);
1853 err->r = x->r - (double)r / rMul;
1854 err->g = x->g - (double)g / gMul;
1855 err->b = x->b - (double)b / bMul;
1856 } else if (numColors == 1) {
1857 gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
1870 r = xoutRound(x->r * (numColors - 1));
1871 g = xoutRound(x->g * (numColors - 1));
1872 b = xoutRound(x->b * (numColors - 1));
1873 pixel = colors[(r * numColors + g) * numColors + b];
1874 err->r = x->r - (double)r / (numColors - 1);
1875 err->g = x->g - (double)g / (numColors - 1);
1876 err->b = x->b - (double)b / (numColors - 1);
1881 void XOutputDev::drawImage(GfxState *state, Stream *str, int width,
1882 int height, GfxImageColorMap *colorMap,
1885 int x0, y0; // top left corner of image
1886 int w0, h0, w1, h1; // size of image
1887 double xt, yt, wt, ht;
1888 GBool rotate, xFlip, yFlip;
1892 int px1, px2, qx, dx;
1893 int py1, py2, qy, dy;
1896 int nComps, nVals, nBits;
1899 RGBColor color2, err;
1900 RGBColor *errRight, *errDown;
1903 // get image position and size
1904 state->transform(0, 0, &xt, &yt);
1905 state->transformDelta(1, 1, &wt, &ht);
1910 x0 = xoutRound(xt + wt);
1911 w0 = xoutRound(-wt);
1917 y0 = xoutRound(yt + ht);
1918 h0 = xoutRound(-ht);
1920 state->transformDelta(1, 0, &xt, &yt);
1921 rotate = fabs(xt) < fabs(yt);
1935 nComps = colorMap->getNumPixelComps();
1936 nVals = width * nComps;
1937 nBits = colorMap->getBits();
1938 dither = nComps > 1 || nBits > 1;
1940 // check for tiny (zero width or height) images
1941 if (w0 == 0 || h0 == 0) {
1942 j = height * ((nVals * nBits + 7) / 8);
1944 for (i = 0; i < j; ++i)
1949 // Bresenham parameters
1951 px2 = w1 - px1 * width;
1953 py2 = h1 - py1 * height;
1956 image = XCreateImage(display, DefaultVisual(display, screenNum),
1957 depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
1958 image->data = (char *)gmalloc(h0 * image->bytes_per_line);
1960 // allocate error diffusion accumulators
1962 errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor));
1963 errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor));
1964 for (j = 0; j < w1; ++j)
1965 errDown[j].r = errDown[j].g = errDown[j].b = 0;
1971 // initialize the image stream
1972 str->resetImage(width, nComps, nBits);
1974 // first line (column)
1975 y = yFlip ? h1 - 1 : 0;
1979 for (i = 0; i < height; ++i) {
1981 // vertical (horizontal) Bresenham
1983 if ((qy += py2) >= height) {
1988 // drop a line (column)
1990 str->skipImageLine();
1994 // first column (line)
1995 x = xFlip ? w1 - 1 : 0;
1998 // clear error accumulator
2000 for (j = 0; j <= py1; ++j)
2001 errRight[j].r = errRight[j].g = errRight[j].b = 0;
2004 // for each column (line)...
2005 for (j = 0; j < width; ++j) {
2007 // horizontal (vertical) Bresenham
2009 if ((qx += px2) >= width) {
2015 str->getImagePixel(pixBuf);
2019 colorMap->getColor(pixBuf, &color);
2029 pixel = findColor(&color2, &err);
2031 if (dx == 1 && dy == 1) {
2033 color2.r = r1 + errRight[0].r + errDown[x].r;
2036 else if (color2.r < 0)
2038 color2.g = g1 + errRight[0].g + errDown[x].g;
2041 else if (color2.g < 0)
2043 color2.b = b1 + errRight[0].b + errDown[x].b;
2046 else if (color2.b < 0)
2048 pixel = findColor(&color2, &err);
2049 errRight[0].r = errDown[x].r = err.r / 2;
2050 errRight[0].g = errDown[x].g = err.g / 2;
2051 errRight[0].b = errDown[x].b = err.b / 2;
2054 XPutPixel(image, y, x, pixel);
2056 XPutPixel(image, x, y, pixel);
2058 for (iy = 0; iy < dy; ++iy) {
2059 for (ix = 0; ix < dx; ++ix) {
2061 color2.r = r1 + errRight[iy].r +
2062 errDown[xFlip ? x - ix : x + ix].r;
2065 else if (color2.r < 0)
2067 color2.g = g1 + errRight[iy].g +
2068 errDown[xFlip ? x - ix : x + ix].g;
2071 else if (color2.g < 0)
2073 color2.b = b1 + errRight[iy].b +
2074 errDown[xFlip ? x - ix : x + ix].b;
2077 else if (color2.b < 0)
2079 pixel = findColor(&color2, &err);
2080 errRight[iy].r = errDown[xFlip ? x - ix : x + ix].r =
2082 errRight[iy].g = errDown[xFlip ? x - ix : x + ix].g =
2084 errRight[iy].b = errDown[xFlip ? x - ix : x + ix].b =
2088 XPutPixel(image, yFlip ? y - iy : y + iy,
2089 xFlip ? x - ix : x + ix, pixel);
2091 XPutPixel(image, xFlip ? x - ix : x + ix,
2092 yFlip ? y - iy : y + iy, pixel);
2098 // next column (line)
2106 // next line (column)
2113 // blit the image into the pixmap
2114 XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0);
2119 XDestroyImage(image);
2124 Gulong XOutputDev::findColor(GfxColor *color) {
2130 r = xoutRound(color->getR() * rMul);
2131 g = xoutRound(color->getG() * gMul);
2132 b = xoutRound(color->getB() * bMul);
2133 pixel = ((Gulong)r << rShift) +
2134 ((Gulong)g << gShift) +
2135 ((Gulong)b << bShift);
2136 } else if (numColors == 1) {
2137 gray = color->getGray();
2143 r = xoutRound(color->getR() * (numColors - 1));
2144 g = xoutRound(color->getG() * (numColors - 1));
2145 b = xoutRound(color->getB() * (numColors - 1));
2146 #if 0 //~ this makes things worse as often as better
2147 // even a very light color shouldn't map to white
2148 if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
2149 if (color->getR() < 0.95)
2151 if (color->getG() < 0.95)
2153 if (color->getB() < 0.95)
2157 pixel = colors[(r * numColors + g) * numColors + b];
2162 GBool XOutputDev::findText(char *s, GBool top, GBool bottom,
2163 int *xMin, int *yMin, int *xMax, int *yMax) {
2164 double xMin1, yMin1, xMax1, yMax1;
2166 xMin1 = (double)*xMin;
2167 yMin1 = (double)*yMin;
2168 xMax1 = (double)*xMax;
2169 yMax1 = (double)*yMax;
2170 if (text->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
2171 *xMin = xoutRound(xMin1);
2172 *xMax = xoutRound(xMax1);
2173 *yMin = xoutRound(yMin1);
2174 *yMax = xoutRound(yMax1);
2180 GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
2181 return text->getText((double)xMin, (double)yMin,
2182 (double)xMax, (double)yMax);