1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
31 #include "UnicodeMap.h"
32 #include "CharCodeToUnicode.h"
35 #include "TextOutputDev.h"
36 #include "XOutputDev.h"
40 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
43 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
48 #if (__VMS_VER < 70000000)
49 extern "C" int unlink(char *filename);
53 #ifdef XlibSpecificationRelease
54 #if XlibSpecificationRelease < 5
55 typedef char *XPointer;
58 typedef char *XPointer;
61 //------------------------------------------------------------------------
62 // Constants and macros
63 //------------------------------------------------------------------------
65 #define xoutRound(x) ((int)(x + 0.5))
67 #define maxCurveSplits 6 // max number of splits when recursively
68 // drawing Bezier curves
70 //------------------------------------------------------------------------
72 //------------------------------------------------------------------------
74 struct XOutFontSubst {
79 // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
80 static XOutFontSubst xOutSubstFonts[16] = {
82 {"Helvetica-Oblique", 0.833},
83 {"Helvetica-Bold", 0.889},
84 {"Helvetica-BoldOblique", 0.889},
85 {"Times-Roman", 0.788},
86 {"Times-Italic", 0.722},
87 {"Times-Bold", 0.833},
88 {"Times-BoldItalic", 0.778},
90 {"Courier-Oblique", 0.600},
91 {"Courier-Bold", 0.600},
92 {"Courier-BoldOblique", 0.600},
99 //------------------------------------------------------------------------
101 static void outputToFile(void *stream, char *data, int len) {
102 fwrite(data, 1, len, (FILE *)stream);
105 //------------------------------------------------------------------------
107 //------------------------------------------------------------------------
109 XOutputFont::XOutputFont(Ref *idA, double m11OrigA, double m12OrigA,
110 double m21OrigA, double m22OrigA,
111 double m11A, double m12A, double m21A, double m22A,
112 Display *displayA, XOutputDev *xOutA) {
126 XOutputFont::~XOutputFont() {
129 void XOutputFont::getCharPath(GfxState *state,
130 CharCode c, Unicode *u, int ulen) {
134 //------------------------------------------------------------------------
136 //------------------------------------------------------------------------
138 XOutputT1Font::XOutputT1Font(Ref *idA, T1FontFile *fontFileA,
139 double m11OrigA, double m12OrigA,
140 double m21OrigA, double m22OrigA,
141 double m11A, double m12A,
142 double m21A, double m22A,
143 Display *displayA, XOutputDev *xOutA):
144 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
145 m11A, m12A, m21A, m22A, displayA, xOutA)
149 fontFile = fontFileA;
151 // create the transformed instance
156 font = new T1Font(fontFile, matrix);
159 XOutputT1Font::~XOutputT1Font() {
165 GBool XOutputT1Font::isOk() {
169 void XOutputT1Font::updateGC(GC gc) {
172 void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
174 double x, double y, double dx, double dy,
175 CharCode c, Unicode *u, int uLen) {
176 font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
177 (int)(rgb->r * 65535), (int)(rgb->g * 65535),
178 (int)(rgb->b * 65535), c, u[0]);
181 void XOutputT1Font::getCharPath(GfxState *state,
182 CharCode c, Unicode *u, int uLen) {
183 font->getCharPath(c, u[0], state);
185 #endif // HAVE_T1LIB_H
187 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
188 //------------------------------------------------------------------------
190 //------------------------------------------------------------------------
192 XOutputFTFont::XOutputFTFont(Ref *idA, FTFontFile *fontFileA,
193 double m11OrigA, double m12OrigA,
194 double m21OrigA, double m22OrigA,
195 double m11A, double m12A,
196 double m21A, double m22A,
197 Display *displayA, XOutputDev *xOutA):
198 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
199 m11A, m12A, m21A, m22A, displayA, xOutA)
203 fontFile = fontFileA;
205 // create the transformed instance
210 font = new FTFont(fontFile, matrix);
213 XOutputFTFont::~XOutputFTFont() {
219 GBool XOutputFTFont::isOk() {
223 void XOutputFTFont::updateGC(GC gc) {
226 void XOutputFTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
228 double x, double y, double dx, double dy,
229 CharCode c, Unicode *u, int uLen) {
230 font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
231 (int)(rgb->r * 65535), (int)(rgb->g * 65535),
232 (int)(rgb->b * 65535), c, uLen > 0 ? u[0] : 0);
235 void XOutputFTFont::getCharPath(GfxState *state,
236 CharCode c, Unicode *u, int uLen) {
237 font->getCharPath(c, u[0], state);
239 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
241 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
242 //------------------------------------------------------------------------
244 //------------------------------------------------------------------------
246 XOutputTTFont::XOutputTTFont(Ref *idA, TTFontFile *fontFileA,
247 double m11OrigA, double m12OrigA,
248 double m21OrigA, double m22OrigA,
249 double m11A, double m12A,
250 double m21A, double m22A,
251 Display *displayA, XOutputDev *xOutA):
252 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
253 m11A, m12A, m21A, m22A, displayA, xOutA)
257 fontFile = fontFileA;
259 // create the transformed instance
264 font = new TTFont(fontFile, matrix);
267 XOutputTTFont::~XOutputTTFont() {
273 GBool XOutputTTFont::isOk() {
277 void XOutputTTFont::updateGC(GC gc) {
280 void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h,
282 double x, double y, double dx, double dy,
283 CharCode c, Unicode *u, int uLen) {
284 font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y),
285 (int)(rgb->r * 65535), (int)(rgb->g * 65535),
286 (int)(rgb->b * 65535), c, u[0]);
288 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
290 //------------------------------------------------------------------------
291 // XOutputServer8BitFont
292 //------------------------------------------------------------------------
294 // Copy <fmt>, substituting <val> for one occurrence of "%s", into
296 static void stringSubst(char *buf, int bufSize, char *fmt, char *val) {
303 if (p[0] == '%' && p[1] == 's') {
305 while (*q && i < bufSize - 1) {
310 if (i < bufSize - 1) {
319 XOutputServer8BitFont::XOutputServer8BitFont(Ref *idA, GString *xlfdFmt,
321 CharCodeToUnicode *fontUMap,
322 double m11OrigA, double m12OrigA,
323 double m21OrigA, double m22OrigA,
324 double m11A, double m12A,
325 double m21A, double m22A,
328 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
329 m11A, m12A, m21A, m22A, displayA, xOutA)
331 double size, ntm11, ntm12, ntm21, ntm22;
334 char fontName[500], fontSize[100];
339 // compute size and normalized transform matrix
340 size = sqrt(m21*m21 + m22*m22);
346 // try to get a rotated font?
347 rotated = !(ntm11 > 0 && ntm22 > 0 &&
348 fabs(ntm11 / ntm22 - 1) < 0.2 &&
349 fabs(ntm12) < 0.01 &&
352 // open X font -- if font is not found (which means the server can't
353 // scale fonts), try progressively smaller and then larger sizes
354 startSize = (int)size;
356 sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
357 ntm11<0 ? "~" : "", fabs(ntm11 * size),
358 ntm12<0 ? "~" : "", fabs(ntm12 * size),
359 ntm21<0 ? "~" : "", fabs(ntm21 * size),
360 ntm22<0 ? "~" : "", fabs(ntm22 * size));
362 sprintf(fontSize, "%d", startSize);
364 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
365 xFont = XLoadQueryFont(display, fontName);
367 for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
368 sprintf(fontSize, "%d", sz);
369 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
370 if ((xFont = XLoadQueryFont(display, fontName)))
374 for (sz = startSize + 1; sz < startSize + 10; ++sz) {
375 sprintf(fontSize, "%d", sz);
376 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
378 if ((xFont = XLoadQueryFont(display, fontName))) {
383 sprintf(fontSize, "%d", startSize);
384 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
386 error(-1, "Failed to open font: '%s'", fontName);
392 // Construct char code map.
394 for (i = 0; i < 256; ++i) {
395 if (fontUMap->mapToUnicode((CID)i, &u, 1) == 1 &&
396 xUMap->mapUnicode(u, &buf, 1) == 1) {
404 XOutputServer8BitFont::~XOutputServer8BitFont() {
406 XFreeFont(display, xFont);
410 GBool XOutputServer8BitFont::isOk() {
411 return xFont != NULL;
414 void XOutputServer8BitFont::updateGC(GC gc) {
415 XSetFont(display, gc, xFont->fid);
418 void XOutputServer8BitFont::drawChar(GfxState *state, Pixmap pixmap,
419 int w, int h, GC gc, GfxRGB *rgb,
420 double x, double y, double dx, double dy,
421 CharCode c, Unicode *u, int uLen) {
430 XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), buf, 1);
432 // substituted character, using more than one character
434 for (i = 0; i < uLen; ++i) {
435 n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
441 for (i = 0; i < uLen; ++i) {
442 m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
443 for (j = 0; j < m; ++j) {
444 XDrawString(display, pixmap, gc,
445 xoutRound(x + k*dx1), xoutRound(y + k*dy1),
454 //------------------------------------------------------------------------
455 // XOutputServer16BitFont
456 //------------------------------------------------------------------------
458 XOutputServer16BitFont::XOutputServer16BitFont(Ref *idA, GString *xlfdFmt,
460 CharCodeToUnicode *fontUMap,
465 double m11A, double m12A,
466 double m21A, double m22A,
469 XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA,
470 m11A, m12A, m21A, m22A, displayA, xOutA)
472 double size, ntm11, ntm12, ntm21, ntm22;
475 char fontName[500], fontSize[100];
480 // compute size and normalized transform matrix
481 size = sqrt(m21*m21 + m22*m22);
487 // try to get a rotated font?
488 rotated = !(ntm11 > 0 && ntm22 > 0 &&
489 fabs(ntm11 / ntm22 - 1) < 0.2 &&
490 fabs(ntm12) < 0.01 &&
493 // open X font -- if font is not found (which means the server can't
494 // scale fonts), try progressively smaller and then larger sizes
495 startSize = (int)size;
497 sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
498 ntm11<0 ? "~" : "", fabs(ntm11 * size),
499 ntm12<0 ? "~" : "", fabs(ntm12 * size),
500 ntm21<0 ? "~" : "", fabs(ntm21 * size),
501 ntm22<0 ? "~" : "", fabs(ntm22 * size));
503 sprintf(fontSize, "%d", startSize);
505 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
506 xFont = XLoadQueryFont(display, fontName);
508 for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) {
509 sprintf(fontSize, "%d", sz);
510 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize);
511 if ((xFont = XLoadQueryFont(display, fontName)))
515 for (sz = startSize + 1; sz < startSize + 10; ++sz) {
516 sprintf(fontSize, "%d", sz);
517 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
519 if ((xFont = XLoadQueryFont(display, fontName))) {
524 sprintf(fontSize, "%d", startSize);
525 stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(),
527 error(-1, "Failed to open font: '%s'", fontName);
534 XOutputServer16BitFont::~XOutputServer16BitFont() {
537 XFreeFont(display, xFont);
541 GBool XOutputServer16BitFont::isOk() {
542 return xFont != NULL;
545 void XOutputServer16BitFont::updateGC(GC gc) {
546 XSetFont(display, gc, xFont->fid);
549 void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap,
550 int w, int h, GC gc, GfxRGB *rgb,
551 double x, double y, double dx, double dy,
552 CharCode c, Unicode *u, int uLen) {
559 for (i = 0; i < uLen; ++i) {
560 n += xUMap->mapUnicode(u[i], buf, sizeof(buf));
566 for (i = 0; i < uLen; ++i) {
567 m = xUMap->mapUnicode(u[i], buf, sizeof(buf));
568 for (j = 0; j+1 < m; j += 2) {
571 XDrawString16(display, pixmap, gc,
572 xoutRound(x + k*dx1), xoutRound(y + k*dy1),
578 // some PDF files use CID 0, which is .notdef, so just ignore it
579 error(-1, "Unknown character (CID=%d Unicode=%04x)",
580 c, uLen > 0 ? u[0] : (Unicode)0);
584 //------------------------------------------------------------------------
586 //------------------------------------------------------------------------
589 XOutputT1FontFile::~XOutputT1FontFile() {
592 unlink(tmpFileName->getCString());
598 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
599 XOutputFTFontFile::~XOutputFTFontFile() {
602 unlink(tmpFileName->getCString());
608 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
609 XOutputTTFontFile::~XOutputTTFontFile() {
612 unlink(tmpFileName->getCString());
618 XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA,
620 FontRastControl t1libControlA,
621 FontRastControl freetypeControlA) {
627 t1libControl = t1libControlA;
632 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
633 freetypeControl = freetypeControlA;
637 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
638 freetypeControl = freetypeControlA;
646 XOutputFontCache::~XOutputFontCache() {
650 void XOutputFontCache::startDoc(int screenNum, Visual *visual,
651 Colormap colormap, GBool trueColor,
652 int rMul, int gMul, int bMul,
653 int rShift, int gShift, int bShift,
654 Gulong *colors, int numColors) {
659 if (t1libControl != fontRastNone) {
660 t1Engine = new T1FontEngine(display, visual, depth, colormap,
661 t1libControl == fontRastAALow ||
662 t1libControl == fontRastAAHigh,
663 t1libControl == fontRastAAHigh);
664 if (t1Engine->isOk()) {
666 t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
668 t1Engine->useColorCube(colors, numColors);
675 #endif // HAVE_T1LIB_H
677 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
678 if (freetypeControl != fontRastNone) {
679 ftEngine = new FTFontEngine(display, visual, depth, colormap,
680 freetypeControl == fontRastAALow ||
681 freetypeControl == fontRastAAHigh);
682 if (ftEngine->isOk()) {
684 ftEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
686 ftEngine->useColorCube(colors, numColors);
693 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
695 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
696 if (freetypeControl != fontRastNone) {
697 ttEngine = new TTFontEngine(display, visual, depth, colormap,
698 freetypeControl == fontRastAALow ||
699 freetypeControl == fontRastAAHigh);
700 if (ttEngine->isOk()) {
702 ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift);
704 ttEngine->useColorCube(colors, numColors);
711 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
714 void XOutputFontCache::delFonts() {
717 for (i = 0; i < nFonts; ++i) {
722 // delete Type 1 font files
724 deleteGList(t1FontFiles, XOutputT1FontFile);
733 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
734 // delete FreeType font files
736 deleteGList(ftFontFiles, XOutputFTFontFile);
745 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
746 // delete TrueType fonts
748 deleteGList(ttFontFiles, XOutputTTFontFile);
758 void XOutputFontCache::clear() {
761 for (i = 0; i < xOutFontCacheSize; ++i) {
767 // clear Type 1 font files
768 t1FontFiles = new GList();
771 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
772 // clear FreeType font cache
773 ftFontFiles = new GList();
776 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
777 // clear TrueType font cache
778 ttFontFiles = new GList();
782 XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont,
783 double m11, double m12,
784 double m21, double m22) {
786 DisplayFontParam *dfp;
788 double m11New, m12New, m21New, m22New;
796 // is it the most recently used font?
797 if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
801 // is it in the cache?
802 for (i = 1; i < nFonts; ++i) {
803 if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
805 for (j = i; j > 0; --j) {
806 fonts[j] = fonts[j-1];
813 // try for a cached FontFile, an embedded font, or an external font
816 switch (gfxFont->getType()) {
820 if (t1libControl != fontRastNone) {
821 font = tryGetT1Font(xref, gfxFont, m11, m12, m21, m22);
824 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
826 if (freetypeControl != fontRastNone) {
827 font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
834 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
835 if (freetypeControl != fontRastNone) {
836 font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
839 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
840 if (freetypeControl != fontRastNone) {
841 font = tryGetTTFont(xref, gfxFont, m11, m12, m21, m22);
847 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
848 if (freetypeControl != fontRastNone) {
849 font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22);
859 // search for a display font mapping
861 if (gfxFont->isCIDFont()) {
862 if (((GfxCIDFont *)gfxFont)->getCollection()) {
864 getDisplayCIDFont(gfxFont->getName(),
865 ((GfxCIDFont *)gfxFont)->getCollection());
867 // this error (no CMap file) was already reported by GfxFont
871 if (gfxFont->getName()) {
872 dfp = globalParams->getDisplayFont(gfxFont->getName());
876 font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
877 m11, m12, m21, m22, gFalse);
880 // substitute a font (8-bit fonts only)
881 if (!font && !gfxFont->isCIDFont()) {
883 // choose a substitute font
884 if (gfxFont->isFixedWidth()) {
886 } else if (gfxFont->isSerif()) {
891 if (gfxFont->isBold()) {
894 if (gfxFont->isItalic()) {
897 substName = new GString(xOutSubstFonts[index].name);
899 // adjust the font matrix -- compare the width of 'm' in the
900 // original font and the substituted font
905 for (code = 0; code < 256; ++code) {
906 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
907 name[0] == 'm' && name[1] == '\0') {
912 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
913 w2 = xOutSubstFonts[index].mWidth;
914 if (gfxFont->getType() == fontType3) {
915 // This is a hack which makes it possible to substitute for some
916 // Type 3 fonts. The problem is that it's impossible to know what
917 // the base coordinate system used in the font is without actually
918 // rendering the font. This code tries to guess by looking at the
919 // width of the character 'm' (which breaks if the font is a
920 // subset that doesn't contain 'm').
921 if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) {
928 fm = gfxFont->getFontMatrix();
929 v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]);
932 } else if (!gfxFont->isSymbolic()) {
933 // if real font is substantially narrower than substituted
934 // font, reduce the font size accordingly
935 if (w1 > 0.01 && w1 < 0.9 * w2) {
944 dfp = globalParams->getDisplayFont(substName);
947 // this should never happen since GlobalParams sets up default
948 // mappings for the Base-14 fonts
949 error(-1, "Couldn't find a font for '%s'",
950 gfxFont->getName()->getCString());
953 font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22,
954 m11New, m12New, m21New, m22New, gTrue);
960 // This will happen if the user specifies a bogus font in the
961 // config file (a non-existent font file or a font that requires a
962 // rasterizer that is disabled or wasn't built in), or if a CID
963 // font has no associated font in the config file.
964 if (gfxFont->isCIDFont()) {
965 error(-1, "Couldn't find a font for the '%s' character collection",
966 ((GfxCIDFont *)gfxFont)->getCollection()->getCString());
968 error(-1, "Couldn't find a font for '%s'",
970 gfxFont->getName()->getCString() : "[unnamed]");
975 // insert font in cache
976 if (nFonts == xOutFontCacheSize) {
978 delete fonts[nFonts];
980 for (j = nFonts; j > 0; --j) {
981 fonts[j] = fonts[j-1];
989 XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp,
991 double m11Orig, double m12Orig,
992 double m21Orig, double m22Orig,
993 double m11, double m12,
994 double m21, double m22,
1000 // create the new font
1001 switch (dfp->kind) {
1004 font = tryGetServerFont(dfp->x.xlfd, dfp->x.encoding, gfxFont,
1005 m11Orig, m12Orig, m21Orig, m22Orig,
1006 m11, m12, m21, m22);
1011 if (t1libControl != fontRastNone && !gfxFont->isCIDFont()) {
1012 font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
1013 m11Orig, m12Orig, m21Orig, m22Orig,
1014 m11, m12, m21, m22, subst);
1017 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1019 if (freetypeControl != fontRastNone) {
1020 font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont,
1021 m11Orig, m12Orig, m21Orig, m22Orig,
1022 m11, m12, m21, m22, gFalse, subst);
1026 #if !((FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)) || defined(HAVE_T1LIB_H))
1027 error(-1, "Config file specifies a Type 1 font,");
1028 error(-1, "but xpdf was not built with t1lib or FreeType2 support");
1033 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1034 if (freetypeControl != fontRastNone) {
1035 font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
1036 m11Orig, m12Orig, m21Orig, m22Orig,
1037 m11, m12, m21, m22, gFalse, subst);
1040 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1041 if (freetypeControl != fontRastNone) {
1042 font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont,
1043 m11Orig, m12Orig, m21Orig, m22Orig,
1044 m11, m12, m21, m22, subst);
1047 #if !(HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1048 error(-1, "Config file specifies a TrueType font,");
1049 error(-1, "but xpdf was not built with FreeType support");
1059 XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref,
1061 double m11, double m12,
1062 double m21, double m22) {
1064 XOutputT1FontFile *xFontFile;
1072 Object refObj, strObj;
1076 // check the already available font files
1077 id = gfxFont->getID();
1078 for (i = 0; i < t1FontFiles->getLength(); ++i) {
1079 xFontFile = (XOutputT1FontFile *)t1FontFiles->get(i);
1080 if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1081 !xFontFile->subst) {
1082 font = new XOutputT1Font(id, xFontFile->fontFile,
1084 m11, m12, m21, m22, display, xOut);
1085 if (!font->isOk()) {
1093 // check for an embedded font
1094 if (gfxFont->getEmbeddedFontID(&embRef)) {
1096 // create the font file
1098 if (!openTempFile(&fileName, &f, "wb", NULL)) {
1099 error(-1, "Couldn't create temporary Type 1 font file");
1102 if (gfxFont->getType() == fontType1C) {
1103 if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
1105 unlink(fileName->getCString());
1109 ff = new Type1CFontFile(fontBuf, fontLen);
1113 unlink(fileName->getCString());
1117 ff->convertToType1(outputToFile, f);
1120 } else { // fontType1
1121 refObj.initRef(embRef.num, embRef.gen);
1122 refObj.fetch(xref, &strObj);
1124 strObj.streamReset();
1125 while ((c = strObj.streamGetChar()) != EOF) {
1128 strObj.streamClose();
1134 font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont,
1136 m11, m12, m21, m22, gFalse);
1138 // on systems with Unix hard link semantics, this will remove the
1139 // last link to the temp file
1140 unlink(fileName->getCString());
1144 // check for an external font file
1145 } else if ((fileName = gfxFont->getExtFontFile())) {
1146 font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont,
1148 m11, m12, m21, m22, gFalse);
1157 XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref,
1165 double m11, double m12,
1166 double m21, double m22,
1169 T1FontFile *fontFile;
1172 // create the t1lib font file
1173 fontFile = new T1FontFile(t1Engine, fileName->getCString(),
1174 ((Gfx8BitFont *)gfxFont)->getEncoding(),
1175 gfxFont->getFontBBox());
1176 if (!fontFile->isOk()) {
1177 error(-1, "Couldn't create t1lib font from '%s'",
1178 fileName->getCString());
1181 unlink(fileName->getCString());
1187 id = gfxFont->getID();
1188 t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen,
1190 deleteFile ? fileName->copy()
1191 : (GString *)NULL));
1194 font = new XOutputT1Font(gfxFont->getID(), fontFile,
1195 m11Orig, m12Orig, m21Orig, m22Orig,
1196 m11, m12, m21, m22, display, xOut);
1197 if (!font->isOk()) {
1203 #endif // HAVE_T1LIB_H
1205 #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1206 XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref,
1208 double m11, double m12,
1209 double m21, double m22) {
1211 XOutputFTFontFile *xFontFile;
1216 #if 1 //~ need this until FT can handle fonts with missing tables
1219 TrueTypeFontFile *ff;
1221 Object refObj, strObj;
1225 // check the already available font files
1226 id = gfxFont->getID();
1227 for (i = 0; i < ftFontFiles->getLength(); ++i) {
1228 xFontFile = (XOutputFTFontFile *)ftFontFiles->get(i);
1229 if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1230 !xFontFile->subst) {
1231 font = new XOutputFTFont(id, xFontFile->fontFile,
1233 m11, m12, m21, m22, display, xOut);
1234 if (!font->isOk()) {
1242 // check for an embedded font
1243 if (gfxFont->getEmbeddedFontID(&embRef)) {
1245 // create the font file
1247 if (!openTempFile(&fileName, &f, "wb", NULL)) {
1248 error(-1, "Couldn't create temporary TrueType font file");
1251 #if 1 //~ need this until FT can handle fonts with missing tables
1252 if (gfxFont->getType() == fontTrueType ||
1253 gfxFont->getType() == fontCIDType2) {
1254 if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) {
1256 unlink(fileName->getCString());
1260 ff = new TrueTypeFontFile(fontBuf, fontLen);
1265 refObj.initRef(embRef.num, embRef.gen);
1266 refObj.fetch(xref, &strObj);
1268 strObj.streamReset();
1269 while ((c = strObj.streamGetChar()) != EOF) {
1272 strObj.streamClose();
1276 refObj.initRef(embRef.num, embRef.gen);
1277 refObj.fetch(xref, &strObj);
1279 strObj.streamReset();
1280 while ((c = strObj.streamGetChar()) != EOF) {
1283 strObj.streamClose();
1289 font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont,
1291 m11, m12, m21, m22, gTrue, gFalse);
1293 // on systems with Unix hard link semantics, this will remove the
1294 // last link to the temp file
1295 unlink(fileName->getCString());
1299 // check for an external font file
1300 } else if ((fileName = gfxFont->getExtFontFile())) {
1301 font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont,
1303 m11, m12, m21, m22, gFalse, gFalse);
1312 XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref,
1320 double m11, double m12,
1321 double m21, double m22,
1325 FTFontFile *fontFile;
1330 TrueTypeFontFile *ff;
1333 // create the FreeType font file
1334 if (gfxFont->isCIDFont()) {
1335 if (gfxFont->getType() == fontCIDType2) {
1336 fontFile = new FTFontFile(ftEngine, fileName->getCString(),
1337 ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1338 ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(),
1340 } else { // fontCIDType0, fontCIDType0C
1341 fontFile = new FTFontFile(ftEngine, fileName->getCString(), embedded);
1344 if (!(f = fopen(fileName->getCString(), "rb"))) {
1347 fseek(f, 0, SEEK_END);
1348 len = (int)ftell(f);
1349 fseek(f, 0, SEEK_SET);
1350 buf = (char *)gmalloc(len);
1351 if ((int)fread(buf, 1, len, f) != len) {
1357 ff = new TrueTypeFontFile(buf, len);
1358 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1359 fontFile = new FTFontFile(ftEngine, fileName->getCString(),
1360 ((Gfx8BitFont *)gfxFont)->getEncoding(),
1366 if (!fontFile->isOk()) {
1367 error(-1, "Couldn't create FreeType font from '%s'",
1368 fileName->getCString());
1371 unlink(fileName->getCString());
1377 id = gfxFont->getID();
1378 ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen,
1380 deleteFile ? fileName->copy()
1381 : (GString *)NULL));
1384 font = new XOutputFTFont(gfxFont->getID(), fontFile,
1385 m11Orig, m12Orig, m21Orig, m22Orig,
1386 m11, m12, m21, m22, display, xOut);
1387 if (!font->isOk()) {
1393 #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1395 #if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1396 XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref,
1398 double m11, double m12,
1399 double m21, double m22) {
1401 XOutputTTFontFile *xFontFile;
1406 Object refObj, strObj;
1410 // check the already available font files
1411 id = gfxFont->getID();
1413 for (i = 0; i < ttFontFiles->getLength(); ++i) {
1414 xFontFile = (XOutputTTFontFile *)ttFontFiles->get(i);
1415 if (xFontFile->num == id->num && xFontFile->gen == id->gen &&
1416 !xFontFile->subst) {
1417 font = new XOutputTTFont(id, xFontFile->fontFile,
1419 m11, m12, m21, m22, display, xOut);
1420 if (!font->isOk()) {
1428 // check for an embedded font
1429 if (gfxFont->getEmbeddedFontID(&embRef)) {
1431 // create the font file
1433 if (!openTempFile(&fileName, &f, "wb", NULL)) {
1434 error(-1, "Couldn't create temporary TrueType font file");
1437 refObj.initRef(embRef.num, embRef.gen);
1438 refObj.fetch(xref, &strObj);
1440 strObj.streamReset();
1441 while ((c = strObj.streamGetChar()) != EOF) {
1444 strObj.streamClose();
1449 font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont,
1451 m11, m12, m21, m22, gFalse);
1453 // on systems with Unix hard link semantics, this will remove the
1454 // last link to the temp file
1455 unlink(fileName->getCString());
1459 } else if ((fileName = gfxFont->getExtFontFile())) {
1460 font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont,
1462 m11, m12, m21, m22, gFalse);
1471 XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref,
1479 double m11, double m12,
1480 double m21, double m22,
1483 TTFontFile *fontFile;
1486 // create the FreeType font file
1487 if (gfxFont->isCIDFont()) {
1489 fontFile = new TTFontFile(ttEngine, fileName->getCString(),
1490 ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1491 ((GfxCIDFont *)gfxFont)->getCIDToGIDLen());
1493 fontFile = new TTFontFile(ttEngine, fileName->getCString(),
1494 ((Gfx8BitFont *)gfxFont)->getEncoding(),
1495 ((Gfx8BitFont *)gfxFont)->getHasEncoding());
1497 if (!fontFile->isOk()) {
1498 error(-1, "Couldn't create FreeType font from '%s'",
1499 fileName->getCString());
1502 unlink(fileName->getCString());
1508 id = gfxFont->getID();
1509 ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen,
1511 deleteFile ? fileName->copy()
1512 : (GString *)NULL));
1515 font = new XOutputTTFont(gfxFont->getID(), fontFile,
1516 m11Orig, m12Orig, m21Orig, m22Orig,
1517 m11, m12, m21, m22, display, xOut);
1518 if (!font->isOk()) {
1524 #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
1526 XOutputFont *XOutputFontCache::tryGetServerFont(GString *xlfd,
1527 GString *encodingName,
1529 double m11Orig, double m12Orig,
1530 double m21Orig, double m22Orig,
1531 double m11, double m12,
1532 double m21, double m22) {
1535 CharCodeToUnicode *ctu;
1537 uMap = globalParams->getUnicodeMap(encodingName);
1538 if (gfxFont->isCIDFont()) {
1539 ctu = ((GfxCIDFont *)gfxFont)->getToUnicode();
1540 font = new XOutputServer16BitFont(gfxFont->getID(), xlfd, uMap, ctu,
1541 m11Orig, m12Orig, m21Orig, m22Orig,
1546 ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode();
1547 font = new XOutputServer8BitFont(gfxFont->getID(), xlfd, uMap, ctu,
1548 m11Orig, m12Orig, m21Orig, m22Orig,
1554 if (!font->isOk()) {
1561 //------------------------------------------------------------------------
1563 //------------------------------------------------------------------------
1565 struct T3FontCacheTag {
1567 Gushort mru; // valid bit (0x8000) and MRU index
1568 double wx, wy; // untransformed glyph metrics
1574 T3FontCache(Ref *fontID, double m11A, double m12A,
1575 double m21A, double m22A,
1576 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1577 Display *displayA, Visual *visual, Guint depth,
1580 GBool matches(Ref *idA, double m11A, double m12A,
1581 double m21A, double m22A)
1582 { return fontID.num == idA->num && fontID.gen == idA->gen &&
1583 m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
1585 Ref fontID; // PDF font ID
1586 double m11, m12, m21, m22; // transform matrix
1587 int glyphX, glyphY; // pixel offset of glyph pixmaps
1588 int glyphW, glyphH; // size of glyph pixmaps, in pixels
1589 int glyphSize; // size of glyph pixmaps, in bytes
1590 int cacheSets; // number of sets in cache
1591 int cacheAssoc; // cache associativity (glyphs per set)
1592 Guchar *cacheData; // glyph pixmap cache
1593 T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
1599 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
1600 double m21A, double m22A,
1601 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1602 Display *displayA, Visual *visual, Guint depth,
1603 Pixmap origPixmap) {
1615 glyphSize = glyphW * glyphH;
1617 if (glyphSize <= 256) {
1619 } else if (glyphSize <= 512) {
1621 } else if (glyphSize <= 1024) {
1626 cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
1627 cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc *
1628 sizeof(T3FontCacheTag));
1629 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
1630 cacheTags[i].mru = i & (cacheAssoc - 1);
1633 pixmap = XCreatePixmap(display, origPixmap, glyphW, glyphH, depth);
1634 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL,
1635 glyphW, glyphH, 8, 0);
1636 image->data = (char *)gmalloc(glyphH * image->bytes_per_line);
1639 T3FontCache::~T3FontCache() {
1642 XFreePixmap(display, pixmap);
1645 XDestroyImage(image);
1648 struct T3GlyphStack {
1653 T3FontCacheTag *cacheTag;
1659 int origPixmapW, origPixmapH;
1663 Region origClipRegion;
1664 double origCTM4, origCTM5;
1665 double wx, wy; // untransformed glyph metrics
1669 //------------------------------------------------------------------------
1671 //------------------------------------------------------------------------
1673 XOutputDev::XOutputDev(Display *displayA, int screenNumA,
1674 Visual *visualA, Colormap colormapA,
1675 GBool reverseVideoA, unsigned long paperColorA,
1676 GBool installCmap, int rgbCubeSize,
1678 XVisualInfo visualTempl;
1679 XVisualInfo *visualList;
1690 // display / screen / visual / colormap
1692 screenNum = screenNumA;
1694 colormap = colormapA;
1697 pixmapW = pixmapH = 0;
1699 // check for TrueColor visual
1700 if (forceDepth != 0) {
1702 trueColor = depth >= 16;
1704 visualTempl.visualid = XVisualIDFromVisual(visual);
1705 visualList = XGetVisualInfo(display, VisualIDMask,
1706 &visualTempl, &nVisuals);
1708 // this shouldn't happen
1709 XFree((XPointer)visualList);
1710 visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
1713 depth = visualList->depth;
1714 if (visualList->c_class == TrueColor) {
1716 for (mask = visualList->red_mask, rShift = 0;
1717 mask && !(mask & 1);
1718 mask >>= 1, ++rShift) ;
1720 for (mask = visualList->green_mask, gShift = 0;
1721 mask && !(mask & 1);
1722 mask >>= 1, ++gShift) ;
1724 for (mask = visualList->blue_mask, bShift = 0;
1725 mask && !(mask & 1);
1726 mask >>= 1, ++bShift) ;
1731 XFree((XPointer)visualList);
1734 // allocate a color cube
1736 redMap[BlackPixel(display, screenNum) & 0xff] = 0;
1737 redMap[WhitePixel(display, screenNum) & 0xff] = 1;
1739 // set colors in private colormap
1741 for (numColors = 6; numColors >= 2; --numColors) {
1742 m = numColors * numColors * numColors;
1743 if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
1747 if (numColors >= 2) {
1748 m = numColors * numColors * numColors;
1749 xcolors = (XColor *)gmalloc(m * sizeof(XColor));
1751 for (r = 0; r < numColors; ++r) {
1752 for (g = 0; g < numColors; ++g) {
1753 for (b = 0; b < numColors; ++b) {
1754 xcolors[n].pixel = colors[n];
1755 xcolors[n].red = (r * 65535) / (numColors - 1);
1756 xcolors[n].green = (g * 65535) / (numColors - 1);
1757 xcolors[n].blue = (b * 65535) / (numColors - 1);
1758 xcolors[n].flags = DoRed | DoGreen | DoBlue;
1759 redMap[xcolors[n].pixel & 0xff] = xcolors[n].red / 65535.0;
1764 XStoreColors(display, colormap, xcolors, m);
1768 colors[0] = BlackPixel(display, screenNum);
1769 colors[1] = WhitePixel(display, screenNum);
1772 // allocate colors in shared colormap
1774 if (rgbCubeSize > maxRGBCube) {
1775 rgbCubeSize = maxRGBCube;
1778 for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
1781 for (r = 0; r < numColors && ok; ++r) {
1782 for (g = 0; g < numColors && ok; ++g) {
1783 for (b = 0; b < numColors && ok; ++b) {
1785 colors[n] = BlackPixel(display, screenNum);
1786 redMap[colors[n] & 0xff] = 0;
1789 xcolor.red = (r * 65535) / (numColors - 1);
1790 xcolor.green = (g * 65535) / (numColors - 1);
1791 xcolor.blue = (b * 65535) / (numColors - 1);
1792 if (XAllocColor(display, colormap, &xcolor)) {
1793 colors[n++] = xcolor.pixel;
1794 redMap[xcolor.pixel & 0xff] = xcolor.red / 65535.0;
1805 XFreeColors(display, colormap, &colors[1], n-1, 0);
1809 colors[0] = BlackPixel(display, screenNum);
1810 colors[1] = WhitePixel(display, screenNum);
1816 reverseVideo = reverseVideoA;
1817 paperColor = paperColorA;
1819 // set up the font cache and fonts
1822 needFontUpdate = gFalse;
1823 fontCache = new XOutputFontCache(display, depth, this,
1824 globalParams->getT1libControl(),
1825 globalParams->getFreeTypeControl());
1827 t3GlyphStack = NULL;
1829 // no text outline clipping path
1830 textClipPath = NULL;
1832 // empty state stack
1835 // create text object
1836 text = new TextPage(gFalse);
1839 XOutputDev::~XOutputDev() {
1843 for (i = 0; i < nT3Fonts; ++i) {
1844 delete t3FontCache[i];
1849 void XOutputDev::startDoc(XRef *xrefA) {
1853 fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul,
1854 rShift, gShift, bShift, colors, numColors);
1855 for (i = 0; i < nT3Fonts; ++i) {
1856 delete t3FontCache[i];
1861 void XOutputDev::startPage(int pageNum, GfxState *state) {
1865 // default line flatness
1869 gcValues.foreground = BlackPixel(display, screenNum);
1870 gcValues.background = WhitePixel(display, screenNum);
1871 gcValues.line_width = 0;
1872 gcValues.line_style = LineSolid;
1873 strokeGC = XCreateGC(display, pixmap,
1874 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1876 fillGC = XCreateGC(display, pixmap,
1877 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1879 gcValues.foreground = paperColor;
1880 paperGC = XCreateGC(display, pixmap,
1881 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
1884 // initialize clip region
1885 clipRegion = XCreateRegion();
1886 rect.x = rect.y = 0;
1887 rect.width = pixmapW;
1888 rect.height = pixmapH;
1889 XUnionRectWithRegion(&rect, clipRegion, clipRegion);
1890 XSetRegion(display, strokeGC, clipRegion);
1891 XSetRegion(display, fillGC, clipRegion);
1898 XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH);
1900 // clear text object
1901 text->startPage(state);
1904 void XOutputDev::endPage() {
1907 text->coalesce(gTrue);
1909 // clear state stack, free all GCs, free the clip region
1913 XFreeGC(display, s->strokeGC);
1914 XFreeGC(display, s->fillGC);
1915 XDestroyRegion(s->clipRegion);
1918 XFreeGC(display, strokeGC);
1919 XFreeGC(display, fillGC);
1920 XFreeGC(display, paperGC);
1921 XDestroyRegion(clipRegion);
1924 void XOutputDev::drawLink(Link *link, Catalog *catalog) {
1925 double x1, y1, x2, y2;
1926 LinkBorderStyle *borderStyle;
1934 link->getRect(&x1, &y1, &x2, &y2);
1935 borderStyle = link->getBorderStyle();
1936 if (borderStyle->getWidth() > 0) {
1937 borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b);
1938 XSetForeground(display, strokeGC, findColor(&rgb));
1939 borderStyle->getDash(&dash, &dashLength);
1940 if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
1941 if (dashLength > 20) {
1944 for (i = 0; i < dashLength; ++i) {
1945 if ((dashList[i] = xoutRound(dash[i])) == 0) {
1949 XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
1950 LineOnOffDash, CapButt, JoinMiter);
1951 XSetDashes(display, strokeGC, 0, dashList, dashLength);
1953 XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()),
1954 LineSolid, CapButt, JoinMiter);
1956 if (borderStyle->getType() == linkBorderUnderlined) {
1957 cvtUserToDev(x1, y1, &x, &y);
1960 cvtUserToDev(x2, y1, &x, &y);
1963 XDrawLine(display, pixmap, strokeGC, points[0].x, points[0].y,
1964 points[1].x, points[1].y);
1966 cvtUserToDev(x1, y1, &x, &y);
1967 points[0].x = points[4].x = x;
1968 points[0].y = points[4].y = y;
1969 cvtUserToDev(x2, y1, &x, &y);
1972 cvtUserToDev(x2, y2, &x, &y);
1975 cvtUserToDev(x1, y2, &x, &y);
1978 XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin);
1983 void XOutputDev::saveState(GfxState *state) {
1987 // save current state
1988 s = new XOutputState;
1989 s->strokeGC = strokeGC;
1991 s->clipRegion = clipRegion;
1993 // push onto state stack
1997 // create a new current state by copying
1998 strokeGC = XCreateGC(display, pixmap, 0, &values);
1999 XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
2000 fillGC = XCreateGC(display, pixmap, 0, &values);
2001 XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
2002 clipRegion = XCreateRegion();
2003 XUnionRegion(s->clipRegion, clipRegion, clipRegion);
2004 XSetRegion(display, strokeGC, clipRegion);
2005 XSetRegion(display, fillGC, clipRegion);
2008 void XOutputDev::restoreState(GfxState *state) {
2012 // kill current state
2013 XFreeGC(display, strokeGC);
2014 XFreeGC(display, fillGC);
2015 XDestroyRegion(clipRegion);
2018 flatness = state->getFlatness();
2019 strokeGC = save->strokeGC;
2020 fillGC = save->fillGC;
2021 clipRegion = save->clipRegion;
2022 XSetRegion(display, strokeGC, clipRegion);
2023 XSetRegion(display, fillGC, clipRegion);
2030 // we'll need to restore the font
2031 needFontUpdate = gTrue;
2035 void XOutputDev::updateAll(GfxState *state) {
2036 updateLineAttrs(state, gTrue);
2037 updateFlatness(state);
2038 updateMiterLimit(state);
2039 updateFillColor(state);
2040 updateStrokeColor(state);
2041 needFontUpdate = gTrue;
2044 void XOutputDev::updateCTM(GfxState *state, double m11, double m12,
2045 double m21, double m22, double m31, double m32) {
2046 updateLineAttrs(state, gTrue);
2049 void XOutputDev::updateLineDash(GfxState *state) {
2050 updateLineAttrs(state, gTrue);
2053 void XOutputDev::updateFlatness(GfxState *state) {
2054 flatness = state->getFlatness();
2057 void XOutputDev::updateLineJoin(GfxState *state) {
2058 updateLineAttrs(state, gFalse);
2061 void XOutputDev::updateLineCap(GfxState *state) {
2062 updateLineAttrs(state, gFalse);
2066 void XOutputDev::updateMiterLimit(GfxState *state) {
2069 void XOutputDev::updateLineWidth(GfxState *state) {
2070 updateLineAttrs(state, gFalse);
2073 void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
2076 double *dashPattern;
2082 width = state->getTransformedLineWidth();
2083 switch (state->getLineCap()) {
2084 case 0: cap = CapButt; break;
2085 case 1: cap = CapRound; break;
2086 case 2: cap = CapProjecting; break;
2088 error(-1, "Bad line cap style (%d)", state->getLineCap());
2092 switch (state->getLineJoin()) {
2093 case 0: join = JoinMiter; break;
2094 case 1: join = JoinRound; break;
2095 case 2: join = JoinBevel; break;
2097 error(-1, "Bad line join style (%d)", state->getLineJoin());
2101 state->getLineDash(&dashPattern, &dashLength, &dashStart);
2102 #if 1 //~ work around a bug in XFree86 (???)
2103 if (dashLength > 0 && cap == CapProjecting) {
2107 XSetLineAttributes(display, strokeGC, xoutRound(width),
2108 dashLength > 0 ? LineOnOffDash : LineSolid,
2110 if (updateDash && dashLength > 0) {
2111 if (dashLength > 20)
2113 for (i = 0; i < dashLength; ++i) {
2114 dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
2115 if (dashList[i] == 0)
2118 XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
2122 void XOutputDev::updateFillColor(GfxState *state) {
2125 state->getFillRGB(&rgb);
2131 XSetForeground(display, fillGC, findColor(&rgb));
2134 void XOutputDev::updateStrokeColor(GfxState *state) {
2137 state->getStrokeRGB(&rgb);
2143 XSetForeground(display, strokeGC, findColor(&rgb));
2146 void XOutputDev::updateFont(GfxState *state) {
2147 double m11, m12, m21, m22;
2149 needFontUpdate = gFalse;
2151 text->updateFont(state);
2153 if (!(gfxFont = state->getFont())) {
2157 if (gfxFont->getType() == fontType3) {
2161 state->getFontTransMat(&m11, &m12, &m21, &m22);
2162 m11 *= state->getHorizScaling();
2163 m12 *= state->getHorizScaling();
2164 font = fontCache->getFont(xref, gfxFont, m11, m12, m21, m22);
2166 font->updateGC(fillGC);
2167 font->updateGC(strokeGC);
2171 void XOutputDev::stroke(GfxState *state) {
2174 int n, size, numPoints, i, j;
2177 n = convertPath(state, state->getPath(),
2178 &points, &size, &numPoints, &lengths, gFalse);
2180 // draw each subpath
2182 for (i = 0; i < n; ++i) {
2183 XDrawLines(display, pixmap, strokeGC, points + j, lengths[i],
2188 // free points and lengths arrays
2189 if (points != tmpPoints)
2191 if (lengths != tmpLengths)
2195 void XOutputDev::fill(GfxState *state) {
2196 doFill(state, WindingRule);
2199 void XOutputDev::eoFill(GfxState *state) {
2200 doFill(state, EvenOddRule);
2204 // X doesn't color the pixels on the right-most and bottom-most
2205 // borders of a polygon. This means that one-pixel-thick polygons
2206 // are not colored at all. I think this is supposed to be a
2207 // feature, but I can't figure out why. So after it fills a
2208 // polygon, it also draws lines around the border. This is done
2209 // only for single-component polygons, since it's not very
2210 // compatible with the compound polygon kludge (see convertPath()).
2212 void XOutputDev::doFill(GfxState *state, int rule) {
2215 int n, size, numPoints, i, j;
2218 XSetFillRule(display, fillGC, rule);
2220 // transform points, build separate polygons
2221 n = convertPath(state, state->getPath(),
2222 &points, &size, &numPoints, &lengths, gTrue);
2226 for (i = 0; i < n; ++i) {
2227 XFillPolygon(display, pixmap, fillGC, points + j, lengths[i],
2228 Complex, CoordModeOrigin);
2229 if (state->getPath()->getNumSubpaths() == 1) {
2230 XDrawLines(display, pixmap, fillGC, points + j, lengths[i],
2233 j += lengths[i] + 1;
2236 // free points and lengths arrays
2237 if (points != tmpPoints)
2239 if (lengths != tmpLengths)
2243 void XOutputDev::clip(GfxState *state) {
2244 doClip(state, state->getPath(), WindingRule);
2247 void XOutputDev::eoClip(GfxState *state) {
2248 doClip(state, state->getPath(), EvenOddRule);
2251 void XOutputDev::doClip(GfxState *state, GfxPath *path, int rule) {
2252 GfxSubpath *subpath;
2253 Region region, region2;
2257 double x0, y0, x1, y1, x2, y2, x3, y3;
2259 int n, size, numPoints, i, j;
2261 // special case for rectangular clipping paths -- this is a common
2262 // case, and we want to make sure not to clip an extra pixel on the
2263 // right and bottom edges due to the difference between the PDF and
2264 // X rendering models
2266 if (path->getNumSubpaths() == 1) {
2267 subpath = path->getSubpath(0);
2268 if ((subpath->isClosed() && subpath->getNumPoints() == 5) ||
2269 (!subpath->isClosed() && subpath->getNumPoints() == 4)) {
2270 state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0);
2271 state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1);
2272 state->transform(subpath->getX(2), subpath->getY(2), &x2, &y2);
2273 state->transform(subpath->getX(3), subpath->getY(3), &x3, &y3);
2274 if (fabs(x0-x1) < 1 && fabs(x2-x3) < 1 &&
2275 fabs(y0-y3) < 1 && fabs(y1-y2) < 1) {
2277 rect[0].x = rect[1].x = rect[4].x = (int)floor(x0);
2278 rect[2].x = rect[3].x = (int)floor(x2) + 1;
2280 rect[0].x = rect[1].x = rect[4].x = (int)floor(x0) + 1;
2281 rect[2].x = rect[3].x = (int)floor(x2);
2284 rect[0].y = rect[3].y = rect[4].y = (int)floor(y0);
2285 rect[1].y = rect[2].y = (int)floor(y1) + 1;
2287 rect[0].y = rect[3].y = rect[4].y = (int)floor(y0) + 1;
2288 rect[1].y = rect[2].y = (int)floor(y1);
2291 } else if (fabs(x0-x3) < 1 && fabs(x1-x2) < 1 &&
2292 fabs(y0-y1) < 1 && fabs(y2-y3) < 1) {
2294 rect[0].x = rect[3].x = rect[4].x = (int)floor(x0);
2295 rect[1].x = rect[2].x = (int)floor(x1) + 1;
2297 rect[0].x = rect[3].x = rect[4].x = (int)floor(x0) + 1;
2298 rect[1].x = rect[2].x = (int)floor(x1);
2301 rect[0].y = rect[1].y = rect[4].y = (int)floor(y0);
2302 rect[2].y = rect[3].y = (int)floor(y2) + 1;
2304 rect[0].y = rect[1].y = rect[4].y = (int)floor(y0) + 1;
2305 rect[2].y = rect[3].y = (int)floor(y2);
2313 region = XPolygonRegion(rect, 5, EvenOddRule);
2316 // transform points, build separate polygons
2317 n = convertPath(state, path, &points, &size, &numPoints, &lengths, gTrue);
2319 // construct union of subpath regions
2320 // (XPolygonRegion chokes if there aren't at least three points --
2321 // this happens if the PDF file does moveto/closepath/clip, which
2322 // sets an empty clipping region)
2323 if (lengths[0] > 2) {
2324 region = XPolygonRegion(points, lengths[0], rule);
2326 region = XCreateRegion();
2329 for (i = 1; i < n; ++i) {
2330 if (lengths[i] > 2) {
2331 region2 = XPolygonRegion(points + j, lengths[i], rule);
2333 region2 = XCreateRegion();
2335 XUnionRegion(region2, region, region);
2336 XDestroyRegion(region2);
2337 j += lengths[i] + 1;
2340 // free points and lengths arrays
2341 if (points != tmpPoints) {
2344 if (lengths != tmpLengths) {
2349 // intersect region with clipping region
2350 XIntersectRegion(region, clipRegion, clipRegion);
2351 XDestroyRegion(region);
2352 XSetRegion(display, strokeGC, clipRegion);
2353 XSetRegion(display, fillGC, clipRegion);
2357 // Transform points in the path and convert curves to line segments.
2358 // Builds a set of subpaths and returns the number of subpaths.
2359 // If <fillHack> is set, close any unclosed subpaths and activate a
2360 // kludge for polygon fills: First, it divides up the subpaths into
2361 // non-overlapping polygons by simply comparing bounding rectangles.
2362 // Then it connects subaths within a single compound polygon to a single
2363 // point so that X can fill the polygon (sort of).
2365 int XOutputDev::convertPath(GfxState *state, GfxPath *path,
2366 XPoint **points, int *size,
2367 int *numPoints, int **lengths, GBool fillHack) {
2368 BoundingRect *rects;
2370 int n, i, ii, j, k, k0;
2372 // get path and number of subpaths
2373 n = path->getNumSubpaths();
2375 // allocate lengths array
2376 if (n < numTmpSubpaths)
2377 *lengths = tmpLengths;
2379 *lengths = (int *)gmalloc(n * sizeof(int));
2381 // allocate bounding rectangles array
2383 if (n < numTmpSubpaths)
2386 rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
2392 *points = tmpPoints;
2393 *size = numTmpPoints;
2395 for (i = 0; i < n; ++i) {
2397 // transform the points
2399 convertSubpath(state, path->getSubpath(i), points, size, numPoints);
2401 // construct bounding rectangle
2403 rects[i].xMin = rects[i].xMax = (*points)[j].x;
2404 rects[i].yMin = rects[i].yMax = (*points)[j].y;
2405 for (k = j + 1; k < *numPoints; ++k) {
2406 if ((*points)[k].x < rects[i].xMin)
2407 rects[i].xMin = (*points)[k].x;
2408 else if ((*points)[k].x > rects[i].xMax)
2409 rects[i].xMax = (*points)[k].x;
2410 if ((*points)[k].y < rects[i].yMin)
2411 rects[i].yMin = (*points)[k].y;
2412 else if ((*points)[k].y > rects[i].yMax)
2413 rects[i].yMax = (*points)[k].y;
2417 // close subpath if necessary
2418 if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x ||
2419 (*points)[*numPoints-1].y != (*points)[j].y)) {
2420 addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y);
2423 // length of this subpath
2424 (*lengths)[i] = *numPoints - j;
2426 // leave an extra point for compound fill hack
2428 addPoint(points, size, numPoints, 0, 0);
2431 // kludge: munge any points that are *way* out of bounds - these can
2432 // crash certain (buggy) X servers
2433 for (i = 0; i < *numPoints; ++i) {
2434 if ((*points)[i].x < -4 * pixmapW) {
2435 (*points)[i].x = -4 * pixmapW;
2436 } else if ((*points)[i].x > 4 * pixmapW) {
2437 (*points)[i].x = 4 * pixmapW;
2439 if ((*points)[i].y < -pixmapH) {
2440 (*points)[i].y = -4 * pixmapH;
2441 } else if ((*points)[i].y > 4 * pixmapH) {
2442 (*points)[i].y = 4 * pixmapH;
2446 // combine compound polygons
2451 // start with subpath i
2453 (*lengths)[j] = (*lengths)[i];
2455 (*points)[k + (*lengths)[i]] = (*points)[k0];
2456 k += (*lengths)[i] + 1;
2459 // combine overlapping polygons
2462 // look for the first subsequent subpath, if any, which overlaps
2463 for (ii = i; ii < n; ++ii) {
2464 if (rects[ii].xMax > rects[i].xMin &&
2465 rects[ii].xMin < rects[i].xMax &&
2466 rects[ii].yMax > rects[i].yMin &&
2467 rects[ii].yMin < rects[i].yMax) {
2472 // if there is an overlap, combine the polygons
2474 for (; i <= ii; ++i) {
2475 if (rects[i].xMin < rect.xMin)
2476 rect.xMin = rects[j].xMin;
2477 if (rects[i].xMax > rect.xMax)
2478 rect.xMax = rects[j].xMax;
2479 if (rects[i].yMin < rect.yMin)
2480 rect.yMin = rects[j].yMin;
2481 if (rects[i].yMax > rect.yMax)
2482 rect.yMax = rects[j].yMax;
2483 (*lengths)[j] += (*lengths)[i] + 1;
2484 (*points)[k + (*lengths)[i]] = (*points)[k0];
2485 k += (*lengths)[i] + 1;
2488 } while (ii < n && i < n);
2493 // free bounding rectangles
2494 if (rects != tmpRects)
2504 // Transform points in a single subpath and convert curves to line
2507 void XOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath,
2508 XPoint **points, int *size, int *n) {
2509 double x0, y0, x1, y1, x2, y2, x3, y3;
2512 m = subpath->getNumPoints();
2515 if (i >= 1 && subpath->getCurve(i)) {
2516 state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
2517 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
2518 state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
2519 state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
2520 doCurve(points, size, n, x0, y0, x1, y1, x2, y2, x3, y3);
2523 state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
2524 addPoint(points, size, n, xoutRound(x1), xoutRound(y1));
2531 // Subdivide a Bezier curve. This uses floating point to avoid
2532 // propagating rounding errors. (The curves look noticeably more
2533 // jagged with integer arithmetic.)
2535 void XOutputDev::doCurve(XPoint **points, int *size, int *n,
2536 double x0, double y0, double x1, double y1,
2537 double x2, double y2, double x3, double y3) {
2538 double x[(1<<maxCurveSplits)+1][3];
2539 double y[(1<<maxCurveSplits)+1][3];
2540 int next[1<<maxCurveSplits];
2542 double xx1, yy1, xx2, yy2;
2543 double dx, dy, mx, my, d1, d2;
2544 double xl0, yl0, xl1, yl1, xl2, yl2;
2545 double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
2549 flat = (double)(flatness * flatness);
2555 p2 = 1<<maxCurveSplits;
2556 x[p1][0] = x0; y[p1][0] = y0;
2557 x[p1][1] = x1; y[p1][1] = y1;
2558 x[p1][2] = x2; y[p1][2] = y2;
2559 x[p2][0] = x3; y[p2][0] = y3;
2562 while (p1 < (1<<maxCurveSplits)) {
2565 xl0 = x[p1][0]; yl0 = y[p1][0];
2566 xx1 = x[p1][1]; yy1 = y[p1][1];
2567 xx2 = x[p1][2]; yy2 = y[p1][2];
2569 xr3 = x[p2][0]; yr3 = y[p2][0];
2571 // compute distances from control points to midpoint of the
2572 // straight line (this is a bit of a hack, but it's much faster
2573 // than computing the actual distances to the line)
2574 mx = (xl0 + xr3) * 0.5;
2575 my = (yl0 + yr3) * 0.5;
2583 // if curve is flat enough, or no more divisions allowed then
2584 // add the straight line segment
2585 if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
2586 addPoint(points, size, n, xoutRound(xr3), xoutRound(yr3));
2589 // otherwise, subdivide the curve
2591 xl1 = (xl0 + xx1) * 0.5;
2592 yl1 = (yl0 + yy1) * 0.5;
2593 xh = (xx1 + xx2) * 0.5;
2594 yh = (yy1 + yy2) * 0.5;
2595 xl2 = (xl1 + xh) * 0.5;
2596 yl2 = (yl1 + yh) * 0.5;
2597 xr2 = (xx2 + xr3) * 0.5;
2598 yr2 = (yy2 + yr3) * 0.5;
2599 xr1 = (xh + xr2) * 0.5;
2600 yr1 = (yh + yr2) * 0.5;
2601 xr0 = (xl2 + xr1) * 0.5;
2602 yr0 = (yl2 + yr1) * 0.5;
2604 // add the new subdivision points
2606 x[p1][1] = xl1; y[p1][1] = yl1;
2607 x[p1][2] = xl2; y[p1][2] = yl2;
2609 x[p3][0] = xr0; y[p3][0] = yr0;
2610 x[p3][1] = xr1; y[p3][1] = yr1;
2611 x[p3][2] = xr2; y[p3][2] = yr2;
2618 // Add a point to the points array. (This would use a generic resizable
2619 // array type if C++ supported parameterized types in some reasonable
2620 // way -- templates are a disgusting kludge.)
2622 void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) {
2625 if (*points == tmpPoints) {
2626 *points = (XPoint *)gmalloc(*size * sizeof(XPoint));
2627 memcpy(*points, tmpPoints, *k * sizeof(XPoint));
2629 *points = (XPoint *)grealloc(*points, *size * sizeof(XPoint));
2632 (*points)[*k].x = x;
2633 (*points)[*k].y = y;
2637 void XOutputDev::beginString(GfxState *state, GString *s) {
2638 text->beginWord(state, state->getCurX(), state->getCurY());
2641 void XOutputDev::endString(GfxState *state) {
2645 void XOutputDev::drawChar(GfxState *state, double x, double y,
2646 double dx, double dy,
2647 double originX, double originY,
2648 CharCode code, Unicode *u, int uLen) {
2650 double x1, y1, dx1, dy1;
2652 double saveCurX, saveCurY;
2656 if (needFontUpdate) {
2660 text->addChar(state, x, y, dx, dy, code, u, uLen);
2666 // check for invisible text -- this is used by Acrobat Capture
2667 render = state->getRender();
2674 state->transform(x, y, &x1, &y1);
2675 state->transformDelta(dx, dy, &dx1, &dy1);
2678 if (!(render & 1)) {
2679 state->getFillRGB(&rgb);
2685 font->drawChar(state, pixmap, pixmapW, pixmapH, fillGC, &rgb,
2686 x1, y1, dx1, dy1, code, u, uLen);
2690 if ((render & 3) == 1 || (render & 3) == 2) {
2691 if (font->hasGetCharPath()) {
2692 saveCurX = state->getCurX();
2693 saveCurY = state->getCurY();
2694 ctm = state->getCTM();
2695 memcpy(saveCTM, ctm, 6 * sizeof(double));
2696 state->setCTM(1, 0, 0, 1, x1, y1);
2697 font->getCharPath(state, code, u, uLen);
2700 state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
2701 saveCTM[4], saveCTM[5]);
2702 state->moveTo(saveCurX, saveCurY);
2704 // can't stroke the outline, so just fill it using the stroke
2706 state->getStrokeRGB(&rgb);
2712 font->drawChar(state, pixmap, pixmapW, pixmapH, strokeGC, &rgb,
2713 x1, y1, dx1, dy1, code, u, uLen);
2719 if (font->hasGetCharPath()) {
2720 saveCurX = state->getCurX();
2721 saveCurY = state->getCurY();
2722 font->getCharPath(state, code, u, uLen);
2723 state->getPath()->offset(x1, y1);
2725 textClipPath->append(state->getPath());
2727 textClipPath = state->getPath()->copy();
2730 state->moveTo(saveCurX, saveCurY);
2735 GBool XOutputDev::beginType3Char(GfxState *state,
2736 CharCode code, Unicode *u, int uLen) {
2740 T3FontCache *t3Font;
2742 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2745 if (needFontUpdate) {
2751 fontID = gfxFont->getID();
2752 ctm = state->getCTM();
2753 state->transform(0, 0, &xt, &yt);
2755 // is it the first (MRU) font in the cache?
2756 if (!(nT3Fonts > 0 &&
2757 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2759 // is the font elsewhere in the cache?
2760 for (i = 1; i < nT3Fonts; ++i) {
2761 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2762 t3Font = t3FontCache[i];
2763 for (j = i; j > 0; --j) {
2764 t3FontCache[j] = t3FontCache[j - 1];
2766 t3FontCache[0] = t3Font;
2770 if (i >= nT3Fonts) {
2772 // create new entry in the font cache
2773 if (nT3Fonts == xOutT3FontCacheSize) {
2774 delete t3FontCache[nT3Fonts - 1];
2777 for (j = nT3Fonts; j > 0; --j) {
2778 t3FontCache[j] = t3FontCache[j - 1];
2781 bbox = gfxFont->getFontBBox();
2782 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2783 // broken bounding box -- just take a guess
2789 state->transform(bbox[0], bbox[1], &x1, &y1);
2792 state->transform(bbox[0], bbox[3], &x1, &y1);
2795 } else if (x1 > xMax) {
2800 } else if (y1 > yMax) {
2803 state->transform(bbox[2], bbox[1], &x1, &y1);
2806 } else if (x1 > xMax) {
2811 } else if (y1 > yMax) {
2814 state->transform(bbox[2], bbox[3], &x1, &y1);
2817 } else if (x1 > xMax) {
2822 } else if (y1 > yMax) {
2826 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2827 (int)floor(xMin - xt),
2828 (int)floor(yMin - yt),
2829 (int)ceil(xMax) - (int)floor(xMin) + 3,
2830 (int)ceil(yMax) - (int)floor(yMin) + 3,
2831 display, visual, depth, pixmap);
2834 t3Font = t3FontCache[0];
2836 // is the glyph in the cache?
2837 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2838 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2839 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2840 t3Font->cacheTags[i+j].code == code) {
2841 state->getFillRGB(&color);
2843 color.r = 1 - color.r;
2844 color.g = 1 - color.g;
2845 color.b = 1 - color.b;
2847 text->addChar(state, 0, 0,
2848 t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy,
2850 drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
2851 t3Font->cacheData + (i+j) * t3Font->glyphSize,
2857 // push a new Type 3 glyph record
2858 t3gs = new T3GlyphStack();
2859 t3gs->next = t3GlyphStack;
2860 t3GlyphStack = t3gs;
2861 t3GlyphStack->cacheable = gFalse;
2862 t3GlyphStack->code = code;
2863 t3GlyphStack->cache = t3Font;
2864 t3GlyphStack->cacheIdx = i;
2865 t3GlyphStack->x = xt;
2866 t3GlyphStack->y = yt;
2867 t3GlyphStack->u = u;
2868 t3GlyphStack->uLen = uLen;
2873 void XOutputDev::endType3Char(GfxState *state) {
2882 if (t3GlyphStack->cacheable) {
2883 image = t3GlyphStack->cache->image;
2884 XGetSubImage(display, pixmap, 0, 0,
2885 t3GlyphStack->cache->glyphW, t3GlyphStack->cache->glyphH,
2886 (1 << depth) - 1, ZPixmap, image, 0, 0);
2887 p = t3GlyphStack->cacheData;
2888 for (y = 0; y < t3GlyphStack->cache->glyphH; ++y) {
2889 for (x = 0; x < t3GlyphStack->cache->glyphW; ++x) {
2890 pixel = XGetPixel(image, x, y);
2892 alpha = (double)((pixel >> rShift) & rMul) / (double)rMul;
2894 alpha = redMap[pixel & 0xff];
2898 } else if (alpha <= 0.4) {
2900 } else if (alpha <= 0.6) {
2902 } else if (alpha <= 0.8) {
2909 XDestroyRegion(clipRegion);
2910 XFreeGC(display, strokeGC);
2911 XFreeGC(display, fillGC);
2912 pixmapW = t3GlyphStack->origPixmapW;
2913 pixmapH = t3GlyphStack->origPixmapH;
2914 pixmap = t3GlyphStack->origPixmap;
2915 strokeGC = t3GlyphStack->origStrokeGC;
2916 fillGC = t3GlyphStack->origFillGC;
2917 clipRegion = t3GlyphStack->origClipRegion;
2918 drawType3Glyph(t3GlyphStack->cache,
2919 t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
2920 t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color);
2921 // the CTM must be restored here in order for TextPage::addChar to
2923 ctm = state->getCTM();
2924 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2925 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2927 text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy,
2928 t3GlyphStack->code, t3GlyphStack->u, t3GlyphStack->uLen);
2929 t3gs = t3GlyphStack;
2930 t3GlyphStack = t3gs->next;
2934 void XOutputDev::drawType3Glyph(T3FontCache *t3Font,
2935 T3FontCacheTag *tag, Guchar *data,
2936 double x, double y, GfxRGB *color) {
2943 int x0, y0, w0, h0, x1, y1;
2946 // compute: (x0,y0) = position in destination pixmap
2947 // (x1,y1) = position in the XImage
2948 // (w0,h0) = size of XImage transfer
2949 x0 = xoutRound(x + t3Font->glyphX);
2950 y0 = xoutRound(y + t3Font->glyphY);
2953 w0 = t3Font->glyphW;
2954 h0 = t3Font->glyphH;
2960 if (x0 + w0 > pixmapW) {
2971 if (y0 + h0 > pixmapH) {
2978 image = t3Font->image;
2979 XGetSubImage(display, pixmap, x0, y0, w0, h0,
2980 (1 << depth) - 1, ZPixmap, image, x1, y1);
2981 xcolor.pixel = XGetPixel(image, t3Font->glyphW / 2, t3Font->glyphH / 2);
2982 XQueryColor(display, colormap, &xcolor);
2983 bg.r = xcolor.red / 65535.0;
2984 bg.g = xcolor.green / 65535.0;
2985 bg.b = xcolor.blue / 65535.0;
2986 rgb.r = 0.25 * (color->r + 3 * bg.r);
2987 rgb.g = 0.25 * (color->g + 3 * bg.g);
2988 rgb.b = 0.25 * (color->b + 3 * bg.b);
2989 map[1] = findColor(&rgb);
2990 rgb.r = 0.5 * (color->r + bg.r);
2991 rgb.g = 0.5 * (color->g + bg.g);
2992 rgb.b = 0.5 * (color->b + bg.b);
2993 map[2] = findColor(&rgb);
2994 rgb.r = 0.25 * (3 * color->r + bg.r);
2995 rgb.g = 0.25 * (3 * color->g + bg.g);
2996 rgb.b = 0.25 * (3 * color->b + bg.b);
2997 map[3] = findColor(&rgb);
2998 map[4] = findColor(color);
3000 for (iy = 0; iy < t3Font->glyphH; ++iy) {
3001 for (ix = 0; ix < t3Font->glyphW; ++ix) {
3004 XPutPixel(image, ix, iy, map[pixel]);
3008 XPutImage(display, pixmap, fillGC, image, x1, y1, x0, y0, w0, h0);
3011 void XOutputDev::type3D0(GfxState *state, double wx, double wy) {
3012 t3GlyphStack->wx = wx;
3013 t3GlyphStack->wy = wy;
3016 void XOutputDev::type3D1(GfxState *state, double wx, double wy,
3017 double llx, double lly, double urx, double ury) {
3022 T3FontCache *t3Font;
3023 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
3026 t3Font = t3GlyphStack->cache;
3027 t3GlyphStack->wx = wx;
3028 t3GlyphStack->wy = wy;
3030 // check for a valid bbox
3031 state->transform(0, 0, &xt, &yt);
3032 state->transform(llx, lly, &x1, &y1);
3035 state->transform(llx, ury, &x1, &y1);
3038 } else if (x1 > xMax) {
3043 } else if (y1 > yMax) {
3046 state->transform(urx, lly, &x1, &y1);
3049 } else if (x1 > xMax) {
3054 } else if (y1 > yMax) {
3057 state->transform(urx, ury, &x1, &y1);
3060 } else if (x1 > xMax) {
3065 } else if (y1 > yMax) {
3068 if (xMin - xt < t3Font->glyphX ||
3069 yMin - yt < t3Font->glyphY ||
3070 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
3071 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
3072 error(-1, "Bad bounding box in Type 3 glyph");
3076 // allocate a cache entry
3077 t3GlyphStack->cacheable = gTrue;
3078 i = t3GlyphStack->cacheIdx;
3079 for (j = 0; j < t3Font->cacheAssoc; ++j) {
3080 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
3081 t3Font->cacheTags[i+j].mru = 0x8000;
3082 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
3083 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
3084 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
3086 ++t3Font->cacheTags[i+j].mru;
3089 t3GlyphStack->cacheTag->wx = wx;
3090 t3GlyphStack->cacheTag->wy = wy;
3092 // prepare to rasterize the glyph
3093 //~ do we need to handle both fill and stroke color?
3094 state->getFillRGB(&t3GlyphStack->color);
3096 t3GlyphStack->color.r = 1 - t3GlyphStack->color.r;
3097 t3GlyphStack->color.g = 1 - t3GlyphStack->color.g;
3098 t3GlyphStack->color.b = 1 - t3GlyphStack->color.b;
3100 fgColor.c[0] = reverseVideo ? 1 : 0;
3101 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
3102 state->setFillColor(&fgColor);
3103 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
3104 state->setStrokeColor(&fgColor);
3105 t3GlyphStack->origPixmapW = pixmapW;
3106 t3GlyphStack->origPixmapH = pixmapH;
3107 t3GlyphStack->origPixmap = pixmap;
3108 t3GlyphStack->origStrokeGC = strokeGC;
3109 t3GlyphStack->origFillGC = fillGC;
3110 t3GlyphStack->origClipRegion = clipRegion;
3111 pixmapW = t3GlyphStack->cache->glyphW;
3112 pixmapH = t3GlyphStack->cache->glyphH;
3113 pixmap = t3GlyphStack->cache->pixmap;
3114 gcValues.foreground = BlackPixel(display, screenNum);
3115 gcValues.background = WhitePixel(display, screenNum);
3116 gcValues.line_width = 0;
3117 gcValues.line_style = LineSolid;
3118 strokeGC = XCreateGC(display, pixmap,
3119 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
3121 updateLineAttrs(state, gTrue);
3122 gcValues.foreground = WhitePixel(display, screenNum);
3123 fillGC = XCreateGC(display, pixmap,
3124 GCForeground | GCBackground | GCLineWidth | GCLineStyle,
3126 XFillRectangle(display, pixmap, fillGC, 0, 0, pixmapW, pixmapH);
3127 XSetForeground(display, fillGC, BlackPixel(display, screenNum));
3128 clipRegion = XCreateRegion();
3129 rect.x = rect.y = 0;
3130 rect.width = pixmapW;
3131 rect.height = pixmapH;
3132 XUnionRectWithRegion(&rect, clipRegion, clipRegion);
3133 XSetRegion(display, strokeGC, clipRegion);
3134 XSetRegion(display, fillGC, clipRegion);
3135 ctm = state->getCTM();
3136 t3GlyphStack->origCTM4 = ctm[4];
3137 t3GlyphStack->origCTM5 = ctm[5];
3138 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
3139 -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY);
3142 void XOutputDev::endTextObject(GfxState *state) {
3147 ctm = state->getCTM();
3148 memcpy(saveCTM, ctm, 6 * sizeof(double));
3149 state->setCTM(1, 0, 0, 1, 0, 0);
3150 doClip(state, textClipPath, WindingRule);
3151 state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3],
3152 saveCTM[4], saveCTM[5]);
3153 delete textClipPath;
3154 textClipPath = NULL;
3158 inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *actual) {
3164 r = xoutRound(x->r * rMul);
3165 g = xoutRound(x->g * gMul);
3166 b = xoutRound(x->b * bMul);
3167 pixel = ((Gulong)r << rShift) +
3168 ((Gulong)g << gShift) +
3169 ((Gulong)b << bShift);
3170 actual->r = (double)r / rMul;
3171 actual->g = (double)g / gMul;
3172 actual->b = (double)b / bMul;
3173 } else if (numColors == 1) {
3174 gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
3177 actual->r = actual->g = actual->b = 0;
3180 actual->r = actual->g = actual->b = 1;
3183 r = xoutRound(x->r * (numColors - 1));
3184 g = xoutRound(x->g * (numColors - 1));
3185 b = xoutRound(x->b * (numColors - 1));
3186 pixel = colors[(r * numColors + g) * numColors + b];
3187 actual->r = (double)r / (numColors - 1);
3188 actual->g = (double)g / (numColors - 1);
3189 actual->b = (double)b / (numColors - 1);
3194 Gulong XOutputDev::findColor(GfxRGB *rgb) {
3200 r = xoutRound(rgb->r * rMul);
3201 g = xoutRound(rgb->g * gMul);
3202 b = xoutRound(rgb->b * bMul);
3203 pixel = ((Gulong)r << rShift) +
3204 ((Gulong)g << gShift) +
3205 ((Gulong)b << bShift);
3206 } else if (numColors == 1) {
3207 gray = 0.299 * rgb->r + 0.587 * rgb->g + 0.114 * rgb->b;
3213 r = xoutRound(rgb->r * (numColors - 1));
3214 g = xoutRound(rgb->g * (numColors - 1));
3215 b = xoutRound(rgb->b * (numColors - 1));
3216 #if 0 // this makes things worse as often as better
3217 // even a very light color shouldn't map to white
3218 if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
3219 if (color->getR() < 0.95)
3221 if (color->getG() < 0.95)
3223 if (color->getB() < 0.95)
3227 pixel = colors[(r * numColors + g) * numColors + b];
3232 void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3233 int width, int height, GBool invert,
3235 ImageStream *imgStr;
3239 double xScale, yScale, xShear, yShear;
3240 int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
3241 int ulx, uly, llx, lly, urx, ury, lrx, lry;
3242 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
3243 int bx0, by0, bx1, by1, bw, bh;
3244 int cx0, cy0, cx1, cy1, cw, ch;
3245 int yp, yq, yt, yStep, lastYStep;
3246 int xp, xq, xt, xStep, xSrc;
3254 double r0, g0, b0, r1, g1, b1;
3257 int x, y, x1, y1, x2, y2;
3260 // get CTM, check for singular matrix
3261 ctm = state->getCTM();
3262 if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
3263 error(-1, "Singular CTM in drawImage");
3265 j = height * ((width + 7) / 8);
3267 for (i = 0; i < j; ++i) {
3275 // compute scale, shear, rotation, translation parameters
3276 rot = fabs(ctm[1]) > fabs(ctm[0]);
3279 yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
3280 xShear = ctm[3] / yScale;
3281 yShear = -ctm[0] / ctm[1];
3284 yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
3285 xShear = -ctm[2] / yScale;
3286 yShear = ctm[1] / ctm[0];
3288 tx = xoutRound(ctm[2] + ctm[4]);
3289 ty = xoutRound(ctm[3] + ctm[5]);
3290 // use ceil() to avoid gaps between "striped" images
3291 scaledWidth = (int)ceil(fabs(xScale));
3292 xSign = (xScale < 0) ? -1 : 1;
3293 scaledHeight = (int)ceil(fabs(yScale));
3294 ySign = (yScale < 0) ? -1 : 1;
3296 // compute corners in device space
3299 urx1 = xSign * (scaledWidth - 1);
3300 ury1 = xoutRound(yShear * urx1);
3301 llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
3302 lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
3303 lrx1 = xSign * (scaledWidth - 1) +
3304 xoutRound(xShear * ySign * (scaledHeight - 1));
3305 lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
3307 ulx = tx + uly1; uly = ty - ulx1;
3308 urx = tx + ury1; ury = ty - urx1;
3309 llx = tx + lly1; lly = ty - llx1;
3310 lrx = tx + lry1; lry = ty - lrx1;
3312 ulx = tx + ulx1; uly = ty + uly1;
3313 urx = tx + urx1; ury = ty + ury1;
3314 llx = tx + llx1; lly = ty + lly1;
3315 lrx = tx + lrx1; lry = ty + lry1;
3319 // (bx0, by0) = upper-left corner
3320 // (bx1, by1) = lower-right corner
3322 bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
3323 : (llx < lrx) ? llx : lrx
3324 : (urx < llx) ? (urx < lrx) ? urx : lrx
3325 : (llx < lrx) ? llx : lrx;
3326 bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
3327 : (llx > lrx) ? llx : lrx
3328 : (urx > llx) ? (urx > lrx) ? urx : lrx
3329 : (llx > lrx) ? llx : lrx;
3330 by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
3331 : (lly < lry) ? lly : lry
3332 : (ury < lly) ? (ury < lry) ? ury : lry
3333 : (lly < lry) ? lly : lry;
3334 by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
3335 : (lly > lry) ? lly : lry
3336 : (ury > lly) ? (ury > lry) ? ury : lry
3337 : (lly > lry) ? lly : lry;
3341 // Bounding box clipped to pixmap, i.e., "valid" rectangle:
3342 // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
3343 // (cx1, cy1) = upper-left corner of valid rectangle in XImage
3344 // (cw, ch) = size of valid rectangle
3345 // These values will be used to transfer the XImage from/to the
3347 cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
3356 ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
3366 // check for tiny (zero width or height) images
3367 // and off-page images
3368 if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
3370 j = height * ((width + 7) / 8);
3372 for (i = 0; i < j; ++i) {
3380 // compute Bresenham parameters for x and y scaling
3381 yp = height / scaledHeight;
3382 yq = height % scaledHeight;
3383 xp = width / scaledWidth;
3384 xq = width % scaledWidth;
3386 // allocate pixel buffer
3387 pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3389 // allocate XImage and read from page pixmap
3390 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
3391 image->data = (char *)gmalloc(bh * image->bytes_per_line);
3392 XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
3396 state->getFillRGB(&rgb);
3406 // initialize background color
3407 // (the specific pixel value doesn't matter here, as long as
3408 // r1,g1,b1 correspond correctly to lastPixel)
3409 xcolor.pixel = lastPixel = 0;
3410 XQueryColor(display, colormap, &xcolor);
3411 r1 = (double)xcolor.red / 65535.0;
3412 g1 = (double)xcolor.green / 65535.0;
3413 b1 = (double)xcolor.blue / 65535.0;
3415 // initialize the image stream
3416 imgStr = new ImageStream(str, width, 1, 1);
3419 // init y scale Bresenham
3423 for (y = 0; y < scaledHeight; ++y) {
3425 // y scale Bresenham
3428 if (yt >= scaledHeight) {
3433 // read row(s) from image
3434 n = (yp > 0) ? yStep : lastYStep;
3437 for (i = 0; i < n; ++i) {
3438 memcpy(p, imgStr->getLine(), width);
3440 for (j = 0; j < width; ++j) {
3449 // init x scale Bresenham
3453 for (x = 0; x < scaledWidth; ++x) {
3455 // x scale Bresenham
3458 if (xt >= scaledWidth) {
3464 x1 = xSign * x + xoutRound(xShear * ySign * y);
3467 y1 = ySign * y + xoutRound(yShear * x1);
3478 // compute the filtered pixel at (x,y) after the
3479 // x and y scaling operations
3480 n = yStep > 0 ? yStep : 1;
3481 m = xStep > 0 ? xStep : 1;
3484 for (i = 0; i < n; ++i) {
3485 for (j = 0; j < m; ++j) {
3491 // x scale Bresenham
3494 // blend image pixel with background
3495 alpha = (double)imgPix / (double)(n * m);
3496 xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0);
3497 if (xcolor.pixel != lastPixel) {
3498 XQueryColor(display, colormap, &xcolor);
3499 r1 = (double)xcolor.red / 65535.0;
3500 g1 = (double)xcolor.green / 65535.0;
3501 b1 = (double)xcolor.blue / 65535.0;
3502 lastPixel = xcolor.pixel;
3504 rgb2.r = r0 * (1 - alpha) + r1 * alpha;
3505 rgb2.g = g0 * (1 - alpha) + g1 * alpha;
3506 rgb2.b = b0 * (1 - alpha) + b1 * alpha;
3507 pix = findColor(&rgb2);
3510 XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
3514 // blit the image into the pixmap
3515 XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
3522 XDestroyImage(image);
3525 void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3526 int width, int height, GfxImageColorMap *colorMap,
3527 int *maskColors, GBool inlineImg) {
3528 ImageStream *imgStr;
3530 int nComps, nVals, nBits;
3534 double xScale, yScale, xShear, yShear;
3535 int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
3536 int ulx, uly, llx, lly, urx, ury, lrx, lry;
3537 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
3538 int bx0, by0, bx1, by1, bw, bh;
3539 int cx0, cy0, cx1, cy1, cw, ch;
3540 int yp, yq, yt, yStep, lastYStep;
3541 int xp, xq, xt, xStep, xSrc;
3543 Guchar *pixBuf1, *alphaBuf;
3544 Guchar pixBuf2[gfxColorMaxComps];
3545 GfxRGB color2, color3, actual, err, errRight;
3546 GfxRGB *errDown0, *errDown1, *errDownTmp;
3547 double r0, g0, b0, alpha, mul;
3550 Guchar *q, *p1, *p2;
3552 GfxRGB oneBitRGB[2];
3553 int x, y, x1, y1, x2, y2;
3557 nComps = colorMap->getNumPixelComps();
3558 nVals = width * nComps;
3559 nBits = colorMap->getBits();
3560 oneBitMode = nComps == 1 && nBits == 1 && !maskColors;
3561 dither = nComps > 1 || nBits > 1;
3563 // get CTM, check for singular matrix
3564 ctm = state->getCTM();
3565 if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) {
3566 error(-1, "Singular CTM in drawImage");
3569 j = height * ((nVals * nBits + 7) / 8);
3570 for (i = 0; i < j; ++i) {
3578 // compute scale, shear, rotation, translation parameters
3579 rot = fabs(ctm[1]) > fabs(ctm[0]);
3582 yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1];
3583 xShear = ctm[3] / yScale;
3584 yShear = -ctm[0] / ctm[1];
3587 yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0];
3588 xShear = -ctm[2] / yScale;
3589 yShear = ctm[1] / ctm[0];
3591 tx = xoutRound(ctm[2] + ctm[4]);
3592 ty = xoutRound(ctm[3] + ctm[5]);
3594 // this is the right edge which needs to be (left + width - 1)
3598 // this is the bottom edge which needs to be (top + height - 1)
3601 // use ceil() to avoid gaps between "striped" images
3602 scaledWidth = (int)ceil(fabs(xScale));
3603 xSign = (xScale < 0) ? -1 : 1;
3604 scaledHeight = (int)ceil(fabs(yScale));
3605 ySign = (yScale < 0) ? -1 : 1;
3607 // compute corners in device space
3610 urx1 = xSign * (scaledWidth - 1);
3611 ury1 = xoutRound(yShear * urx1);
3612 llx1 = xoutRound(xShear * ySign * (scaledHeight - 1));
3613 lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1);
3614 lrx1 = xSign * (scaledWidth - 1) +
3615 xoutRound(xShear * ySign * (scaledHeight - 1));
3616 lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1);
3618 ulx = tx + uly1; uly = ty - ulx1;
3619 urx = tx + ury1; ury = ty - urx1;
3620 llx = tx + lly1; lly = ty - llx1;
3621 lrx = tx + lry1; lry = ty - lrx1;
3623 ulx = tx + ulx1; uly = ty + uly1;
3624 urx = tx + urx1; ury = ty + ury1;
3625 llx = tx + llx1; lly = ty + lly1;
3626 lrx = tx + lrx1; lry = ty + lry1;
3630 // (bx0, by0) = upper-left corner
3631 // (bx1, by1) = lower-right corner
3633 bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
3634 : (llx < lrx) ? llx : lrx
3635 : (urx < llx) ? (urx < lrx) ? urx : lrx
3636 : (llx < lrx) ? llx : lrx;
3637 bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
3638 : (llx > lrx) ? llx : lrx
3639 : (urx > llx) ? (urx > lrx) ? urx : lrx
3640 : (llx > lrx) ? llx : lrx;
3641 by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
3642 : (lly < lry) ? lly : lry
3643 : (ury < lly) ? (ury < lry) ? ury : lry
3644 : (lly < lry) ? lly : lry;
3645 by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
3646 : (lly > lry) ? lly : lry
3647 : (ury > lly) ? (ury > lry) ? ury : lry
3648 : (lly > lry) ? lly : lry;
3652 // Bounding box clipped to pixmap, i.e., "valid" rectangle:
3653 // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap
3654 // (cx1, cy1) = upper-left corner of valid rectangle in XImage
3655 // (cw, ch) = size of valid rectangle
3656 // These values will be used to transfer the XImage from/to the
3658 cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw;
3667 ch = (by1 >= pixmapH) ? pixmapH - by0 : bh;
3677 // check for tiny (zero width or height) images
3678 // and off-page images
3679 if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) {
3682 j = height * ((nVals * nBits + 7) / 8);
3683 for (i = 0; i < j; ++i)
3690 // compute Bresenham parameters for x and y scaling
3691 yp = height / scaledHeight;
3692 yq = height % scaledHeight;
3693 xp = width / scaledWidth;
3694 xq = width % scaledWidth;
3696 // allocate pixel buffer
3698 pixBuf1 = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3701 pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB));
3705 alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar));
3711 image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0);
3712 image->data = (char *)gmalloc(bh * image->bytes_per_line);
3714 // if the transform is anything other than a 0/90/180/270 degree
3715 // rotation/flip, or if there is color key masking, read the
3716 // backgound pixmap to fill in the corners
3717 if (!((ulx == llx && uly == ury) ||
3718 (uly == lly && ulx == urx)) ||
3720 XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap,
3724 // allocate error diffusion accumulators
3726 errDown0 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
3727 errDown1 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB));
3728 for (j = 0; j < scaledWidth + 2; ++j) {
3729 errDown0[j].r = errDown0[j].g = errDown0[j].b = 0;
3730 errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
3733 errDown0 = errDown1 = NULL;
3736 // optimize the one-bit-deep image case
3739 colorMap->getRGB(pixBuf2, &oneBitRGB[0]);
3741 colorMap->getRGB(pixBuf2, &oneBitRGB[1]);
3744 // initialize the image stream
3745 imgStr = new ImageStream(str, width, nComps, nBits);
3748 // init y scale Bresenham
3752 for (y = 0; y < scaledHeight; ++y) {
3754 // initialize error diffusion accumulators
3756 errDownTmp = errDown0;
3757 errDown0 = errDown1;
3758 errDown1 = errDownTmp;
3759 for (j = 0; j < scaledWidth + 2; ++j) {
3760 errDown1[j].r = errDown1[j].g = errDown1[j].b = 0;
3762 errRight.r = errRight.g = errRight.b = 0;
3765 // y scale Bresenham
3768 if (yt >= scaledHeight) {
3773 // read row(s) from image
3774 n = (yp > 0) ? yStep : lastYStep;
3778 for (i = 0; i < n; ++i) {
3779 p2 = imgStr->getLine();
3780 memcpy(p1, p2, width);
3786 for (i = 0; i < n; ++i) {
3787 p2 = imgStr->getLine();
3788 for (j = 0; j < width; ++j) {
3789 colorMap->getRGB(p2, p);
3793 for (k = 0; k < nComps; ++k) {
3794 if (p2[k] < maskColors[2*k] ||
3795 p2[k] > maskColors[2*k+1]) {
3809 // init x scale Bresenham
3813 for (x = 0; x < scaledWidth; ++x) {
3815 // x scale Bresenham
3818 if (xt >= scaledWidth) {
3824 x1 = xSign * x + xoutRound(xShear * ySign * y);
3827 y1 = ySign * y + xoutRound(yShear * x1);
3838 // compute the filtered pixel at (x,y) after the
3839 // x and y scaling operations
3840 n = yStep > 0 ? yStep : 1;
3841 m = xStep > 0 ? xStep : 1;
3843 p1 = pixBuf1 + xSrc;
3845 for (i = 0; i < n; ++i) {
3846 for (j = 0; j < m; ++j) {
3851 mul = (double)k / (double)(n * m);
3852 r0 = mul * oneBitRGB[1].r + (1 - mul) * oneBitRGB[0].r;
3853 g0 = mul * oneBitRGB[1].g + (1 - mul) * oneBitRGB[0].g;
3854 b0 = mul * oneBitRGB[1].b + (1 - mul) * oneBitRGB[0].b;
3858 q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL;
3861 for (i = 0; i < n; ++i) {
3862 for (j = 0; j < m; ++j) {
3873 mul = 1 / (double)(n * m);
3880 // x scale Bresenham
3885 color2.r = r0 + errRight.r + errDown0[x + 1].r;
3888 } else if (color2.r < 0) {
3891 color3.r = color2.r;
3893 color2.g = g0 + errRight.g + errDown0[x + 1].g;
3896 } else if (color2.g < 0) {
3899 color3.g = color2.g;
3901 color2.b = b0 + errRight.b + errDown0[x + 1].b;
3904 } else if (color2.b < 0) {
3907 color3.b = color2.b;
3909 pix = findColor(&color3, &actual);
3910 err.r = (color2.r - actual.r) / 16;
3911 err.g = (color2.g - actual.g) / 16;
3912 err.b = (color2.b - actual.b) / 16;
3913 errRight.r = 7 * err.r;
3914 errRight.g = 7 * err.g;
3915 errRight.b = 7 * err.b;
3916 errDown1[x].r += 3 * err.r;
3917 errDown1[x].g += 3 * err.g;
3918 errDown1[x].b += 3 * err.b;
3919 errDown1[x + 1].r += 5 * err.r;
3920 errDown1[x + 1].g += 5 * err.g;
3921 errDown1[x + 1].b += 5 * err.b;
3922 errDown1[x + 2].r = err.r;
3923 errDown1[x + 2].g = err.g;
3924 errDown1[x + 2].b = err.b;
3929 pix = findColor(&color2, &actual);
3933 //~ this should do a blend when 0 < alpha < 1
3935 XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix);
3940 // blit the image into the pixmap
3941 XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch);
3955 XDestroyImage(image);
3960 GBool XOutputDev::findText(Unicode *s, int len,
3961 GBool startAtTop, GBool stopAtBottom,
3962 GBool startAtLast, GBool stopAtLast,
3963 int *xMin, int *yMin,
3964 int *xMax, int *yMax) {
3965 double xMin1, yMin1, xMax1, yMax1;
3967 xMin1 = (double)*xMin;
3968 yMin1 = (double)*yMin;
3969 xMax1 = (double)*xMax;
3970 yMax1 = (double)*yMax;
3971 if (text->findText(s, len, startAtTop, stopAtBottom,
3972 startAtLast, stopAtLast,
3973 &xMin1, &yMin1, &xMax1, &yMax1)) {
3974 *xMin = xoutRound(xMin1);
3975 *xMax = xoutRound(xMax1);
3976 *yMin = xoutRound(yMin1);
3977 *yMax = xoutRound(yMax1);
3983 GString *XOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
3984 return text->getText((double)xMin, (double)yMin,
3985 (double)xMax, (double)yMax);