X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=pdf%2Fxpdf%2FXOutputDev.cc;h=a156b5584dfcb2fcf554ee8ae4e918bae51488f2;hb=ad63666daeeda50acc7630132d61fe044634fddd;hp=119162cb35ae4215d38d42a16bd8078a2ab55e03;hpb=50e9d31c05e9ca11ad43cc570556094782c1b956;p=evince.git diff --git a/pdf/xpdf/XOutputDev.cc b/pdf/xpdf/XOutputDev.cc index 119162cb..a156b558 100644 --- a/pdf/xpdf/XOutputDev.cc +++ b/pdf/xpdf/XOutputDev.cc @@ -2,11 +2,13 @@ // // XOutputDev.cc // -// Copyright 1996 Derek B. Noonburg +// Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== -#ifdef __GNUC__ +#include + +#ifdef USE_GCC_PRAGMAS #pragma implementation #endif @@ -18,19 +20,35 @@ #include #include #include "gmem.h" +#include "gfile.h" #include "GString.h" +#include "GList.h" #include "Object.h" #include "Stream.h" +#include "Link.h" #include "GfxState.h" #include "GfxFont.h" +#include "UnicodeMap.h" +#include "CharCodeToUnicode.h" #include "FontFile.h" -#include "FontEncoding.h" #include "Error.h" -#include "Params.h" #include "TextOutputDev.h" #include "XOutputDev.h" +#if HAVE_T1LIB_H +#include "T1Font.h" +#endif +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +#include "FTFont.h" +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +#include "TTFont.h" +#endif -#include "XOutputFontInfo.h" +#ifdef VMS +#if (__VMS_VER < 70000000) +extern "C" int unlink(char *filename); +#endif +#endif #ifdef XlibSpecificationRelease #if XlibSpecificationRelease < 5 @@ -50,432 +68,421 @@ typedef char *XPointer; // drawing Bezier curves //------------------------------------------------------------------------ -// Parameters +// Font substitutions //------------------------------------------------------------------------ -GBool installCmap = gFalse; +struct XOutFontSubst { + char *name; + double mWidth; +}; -int rgbCubeSize = defaultRGBCube; +// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic +static XOutFontSubst xOutSubstFonts[16] = { + {"Helvetica", 0.833}, + {"Helvetica-Oblique", 0.833}, + {"Helvetica-Bold", 0.889}, + {"Helvetica-BoldOblique", 0.889}, + {"Times-Roman", 0.788}, + {"Times-Italic", 0.722}, + {"Times-Bold", 0.833}, + {"Times-BoldItalic", 0.778}, + {"Courier", 0.600}, + {"Courier-Oblique", 0.600}, + {"Courier-Bold", 0.600}, + {"Courier-BoldOblique", 0.600}, + {"Symbol", 0.576}, + {"Symbol", 0.576}, + {"Symbol", 0.576}, + {"Symbol", 0.576} +}; -#if HAVE_T1LIB_H -GString *t1libControl = NULL; -#endif +//------------------------------------------------------------------------ -GString *t1Courier = NULL; -GString *t1CourierBold = NULL; -GString *t1CourierBoldOblique = NULL; -GString *t1CourierOblique = NULL; -GString *t1Helvetica = NULL; -GString *t1HelveticaBold = NULL; -GString *t1HelveticaBoldOblique = NULL; -GString *t1HelveticaOblique = NULL; -GString *t1Symbol = NULL; -GString *t1TimesBold = NULL; -GString *t1TimesBoldItalic = NULL; -GString *t1TimesItalic = NULL; -GString *t1TimesRoman = NULL; -GString *t1ZapfDingbats = NULL; - -GBool useEUCJP = gFalse; -#if JAPANESE_SUPPORT -GString *japan12Font = NULL; -#endif +static void outputToFile(void *stream, char *data, int len) { + fwrite(data, 1, len, (FILE *)stream); +} //------------------------------------------------------------------------ -// Font map +// XOutputFont //------------------------------------------------------------------------ -struct FontMapEntry { - char *pdfFont; - char *xFont; - GString **t1Font; - FontEncoding *encoding; -}; +XOutputFont::XOutputFont(Ref *idA, double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, double m21A, double m22A, + Display *displayA, XOutputDev *xOutA) { + id = *idA; + display = displayA; + xOut = xOutA; + m11Orig = m11OrigA; + m12Orig = m12OrigA; + m21Orig = m21OrigA; + m22Orig = m22OrigA; + m11 = m11A; + m12 = m12A; + m21 = m21A; + m22 = m22A; +} -static FontMapEntry fontMap[] = { - {"Courier", "-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Courier, &isoLatin1Encoding}, - {"Courier-Bold", "-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBold, &isoLatin1Encoding}, - {"Courier-BoldOblique", "-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBoldOblique, &isoLatin1Encoding}, - {"Courier-Oblique", "-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierOblique, &isoLatin1Encoding}, - {"Helvetica", "-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Helvetica, &isoLatin1Encoding}, - {"Helvetica-Bold", "-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBold, &isoLatin1Encoding}, - {"Helvetica-BoldOblique", "-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBoldOblique, &isoLatin1Encoding}, - {"Helvetica-Oblique", "-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaOblique, &isoLatin1Encoding}, - {"Symbol", "-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, &symbolEncoding}, - {"Times-Bold", "-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBold, &isoLatin1Encoding}, - {"Times-BoldItalic", "-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBoldItalic, &isoLatin1Encoding}, - {"Times-Italic", "-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesItalic, &isoLatin1Encoding}, - {"Times-Roman", "-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesRoman, &isoLatin1Encoding}, - {"ZapfDingbats", "-*-zapfdingbats-medium-r-normal-*-%s-*-*-*-*-*-*-*", &t1ZapfDingbats, &zapfDingbatsEncoding}, - {NULL} -}; +XOutputFont::~XOutputFont() { +} -static FontMapEntry *userFontMap; +void XOutputFont::getCharPath(GfxState *state, + CharCode c, Unicode *u, int ulen) { +} +#if HAVE_T1LIB_H //------------------------------------------------------------------------ -// Font substitutions +// XOutputT1Font //------------------------------------------------------------------------ -struct FontSubst { - char *xFont; - GString **t1Font; - double mWidth; -}; - -// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic -static FontSubst fontSubst[16] = { - {"-*-helvetica-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Helvetica, 0.833}, - {"-*-helvetica-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaOblique, 0.833}, - {"-*-helvetica-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBold, 0.889}, - {"-*-helvetica-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1HelveticaBoldOblique, 0.889}, - {"-*-times-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesRoman, 0.788}, - {"-*-times-medium-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesItalic, 0.722}, - {"-*-times-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBold, 0.833}, - {"-*-times-bold-i-normal-*-%s-*-*-*-*-*-iso8859-1", &t1TimesBoldItalic, 0.778}, - {"-*-courier-medium-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1Courier, 0.600}, - {"-*-courier-medium-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierOblique, 0.600}, - {"-*-courier-bold-r-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBold, 0.600}, - {"-*-courier-bold-o-normal-*-%s-*-*-*-*-*-iso8859-1", &t1CourierBoldOblique, 0.600}, - {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576}, - {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576}, - {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576}, - {"-*-symbol-medium-r-normal-*-%s-*-*-*-*-*-adobe-fontspecific", &t1Symbol, 0.576} -}; +XOutputT1Font::XOutputT1Font(Ref *idA, T1FontFile *fontFileA, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double matrix[4]; -//------------------------------------------------------------------------ -// 16-bit fonts -//------------------------------------------------------------------------ + fontFile = fontFileA; -#if JAPANESE_SUPPORT - -static char *japan12DefFont = - "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-jisx0208.1983-0"; - -// CID 0 .. 96 -static Gushort japan12Map[96] = { - 0x2120, 0x2120, 0x212a, 0x2149, 0x2174, 0x2170, 0x2173, 0x2175, // 00 .. 07 - 0x2147, 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x213e, 0x2123, // 08 .. 0f - 0x213f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, // 10 .. 17 - 0x2337, 0x2338, 0x2339, 0x2127, 0x2128, 0x2163, 0x2161, 0x2164, // 18 .. 1f - 0x2129, 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, // 20 .. 27 - 0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, // 28 .. 2f - 0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, // 30 .. 37 - 0x2357, 0x2358, 0x2359, 0x235a, 0x214e, 0x216f, 0x214f, 0x2130, // 38 .. 3f - 0x2132, 0x2146, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, // 40 .. 47 - 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, // 48 .. 4f - 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, // 50 .. 57 - 0x2377, 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141 // 58 .. 5f -}; + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new T1Font(fontFile, matrix); +} -// CID 325 .. 421 -static Gushort japan12KanaMap1[97] = { - 0x2131, 0x2121, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, - 0x2521, 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, - 0x2543, 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, - 0x252d, 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, - 0x253d, 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, - 0x254c, 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, - 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, - 0x2569, 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, - 0x212c, 0x212e, 0x2570, 0x2571, 0x256e, 0x2575, 0x2576, 0x2574, - 0x252c, 0x252e, 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, - 0x253c, 0x253e, 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x2550, - 0x2551, 0x2553, 0x2554, 0x2556, 0x2557, 0x2559, 0x255a, 0x255c, - 0x255d -}; +XOutputT1Font::~XOutputT1Font() { + if (font) { + delete font; + } +} -// CID 501 .. 598 -static Gushort japan12KanaMap2[98] = { - 0x212d, 0x212f, 0x216d, 0x214c, 0x214d, 0x2152, 0x2153, 0x2154, - 0x2155, 0x2158, 0x2159, 0x215a, 0x215b, 0x213d, 0x2121, 0x2472, - 0x2421, 0x2423, 0x2425, 0x2427, 0x2429, 0x2463, 0x2465, 0x2467, - 0x2443, 0x2422, 0x2424, 0x2426, 0x2428, 0x242a, 0x242b, 0x242d, - 0x242f, 0x2431, 0x2433, 0x2435, 0x2437, 0x2439, 0x243b, 0x243d, - 0x243f, 0x2441, 0x2444, 0x2446, 0x2448, 0x244a, 0x244b, 0x244c, - 0x244d, 0x244e, 0x244f, 0x2452, 0x2455, 0x2458, 0x245b, 0x245e, - 0x245f, 0x2460, 0x2461, 0x2462, 0x2464, 0x2466, 0x2468, 0x2469, - 0x246a, 0x246b, 0x246c, 0x246d, 0x246f, 0x2473, 0x2470, 0x2471, - 0x246e, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438, - 0x243a, 0x243c, 0x243e, 0x2440, 0x2442, 0x2445, 0x2447, 0x2449, - 0x2450, 0x2451, 0x2453, 0x2454, 0x2456, 0x2457, 0x2459, 0x245a, - 0x245c, 0x245d -}; +GBool XOutputT1Font::isOk() { + return font != NULL; +} -static char *japan12Roman[10] = { - "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X" -}; +void XOutputT1Font::updateGC(GC gc) { +} -static char *japan12Abbrev1[6] = { - "mm", "cm", "km", "mg", "kg", "cc" -}; +void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h, + GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y), + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, u[0]); +} -#endif +void XOutputT1Font::getCharPath(GfxState *state, + CharCode c, Unicode *u, int uLen) { + font->getCharPath(c, u[0], state); +} +#endif // HAVE_T1LIB_H +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) //------------------------------------------------------------------------ -// Constructed characters +// XOutputFTFont //------------------------------------------------------------------------ -#define lastRegularChar 0x0ff -#define firstSubstChar 0x100 -#define lastSubstChar 0x104 -#define firstConstrChar 0x105 -#define lastConstrChar 0x106 -#define firstMultiChar 0x107 -#define lastMultiChar 0x110 - -// substituted chars -static Guchar substChars[] = { - 0x27, // 100: quotesingle --> quoteright - 0x2d, // 101: emdash --> hyphen - 0xad, // 102: hyphen --> endash - 0x2f, // 103: fraction --> slash - 0xb0, // 104: ring --> degree -}; +XOutputFTFont::XOutputFTFont(Ref *idA, FTFontFile *fontFileA, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double matrix[4]; -// constructed chars -// 105: bullet -// 106: trademark - -// built-up chars -static char *multiChars[] = { - "fi", // 107: fi - "fl", // 108: fl - "ff", // 109: ff - "ffi", // 10a: ffi - "ffl", // 10b: ffl - "OE", // 10c: OE - "oe", // 10d: oe - "...", // 10e: ellipsis - "``", // 10f: quotedblleft - "''" // 110: quotedblright -}; + fontFile = fontFileA; -// ignored chars -// 111: Lslash -// : Scaron -// : Zcaron -// : Ydieresis -// : breve -// : caron -// : circumflex -// : dagger -// : daggerdbl -// : dotaccent -// : dotlessi -// : florin -// : grave -// : guilsinglleft -// : guilsinglright -// : hungarumlaut -// : lslash -// : ogonek -// : perthousand -// : quotedblbase -// : quotesinglbase -// : scaron -// : tilde -// : zcaron + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new FTFont(fontFile, matrix); +} -//------------------------------------------------------------------------ -// XOutputFont -//------------------------------------------------------------------------ +XOutputFTFont::~XOutputFTFont() { + if (font) { + delete font; + } +} -XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12, - double m21, double m22, Display *display, - XOutputFontCache *cache) { - int code; - char *charName; +GBool XOutputFTFont::isOk() { + return font != NULL; +} - id = gfxFont->getID(); - this->display = display; - tm11 = m11; - tm12 = m12; - tm21 = m21; - tm22 = m22; - - // check for hex char names - hex = gFalse; - if (!gfxFont->is16Bit()) { - for (code = 0; code < 256; ++code) { - if ((charName = gfxFont->getCharName(code))) { - if ((charName[0] == 'B' || charName[0] == 'C' || - charName[0] == 'G') && - strlen(charName) == 3 && - ((charName[1] >= 'a' && charName[1] <= 'f') || - (charName[1] >= 'A' && charName[1] <= 'F') || - (charName[2] >= 'a' && charName[2] <= 'f') || - (charName[2] >= 'A' && charName[2] <= 'F'))) { - hex = gTrue; - break; - } - } - } - } +void XOutputFTFont::updateGC(GC gc) { } -XOutputFont::~XOutputFont() { +void XOutputFTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h, + GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y), + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, uLen > 0 ? u[0] : 0); } -#if HAVE_T1LIB_H +void XOutputFTFont::getCharPath(GfxState *state, + CharCode c, Unicode *u, int uLen) { + font->getCharPath(c, u[0], state); +} +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) //------------------------------------------------------------------------ -// XOutputT1Font +// XOutputTTFont //------------------------------------------------------------------------ -XOutputT1Font::XOutputT1Font(GfxFont *gfxFont, GString *pdfBaseFont, - double m11, double m12, double m21, double m22, - double size, double ntm11, double ntm12, - double ntm21, double ntm22, - Display *display, XOutputFontCache *cache): - XOutputFont(gfxFont, m11, m12, m21, m22, display, cache) +XOutputTTFont::XOutputTTFont(Ref *idA, TTFontFile *fontFileA, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) { - Ref embRef; - T1_TMATRIX xform; - - t1ID = -1; - t1libAA = cache->getT1libAA(); - - // keep size info (for drawChar()) - this->size = (float)size; - - // we can only handle 8-bit, Type 1/1C, with embedded font file - // or user-specified base fonts - //~ also look for external font files - if (!(pdfBaseFont || - (!gfxFont->is16Bit() && - (gfxFont->getType() == fontType1 || - gfxFont->getType() == fontType1C) && - gfxFont->getEmbeddedFontID(&embRef)))) { - return; - } + double matrix[4]; - // load the font - if ((t1ID = cache->getT1Font(gfxFont, pdfBaseFont)) < 0) - return; + fontFile = fontFileA; - // transform the font - xform.cxx = ntm11; - xform.cxy = -ntm12; - xform.cyx = ntm21; - xform.cyy = -ntm22; - T1_TransformFont(t1ID, &xform); + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new TTFont(fontFile, matrix); } -XOutputT1Font::~XOutputT1Font() { - if (t1ID >= 0) { - T1_DeleteFont(t1ID); +XOutputTTFont::~XOutputTTFont() { + if (font) { + delete font; } } -GBool XOutputT1Font::isOk() { - return t1ID >= 0; +GBool XOutputTTFont::isOk() { + return font != NULL; } -void XOutputT1Font::updateGC(GC gc) { +void XOutputTTFont::updateGC(GC gc) { } -void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, GC gc, - double x, double y, int c) { - if (t1libAA) { - T1_AASetCharX(pixmap, gc, 0, xoutRound(x), xoutRound(y), - t1ID, c, size, NULL); - } else { - T1_SetCharX(pixmap, gc, 0, xoutRound(x), xoutRound(y), - t1ID, c, size, NULL); - } +void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h, + GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + font->drawChar(pixmap, w, h, gc, xoutRound(x), xoutRound(y), + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, u[0]); } -#endif // HAVE_T1LIB_H +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) //------------------------------------------------------------------------ -// XOutputServerFont +// XOutputServer8BitFont //------------------------------------------------------------------------ -XOutputServerFont::XOutputServerFont(GfxFont *gfxFont, char *fontNameFmt, - FontEncoding *encoding, - double m11, double m12, - double m21, double m22, - double size, - double ntm11, double ntm12, - double ntm21, double ntm22, - Display *display, - XOutputFontCache *cache): - XOutputFont(gfxFont, m11, m12, m21, m22, display, cache) +// Copy , substituting for one occurrence of "%s", into +// . +static void stringSubst(char *buf, int bufSize, char *fmt, char *val) { + char *p, *q; + int i; + + i = 0; + p = fmt; + while (*p) { + if (p[0] == '%' && p[1] == 's') { + q = val; + while (*q && i < bufSize - 1) { + buf[i++] = *q++; + } + p += 2; + } else { + if (i < bufSize - 1) { + buf[i++] = *p; + } + ++p; + } + } + buf[i] = '\0'; +} + +XOutputServer8BitFont::XOutputServer8BitFont(Ref *idA, GString *xlfdFmt, + UnicodeMap *xUMapA, + CharCodeToUnicode *fontUMap, + double m11OrigA, double m12OrigA, + double m21OrigA, double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, + XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) { - char fontName[200], fontSize[100]; + double size, ntm11, ntm12, ntm21, ntm22; GBool rotated; int startSize, sz; - int code, code2; - char *charName; - int n; - - xFont = NULL; - - // Construct forward and reverse map. - // This tries to deal with font subset character names of the - // form 'Bxx', 'Cxx', 'Gxx', with decimal or hex numbering. - if (!gfxFont->is16Bit()) { - for (code = 0; code < 256; ++code) - revMap[code] = 0; - if (encoding) { - for (code = 0; code < 256; ++code) { - if ((charName = gfxFont->getCharName(code))) { - if ((code2 = encoding->getCharCode(charName)) < 0) { - n = strlen(charName); - if (hex && n == 3 && - (charName[0] == 'B' || charName[0] == 'C' || - charName[0] == 'G') && - isxdigit(charName[1]) && isxdigit(charName[2])) { - sscanf(charName+1, "%x", &code2); - } else if (!hex && n >= 2 && n <= 3 && - isdigit(charName[0]) && isdigit(charName[1])) { - code2 = atoi(charName); - if (code2 >= 256) - code2 = -1; - } else if (!hex && n >= 3 && n <= 5 && isdigit(charName[1])) { - code2 = atoi(charName+1); - if (code2 >= 256) - code2 = -1; - } - //~ this is a kludge -- is there a standard internal encoding - //~ used by all/most Type 1 fonts? - if (code2 == 262) // hyphen - code2 = 45; - else if (code2 == 266) // emdash - code2 = 208; - } - if (code2 >= 0) { - map[code] = (Gushort)code2; - if (code2 < 256) - revMap[code2] = (Guchar)code; - } else { - map[code] = 0; - } - } else { - map[code] = 0; + char fontName[500], fontSize[100]; + Unicode u; + char buf; + int i; + + // compute size and normalized transform matrix + size = sqrt(m21*m21 + m22*m22); + ntm11 = m11 / size; + ntm12 = -m12 / size; + ntm21 = m21 / size; + ntm22 = -m22 / size; + + // try to get a rotated font? + rotated = !(ntm11 > 0 && ntm22 > 0 && + fabs(ntm11 / ntm22 - 1) < 0.2 && + fabs(ntm12) < 0.01 && + fabs(ntm21) < 0.01); + + // open X font -- if font is not found (which means the server can't + // scale fonts), try progressively smaller and then larger sizes + startSize = (int)size; + if (rotated) { + sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]", + ntm11<0 ? "~" : "", fabs(ntm11 * size), + ntm12<0 ? "~" : "", fabs(ntm12 * size), + ntm21<0 ? "~" : "", fabs(ntm21 * size), + ntm22<0 ? "~" : "", fabs(ntm22 * size)); + } else { + sprintf(fontSize, "%d", startSize); + } + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); + xFont = XLoadQueryFont(display, fontName); + if (!xFont) { + for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) { + sprintf(fontSize, "%d", sz); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) + break; + } + if (!xFont) { + for (sz = startSize + 1; sz < startSize + 10; ++sz) { + sprintf(fontSize, "%d", sz); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) { + break; } } + if (!xFont) { + sprintf(fontSize, "%d", startSize); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + error(-1, "Failed to open font: '%s'", fontName); + return; + } + } + } + + // Construct char code map. + xUMap = xUMapA; + for (i = 0; i < 256; ++i) { + if (fontUMap->mapToUnicode((CID)i, &u, 1) == 1 && + xUMap->mapUnicode(u, &buf, 1) == 1) { + map[i] = buf & 0xff; } else { - code2 = 0; // to make gcc happy - //~ this is a hack to get around the fact that X won't draw - //~ chars 0..31; this works when the fonts have duplicate encodings - //~ for those chars - for (code = 0; code < 32; ++code) { - if ((charName = gfxFont->getCharName(code)) && - (code2 = gfxFont->getCharCode(charName)) >= 0) { - map[code] = (Gushort)code2; - if (code2 < 256) - revMap[code2] = (Guchar)code; + map[i] = 0; + } + } +} + +XOutputServer8BitFont::~XOutputServer8BitFont() { + if (xFont) { + XFreeFont(display, xFont); + } +} + +GBool XOutputServer8BitFont::isOk() { + return xFont != NULL; +} + +void XOutputServer8BitFont::updateGC(GC gc) { + XSetFont(display, gc, xFont->fid); +} + +void XOutputServer8BitFont::drawChar(GfxState *state, Pixmap pixmap, + int w, int h, GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + Gushort c1; + char buf[8]; + double dx1, dy1; + int m, n, i, j, k; + + c1 = map[c]; + if (c1 > 0) { + buf[0] = (char)c1; + XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), buf, 1); + } else { + // substituted character, using more than one character + n = 0; + for (i = 0; i < uLen; ++i) { + n += xUMap->mapUnicode(u[i], buf, sizeof(buf)); + } + if (n > 0) { + dx1 = dx / n; + dy1 = dy / n; + k = 0; + for (i = 0; i < uLen; ++i) { + m = xUMap->mapUnicode(u[i], buf, sizeof(buf)); + for (j = 0; j < m; ++j) { + XDrawString(display, pixmap, gc, + xoutRound(x + k*dx1), xoutRound(y + k*dy1), + buf + j, 1); + ++k; } } - for (code = 32; code < 256; ++code) { - map[code] = (Gushort)code; - revMap[code] = (Guchar)code; - } } } +} + +//------------------------------------------------------------------------ +// XOutputServer16BitFont +//------------------------------------------------------------------------ + +XOutputServer16BitFont::XOutputServer16BitFont(Ref *idA, GString *xlfdFmt, + UnicodeMap *xUMapA, + CharCodeToUnicode *fontUMap, + double m11OrigA, + double m12OrigA, + double m21OrigA, + double m22OrigA, + double m11A, double m12A, + double m21A, double m22A, + Display *displayA, + XOutputDev *xOutA): + XOutputFont(idA, m11OrigA, m12OrigA, m21OrigA, m22OrigA, + m11A, m12A, m21A, m22A, displayA, xOutA) +{ + double size, ntm11, ntm12, ntm21, ntm22; + GBool rotated; + int startSize, sz; + char fontName[500], fontSize[100]; + + xUMap = xUMapA; + xUMap->incRefCnt(); - // adjust transform for the X transform convention - ntm12 = -ntm12; - ntm22 = -ntm22; + // compute size and normalized transform matrix + size = sqrt(m21*m21 + m22*m22); + ntm11 = m11 / size; + ntm12 = -m12 / size; + ntm21 = m21 / size; + ntm22 = -m22 / size; // try to get a rotated font? rotated = !(ntm11 > 0 && ntm22 > 0 && @@ -485,36 +492,38 @@ XOutputServerFont::XOutputServerFont(GfxFont *gfxFont, char *fontNameFmt, // open X font -- if font is not found (which means the server can't // scale fonts), try progressively smaller and then larger sizes - //~ This does a linear search -- it should get a list of fonts from - //~ the server and pick the closest. startSize = (int)size; - if (rotated) + if (rotated) { sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]", ntm11<0 ? "~" : "", fabs(ntm11 * size), ntm12<0 ? "~" : "", fabs(ntm12 * size), ntm21<0 ? "~" : "", fabs(ntm21 * size), ntm22<0 ? "~" : "", fabs(ntm22 * size)); - else + } else { sprintf(fontSize, "%d", startSize); - sprintf(fontName, fontNameFmt, fontSize); + } + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); xFont = XLoadQueryFont(display, fontName); if (!xFont) { for (sz = startSize; sz >= startSize/2 && sz >= 1; --sz) { sprintf(fontSize, "%d", sz); - sprintf(fontName, fontNameFmt, fontSize); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), fontSize); if ((xFont = XLoadQueryFont(display, fontName))) break; } if (!xFont) { for (sz = startSize + 1; sz < startSize + 10; ++sz) { sprintf(fontSize, "%d", sz); - sprintf(fontName, fontNameFmt, fontSize); - if ((xFont = XLoadQueryFont(display, fontName))) + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); + if ((xFont = XLoadQueryFont(display, fontName))) { break; + } } if (!xFont) { sprintf(fontSize, "%d", startSize); - sprintf(fontName, fontNameFmt, fontSize); + stringSubst(fontName, sizeof(fontName), xlfdFmt->getCString(), + fontSize); error(-1, "Failed to open font: '%s'", fontName); return; } @@ -522,75 +531,53 @@ XOutputServerFont::XOutputServerFont(GfxFont *gfxFont, char *fontNameFmt, } } -XOutputServerFont::~XOutputServerFont() { - if (xFont) +XOutputServer16BitFont::~XOutputServer16BitFont() { + xUMap->decRefCnt(); + if (xFont) { XFreeFont(display, xFont); + } } -GBool XOutputServerFont::isOk() { +GBool XOutputServer16BitFont::isOk() { return xFont != NULL; } -void XOutputServerFont::updateGC(GC gc) { +void XOutputServer16BitFont::updateGC(GC gc) { XSetFont(display, gc, xFont->fid); } -void XOutputServerFont::drawChar(GfxState *state, Pixmap pixmap, GC gc, - double x, double y, int c) { - GfxFont *gfxFont; - Gushort c1; - char buf; - char *p; - int n, i; - double tx; - - c1 = map[c]; - if (c1 <= lastRegularChar) { - buf = (char)c1; - XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), &buf, 1); - } else if (c1 <= lastSubstChar) { - buf = (char)substChars[c1 - firstSubstChar]; - XDrawString(display, pixmap, gc, xoutRound(x), xoutRound(y), &buf, 1); - } else if (c1 <= lastConstrChar) { - gfxFont = state->getFont(); - //~ need to deal with rotated text here - switch (c1 - firstConstrChar) { - case 0: // bullet - tx = 0.25 * state->getTransformedFontSize() * gfxFont->getWidth(c); - XFillRectangle(display, pixmap, gc, - xoutRound(x + tx), - xoutRound(y - 0.4 * xFont->ascent - tx), - xoutRound(2 * tx), xoutRound(2 * tx)); - break; - case 1: // trademark -//~ this should use a smaller font -// tx = state->getTransformedFontSize() * -// (gfxFont->getWidth(c) - -// gfxFont->getWidth(font->revMap['M'])); - tx = 0.9 * state->getTransformedFontSize() * - gfxFont->getWidth(revMap['T']); - y -= 0.33 * (double)xFont->ascent; - buf = 'T'; - XDrawString(display, pixmap, gc, - xoutRound(x), xoutRound(y), &buf, 1); - x += tx; - buf = 'M'; - XDrawString(display, pixmap, gc, - xoutRound(x), xoutRound(y), &buf, 1); - break; - } - } else if (c1 <= lastMultiChar) { - gfxFont = state->getFont(); - p = multiChars[c1 - firstMultiChar]; - n = strlen(p); - tx = gfxFont->getWidth(c); - tx -= gfxFont->getWidth(revMap[p[n-1]]); - tx = tx * state->getTransformedFontSize() / (double)(n - 1); - for (i = 0; i < n; ++i) { - XDrawString(display, pixmap, gc, - xoutRound(x), xoutRound(y), p + i, 1); - x += tx; +void XOutputServer16BitFont::drawChar(GfxState *state, Pixmap pixmap, + int w, int h, GC gc, GfxRGB *rgb, + double x, double y, double dx, double dy, + CharCode c, Unicode *u, int uLen) { + char buf[16]; + XChar2b c1; + double dx1, dy1; + int m, n, i, j, k; + + n = 0; + for (i = 0; i < uLen; ++i) { + n += xUMap->mapUnicode(u[i], buf, sizeof(buf)); + } + if (n > 0) { + dx1 = dx / n; + dy1 = dy / n; + k = 0; + for (i = 0; i < uLen; ++i) { + m = xUMap->mapUnicode(u[i], buf, sizeof(buf)); + for (j = 0; j+1 < m; j += 2) { + c1.byte1 = buf[j]; + c1.byte2 = buf[j+1]; + XDrawString16(display, pixmap, gc, + xoutRound(x + k*dx1), xoutRound(y + k*dy1), + &c1, 1); + ++k; + } } + } else if (c != 0) { + // some PDF files use CID 0, which is .notdef, so just ignore it + error(-1, "Unknown character (CID=%d Unicode=%04x)", + c, uLen > 0 ? u[0] : (Unicode)0); } } @@ -598,20 +585,61 @@ void XOutputServerFont::drawChar(GfxState *state, Pixmap pixmap, GC gc, // XOutputFontCache //------------------------------------------------------------------------ -XOutputFontCache::XOutputFontCache(Display *display) { - this->display = display; #if HAVE_T1LIB_H - t1Init = gFalse; - if (t1libControl) { - useT1lib = t1libControl->cmp("none") != 0; - t1libAA = t1libControl->cmp("plain") != 0; - t1libAAHigh = t1libControl->cmp("high") == 0; - } else { - useT1lib = gFalse; - t1libAA = gFalse; - t1libAAHigh = gFalse; +XOutputT1FontFile::~XOutputT1FontFile() { + delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; } +} +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFTFontFile::~XOutputFTFontFile() { + delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } +} +#endif + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputTTFontFile::~XOutputTTFontFile() { + delete fontFile; + if (tmpFileName) { + unlink(tmpFileName->getCString()); + delete tmpFileName; + } +} +#endif + +XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA, + XOutputDev *xOutA, + FontRastControl t1libControlA, + FontRastControl freetypeControlA) { + display = displayA; + depth = depthA; + xOut = xOutA; + +#if HAVE_T1LIB_H + t1libControl = t1libControlA; + t1Engine = NULL; + t1FontFiles = NULL; +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + freetypeControl = freetypeControlA; + ftEngine = NULL; + ftFontFiles = NULL; +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + freetypeControl = freetypeControlA; + ttEngine = NULL; + ttFontFiles = NULL; #endif + clear(); } @@ -619,177 +647,240 @@ XOutputFontCache::~XOutputFontCache() { delFonts(); } -void XOutputFontCache::startDoc(int screenNum, Guint depth, - Colormap colormap) { +void XOutputFontCache::startDoc(int screenNum, Visual *visual, + Colormap colormap, GBool trueColor, + int rMul, int gMul, int bMul, + int rShift, int gShift, int bShift, + Gulong *colors, int numColors) { delFonts(); clear(); #if HAVE_T1LIB_H - if (useT1lib) { - if (T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE | - T1_NO_AFM)) { - if (t1libAA) { - T1_AASetLevel(t1libAAHigh ? T1_AA_HIGH : T1_AA_LOW); - if (depth <= 8) - T1_AASetBitsPerPixel(8); - else if (depth <= 16) - T1_AASetBitsPerPixel(16); - else - T1_AASetBitsPerPixel(32); - } - T1_SetX11Params(display, DefaultVisual(display, screenNum), - depth, colormap); - t1Init = gTrue; + if (t1libControl != fontRastNone) { + t1Engine = new T1FontEngine(display, visual, depth, colormap, + t1libControl == fontRastAALow || + t1libControl == fontRastAAHigh, + t1libControl == fontRastAAHigh); + if (t1Engine->isOk()) { + if (trueColor) { + t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); + } else { + t1Engine->useColorCube(colors, numColors); + } } else { - useT1lib = gFalse; + delete t1Engine; + t1Engine = NULL; } } #endif // HAVE_T1LIB_H + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + ftEngine = new FTFontEngine(display, visual, depth, colormap, + freetypeControl == fontRastAALow || + freetypeControl == fontRastAAHigh); + if (ftEngine->isOk()) { + if (trueColor) { + ftEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); + } else { + ftEngine->useColorCube(colors, numColors); + } + } else { + delete ftEngine; + ftEngine = NULL; + } + } +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + ttEngine = new TTFontEngine(display, visual, depth, colormap, + freetypeControl == fontRastAALow || + freetypeControl == fontRastAAHigh); + if (ttEngine->isOk()) { + if (trueColor) { + ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); + } else { + ttEngine->useColorCube(colors, numColors); + } + } else { + delete ttEngine; + ttEngine = NULL; + } + } +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) } void XOutputFontCache::delFonts() { int i; + for (i = 0; i < nFonts; ++i) { + delete fonts[i]; + } + #if HAVE_T1LIB_H - // delete Type 1 fonts - for (i = 0; i < nT1Fonts; ++i) - delete t1Fonts[i]; - for (i = 0; i < t1BaseFontsSize && t1BaseFonts[i].num >= 0; ++i) { - T1_DeleteFont(t1BaseFonts[i].t1ID); - gfree(t1BaseFonts[i].encStr); - gfree(t1BaseFonts[i].enc); - } - gfree(t1BaseFonts); - if (t1Init) { - T1_CloseLib(); + // delete Type 1 font files + if (t1FontFiles) { + deleteGList(t1FontFiles, XOutputT1FontFile); + t1FontFiles = NULL; + } + if (t1Engine) { + delete t1Engine; + t1Engine = NULL; + } +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // delete FreeType font files + if (ftFontFiles) { + deleteGList(ftFontFiles, XOutputFTFontFile); + ftFontFiles = NULL; + } + if (ftEngine) { + delete ftEngine; + ftEngine = NULL; } #endif - // delete server fonts - for (i = 0; i < nServerFonts; ++i) - delete serverFonts[i]; +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // delete TrueType fonts + if (ttFontFiles) { + deleteGList(ttFontFiles, XOutputTTFontFile); + ttFontFiles = NULL; + } + if (ttEngine) { + delete ttEngine; + ttEngine = NULL; + } +#endif } void XOutputFontCache::clear() { int i; + for (i = 0; i < xOutFontCacheSize; ++i) { + fonts[i] = NULL; + } + nFonts = 0; + #if HAVE_T1LIB_H - // clear Type 1 font cache - for (i = 0; i < t1FontCacheSize; ++i) - t1Fonts[i] = NULL; - nT1Fonts = 0; - t1BaseFonts = NULL; - t1BaseFontsSize = 0; + // clear Type 1 font files + t1FontFiles = new GList(); +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // clear FreeType font cache + ftFontFiles = new GList(); #endif - // clear server font cache - for (i = 0; i < serverFontCacheSize; ++i) - serverFonts[i] = NULL; - nServerFonts = 0; +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // clear TrueType font cache + ttFontFiles = new GList(); +#endif } -XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont, +XOutputFont *XOutputFontCache::getFont(XRef *xref, GfxFont *gfxFont, double m11, double m12, double m21, double m22) { -#if HAVE_T1LIB_H - XOutputT1Font *t1Font; -#endif - XOutputServerFont *serverFont; - FontMapEntry *fme; - GString *t1FontName; - char *xFontName; - FontEncoding *xEncoding; - double size; - double ntm11, ntm12, ntm21, ntm22; + XOutputFont *font; + DisplayFontParam *dfp; + GString *substName; + double m11New, m12New, m21New, m22New; double w1, w2, v; double *fm; + char *name; int index; int code; int i, j; - // is it the most recently used Type 1 or server font? -#if HAVE_T1LIB_H - if (useT1lib && nT1Fonts > 0 && - t1Fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) { - return t1Fonts[0]; + // is it the most recently used font? + if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) { + return fonts[0]; } -#endif - if (nServerFonts > 0 && serverFonts[0]->matches(gfxFont->getID(), - m11, m12, m21, m22)) - return serverFonts[0]; -#if HAVE_T1LIB_H - // is it in the Type 1 cache? - if (useT1lib) { - for (i = 1; i < nT1Fonts; ++i) { - if (t1Fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) { - t1Font = t1Fonts[i]; - for (j = i; j > 0; --j) - t1Fonts[j] = t1Fonts[j-1]; - t1Fonts[0] = t1Font; - return t1Font; + // is it in the cache? + for (i = 1; i < nFonts; ++i) { + if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) { + font = fonts[i]; + for (j = i; j > 0; --j) { + fonts[j] = fonts[j-1]; } + fonts[0] = font; + return font; } } -#endif - // is it in the server cache? - for (i = 1; i < nServerFonts; ++i) { - if (serverFonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) { - serverFont = serverFonts[i]; - for (j = i; j > 0; --j) - serverFonts[j] = serverFonts[j-1]; - serverFonts[0] = serverFont; - return serverFont; + // try for a cached FontFile, an embedded font, or an external font + // file + font = NULL; + switch (gfxFont->getType()) { + case fontType1: + case fontType1C: +#if HAVE_T1LIB_H + if (t1libControl != fontRastNone) { + font = tryGetT1Font(xref, gfxFont, m11, m12, m21, m22); } - } - - // compute size and normalized transform matrix - size = sqrt(m21*m21 + m22*m22); - ntm11 = m11 / size; - ntm12 = m12 / size; - ntm21 = m21 / size; - ntm22 = m22 / size; - - // search for a font map entry - t1FontName = NULL; - xFontName = NULL; - xEncoding = NULL; - if (!gfxFont->is16Bit() && gfxFont->getName()) { - for (fme = userFontMap; fme->pdfFont; ++fme) { - if (!gfxFont->getName()->cmp(fme->pdfFont)) { - break; +#endif +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (!font) { + if (freetypeControl != fontRastNone) { + font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22); } } - if (!fme->pdfFont) { - for (fme = fontMap; fme->pdfFont; ++fme) { - if (!gfxFont->getName()->cmp(fme->pdfFont)) { - break; - } - } +#endif + break; + case fontTrueType: + case fontCIDType2: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22); } - if (fme && fme->t1Font) { - t1FontName = *fme->t1Font; +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetTTFont(xref, gfxFont, m11, m12, m21, m22); } - if (fme && fme->xFont && fme->encoding) { - xFontName = fme->xFont; - xEncoding = fme->encoding; +#endif + break; + case fontCIDType0: + case fontCIDType0C: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFont(xref, gfxFont, m11, m12, m21, m22); } +#endif + break; + default: + break; } - // no font map entry found, so substitute a font - if (!t1FontName && !xFontName) { - if (gfxFont->is16Bit()) { - xFontName = fontSubst[0].xFont; - t1FontName = NULL; - switch (gfxFont->getCharSet16()) { - case font16AdobeJapan12: -#if JAPANESE_SUPPORT - xFontName = japan12Font ? japan12Font->getCString() : japan12DefFont; -#endif - break; + if (!font) { + + // search for a display font mapping + dfp = NULL; + if (gfxFont->isCIDFont()) { + if (((GfxCIDFont *)gfxFont)->getCollection()) { + dfp = globalParams-> + getDisplayCIDFont(gfxFont->getName(), + ((GfxCIDFont *)gfxFont)->getCollection()); + } else { + // this error (no CMap file) was already reported by GfxFont + return NULL; } } else { + if (gfxFont->getName()) { + dfp = globalParams->getDisplayFont(gfxFont->getName()); + } + } + if (dfp) { + font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + } + + // substitute a font (8-bit fonts only) + if (!font && !gfxFont->isCIDFont()) { + + // choose a substitute font if (gfxFont->isFixedWidth()) { index = 8; } else if (gfxFont->isSerif()) { @@ -797,307 +888,861 @@ XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont, } else { index = 0; } - if (gfxFont->isBold()) + if (gfxFont->isBold()) { index += 2; - if (gfxFont->isItalic()) + } + if (gfxFont->isItalic()) { index += 1; - xFontName = fontSubst[index].xFont; - t1FontName = *fontSubst[index].t1Font; - xEncoding = &isoLatin1Encoding; - // un-normalize - ntm11 = m11; - ntm12 = m12; - ntm21 = m21; - ntm22 = m22; - // get width of 'm' in real font and substituted font - if ((code = gfxFont->getCharCode("m")) >= 0) - w1 = gfxFont->getWidth(code); - else - w1 = 0; - w2 = fontSubst[index].mWidth; - if (gfxFont->getType() == fontType3) { - // This is a hack which makes it possible to substitute for some - // Type 3 fonts. The problem is that it's impossible to know what - // the base coordinate system used in the font is without actually - // rendering the font. This code tries to guess by looking at the - // width of the character 'm' (which breaks if the font is a - // subset that doesn't contain 'm'). - if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) { - w1 /= w2; - ntm11 = m11 * w1; - ntm12 = m12 * w1; - ntm21 = m21 * w1; - ntm22 = m22 * w1; + } + substName = new GString(xOutSubstFonts[index].name); + + // adjust the font matrix -- compare the width of 'm' in the + // original font and the substituted font + m11New = m11; + m12New = m12; + m21New = m21; + m22New = m22; + for (code = 0; code < 256; ++code) { + if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && + name[0] == 'm' && name[1] == '\0') { + break; } - fm = gfxFont->getFontMatrix(); - v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]); - ntm12 *= v; - ntm22 *= v; - } else if (!gfxFont->isSymbolic()) { - // if real font is substantially narrower than substituted - // font, reduce the font size accordingly - if (w1 > 0.01 && w1 < 0.9 * w2) { - w1 /= w2; - if (w1 < 0.8) - w1 = 0.8; - ntm11 = m11 * w1; - ntm12 = m12 * w1; - ntm21 = m21 * w1; - ntm22 = m22 * w1; + } + if (code < 256) { + w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); + w2 = xOutSubstFonts[index].mWidth; + if (gfxFont->getType() == fontType3) { + // This is a hack which makes it possible to substitute for some + // Type 3 fonts. The problem is that it's impossible to know what + // the base coordinate system used in the font is without actually + // rendering the font. This code tries to guess by looking at the + // width of the character 'm' (which breaks if the font is a + // subset that doesn't contain 'm'). + if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) { + w1 /= w2; + m11New *= w1; + m12New *= w1; + m21New *= w1; + m22New *= w1; + } + fm = gfxFont->getFontMatrix(); + v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]); + m21New *= v; + m22New *= v; + } else if (!gfxFont->isSymbolic()) { + // if real font is substantially narrower than substituted + // font, reduce the font size accordingly + if (w1 > 0.01 && w1 < 0.9 * w2) { + w1 /= w2; + m11New *= w1; + m21New *= w1; + } } } - // renormalize - size = sqrt(ntm21*ntm21 + ntm22*ntm22); - ntm11 /= size; - ntm12 /= size; - ntm21 /= size; - ntm22 /= size; - } - } - -#if HAVE_T1LIB_H - // try to create a new Type 1 font - if (useT1lib) { - t1Font = new XOutputT1Font(gfxFont, t1FontName, - m11, m12, m21, m22, - size, ntm11, ntm12, ntm21, ntm22, - display, this); - if (t1Font->isOk()) { - // insert in cache - if (nT1Fonts == t1FontCacheSize) { - --nT1Fonts; - delete t1Fonts[nT1Fonts]; + // get the font + dfp = globalParams->getDisplayFont(substName); + delete substName; + if (!dfp) { + // this should never happen since GlobalParams sets up default + // mappings for the Base-14 fonts + error(-1, "Couldn't find a font for '%s'", + gfxFont->getName()->getCString()); + return NULL; } - for (j = nT1Fonts; j > 0; --j) - t1Fonts[j] = t1Fonts[j-1]; - t1Fonts[0] = t1Font; - ++nT1Fonts; + font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22, + m11New, m12New, m21New, m22New, gTrue); + } + } - return t1Font; + // check for error + if (!font) { + // This will happen if the user specifies a bogus font in the + // config file (a non-existent font file or a font that requires a + // rasterizer that is disabled or wasn't built in), or if a CID + // font has no associated font in the config file. + if (gfxFont->isCIDFont()) { + error(-1, "Couldn't find a font for the '%s' character collection", + ((GfxCIDFont *)gfxFont)->getCollection()->getCString()); + } else { + error(-1, "Couldn't find a font for '%s'", + gfxFont->getName() ? + gfxFont->getName()->getCString() : "[unnamed]"); } - delete t1Font; + return NULL; } -#endif - // create a new server font - serverFont = new XOutputServerFont(gfxFont, xFontName, xEncoding, - m11, m12, m21, m22, - size, ntm11, ntm12, ntm21, ntm22, - display, this); - if (serverFont->isOk()) { + // insert font in cache + if (nFonts == xOutFontCacheSize) { + --nFonts; + delete fonts[nFonts]; + } + for (j = nFonts; j > 0; --j) { + fonts[j] = fonts[j-1]; + } + fonts[0] = font; + ++nFonts; + + return font; +} + +XOutputFont *XOutputFontCache::tryGetFont(XRef *xref, DisplayFontParam *dfp, + GfxFont *gfxFont, + double m11Orig, double m12Orig, + double m21Orig, double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + XOutputFont *font; - // insert in cache - if (nServerFonts == serverFontCacheSize) { - --nServerFonts; - delete serverFonts[nServerFonts]; + font = NULL; + + // create the new font + switch (dfp->kind) { + + case displayFontX: + font = tryGetServerFont(dfp->x.xlfd, dfp->x.encoding, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22); + break; + + case displayFontT1: +#if HAVE_T1LIB_H + if (t1libControl != fontRastNone && !gfxFont->isCIDFont()) { + font = tryGetT1FontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } +#endif +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (!font) { + if (freetypeControl != fontRastNone) { + font = tryGetFTFontFromFile(xref, dfp->t1.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, gFalse, subst); + } } - for (j = nServerFonts; j > 0; --j) - serverFonts[j] = serverFonts[j-1]; - serverFonts[0] = serverFont; - ++nServerFonts; +#endif +#if !((FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)) || defined(HAVE_T1LIB_H)) + error(-1, "Config file specifies a Type 1 font,"); + error(-1, "but xpdf was not built with t1lib or FreeType2 support"); +#endif + break; - return serverFont; + case displayFontTT: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, gFalse, subst); + } +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gFalse, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } +#endif +#if !(HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + error(-1, "Config file specifies a TrueType font,"); + error(-1, "but xpdf was not built with FreeType support"); + dfp = NULL; +#endif + break; } - delete serverFont; - return NULL; + return font; } #if HAVE_T1LIB_H -int XOutputFontCache::getT1Font(GfxFont *gfxFont, GString *pdfBaseFont) { - Ref id; - char *fileName; - char tmpFileName[60]; +XOutputFont *XOutputFontCache::tryGetT1Font(XRef *xref, + GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + Ref *id; + XOutputT1FontFile *xFontFile; + XOutputFont *font; + Ref embRef; + GString *fileName; FILE *f; char *fontBuf; int fontLen; - Type1CFontConverter *cvt; - Ref embRef; + Type1CFontFile *ff; Object refObj, strObj; - FontEncoding *enc; - int encStrSize; - char *encPtr; - int t1ID; int c; - int i, j; + int i; + // check the already available font files id = gfxFont->getID(); + for (i = 0; i < t1FontFiles->getLength(); ++i) { + xFontFile = (XOutputT1FontFile *)t1FontFiles->get(i); + if (xFontFile->num == id->num && xFontFile->gen == id->gen && + !xFontFile->subst) { + font = new XOutputT1Font(id, xFontFile->fontFile, + m11, m12, m21, m22, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; + } + } + + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { + + // create the font file + fileName = NULL; + if (!openTempFile(&fileName, &f, "wb", NULL)) { + error(-1, "Couldn't create temporary Type 1 font file"); + return NULL; + } + if (gfxFont->getType() == fontType1C) { + if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) { + fclose(f); + unlink(fileName->getCString()); + delete fileName; + return NULL; + } + ff = new Type1CFontFile(fontBuf, fontLen); + if (!ff->isOk()) { + delete ff; + gfree(fontBuf); + unlink(fileName->getCString()); + delete fileName; + return NULL; + } + ff->convertToType1(outputToFile, f); + delete ff; + gfree(fontBuf); + } else { // fontType1 + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); + } + fclose(f); + + // create the Font + font = tryGetT1FontFromFile(xref, fileName, gTrue, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file + unlink(fileName->getCString()); + + delete fileName; + + // check for an external font file + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetT1FontFromFile(xref, fileName, gFalse, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); - // check available fonts - t1ID = -1; - for (i = 0; i < t1BaseFontsSize && t1BaseFonts[i].num >= 0; ++i) { - if (t1BaseFonts[i].num == id.num && t1BaseFonts[i].gen == id.gen) { - t1ID = t1BaseFonts[i].t1ID; + } else { + font = NULL; + } + + return font; +} + +XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref, + GString *fileName, + GBool deleteFile, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + Ref *id; + T1FontFile *fontFile; + XOutputFont *font; + + // create the t1lib font file + fontFile = new T1FontFile(t1Engine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + gfxFont->getFontBBox()); + if (!fontFile->isOk()) { + error(-1, "Couldn't create t1lib font from '%s'", + fileName->getCString()); + delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); } + return NULL; + } + + // add to list + id = gfxFont->getID(); + t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen, + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); + + // create the Font + font = new XOutputT1Font(gfxFont->getID(), fontFile, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; } + return font; +} +#endif // HAVE_T1LIB_H - // create a new base font - if (t1ID < 0) { +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFont *XOutputFontCache::tryGetFTFont(XRef *xref, + GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + Ref *id; + XOutputFTFontFile *xFontFile; + XOutputFont *font; + Ref embRef; + GString *fileName; + FILE *f; +#if 1 //~ need this until FT can handle fonts with missing tables + char *fontBuf; + int fontLen; + TrueTypeFontFile *ff; +#endif + Object refObj, strObj; + int c; + int i; - // resize t1BaseFonts if necessary - if (i == t1BaseFontsSize) { - t1BaseFonts = (XOutputT1BaseFont *) - grealloc(t1BaseFonts, - (t1BaseFontsSize + 16) * sizeof(XOutputT1BaseFont)); - for (j = 0; j < 16; ++j) { - t1BaseFonts[t1BaseFontsSize + j].num = -1; + // check the already available font files + id = gfxFont->getID(); + for (i = 0; i < ftFontFiles->getLength(); ++i) { + xFontFile = (XOutputFTFontFile *)ftFontFiles->get(i); + if (xFontFile->num == id->num && xFontFile->gen == id->gen && + !xFontFile->subst) { + font = new XOutputFTFont(id, xFontFile->fontFile, + m11, m12, m21, m22, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; } - t1BaseFontsSize += 16; + return font; } + } + + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { // create the font file - tmpFileName[0] = '\0'; - if (!gfxFont->is16Bit() && - (gfxFont->getType() == fontType1 || - gfxFont->getType() == fontType1C) && - gfxFont->getEmbeddedFontID(&embRef)) { - tmpnam(tmpFileName); - if (!(f = fopen(tmpFileName, "wb"))) { - error(-1, "Couldn't open temporary Type 1 font file '%s'", - tmpFileName); - return -1; - } - if (gfxFont->getType() == fontType1C) { - fontBuf = gfxFont->readEmbFontFile(&fontLen); - cvt = new Type1CFontConverter(fontBuf, fontLen, f); - cvt->convert(); - delete cvt; - gfree(fontBuf); - } else { - gfxFont->getEmbeddedFontID(&embRef); - refObj.initRef(embRef.num, embRef.gen); - refObj.fetch(&strObj); - refObj.free(); - strObj.streamReset(); - while ((c = strObj.streamGetChar()) != EOF) - fputc(c, f); + fileName = NULL; + if (!openTempFile(&fileName, &f, "wb", NULL)) { + error(-1, "Couldn't create temporary TrueType font file"); + return NULL; + } +#if 1 //~ need this until FT can handle fonts with missing tables + if (gfxFont->getType() == fontTrueType || + gfxFont->getType() == fontCIDType2) { + if (!(fontBuf = gfxFont->readEmbFontFile(xref, &fontLen))) { + fclose(f); + unlink(fileName->getCString()); + delete fileName; + return NULL; } - fclose(f); - strObj.free(); - fileName = tmpFileName; + ff = new TrueTypeFontFile(fontBuf, fontLen); + ff->writeTTF(f); + delete ff; + gfree(fontBuf); } else { - fileName = pdfBaseFont->getCString(); + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); } - - // create the t1lib font - if ((t1ID = T1_AddFont(fileName)) < 0) { - error(-1, "Couldn't create t1lib font from '%s'", fileName); - return -1; +#else + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); } - T1_LoadFont(t1ID); - t1BaseFonts[i].num = id.num; - t1BaseFonts[i].gen = id.gen; - t1BaseFonts[i].t1ID = t1ID; + strObj.streamClose(); + strObj.free(); +#endif + fclose(f); + + // create the Font + font = tryGetFTFontFromFile(xref, fileName, gTrue, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gTrue, gFalse); + + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file + unlink(fileName->getCString()); + + delete fileName; + + // check for an external font file + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetFTFontFromFile(xref, fileName, gFalse, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse, gFalse); + + } else { + font = NULL; + } + + return font; +} - // remove the font file - if (tmpFileName[0]) { - unlink(tmpFileName); +XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref, + GString *fileName, + GBool deleteFile, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool embedded, + GBool subst) { + Ref *id; + FTFontFile *fontFile; + XOutputFont *font; + char *buf; + int len; + FILE *f; + TrueTypeFontFile *ff; + Gushort *codeToGID; + + // create the FreeType font file + if (gfxFont->isCIDFont()) { + if (gfxFont->getType() == fontCIDType2) { + fontFile = new FTFontFile(ftEngine, fileName->getCString(), + ((GfxCIDFont *)gfxFont)->getCIDToGID(), + ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(), + embedded); + } else { // fontCIDType0, fontCIDType0C + fontFile = new FTFontFile(ftEngine, fileName->getCString(), embedded); + } + } else { + if (!(f = fopen(fileName->getCString(), "rb"))) { + return NULL; + } + fseek(f, 0, SEEK_END); + len = (int)ftell(f); + fseek(f, 0, SEEK_SET); + buf = (char *)gmalloc(len); + if ((int)fread(buf, 1, len, f) != len) { + gfree(buf); + fclose(f); + return NULL; } + fclose(f); + ff = new TrueTypeFontFile(buf, len); + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); + fontFile = new FTFontFile(ftEngine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + codeToGID); + gfree(codeToGID); + delete ff; + gfree(buf); + } + if (!fontFile->isOk()) { + error(-1, "Couldn't create FreeType font from '%s'", + fileName->getCString()); + delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); + } + return NULL; + } + + // add to list + id = gfxFont->getID(); + ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen, + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); + + // create the Font + font = new XOutputFTFont(gfxFont->getID(), fontFile, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; +} +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFont *XOutputFontCache::tryGetTTFont(XRef *xref, + GfxFont *gfxFont, + double m11, double m12, + double m21, double m22) { + Ref *id; + XOutputTTFontFile *xFontFile; + XOutputFont *font; + Ref embRef; + GString *fileName; + FILE *f; + Object refObj, strObj; + int c; + int i; - // reencode it - enc = gfxFont->getEncoding(); - encStrSize = 0; - for (j = 0; j < 256 && j < enc->getSize(); ++j) { - if (enc->getCharName(j)) { - encStrSize += strlen(enc->getCharName(j)) + 1; + // check the already available font files + id = gfxFont->getID(); + xFontFile = NULL; + for (i = 0; i < ttFontFiles->getLength(); ++i) { + xFontFile = (XOutputTTFontFile *)ttFontFiles->get(i); + if (xFontFile->num == id->num && xFontFile->gen == id->gen && + !xFontFile->subst) { + font = new XOutputTTFont(id, xFontFile->fontFile, + m11, m12, m21, m22, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; } + return font; } - t1BaseFonts[i].enc = (char **)gmalloc(257 * sizeof(char *)); - encPtr = (char *)gmalloc(encStrSize * sizeof(char)); - t1BaseFonts[i].encStr = encPtr; - for (j = 0; j < 256 && j < enc->getSize(); ++j) { - if (enc->getCharName(j)) { - strcpy(encPtr, enc->getCharName(j)); - t1BaseFonts[i].enc[j] = encPtr; - encPtr += strlen(encPtr) + 1; - } else { - t1BaseFonts[i].enc[j] = ".notdef"; - } + } + + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { + + // create the font file + fileName = NULL; + if (!openTempFile(&fileName, &f, "wb", NULL)) { + error(-1, "Couldn't create temporary TrueType font file"); + return NULL; } - for (; j < 256; ++j) { - t1BaseFonts[i].enc[j] = ".notdef"; + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + strObj.streamClose(); + strObj.free(); + fclose(f); + + // create the Font + font = tryGetTTFontFromFile(xref, fileName, gTrue, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + // on systems with Unix hard link semantics, this will remove the + // last link to the temp file + unlink(fileName->getCString()); + + delete fileName; + + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetTTFontFromFile(xref, fileName, gFalse, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; + } + + return font; +} + +XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref, + GString *fileName, + GBool deleteFile, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + Ref *id; + TTFontFile *fontFile; + XOutputFont *font; + + // create the FreeType font file + if (gfxFont->isCIDFont()) { + // fontCIDType2 + fontFile = new TTFontFile(ttEngine, fileName->getCString(), + ((GfxCIDFont *)gfxFont)->getCIDToGID(), + ((GfxCIDFont *)gfxFont)->getCIDToGIDLen()); + } else { + fontFile = new TTFontFile(ttEngine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + ((Gfx8BitFont *)gfxFont)->getHasEncoding()); + } + if (!fontFile->isOk()) { + error(-1, "Couldn't create FreeType font from '%s'", + fileName->getCString()); + delete fontFile; + if (deleteFile) { + unlink(fileName->getCString()); } - t1BaseFonts[i].enc[256] = "custom"; - T1_ReencodeFont(t1BaseFonts[i].t1ID, t1BaseFonts[i].enc); + return NULL; + } + + // add to list + id = gfxFont->getID(); + ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen, + subst, fontFile, + deleteFile ? fileName->copy() + : (GString *)NULL)); + + // create the Font + font = new XOutputTTFont(gfxFont->getID(), fontFile, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, display, xOut); + if (!font->isOk()) { + delete font; + return NULL; + } + return font; +} +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + +XOutputFont *XOutputFontCache::tryGetServerFont(GString *xlfd, + GString *encodingName, + GfxFont *gfxFont, + double m11Orig, double m12Orig, + double m21Orig, double m22Orig, + double m11, double m12, + double m21, double m22) { + XOutputFont *font; + UnicodeMap *uMap; + CharCodeToUnicode *ctu; + + uMap = globalParams->getUnicodeMap(encodingName); + if (gfxFont->isCIDFont()) { + ctu = ((GfxCIDFont *)gfxFont)->getToUnicode(); + font = new XOutputServer16BitFont(gfxFont->getID(), xlfd, uMap, ctu, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, + display, xOut); + ctu->decRefCnt(); + } else { + ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode(); + font = new XOutputServer8BitFont(gfxFont->getID(), xlfd, uMap, ctu, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, + display, xOut); + ctu->decRefCnt(); + } + uMap->decRefCnt(); + if (!font->isOk()) { + delete font; + return NULL; } + return font; +} + +//------------------------------------------------------------------------ +// T3FontCache +//------------------------------------------------------------------------ + +struct T3FontCacheTag { + Gushort code; + Gushort mru; // valid bit (0x8000) and MRU index + double wx, wy; // untransformed glyph metrics +}; - // copy it - t1ID = T1_CopyFont(t1ID); +class T3FontCache { +public: + + T3FontCache(Ref *fontID, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + Display *displayA, Visual *visual, Guint depth, + Pixmap origPixmap); + ~T3FontCache(); + GBool matches(Ref *idA, double m11A, double m12A, + double m21A, double m22A) + { return fontID.num == idA->num && fontID.gen == idA->gen && + m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } + + Ref fontID; // PDF font ID + double m11, m12, m21, m22; // transform matrix + int glyphX, glyphY; // pixel offset of glyph pixmaps + int glyphW, glyphH; // size of glyph pixmaps, in pixels + int glyphSize; // size of glyph pixmaps, in bytes + int cacheSets; // number of sets in cache + int cacheAssoc; // cache associativity (glyphs per set) + Guchar *cacheData; // glyph pixmap cache + T3FontCacheTag *cacheTags; // cache tags, i.e., char codes + Display *display; + Pixmap pixmap; + XImage *image; +}; + +T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + Display *displayA, Visual *visual, Guint depth, + Pixmap origPixmap) { + int i; - return t1ID; + fontID = *fontIDA; + m11 = m11A; + m12 = m12A; + m21 = m21A; + m22 = m22A; + glyphX = glyphXA; + glyphY = glyphYA; + glyphW = glyphWA; + glyphH = glyphHA; + glyphSize = glyphW * glyphH; + cacheAssoc = 8; + if (glyphSize <= 256) { + cacheSets = 8; + } else if (glyphSize <= 512) { + cacheSets = 4; + } else if (glyphSize <= 1024) { + cacheSets = 2; + } else { + cacheSets = 1; + } + cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize); + cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc * + sizeof(T3FontCacheTag)); + for (i = 0; i < cacheSets * cacheAssoc; ++i) { + cacheTags[i].mru = i & (cacheAssoc - 1); + } + display = displayA; + pixmap = XCreatePixmap(display, origPixmap, glyphW, glyphH, depth); + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, + glyphW, glyphH, 8, 0); + image->data = (char *)gmalloc(glyphH * image->bytes_per_line); } -#endif + +T3FontCache::~T3FontCache() { + gfree(cacheData); + gfree(cacheTags); + XFreePixmap(display, pixmap); + gfree(image->data); + image->data = NULL; + XDestroyImage(image); +} + +struct T3GlyphStack { + GBool cacheable; + Gushort code; + T3FontCache *cache; + int cacheIdx; + T3FontCacheTag *cacheTag; + Guchar *cacheData; + double x, y; + Unicode *u; + int uLen; + GfxRGB color; + int origPixmapW, origPixmapH; + Pixmap origPixmap; + GC origStrokeGC; + GC origFillGC; + Region origClipRegion; + double origCTM4, origCTM5; + double wx, wy; // untransformed glyph metrics + T3GlyphStack *next; +}; //------------------------------------------------------------------------ // XOutputDev //------------------------------------------------------------------------ -XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, - Colormap colormap, unsigned long paperColor) { +XOutputDev::XOutputDev(Display *displayA, int screenNumA, + Visual *visualA, Colormap colormapA, + GBool reverseVideoA, unsigned long paperColorA, + GBool installCmap, int rgbCubeSize, + int forceDepth) { XVisualInfo visualTempl; XVisualInfo *visualList; int nVisuals; Gulong mask; - XGCValues gcValues; XColor xcolor; XColor *xcolors; - int r, g, b, n, m, i; + int r, g, b, n, m; GBool ok; - // get display/pixmap info - this->display = display; - screenNum = DefaultScreen(display); - this->pixmap = pixmap; - this->depth = depth; - this->colormap = colormap; + // no document yet + xref = NULL; + + // display / screen / visual / colormap + display = displayA; + screenNum = screenNumA; + visual = visualA; + colormap = colormapA; + + // no pixmap yet + pixmapW = pixmapH = 0; // check for TrueColor visual - trueColor = gFalse; - if (depth == 0) { - this->depth = DefaultDepth(display, screenNum); - visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals); - for (i = 0; i < nVisuals; ++i) { - if (visualList[i].visual == DefaultVisual(display, screenNum)) { - if (visualList[i].c_class == TrueColor) { - trueColor = gTrue; - mask = visualList[i].red_mask; - rShift = 0; - while (mask && !(mask & 1)) { - ++rShift; - mask >>= 1; - } - rMul = (int)mask; - mask = visualList[i].green_mask; - gShift = 0; - while (mask && !(mask & 1)) { - ++gShift; - mask >>= 1; - } - gMul = (int)mask; - mask = visualList[i].blue_mask; - bShift = 0; - while (mask && !(mask & 1)) { - ++bShift; - mask >>= 1; - } - bMul = (int)mask; - } - break; - } + if (forceDepth != 0) { + depth = forceDepth; + trueColor = depth >= 16; + } else { + visualTempl.visualid = XVisualIDFromVisual(visual); + visualList = XGetVisualInfo(display, VisualIDMask, + &visualTempl, &nVisuals); + if (nVisuals < 1) { + // this shouldn't happen + XFree((XPointer)visualList); + visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl, + &nVisuals); + } + depth = visualList->depth; + if (visualList->c_class == TrueColor) { + trueColor = gTrue; + for (mask = visualList->red_mask, rShift = 0; + mask && !(mask & 1); + mask >>= 1, ++rShift) ; + rMul = (int)mask; + for (mask = visualList->green_mask, gShift = 0; + mask && !(mask & 1); + mask >>= 1, ++gShift) ; + gMul = (int)mask; + for (mask = visualList->blue_mask, bShift = 0; + mask && !(mask & 1); + mask >>= 1, ++bShift) ; + bMul = (int)mask; + } else { + trueColor = gFalse; } XFree((XPointer)visualList); } // allocate a color cube if (!trueColor) { + redMap[BlackPixel(display, screenNum) & 0xff] = 0; + redMap[WhitePixel(display, screenNum) & 0xff] = 1; // set colors in private colormap if (installCmap) { for (numColors = 6; numColors >= 2; --numColors) { m = numColors * numColors * numColors; - if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) + if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) { break; + } } if (numColors >= 2) { m = numColors * numColors * numColors; @@ -1111,6 +1756,7 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, xcolors[n].green = (g * 65535) / (numColors - 1); xcolors[n].blue = (b * 65535) / (numColors - 1); xcolors[n].flags = DoRed | DoGreen | DoBlue; + redMap[xcolors[n].pixel & 0xff] = xcolors[n].red / 65535.0; ++n; } } @@ -1125,8 +1771,9 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, // allocate colors in shared colormap } else { - if (rgbCubeSize > maxRGBCube) + if (rgbCubeSize > maxRGBCube) { rgbCubeSize = maxRGBCube; + } ok = gFalse; for (numColors = rgbCubeSize; numColors >= 2; --numColors) { ok = gTrue; @@ -1135,21 +1782,26 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, for (g = 0; g < numColors && ok; ++g) { for (b = 0; b < numColors && ok; ++b) { if (n == 0) { - colors[n++] = BlackPixel(display, screenNum); + colors[n] = BlackPixel(display, screenNum); + redMap[colors[n] & 0xff] = 0; + ++n; } else { xcolor.red = (r * 65535) / (numColors - 1); xcolor.green = (g * 65535) / (numColors - 1); xcolor.blue = (b * 65535) / (numColors - 1); - if (XAllocColor(display, colormap, &xcolor)) + if (XAllocColor(display, colormap, &xcolor)) { colors[n++] = xcolor.pixel; - else + redMap[xcolor.pixel & 0xff] = xcolor.red / 65535.0; + } else { ok = gFalse; + } } } } } - if (ok) + if (ok) { break; + } XFreeColors(display, colormap, &colors[1], n-1, 0); } if (!ok) { @@ -1160,109 +1812,81 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, } } - // allocate GCs - gcValues.foreground = BlackPixel(display, screenNum); - gcValues.background = WhitePixel(display, screenNum); - gcValues.line_width = 0; - gcValues.line_style = LineSolid; - strokeGC = XCreateGC(display, pixmap, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - fillGC = XCreateGC(display, pixmap, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - gcValues.foreground = paperColor; - paperGC = XCreateGC(display, pixmap, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - - // no clip region yet - clipRegion = NULL; - - // get user font map - for (n = 0; devFontMap[n].pdfFont; ++n) ; - userFontMap = (FontMapEntry *)gmalloc((n+1) * sizeof(FontMapEntry)); - for (i = 0; i < n; ++i) { - userFontMap[i].pdfFont = devFontMap[i].pdfFont; - userFontMap[i].xFont = devFontMap[i].devFont; - m = strlen(userFontMap[i].xFont); - if (m >= 10 && !strcmp(userFontMap[i].xFont + m - 10, "-iso8859-2")) - userFontMap[i].encoding = &isoLatin2Encoding; - else if (m >= 13 && !strcmp(userFontMap[i].xFont + m - 13, - "-fontspecific")) - userFontMap[i].encoding = NULL; - else - userFontMap[i].encoding = &isoLatin1Encoding; - userFontMap[i].t1Font = NULL; - } - userFontMap[n].pdfFont = NULL; + // misc parameters + reverseVideo = reverseVideoA; + paperColor = paperColorA; // set up the font cache and fonts gfxFont = NULL; font = NULL; - fontCache = new XOutputFontCache(display); + needFontUpdate = gFalse; + fontCache = new XOutputFontCache(display, depth, this, + globalParams->getT1libControl(), + globalParams->getFreeTypeControl()); + nT3Fonts = 0; + t3GlyphStack = NULL; + + // no text outline clipping path + textClipPath = NULL; // empty state stack save = NULL; // create text object - text = new TextPage(useEUCJP, gFalse); + text = new TextPage(gFalse); } XOutputDev::~XOutputDev() { - gfree(userFontMap); + int i; + delete fontCache; - XFreeGC(display, strokeGC); - XFreeGC(display, fillGC); - XFreeGC(display, paperGC); - if (clipRegion) - XDestroyRegion(clipRegion); + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } delete text; } -void XOutputDev::startDoc() { - fontCache->startDoc(screenNum, depth, colormap); +void XOutputDev::startDoc(XRef *xrefA) { + int i; + + xref = xrefA; + fontCache->startDoc(screenNum, visual, colormap, trueColor, rMul, gMul, bMul, + rShift, gShift, bShift, colors, numColors); + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + nT3Fonts = 0; } void XOutputDev::startPage(int pageNum, GfxState *state) { - XOutputState *s; XGCValues gcValues; XRectangle rect; - // clear state stack - while (save) { - s = save; - save = save->next; - XFreeGC(display, s->strokeGC); - XFreeGC(display, s->fillGC); - XDestroyRegion(s->clipRegion); - delete s; - } - save = NULL; - // default line flatness flatness = 0; - // reset GCs + // allocate GCs gcValues.foreground = BlackPixel(display, screenNum); gcValues.background = WhitePixel(display, screenNum); gcValues.line_width = 0; gcValues.line_style = LineSolid; - XChangeGC(display, strokeGC, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - XChangeGC(display, fillGC, - GCForeground | GCBackground | GCLineWidth | GCLineStyle, - &gcValues); - - // clear clipping region - if (clipRegion) - XDestroyRegion(clipRegion); - clipRegion = XCreateRegion(); - rect.x = rect.y = 0; - rect.width = pixmapW; - rect.height = pixmapH; - XUnionRectWithRegion(&rect, clipRegion, clipRegion); + strokeGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + fillGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + gcValues.foreground = paperColor; + paperGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + + // initialize clip region + clipRegion = XCreateRegion(); + rect.x = rect.y = 0; + rect.width = pixmapW; + rect.height = pixmapH; + XUnionRectWithRegion(&rect, clipRegion, clipRegion); XSetRegion(display, strokeGC, clipRegion); XSetRegion(display, fillGC, clipRegion); @@ -1274,36 +1898,86 @@ void XOutputDev::startPage(int pageNum, GfxState *state) { XFillRectangle(display, pixmap, paperGC, 0, 0, pixmapW, pixmapH); // clear text object - text->clear(); + text->startPage(state); } void XOutputDev::endPage() { - text->coalesce(); + XOutputState *s; + + text->coalesce(gTrue); + + // clear state stack, free all GCs, free the clip region + while (save) { + s = save; + save = save->next; + XFreeGC(display, s->strokeGC); + XFreeGC(display, s->fillGC); + XDestroyRegion(s->clipRegion); + delete s; + } + XFreeGC(display, strokeGC); + XFreeGC(display, fillGC); + XFreeGC(display, paperGC); + XDestroyRegion(clipRegion); } -void XOutputDev::drawLinkBorder(double x1, double y1, double x2, double y2, - double w) { - GfxColor color; +void XOutputDev::drawLink(Link *link, Catalog *catalog) { + double x1, y1, x2, y2; + LinkBorderStyle *borderStyle; + GfxRGB rgb; + double *dash; + char dashList[20]; + int dashLength; XPoint points[5]; - int x, y; - - color.setRGB(0, 0, 1); - XSetForeground(display, strokeGC, findColor(&color)); - XSetLineAttributes(display, strokeGC, xoutRound(w), - LineSolid, CapRound, JoinRound); - cvtUserToDev(x1, y1, &x, &y); - points[0].x = points[4].x = x; - points[0].y = points[4].y = y; - cvtUserToDev(x2, y1, &x, &y); - points[1].x = x; - points[1].y = y; - cvtUserToDev(x2, y2, &x, &y); - points[2].x = x; - points[2].y = y; - cvtUserToDev(x1, y2, &x, &y); - points[3].x = x; - points[3].y = y; - XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin); + int x, y, i; + + link->getRect(&x1, &y1, &x2, &y2); + borderStyle = link->getBorderStyle(); + if (borderStyle->getWidth() > 0) { + borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b); + XSetForeground(display, strokeGC, findColor(&rgb)); + borderStyle->getDash(&dash, &dashLength); + if (borderStyle->getType() == linkBorderDashed && dashLength > 0) { + if (dashLength > 20) { + dashLength = 20; + } + for (i = 0; i < dashLength; ++i) { + if ((dashList[i] = xoutRound(dash[i])) == 0) { + dashList[i] = 1; + } + } + XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()), + LineOnOffDash, CapButt, JoinMiter); + XSetDashes(display, strokeGC, 0, dashList, dashLength); + } else { + XSetLineAttributes(display, strokeGC, xoutRound(borderStyle->getWidth()), + LineSolid, CapButt, JoinMiter); + } + if (borderStyle->getType() == linkBorderUnderlined) { + cvtUserToDev(x1, y1, &x, &y); + points[0].x = x; + points[0].y = y; + cvtUserToDev(x2, y1, &x, &y); + points[1].x = x; + points[1].y = y; + XDrawLine(display, pixmap, strokeGC, points[0].x, points[0].y, + points[1].x, points[1].y); + } else { + cvtUserToDev(x1, y1, &x, &y); + points[0].x = points[4].x = x; + points[0].y = points[4].y = y; + cvtUserToDev(x2, y1, &x, &y); + points[1].x = x; + points[1].y = y; + cvtUserToDev(x2, y2, &x, &y); + points[2].x = x; + points[2].y = y; + cvtUserToDev(x1, y2, &x, &y); + points[3].x = x; + points[3].y = y; + XDrawLines(display, pixmap, strokeGC, points, 5, CoordModeOrigin); + } + } } void XOutputDev::saveState(GfxState *state) { @@ -1352,6 +2026,9 @@ void XOutputDev::restoreState(GfxState *state) { s = save; save = save->next; delete s; + + // we'll need to restore the font + needFontUpdate = gTrue; } } @@ -1361,7 +2038,7 @@ void XOutputDev::updateAll(GfxState *state) { updateMiterLimit(state); updateFillColor(state); updateStrokeColor(state); - updateFont(state); + needFontUpdate = gTrue; } void XOutputDev::updateCTM(GfxState *state, double m11, double m12, @@ -1412,11 +2089,6 @@ void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) { cap = CapButt; break; } -#if 1 //~ work around a bug in XFree86 (???) - if (dashLength > 0 && cap == CapProjecting) { - cap = CapButt; - } -#endif switch (state->getLineJoin()) { case 0: join = JoinMiter; break; case 1: join = JoinRound; break; @@ -1427,6 +2099,11 @@ void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) { break; } state->getLineDash(&dashPattern, &dashLength, &dashStart); +#if 1 //~ work around a bug in XFree86 (???) + if (dashLength > 0 && cap == CapProjecting) { + cap = CapButt; + } +#endif XSetLineAttributes(display, strokeGC, xoutRound(width), dashLength > 0 ? LineOnOffDash : LineSolid, cap, join); @@ -1443,24 +2120,48 @@ void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) { } void XOutputDev::updateFillColor(GfxState *state) { - XSetForeground(display, fillGC, findColor(state->getFillColor())); + GfxRGB rgb; + + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + XSetForeground(display, fillGC, findColor(&rgb)); } void XOutputDev::updateStrokeColor(GfxState *state) { - XSetForeground(display, strokeGC, findColor(state->getStrokeColor())); + GfxRGB rgb; + + state->getStrokeRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + XSetForeground(display, strokeGC, findColor(&rgb)); } void XOutputDev::updateFont(GfxState *state) { double m11, m12, m21, m22; + needFontUpdate = gFalse; + + text->updateFont(state); + if (!(gfxFont = state->getFont())) { font = NULL; return; } + if (gfxFont->getType() == fontType3) { + font = NULL; + return; + } state->getFontTransMat(&m11, &m12, &m21, &m22); m11 *= state->getHorizScaling(); - m21 *= state->getHorizScaling(); - font = fontCache->getFont(gfxFont, m11, m12, m21, m22); + m12 *= state->getHorizScaling(); + font = fontCache->getFont(xref, gfxFont, m11, m12, m21, m22); if (font) { font->updateGC(fillGC); font->updateGC(strokeGC); @@ -1473,7 +2174,8 @@ void XOutputDev::stroke(GfxState *state) { int n, size, numPoints, i, j; // transform points - n = convertPath(state, &points, &size, &numPoints, &lengths, gFalse); + n = convertPath(state, state->getPath(), + &points, &size, &numPoints, &lengths, gFalse); // draw each subpath j = 0; @@ -1516,7 +2218,8 @@ void XOutputDev::doFill(GfxState *state, int rule) { XSetFillRule(display, fillGC, rule); // transform points, build separate polygons - n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); + n = convertPath(state, state->getPath(), + &points, &size, &numPoints, &lengths, gTrue); // fill them j = 0; @@ -1538,30 +2241,109 @@ void XOutputDev::doFill(GfxState *state, int rule) { } void XOutputDev::clip(GfxState *state) { - doClip(state, WindingRule); + doClip(state, state->getPath(), WindingRule); } void XOutputDev::eoClip(GfxState *state) { - doClip(state, EvenOddRule); + doClip(state, state->getPath(), EvenOddRule); } -void XOutputDev::doClip(GfxState *state, int rule) { +void XOutputDev::doClip(GfxState *state, GfxPath *path, int rule) { + GfxSubpath *subpath; Region region, region2; + XPoint rect[5]; XPoint *points; int *lengths; + double x0, y0, x1, y1, x2, y2, x3, y3; + GBool gotRect; int n, size, numPoints, i, j; - // transform points, build separate polygons - n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); - - // construct union of subpath regions - region = XPolygonRegion(points, lengths[0], rule); - j = lengths[0] + 1; - for (i = 1; i < n; ++i) { - region2 = XPolygonRegion(points + j, lengths[i], rule); - XUnionRegion(region2, region, region); - XDestroyRegion(region2); - j += lengths[i] + 1; + // special case for rectangular clipping paths -- this is a common + // case, and we want to make sure not to clip an extra pixel on the + // right and bottom edges due to the difference between the PDF and + // X rendering models + gotRect = gFalse; + if (path->getNumSubpaths() == 1) { + subpath = path->getSubpath(0); + if ((subpath->isClosed() && subpath->getNumPoints() == 5) || + (!subpath->isClosed() && subpath->getNumPoints() == 4)) { + state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0); + state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1); + state->transform(subpath->getX(2), subpath->getY(2), &x2, &y2); + state->transform(subpath->getX(3), subpath->getY(3), &x3, &y3); + if (fabs(x0-x1) < 1 && fabs(x2-x3) < 1 && + fabs(y0-y3) < 1 && fabs(y1-y2) < 1) { + if (x0 < x2) { + rect[0].x = rect[1].x = rect[4].x = (int)floor(x0); + rect[2].x = rect[3].x = (int)floor(x2) + 1; + } else { + rect[0].x = rect[1].x = rect[4].x = (int)floor(x0) + 1; + rect[2].x = rect[3].x = (int)floor(x2); + } + if (y0 < y1) { + rect[0].y = rect[3].y = rect[4].y = (int)floor(y0); + rect[1].y = rect[2].y = (int)floor(y1) + 1; + } else { + rect[0].y = rect[3].y = rect[4].y = (int)floor(y0) + 1; + rect[1].y = rect[2].y = (int)floor(y1); + } + gotRect = gTrue; + } else if (fabs(x0-x3) < 1 && fabs(x1-x2) < 1 && + fabs(y0-y1) < 1 && fabs(y2-y3) < 1) { + if (x0 < x1) { + rect[0].x = rect[3].x = rect[4].x = (int)floor(x0); + rect[1].x = rect[2].x = (int)floor(x1) + 1; + } else { + rect[0].x = rect[3].x = rect[4].x = (int)floor(x0) + 1; + rect[1].x = rect[2].x = (int)floor(x1); + } + if (y0 < y2) { + rect[0].y = rect[1].y = rect[4].y = (int)floor(y0); + rect[2].y = rect[3].y = (int)floor(y2) + 1; + } else { + rect[0].y = rect[1].y = rect[4].y = (int)floor(y0) + 1; + rect[2].y = rect[3].y = (int)floor(y2); + } + gotRect = gTrue; + } + } + } + + if (gotRect) { + region = XPolygonRegion(rect, 5, EvenOddRule); + + } else { + // transform points, build separate polygons + n = convertPath(state, path, &points, &size, &numPoints, &lengths, gTrue); + + // construct union of subpath regions + // (XPolygonRegion chokes if there aren't at least three points -- + // this happens if the PDF file does moveto/closepath/clip, which + // sets an empty clipping region) + if (lengths[0] > 2) { + region = XPolygonRegion(points, lengths[0], rule); + } else { + region = XCreateRegion(); + } + j = lengths[0] + 1; + for (i = 1; i < n; ++i) { + if (lengths[i] > 2) { + region2 = XPolygonRegion(points + j, lengths[i], rule); + } else { + region2 = XCreateRegion(); + } + XUnionRegion(region2, region, region); + XDestroyRegion(region2); + j += lengths[i] + 1; + } + + // free points and lengths arrays + if (points != tmpPoints) { + gfree(points); + } + if (lengths != tmpLengths) { + gfree(lengths); + } } // intersect region with clipping region @@ -1569,12 +2351,6 @@ void XOutputDev::doClip(GfxState *state, int rule) { XDestroyRegion(region); XSetRegion(display, strokeGC, clipRegion); XSetRegion(display, fillGC, clipRegion); - - // free points and lengths arrays - if (points != tmpPoints) - gfree(points); - if (lengths != tmpLengths) - gfree(lengths); } // @@ -1586,15 +2362,14 @@ void XOutputDev::doClip(GfxState *state, int rule) { // Then it connects subaths within a single compound polygon to a single // point so that X can fill the polygon (sort of). // -int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, +int XOutputDev::convertPath(GfxState *state, GfxPath *path, + XPoint **points, int *size, int *numPoints, int **lengths, GBool fillHack) { - GfxPath *path; BoundingRect *rects; BoundingRect rect; int n, i, ii, j, k, k0; // get path and number of subpaths - path = state->getPath(); n = path->getNumSubpaths(); // allocate lengths array @@ -1641,7 +2416,7 @@ int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, // close subpath if necessary if (fillHack && ((*points)[*numPoints-1].x != (*points)[j].x || - (*points)[*numPoints-1].y != (*points)[j].y)) { + (*points)[*numPoints-1].y != (*points)[j].y)) { addPoint(points, size, numPoints, (*points)[j].x, (*points)[j].y); } @@ -1653,6 +2428,21 @@ int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, addPoint(points, size, numPoints, 0, 0); } + // kludge: munge any points that are *way* out of bounds - these can + // crash certain (buggy) X servers + for (i = 0; i < *numPoints; ++i) { + if ((*points)[i].x < -4 * pixmapW) { + (*points)[i].x = -4 * pixmapW; + } else if ((*points)[i].x > 4 * pixmapW) { + (*points)[i].x = 4 * pixmapW; + } + if ((*points)[i].y < -pixmapH) { + (*points)[i].y = -4 * pixmapH; + } else if ((*points)[i].y > 4 * pixmapH) { + (*points)[i].y = 4 * pixmapH; + } + } + // combine compound polygons if (fillHack) { i = j = k = 0; @@ -1671,13 +2461,12 @@ int XOutputDev::convertPath(GfxState *state, XPoint **points, int *size, // look for the first subsequent subpath, if any, which overlaps for (ii = i; ii < n; ++ii) { - if (((rects[ii].xMin > rect.xMin && rects[ii].xMin < rect.xMax) || - (rects[ii].xMax > rect.xMin && rects[ii].xMax < rect.xMax) || - (rects[ii].xMin < rect.xMin && rects[ii].xMax > rect.xMax)) && - ((rects[ii].yMin > rect.yMin && rects[ii].yMin < rect.yMax) || - (rects[ii].yMax > rect.yMin && rects[ii].yMax < rect.yMax) || - (rects[ii].yMin < rect.yMin && rects[ii].yMax > rect.yMax))) + if (rects[ii].xMax > rects[i].xMin && + rects[ii].xMin < rects[i].xMax && + rects[ii].yMax > rects[i].yMin && + rects[ii].yMin < rects[i].yMax) { break; + } } // if there is an overlap, combine the polygons @@ -1846,414 +2635,527 @@ void XOutputDev::addPoint(XPoint **points, int *size, int *k, int x, int y) { } void XOutputDev::beginString(GfxState *state, GString *s) { - text->beginString(state, s, font ? font->isHex() : gFalse); + text->beginWord(state, state->getCurX(), state->getCurY()); } void XOutputDev::endString(GfxState *state) { - text->endString(); + text->endWord(); } void XOutputDev::drawChar(GfxState *state, double x, double y, - double dx, double dy, Guchar c) { - double x1, y1; + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen) { + int render; + double x1, y1, dx1, dy1; + GfxRGB rgb; + double saveCurX, saveCurY; + double *ctm; + double saveCTM[6]; + + if (needFontUpdate) { + updateFont(state); + } - text->addChar(state, x, y, dx, dy, c); + text->addChar(state, x, y, dx, dy, code, u, uLen); - if (!font) + if (!font) { return; + } // check for invisible text -- this is used by Acrobat Capture - if ((state->getRender() & 3) == 3) + render = state->getRender(); + if (render == 3) { return; + } + x -= originX; + y -= originY; state->transform(x, y, &x1, &y1); - - font->drawChar(state, pixmap, (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c); -} - -void XOutputDev::drawChar16(GfxState *state, double x, double y, - double dx, double dy, int c) { - int c1; - XChar2b c2[4]; - double x1, y1; -#if JAPANESE_SUPPORT - int t1, t2; - double x2; - char *p; - int n, i; -#endif - - if (gfxFont) { - text->addChar16(state, x, y, dx, dy, c, gfxFont->getCharSet16()); + state->transformDelta(dx, dy, &dx1, &dy1); + + // fill + if (!(render & 1)) { + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + font->drawChar(state, pixmap, pixmapW, pixmapH, fillGC, &rgb, + x1, y1, dx1, dy1, code, u, uLen); } - //~ assumes font is an XOutputServerFont + // stroke + if ((render & 3) == 1 || (render & 3) == 2) { + if (font->hasGetCharPath()) { + saveCurX = state->getCurX(); + saveCurY = state->getCurY(); + ctm = state->getCTM(); + memcpy(saveCTM, ctm, 6 * sizeof(double)); + state->setCTM(1, 0, 0, 1, x1, y1); + font->getCharPath(state, code, u, uLen); + stroke(state); + state->clearPath(); + state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3], + saveCTM[4], saveCTM[5]); + state->moveTo(saveCurX, saveCurY); + } else { + // can't stroke the outline, so just fill it using the stroke + // color + state->getStrokeRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + font->drawChar(state, pixmap, pixmapW, pixmapH, strokeGC, &rgb, + x1, y1, dx1, dy1, code, u, uLen); + } + } - if (!font) - return; + // clip + if (render & 4) { + if (font->hasGetCharPath()) { + saveCurX = state->getCurX(); + saveCurY = state->getCurY(); + font->getCharPath(state, code, u, uLen); + state->getPath()->offset(x1, y1); + if (textClipPath) { + textClipPath->append(state->getPath()); + } else { + textClipPath = state->getPath()->copy(); + } + state->clearPath(); + state->moveTo(saveCurX, saveCurY); + } + } +} - // check for invisible text -- this is used by Acrobat Capture - if ((state->getRender() & 3) == 3) - return; +GBool XOutputDev::beginType3Char(GfxState *state, + CharCode code, Unicode *u, int uLen) { + Ref *fontID; + double *ctm, *bbox; + GfxRGB color; + T3FontCache *t3Font; + T3GlyphStack *t3gs; + double x1, y1, xMin, yMin, xMax, yMax, xt, yt; + int i, j; - // handle origin offset for vertical fonts - if (gfxFont->getWMode16() == 1) { - x -= gfxFont->getOriginX16(c) * state->getFontSize(); - y -= gfxFont->getOriginY16(c) * state->getFontSize(); + if (needFontUpdate) { + updateFont(state); } + if (!gfxFont) { + return gFalse; + } + fontID = gfxFont->getID(); + ctm = state->getCTM(); + state->transform(0, 0, &xt, &yt); - state->transform(x, y, &x1, &y1); + // is it the first (MRU) font in the cache? + if (!(nT3Fonts > 0 && + t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { - c1 = 0; - switch (gfxFont->getCharSet16()) { - - // convert Adobe-Japan1-2 to JIS X 0208-1983 - case font16AdobeJapan12: -#if JAPANESE_SUPPORT - if (c <= 96) { - c1 = japan12Map[c]; - } else if (c <= 632) { - if (c <= 230) - c1 = 0; - else if (c <= 324) - c1 = japan12Map[c - 230]; - else if (c <= 421) - c1 = japan12KanaMap1[c - 325]; - else if (c <= 500) - c1 = 0; - else if (c <= 598) - c1 = japan12KanaMap2[c - 501]; - else - c1 = 0; - } else if (c <= 1124) { - if (c <= 779) { - if (c <= 726) - c1 = 0x2121 + (c - 633); - else if (c <= 740) - c1 = 0x2221 + (c - 727); - else if (c <= 748) - c1 = 0x223a + (c - 741); - else if (c <= 755) - c1 = 0x224a + (c - 749); - else if (c <= 770) - c1 = 0x225c + (c - 756); - else if (c <= 778) - c1 = 0x2272 + (c - 771); - else - c1 = 0x227e; - } else if (c <= 841) { - if (c <= 789) - c1 = 0x2330 + (c - 780); - else if (c <= 815) - c1 = 0x2341 + (c - 790); - else - c1 = 0x2361 + (c - 816); - } else if (c <= 1010) { - if (c <= 924) - c1 = 0x2421 + (c - 842); - else - c1 = 0x2521 + (c - 925); - } else { - if (c <= 1034) - c1 = 0x2621 + (c - 1011); - else if (c <= 1058) - c1 = 0x2641 + (c - 1035); - else if (c <= 1091) - c1 = 0x2721 + (c - 1059); - else - c1 = 0x2751 + (c - 1092); - } - } else if (c <= 4089) { - t1 = (c - 1125) / 94; - t2 = (c - 1125) % 94; - c1 = 0x3021 + (t1 << 8) + t2; - } else if (c <= 7477) { - t1 = (c - 4090) / 94; - t2 = (c - 4090) % 94; - c1 = 0x5021 + (t1 << 8) + t2; - } else if (c <= 7554) { - c1 = 0; - } else if (c <= 7563) { // circled Arabic numbers 1..9 - c1 = 0x2331 + (c - 7555); - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c2, 1); - c1 = 0x227e; - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c2, 1); - c1 = -1; - } else if (c <= 7574) { // circled Arabic numbers 10..20 - n = c - 7564 + 10; - x2 = x1; - for (i = 0; i < 2; ++i) { - c1 = 0x2330 + (i == 0 ? (n / 10) : (n % 10)); - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x2), xoutRound(y1), c2, 1); - x2 += 0.5 * state->getTransformedFontSize(); - } - c1 = 0x227e; - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c2, 1); - c1 = -1; - } else if (c <= 7584) { // Roman numbers I..X - p = japan12Roman[c - 7575]; - n = strlen(p); - for (; *p; ++p) { - if (*p == 'I') - c1 = 0x2349; - else if (*p == 'V') - c1 = 0x2356; - else // 'X' - c1 = 0x2358; - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c2, 1); - if (*p == 'I') - x1 += 0.2 * state->getTransformedFontSize(); - else - x1 += 0.5 * state->getTransformedFontSize(); - } - c1 = -1; - } else if (c <= 7632) { - if (c <= 7600) { - c1 = 0; - } else if (c <= 7606) { - p = japan12Abbrev1[c - 7601]; - n = strlen(p); - for (; *p; ++p) { - c1 = 0x2300 + *p; - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c2, 1); - x1 += 0.5 * state->getTransformedFontSize(); + // is the font elsewhere in the cache? + for (i = 1; i < nT3Fonts; ++i) { + if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { + t3Font = t3FontCache[i]; + for (j = i; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; } - c1 = -1; + t3FontCache[0] = t3Font; + break; + } + } + if (i >= nT3Fonts) { + + // create new entry in the font cache + if (nT3Fonts == xOutT3FontCacheSize) { + delete t3FontCache[nT3Fonts - 1]; + --nT3Fonts; + } + for (j = nT3Fonts; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + ++nT3Fonts; + bbox = gfxFont->getFontBBox(); + if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { + // broken bounding box -- just take a guess + xMin = xt - 5; + xMax = xMin + 30; + yMax = yt + 15; + yMin = yMax - 45; } else { - c1 = 0; + state->transform(bbox[0], bbox[1], &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(bbox[0], bbox[3], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(bbox[2], bbox[1], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(bbox[2], bbox[3], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } } - } else { - c1 = 0; + t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], + (int)floor(xMin - xt), + (int)floor(yMin - yt), + (int)ceil(xMax) - (int)floor(xMin) + 3, + (int)ceil(yMax) - (int)floor(yMin) + 3, + display, visual, depth, pixmap); } -#if 0 //~ - if (c1 == 0) - error(-1, "Unsupported Adobe-Japan1-2 character: %d", c); -#endif -#endif // JAPANESE_SUPPORT - break; } - - if (c1 > 0) { - c2[0].byte1 = c1 >> 8; - c2[0].byte2 = c1 & 0xff; - XDrawString16(display, pixmap, - (state->getRender() & 1) ? strokeGC : fillGC, - xoutRound(x1), xoutRound(y1), c2, 1); + t3Font = t3FontCache[0]; + + // is the glyph in the cache? + i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x8000) && + t3Font->cacheTags[i+j].code == code) { + state->getFillRGB(&color); + if (reverseVideo) { + color.r = 1 - color.r; + color.g = 1 - color.g; + color.b = 1 - color.b; + } + text->addChar(state, 0, 0, + t3Font->cacheTags[i+j].wx, t3Font->cacheTags[i+j].wy, + code, u, uLen); + drawType3Glyph(t3Font, &t3Font->cacheTags[i+j], + t3Font->cacheData + (i+j) * t3Font->glyphSize, + xt, yt, &color); + return gTrue; + } } + + // push a new Type 3 glyph record + t3gs = new T3GlyphStack(); + t3gs->next = t3GlyphStack; + t3GlyphStack = t3gs; + t3GlyphStack->cacheable = gFalse; + t3GlyphStack->code = code; + t3GlyphStack->cache = t3Font; + t3GlyphStack->cacheIdx = i; + t3GlyphStack->x = xt; + t3GlyphStack->y = yt; + t3GlyphStack->u = u; + t3GlyphStack->uLen = uLen; + + return gFalse; } -void XOutputDev::drawImageMask(GfxState *state, Stream *str, - int width, int height, GBool invert, - GBool inlineImg) { - ImageStream *imgStr; +void XOutputDev::endType3Char(GfxState *state) { XImage *image; - int x0, y0; // top left corner of image - int w0, h0, w1, h1; // size of image - int x2, y2; - int w2, h2; - double xt, yt, wt, ht; - GBool rotate, xFlip, yFlip; + Guchar *p; int x, y; - int ix, iy; - int px1, px2, qx, dx; - int py1, py2, qy, dy; - Guchar pixBuf; - Gulong color; - int i, j; - - // get image position and size - state->transform(0, 0, &xt, &yt); - state->transformDelta(1, 1, &wt, &ht); - if (wt > 0) { - x0 = xoutRound(xt); - w0 = xoutRound(wt); - } else { - x0 = xoutRound(xt + wt); - w0 = xoutRound(-wt); - } - if (ht > 0) { - y0 = xoutRound(yt); - h0 = xoutRound(ht); - } else { - y0 = xoutRound(yt + ht); - h0 = xoutRound(-ht); - } - state->transformDelta(1, 0, &xt, &yt); - rotate = fabs(xt) < fabs(yt); - if (rotate) { - w1 = h0; - h1 = w0; - xFlip = ht < 0; - yFlip = wt > 0; - } else { - w1 = w0; - h1 = h0; - xFlip = wt < 0; - yFlip = ht > 0; - } - - // set up - color = findColor(state->getFillColor()); - - // check for tiny (zero width or height) images - if (w0 == 0 || h0 == 0) { - j = height * ((width + 7) / 8); - str->reset(); - for (i = 0; i < j; ++i) - str->getChar(); - return; + Gulong pixel; + double alpha; + T3GlyphStack *t3gs; + double *ctm; + + if (t3GlyphStack->cacheable) { + image = t3GlyphStack->cache->image; + XGetSubImage(display, pixmap, 0, 0, + t3GlyphStack->cache->glyphW, t3GlyphStack->cache->glyphH, + (1 << depth) - 1, ZPixmap, image, 0, 0); + p = t3GlyphStack->cacheData; + for (y = 0; y < t3GlyphStack->cache->glyphH; ++y) { + for (x = 0; x < t3GlyphStack->cache->glyphW; ++x) { + pixel = XGetPixel(image, x, y); + if (trueColor) { + alpha = (double)((pixel >> rShift) & rMul) / (double)rMul; + } else { + alpha = redMap[pixel & 0xff]; + } + if (alpha <= 0.2) { + *p++ = 4; + } else if (alpha <= 0.4) { + *p++ = 3; + } else if (alpha <= 0.6) { + *p++ = 2; + } else if (alpha <= 0.8) { + *p++ = 1; + } else { + *p++ = 0; + } + } + } + XDestroyRegion(clipRegion); + XFreeGC(display, strokeGC); + XFreeGC(display, fillGC); + pixmapW = t3GlyphStack->origPixmapW; + pixmapH = t3GlyphStack->origPixmapH; + pixmap = t3GlyphStack->origPixmap; + strokeGC = t3GlyphStack->origStrokeGC; + fillGC = t3GlyphStack->origFillGC; + clipRegion = t3GlyphStack->origClipRegion; + drawType3Glyph(t3GlyphStack->cache, + t3GlyphStack->cacheTag, t3GlyphStack->cacheData, + t3GlyphStack->x, t3GlyphStack->y, &t3GlyphStack->color); + // the CTM must be restored here in order for TextPage::addChar to + // work correctly + ctm = state->getCTM(); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); } + text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy, + t3GlyphStack->code, t3GlyphStack->u, t3GlyphStack->uLen); + t3gs = t3GlyphStack; + t3GlyphStack = t3gs->next; + delete t3gs; +} - // Bresenham parameters - px1 = w1 / width; - px2 = w1 - px1 * width; - py1 = h1 / height; - py2 = h1 - py1 * height; +void XOutputDev::drawType3Glyph(T3FontCache *t3Font, + T3FontCacheTag *tag, Guchar *data, + double x, double y, GfxRGB *color) { + XImage *image; + XColor xcolor; + GfxRGB bg, rgb; + Gulong map[5]; + Gulong pixel; + Guchar *p; + int x0, y0, w0, h0, x1, y1; + int ix, iy; - // allocate XImage - image = XCreateImage(display, DefaultVisual(display, screenNum), - depth, ZPixmap, 0, NULL, w0, h0, 8, 0); - image->data = (char *)gmalloc(h0 * image->bytes_per_line); - if (x0 + w0 > pixmapW) - w2 = pixmapW - x0; - else - w2 = w0; + // compute: (x0,y0) = position in destination pixmap + // (x1,y1) = position in the XImage + // (w0,h0) = size of XImage transfer + x0 = xoutRound(x + t3Font->glyphX); + y0 = xoutRound(y + t3Font->glyphY); + x1 = 0; + y1 = 0; + w0 = t3Font->glyphW; + h0 = t3Font->glyphH; if (x0 < 0) { - x2 = -x0; - w2 += x0; + x1 = -x0; + w0 += x0; x0 = 0; - } else { - x2 = 0; } - if (y0 + h0 > pixmapH) - h2 = pixmapH - y0; - else - h2 = h0; + if (x0 + w0 > pixmapW) { + w0 = pixmapW - x0; + } + if (w0 <= 0) { + return; + } if (y0 < 0) { - y2 = -y0; - h2 += y0; + y1 = -y0; + h0 += y0; y0 = 0; - } else { - y2 = 0; } - XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap, - image, x2, y2); - - // initialize the image stream - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - - // first line (column) - y = yFlip ? h1 - 1 : 0; - qy = 0; - - // read image - for (i = 0; i < height; ++i) { + if (y0 + h0 > pixmapH) { + h0 = pixmapH - y0; + } + if (h0 <= 0) { + return; + } - // vertical (horizontal) Bresenham - dy = py1; - if ((qy += py2) >= height) { - ++dy; - qy -= height; + image = t3Font->image; + XGetSubImage(display, pixmap, x0, y0, w0, h0, + (1 << depth) - 1, ZPixmap, image, x1, y1); + xcolor.pixel = XGetPixel(image, t3Font->glyphW / 2, t3Font->glyphH / 2); + XQueryColor(display, colormap, &xcolor); + bg.r = xcolor.red / 65535.0; + bg.g = xcolor.green / 65535.0; + bg.b = xcolor.blue / 65535.0; + rgb.r = 0.25 * (color->r + 3 * bg.r); + rgb.g = 0.25 * (color->g + 3 * bg.g); + rgb.b = 0.25 * (color->b + 3 * bg.b); + map[1] = findColor(&rgb); + rgb.r = 0.5 * (color->r + bg.r); + rgb.g = 0.5 * (color->g + bg.g); + rgb.b = 0.5 * (color->b + bg.b); + map[2] = findColor(&rgb); + rgb.r = 0.25 * (3 * color->r + bg.r); + rgb.g = 0.25 * (3 * color->g + bg.g); + rgb.b = 0.25 * (3 * color->b + bg.b); + map[3] = findColor(&rgb); + map[4] = findColor(color); + p = data; + for (iy = 0; iy < t3Font->glyphH; ++iy) { + for (ix = 0; ix < t3Font->glyphW; ++ix) { + pixel = *p++; + if (pixel > 0) { + XPutPixel(image, ix, iy, map[pixel]); + } } + } + XPutImage(display, pixmap, fillGC, image, x1, y1, x0, y0, w0, h0); +} - // drop a line (column) - if (dy == 0) { - imgStr->skipLine(); - - } else { - - // first column (line) - x = xFlip ? w1 - 1 : 0; - qx = 0; +void XOutputDev::type3D0(GfxState *state, double wx, double wy) { + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; +} - // for each column (line)... - for (j = 0; j < width; ++j) { +void XOutputDev::type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury) { + GfxColor fgColor; + XGCValues gcValues; + XRectangle rect; + double *ctm; + T3FontCache *t3Font; + double xt, yt, xMin, xMax, yMin, yMax, x1, y1; + int i, j; - // horizontal (vertical) Bresenham - dx = px1; - if ((qx += px2) >= width) { - ++dx; - qx -= width; - } + t3Font = t3GlyphStack->cache; + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; - // get image pixel - imgStr->getPixel(&pixBuf); - if (invert) - pixBuf ^= 1; - - // draw image pixel - if (dx > 0 && pixBuf == 0) { - if (dx == 1 && dy == 1) { - if (rotate) - XPutPixel(image, y, x, color); - else - XPutPixel(image, x, y, color); - } else { - for (ix = 0; ix < dx; ++ix) { - for (iy = 0; iy < dy; ++iy) { - if (rotate) - XPutPixel(image, yFlip ? y - iy : y + iy, - xFlip ? x - ix : x + ix, color); - else - XPutPixel(image, xFlip ? x - ix : x + ix, - yFlip ? y - iy : y + iy, color); - } - } - } - } + // check for a valid bbox + state->transform(0, 0, &xt, &yt); + state->transform(llx, lly, &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(llx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, lly, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + if (xMin - xt < t3Font->glyphX || + yMin - yt < t3Font->glyphY || + xMax - xt > t3Font->glyphX + t3Font->glyphW || + yMax - yt > t3Font->glyphY + t3Font->glyphH) { + error(-1, "Bad bounding box in Type 3 glyph"); + return; + } - // next column (line) - if (xFlip) - x -= dx; - else - x += dx; - } + // allocate a cache entry + t3GlyphStack->cacheable = gTrue; + i = t3GlyphStack->cacheIdx; + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { + t3Font->cacheTags[i+j].mru = 0x8000; + t3Font->cacheTags[i+j].code = t3GlyphStack->code; + t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; + t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; + } else { + ++t3Font->cacheTags[i+j].mru; } - - // next line (column) - if (yFlip) - y -= dy; - else - y += dy; } + t3GlyphStack->cacheTag->wx = wx; + t3GlyphStack->cacheTag->wy = wy; + + // prepare to rasterize the glyph + //~ do we need to handle both fill and stroke color? + state->getFillRGB(&t3GlyphStack->color); + if (reverseVideo) { + t3GlyphStack->color.r = 1 - t3GlyphStack->color.r; + t3GlyphStack->color.g = 1 - t3GlyphStack->color.g; + t3GlyphStack->color.b = 1 - t3GlyphStack->color.b; + } + fgColor.c[0] = reverseVideo ? 1 : 0; + state->setFillColorSpace(new GfxDeviceGrayColorSpace()); + state->setFillColor(&fgColor); + state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); + state->setStrokeColor(&fgColor); + t3GlyphStack->origPixmapW = pixmapW; + t3GlyphStack->origPixmapH = pixmapH; + t3GlyphStack->origPixmap = pixmap; + t3GlyphStack->origStrokeGC = strokeGC; + t3GlyphStack->origFillGC = fillGC; + t3GlyphStack->origClipRegion = clipRegion; + pixmapW = t3GlyphStack->cache->glyphW; + pixmapH = t3GlyphStack->cache->glyphH; + pixmap = t3GlyphStack->cache->pixmap; + gcValues.foreground = BlackPixel(display, screenNum); + gcValues.background = WhitePixel(display, screenNum); + gcValues.line_width = 0; + gcValues.line_style = LineSolid; + strokeGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + updateLineAttrs(state, gTrue); + gcValues.foreground = WhitePixel(display, screenNum); + fillGC = XCreateGC(display, pixmap, + GCForeground | GCBackground | GCLineWidth | GCLineStyle, + &gcValues); + XFillRectangle(display, pixmap, fillGC, 0, 0, pixmapW, pixmapH); + XSetForeground(display, fillGC, BlackPixel(display, screenNum)); + clipRegion = XCreateRegion(); + rect.x = rect.y = 0; + rect.width = pixmapW; + rect.height = pixmapH; + XUnionRectWithRegion(&rect, clipRegion, clipRegion); + XSetRegion(display, strokeGC, clipRegion); + XSetRegion(display, fillGC, clipRegion); + ctm = state->getCTM(); + t3GlyphStack->origCTM4 = ctm[4]; + t3GlyphStack->origCTM5 = ctm[5]; + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY); +} - // blit the image into the pixmap - XPutImage(display, pixmap, fillGC, image, x2, y2, x0, y0, w2, h2); - - // free memory - delete imgStr; - gfree(image->data); - image->data = NULL; - XDestroyImage(image); +void XOutputDev::endTextObject(GfxState *state) { + double *ctm; + double saveCTM[6]; + + if (textClipPath) { + ctm = state->getCTM(); + memcpy(saveCTM, ctm, 6 * sizeof(double)); + state->setCTM(1, 0, 0, 1, 0, 0); + doClip(state, textClipPath, WindingRule); + state->setCTM(saveCTM[0], saveCTM[1], saveCTM[2], saveCTM[3], + saveCTM[4], saveCTM[5]); + delete textClipPath; + textClipPath = NULL; + } } -inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *err) { +inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *actual) { double gray; int r, g, b; Gulong pixel; @@ -2265,327 +3167,810 @@ inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *err) { pixel = ((Gulong)r << rShift) + ((Gulong)g << gShift) + ((Gulong)b << bShift); - err->r = x->r - (double)r / rMul; - err->g = x->g - (double)g / gMul; - err->b = x->b - (double)b / bMul; + actual->r = (double)r / rMul; + actual->g = (double)g / gMul; + actual->b = (double)b / bMul; } else if (numColors == 1) { gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b; if (gray < 0.5) { pixel = colors[0]; - err->r = x->r; - err->g = x->g; - err->b = x->b; + actual->r = actual->g = actual->b = 0; } else { pixel = colors[1]; - err->r = x->r - 1; - err->g = x->g - 1; - err->b = x->b - 1; + actual->r = actual->g = actual->b = 1; } } else { r = xoutRound(x->r * (numColors - 1)); g = xoutRound(x->g * (numColors - 1)); b = xoutRound(x->b * (numColors - 1)); pixel = colors[(r * numColors + g) * numColors + b]; - err->r = x->r - (double)r / (numColors - 1); - err->g = x->g - (double)g / (numColors - 1); - err->b = x->b - (double)b / (numColors - 1); + actual->r = (double)r / (numColors - 1); + actual->g = (double)g / (numColors - 1); + actual->b = (double)b / (numColors - 1); + } + return pixel; +} + +Gulong XOutputDev::findColor(GfxRGB *rgb) { + int r, g, b; + double gray; + Gulong pixel; + + if (trueColor) { + r = xoutRound(rgb->r * rMul); + g = xoutRound(rgb->g * gMul); + b = xoutRound(rgb->b * bMul); + pixel = ((Gulong)r << rShift) + + ((Gulong)g << gShift) + + ((Gulong)b << bShift); + } else if (numColors == 1) { + gray = 0.299 * rgb->r + 0.587 * rgb->g + 0.114 * rgb->b; + if (gray < 0.5) + pixel = colors[0]; + else + pixel = colors[1]; + } else { + r = xoutRound(rgb->r * (numColors - 1)); + g = xoutRound(rgb->g * (numColors - 1)); + b = xoutRound(rgb->b * (numColors - 1)); +#if 0 // this makes things worse as often as better + // even a very light color shouldn't map to white + if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) { + if (color->getR() < 0.95) + --r; + if (color->getG() < 0.95) + --g; + if (color->getB() < 0.95) + --b; + } +#endif + pixel = colors[(r * numColors + g) * numColors + b]; } return pixel; } -void XOutputDev::drawImage(GfxState *state, Stream *str, int width, - int height, GfxImageColorMap *colorMap, - GBool inlineImg) { +void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg) { ImageStream *imgStr; XImage *image; - int x0, y0; // top left corner of image - int w0, h0, w1, h1; // size of image - double xt, yt, wt, ht; - GBool rotate, xFlip, yFlip; - GBool dither; - int x, y; - int ix, iy; - int px1, px2, qx, dx; - int py1, py2, qy, dy; - Guchar pixBuf[4]; - Gulong pixel; - int nComps, nVals, nBits; - double r1, g1, b1; - GfxColor color; - RGBColor color2, err; - RGBColor *errRight, *errDown; - int i, j; + double *ctm; + GBool rot; + double xScale, yScale, xShear, yShear; + int tx, ty, scaledWidth, scaledHeight, xSign, ySign; + int ulx, uly, llx, lly, urx, ury, lrx, lry; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; + int bx0, by0, bx1, by1, bw, bh; + int cx0, cy0, cx1, cy1, cw, ch; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; + GfxRGB rgb; + Guchar *pixBuf; + int imgPix; + double alpha; + XColor xcolor; + Gulong lastPixel; + GfxRGB rgb2; + double r0, g0, b0, r1, g1, b1; + Gulong pix; + Guchar *p; + int x, y, x1, y1, x2, y2; + int n, m, i, j; + + // get CTM, check for singular matrix + ctm = state->getCTM(); + if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) { + error(-1, "Singular CTM in drawImage"); + if (inlineImg) { + j = height * ((width + 7) / 8); + str->reset(); + for (i = 0; i < j; ++i) { + str->getChar(); + } + str->close(); + } + return; + } - // get image position and size - state->transform(0, 0, &xt, &yt); - state->transformDelta(1, 1, &wt, &ht); - if (wt > 0) { - x0 = xoutRound(xt); - w0 = xoutRound(wt); + // compute scale, shear, rotation, translation parameters + rot = fabs(ctm[1]) > fabs(ctm[0]); + if (rot) { + xScale = -ctm[1]; + yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1]; + xShear = ctm[3] / yScale; + yShear = -ctm[0] / ctm[1]; } else { - x0 = xoutRound(xt + wt); - w0 = xoutRound(-wt); + xScale = ctm[0]; + yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0]; + xShear = -ctm[2] / yScale; + yShear = ctm[1] / ctm[0]; } - if (ht > 0) { - y0 = xoutRound(yt); - h0 = xoutRound(ht); + tx = xoutRound(ctm[2] + ctm[4]); + ty = xoutRound(ctm[3] + ctm[5]); + // use ceil() to avoid gaps between "striped" images + scaledWidth = (int)ceil(fabs(xScale)); + xSign = (xScale < 0) ? -1 : 1; + scaledHeight = (int)ceil(fabs(yScale)); + ySign = (yScale < 0) ? -1 : 1; + + // compute corners in device space + ulx1 = 0; + uly1 = 0; + urx1 = xSign * (scaledWidth - 1); + ury1 = xoutRound(yShear * urx1); + llx1 = xoutRound(xShear * ySign * (scaledHeight - 1)); + lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1); + lrx1 = xSign * (scaledWidth - 1) + + xoutRound(xShear * ySign * (scaledHeight - 1)); + lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1); + if (rot) { + ulx = tx + uly1; uly = ty - ulx1; + urx = tx + ury1; ury = ty - urx1; + llx = tx + lly1; lly = ty - llx1; + lrx = tx + lry1; lry = ty - lrx1; } else { - y0 = xoutRound(yt + ht); - h0 = xoutRound(-ht); - } - state->transformDelta(1, 0, &xt, &yt); - rotate = fabs(xt) < fabs(yt); - if (rotate) { - w1 = h0; - h1 = w0; - xFlip = ht < 0; - yFlip = wt > 0; + ulx = tx + ulx1; uly = ty + uly1; + urx = tx + urx1; ury = ty + ury1; + llx = tx + llx1; lly = ty + lly1; + lrx = tx + lrx1; lry = ty + lry1; + } + + // bounding box: + // (bx0, by0) = upper-left corner + // (bx1, by1) = lower-right corner + // (bw, bh) = size + bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx + : (llx < lrx) ? llx : lrx + : (urx < llx) ? (urx < lrx) ? urx : lrx + : (llx < lrx) ? llx : lrx; + bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx + : (llx > lrx) ? llx : lrx + : (urx > llx) ? (urx > lrx) ? urx : lrx + : (llx > lrx) ? llx : lrx; + by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry + : (lly < lry) ? lly : lry + : (ury < lly) ? (ury < lry) ? ury : lry + : (lly < lry) ? lly : lry; + by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry + : (lly > lry) ? lly : lry + : (ury > lly) ? (ury > lry) ? ury : lry + : (lly > lry) ? lly : lry; + bw = bx1 - bx0 + 1; + bh = by1 - by0 + 1; + + // Bounding box clipped to pixmap, i.e., "valid" rectangle: + // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap + // (cx1, cy1) = upper-left corner of valid rectangle in XImage + // (cw, ch) = size of valid rectangle + // These values will be used to transfer the XImage from/to the + // Pixmap. + cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw; + if (bx0 < 0) { + cx0 = 0; + cx1 = -bx0; + cw += bx0; + } else { + cx0 = bx0; + cx1 = 0; + } + ch = (by1 >= pixmapH) ? pixmapH - by0 : bh; + if (by0 < 0) { + cy0 = 0; + cy1 = -by0; + ch += by0; } else { - w1 = w0; - h1 = h0; - xFlip = wt < 0; - yFlip = ht > 0; + cy0 = by0; + cy1 = 0; + } + + // check for tiny (zero width or height) images + // and off-page images + if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) { + if (inlineImg) { + j = height * ((width + 7) / 8); + str->reset(); + for (i = 0; i < j; ++i) { + str->getChar(); + } + str->close(); + } + return; } - // set up + // compute Bresenham parameters for x and y scaling + yp = height / scaledHeight; + yq = height % scaledHeight; + xp = width / scaledWidth; + xq = width % scaledWidth; + + // allocate pixel buffer + pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + + // allocate XImage and read from page pixmap + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0); + image->data = (char *)gmalloc(bh * image->bytes_per_line); + XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap, + image, cx1, cy1); + + // get mask color + state->getFillRGB(&rgb); + if (reverseVideo) { + rgb.r = 1 - rgb.r; + rgb.g = 1 - rgb.g; + rgb.b = 1 - rgb.b; + } + r0 = rgb.r; + g0 = rgb.g; + b0 = rgb.b; + + // initialize background color + // (the specific pixel value doesn't matter here, as long as + // r1,g1,b1 correspond correctly to lastPixel) + xcolor.pixel = lastPixel = 0; + XQueryColor(display, colormap, &xcolor); + r1 = (double)xcolor.red / 65535.0; + g1 = (double)xcolor.green / 65535.0; + b1 = (double)xcolor.blue / 65535.0; + + // initialize the image stream + imgStr = new ImageStream(str, width, 1, 1); + imgStr->reset(); + + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } + + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + p = pixBuf; + for (i = 0; i < n; ++i) { + memcpy(p, imgStr->getLine(), width); + if (invert) { + for (j = 0; j < width; ++j) { + p[j] ^= 1; + } + } + p += width; + } + } + lastYStep = yStep; + + // init x scale Bresenham + xt = 0; + xSrc = 0; + + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // x shear + x1 = xSign * x + xoutRound(xShear * ySign * y); + + // y shear + y1 = ySign * y + xoutRound(yShear * x1); + + // rotation + if (rot) { + x2 = y1; + y2 = -x1; + } else { + x2 = x1; + y2 = y1; + } + + // compute the filtered pixel at (x,y) after the + // x and y scaling operations + n = yStep > 0 ? yStep : 1; + m = xStep > 0 ? xStep : 1; + p = pixBuf + xSrc; + imgPix = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + imgPix += *p++; + } + p += width - m; + } + + // x scale Bresenham + xSrc += xStep; + + // blend image pixel with background + alpha = (double)imgPix / (double)(n * m); + xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0); + if (xcolor.pixel != lastPixel) { + XQueryColor(display, colormap, &xcolor); + r1 = (double)xcolor.red / 65535.0; + g1 = (double)xcolor.green / 65535.0; + b1 = (double)xcolor.blue / 65535.0; + lastPixel = xcolor.pixel; + } + rgb2.r = r0 * (1 - alpha) + r1 * alpha; + rgb2.g = g0 * (1 - alpha) + g1 * alpha; + rgb2.b = b0 * (1 - alpha) + b1 * alpha; + pix = findColor(&rgb2); + + // set pixel + XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix); + } + } + + // blit the image into the pixmap + XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch); + + // free memory + delete imgStr; + gfree(pixBuf); + gfree(image->data); + image->data = NULL; + XDestroyImage(image); +} + +void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg) { + ImageStream *imgStr; + XImage *image; + int nComps, nVals, nBits; + GBool dither; + double *ctm; + GBool rot; + double xScale, yScale, xShear, yShear; + int tx, ty, scaledWidth, scaledHeight, xSign, ySign; + int ulx, uly, llx, lly, urx, ury, lrx, lry; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; + int bx0, by0, bx1, by1, bw, bh; + int cx0, cy0, cx1, cy1, cw, ch; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; + GfxRGB *pixBuf; + Guchar *pixBuf1, *alphaBuf; + Guchar pixBuf2[gfxColorMaxComps]; + GfxRGB color2, color3, actual, err, errRight; + GfxRGB *errDown0, *errDown1, *errDownTmp; + double r0, g0, b0, alpha, mul; + Gulong pix; + GfxRGB *p; + Guchar *q, *p1, *p2; + GBool oneBitMode; + GfxRGB oneBitRGB[2]; + int x, y, x1, y1, x2, y2; + int n, m, i, j, k; + + // image parameters nComps = colorMap->getNumPixelComps(); nVals = width * nComps; nBits = colorMap->getBits(); + oneBitMode = nComps == 1 && nBits == 1 && !maskColors; dither = nComps > 1 || nBits > 1; + // get CTM, check for singular matrix + ctm = state->getCTM(); + if (fabs(ctm[0] * ctm[3] - ctm[1] * ctm[2]) < 0.000001) { + error(-1, "Singular CTM in drawImage"); + if (inlineImg) { + str->reset(); + j = height * ((nVals * nBits + 7) / 8); + for (i = 0; i < j; ++i) { + str->getChar(); + } + str->close(); + } + return; + } + + // compute scale, shear, rotation, translation parameters + rot = fabs(ctm[1]) > fabs(ctm[0]); + if (rot) { + xScale = -ctm[1]; + yScale = -ctm[2] + (ctm[0] * ctm[3]) / ctm[1]; + xShear = ctm[3] / yScale; + yShear = -ctm[0] / ctm[1]; + } else { + xScale = ctm[0]; + yScale = -ctm[3] + (ctm[1] * ctm[2]) / ctm[0]; + xShear = -ctm[2] / yScale; + yShear = ctm[1] / ctm[0]; + } + tx = xoutRound(ctm[2] + ctm[4]); + ty = xoutRound(ctm[3] + ctm[5]); + if (xScale < 0) { + // this is the right edge which needs to be (left + width - 1) + --tx; + } + if (yScale < 0) { + // this is the bottom edge which needs to be (top + height - 1) + --ty; + } + // use ceil() to avoid gaps between "striped" images + scaledWidth = (int)ceil(fabs(xScale)); + xSign = (xScale < 0) ? -1 : 1; + scaledHeight = (int)ceil(fabs(yScale)); + ySign = (yScale < 0) ? -1 : 1; + + // compute corners in device space + ulx1 = 0; + uly1 = 0; + urx1 = xSign * (scaledWidth - 1); + ury1 = xoutRound(yShear * urx1); + llx1 = xoutRound(xShear * ySign * (scaledHeight - 1)); + lly1 = ySign * (scaledHeight - 1) + xoutRound(yShear * llx1); + lrx1 = xSign * (scaledWidth - 1) + + xoutRound(xShear * ySign * (scaledHeight - 1)); + lry1 = ySign * (scaledHeight - 1) + xoutRound(yShear * lrx1); + if (rot) { + ulx = tx + uly1; uly = ty - ulx1; + urx = tx + ury1; ury = ty - urx1; + llx = tx + lly1; lly = ty - llx1; + lrx = tx + lry1; lry = ty - lrx1; + } else { + ulx = tx + ulx1; uly = ty + uly1; + urx = tx + urx1; ury = ty + ury1; + llx = tx + llx1; lly = ty + lly1; + lrx = tx + lrx1; lry = ty + lry1; + } + + // bounding box: + // (bx0, by0) = upper-left corner + // (bx1, by1) = lower-right corner + // (bw, bh) = size + bx0 = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx + : (llx < lrx) ? llx : lrx + : (urx < llx) ? (urx < lrx) ? urx : lrx + : (llx < lrx) ? llx : lrx; + bx1 = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx + : (llx > lrx) ? llx : lrx + : (urx > llx) ? (urx > lrx) ? urx : lrx + : (llx > lrx) ? llx : lrx; + by0 = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry + : (lly < lry) ? lly : lry + : (ury < lly) ? (ury < lry) ? ury : lry + : (lly < lry) ? lly : lry; + by1 = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry + : (lly > lry) ? lly : lry + : (ury > lly) ? (ury > lry) ? ury : lry + : (lly > lry) ? lly : lry; + bw = bx1 - bx0 + 1; + bh = by1 - by0 + 1; + + // Bounding box clipped to pixmap, i.e., "valid" rectangle: + // (cx0, cy0) = upper-left corner of valid rectangle in Pixmap + // (cx1, cy1) = upper-left corner of valid rectangle in XImage + // (cw, ch) = size of valid rectangle + // These values will be used to transfer the XImage from/to the + // Pixmap. + cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw; + if (bx0 < 0) { + cx0 = 0; + cx1 = -bx0; + cw += bx0; + } else { + cx0 = bx0; + cx1 = 0; + } + ch = (by1 >= pixmapH) ? pixmapH - by0 : bh; + if (by0 < 0) { + cy0 = 0; + cy1 = -by0; + ch += by0; + } else { + cy0 = by0; + cy1 = 0; + } + // check for tiny (zero width or height) images - if (w0 == 0 || h0 == 0) { - j = height * ((nVals * nBits + 7) / 8); - str->reset(); - for (i = 0; i < j; ++i) - str->getChar(); + // and off-page images + if (scaledWidth <= 0 || scaledHeight <= 0 || cw <= 0 || ch <= 0) { + if (inlineImg) { + str->reset(); + j = height * ((nVals * nBits + 7) / 8); + for (i = 0; i < j; ++i) + str->getChar(); + str->close(); + } return; } - // Bresenham parameters - px1 = w1 / width; - px2 = w1 - px1 * width; - py1 = h1 / height; - py2 = h1 - py1 * height; + // compute Bresenham parameters for x and y scaling + yp = height / scaledHeight; + yq = height % scaledHeight; + xp = width / scaledWidth; + xq = width % scaledWidth; + + // allocate pixel buffer + if (oneBitMode) { + pixBuf1 = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + pixBuf = NULL; + } else { + pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB)); + pixBuf1 = NULL; + } + if (maskColors) { + alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + } else { + alphaBuf = NULL; + } // allocate XImage - image = XCreateImage(display, DefaultVisual(display, screenNum), - depth, ZPixmap, 0, NULL, w0, h0, 8, 0); - image->data = (char *)gmalloc(h0 * image->bytes_per_line); + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bw, bh, 8, 0); + image->data = (char *)gmalloc(bh * image->bytes_per_line); + + // if the transform is anything other than a 0/90/180/270 degree + // rotation/flip, or if there is color key masking, read the + // backgound pixmap to fill in the corners + if (!((ulx == llx && uly == ury) || + (uly == lly && ulx == urx)) || + maskColors) { + XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap, + image, cx1, cy1); + } // allocate error diffusion accumulators if (dither) { - errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor)); - errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor)); - for (j = 0; j < w1; ++j) - errDown[j].r = errDown[j].g = errDown[j].b = 0; + errDown0 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB)); + errDown1 = (GfxRGB *)gmalloc((scaledWidth + 2) * sizeof(GfxRGB)); + for (j = 0; j < scaledWidth + 2; ++j) { + errDown0[j].r = errDown0[j].g = errDown0[j].b = 0; + errDown1[j].r = errDown1[j].g = errDown1[j].b = 0; + } } else { - errDown = NULL; - errRight = NULL; + errDown0 = errDown1 = NULL; + } + + // optimize the one-bit-deep image case + if (oneBitMode) { + pixBuf2[0] = 0; + colorMap->getRGB(pixBuf2, &oneBitRGB[0]); + pixBuf2[0] = 1; + colorMap->getRGB(pixBuf2, &oneBitRGB[1]); } // initialize the image stream imgStr = new ImageStream(str, width, nComps, nBits); imgStr->reset(); - // first line (column) - y = yFlip ? h1 - 1 : 0; - qy = 0; + // init y scale Bresenham + yt = 0; + lastYStep = 1; - // read image - for (i = 0; i < height; ++i) { + for (y = 0; y < scaledHeight; ++y) { - // vertical (horizontal) Bresenham - dy = py1; - if ((qy += py2) >= height) { - ++dy; - qy -= height; + // initialize error diffusion accumulators + if (dither) { + errDownTmp = errDown0; + errDown0 = errDown1; + errDown1 = errDownTmp; + for (j = 0; j < scaledWidth + 2; ++j) { + errDown1[j].r = errDown1[j].g = errDown1[j].b = 0; + } + errRight.r = errRight.g = errRight.b = 0; } - // drop a line (column) - if (dy == 0) { - imgStr->skipLine(); + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } - } else { + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + if (oneBitMode) { + p1 = pixBuf1; + for (i = 0; i < n; ++i) { + p2 = imgStr->getLine(); + memcpy(p1, p2, width); + p1 += width; + } + } else { + p = pixBuf; + q = alphaBuf; + for (i = 0; i < n; ++i) { + p2 = imgStr->getLine(); + for (j = 0; j < width; ++j) { + colorMap->getRGB(p2, p); + ++p; + if (q) { + *q = 1; + for (k = 0; k < nComps; ++k) { + if (p2[k] < maskColors[2*k] || + p2[k] > maskColors[2*k+1]) { + *q = 0; + break; + } + } + ++q; + } + p2 += nComps; + } + } + } + } + lastYStep = yStep; - // first column (line) - x = xFlip ? w1 - 1 : 0; - qx = 0; + // init x scale Bresenham + xt = 0; + xSrc = 0; - // clear error accumulator - if (dither) { - for (j = 0; j <= py1; ++j) - errRight[j].r = errRight[j].g = errRight[j].b = 0; + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; } - // for each column (line)... - for (j = 0; j < width; ++j) { + // x shear + x1 = xSign * x + xoutRound(xShear * ySign * y); - // horizontal (vertical) Bresenham - dx = px1; - if ((qx += px2) >= width) { - ++dx; - qx -= width; - } + // y shear + y1 = ySign * y + xoutRound(yShear * x1); + + // rotation + if (rot) { + x2 = y1; + y2 = -x1; + } else { + x2 = x1; + y2 = y1; + } - // get image pixel - imgStr->getPixel(pixBuf); - - // draw image pixel - if (dx > 0) { - colorMap->getColor(pixBuf, &color); - r1 = color.getR(); - g1 = color.getG(); - b1 = color.getB(); - if (dither) { - pixel = 0; - } else { - color2.r = r1; - color2.g = g1; - color2.b = b1; - pixel = findColor(&color2, &err); + // compute the filtered pixel at (x,y) after the + // x and y scaling operations + n = yStep > 0 ? yStep : 1; + m = xStep > 0 ? xStep : 1; + if (oneBitMode) { + p1 = pixBuf1 + xSrc; + k = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + k += *p1++; } - if (dx == 1 && dy == 1) { - if (dither) { - color2.r = r1 + errRight[0].r + errDown[x].r; - if (color2.r > 1) - color2.r = 1; - else if (color2.r < 0) - color2.r = 0; - color2.g = g1 + errRight[0].g + errDown[x].g; - if (color2.g > 1) - color2.g = 1; - else if (color2.g < 0) - color2.g = 0; - color2.b = b1 + errRight[0].b + errDown[x].b; - if (color2.b > 1) - color2.b = 1; - else if (color2.b < 0) - color2.b = 0; - pixel = findColor(&color2, &err); - errRight[0].r = errDown[x].r = err.r / 2; - errRight[0].g = errDown[x].g = err.g / 2; - errRight[0].b = errDown[x].b = err.b / 2; - } - if (rotate) - XPutPixel(image, y, x, pixel); - else - XPutPixel(image, x, y, pixel); - } else { - for (iy = 0; iy < dy; ++iy) { - for (ix = 0; ix < dx; ++ix) { - if (dither) { - color2.r = r1 + errRight[iy].r + - errDown[xFlip ? x - ix : x + ix].r; - if (color2.r > 1) - color2.r = 1; - else if (color2.r < 0) - color2.r = 0; - color2.g = g1 + errRight[iy].g + - errDown[xFlip ? x - ix : x + ix].g; - if (color2.g > 1) - color2.g = 1; - else if (color2.g < 0) - color2.g = 0; - color2.b = b1 + errRight[iy].b + - errDown[xFlip ? x - ix : x + ix].b; - if (color2.b > 1) - color2.b = 1; - else if (color2.b < 0) - color2.b = 0; - pixel = findColor(&color2, &err); - errRight[iy].r = errDown[xFlip ? x - ix : x + ix].r = - err.r / 2; - errRight[iy].g = errDown[xFlip ? x - ix : x + ix].g = - err.g / 2; - errRight[iy].b = errDown[xFlip ? x - ix : x + ix].b = - err.b / 2; - } - if (rotate) - XPutPixel(image, yFlip ? y - iy : y + iy, - xFlip ? x - ix : x + ix, pixel); - else - XPutPixel(image, xFlip ? x - ix : x + ix, - yFlip ? y - iy : y + iy, pixel); - } + p1 += width - m; + } + mul = (double)k / (double)(n * m); + r0 = mul * oneBitRGB[1].r + (1 - mul) * oneBitRGB[0].r; + g0 = mul * oneBitRGB[1].g + (1 - mul) * oneBitRGB[0].g; + b0 = mul * oneBitRGB[1].b + (1 - mul) * oneBitRGB[0].b; + alpha = 0; + } else { + p = pixBuf + xSrc; + q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL; + alpha = 0; + r0 = g0 = b0 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + r0 += p->r; + g0 += p->g; + b0 += p->b; + ++p; + if (q) { + alpha += *q++; } } + p += width - m; + } + mul = 1 / (double)(n * m); + r0 *= mul; + g0 *= mul; + b0 *= mul; + alpha *= mul; + } + + // x scale Bresenham + xSrc += xStep; + + // compute pixel + if (dither) { + color2.r = r0 + errRight.r + errDown0[x + 1].r; + if (color2.r > 1) { + color3.r = 1; + } else if (color2.r < 0) { + color3.r = 0; + } else { + color3.r = color2.r; + } + color2.g = g0 + errRight.g + errDown0[x + 1].g; + if (color2.g > 1) { + color3.g = 1; + } else if (color2.g < 0) { + color3.g = 0; + } else { + color3.g = color2.g; } + color2.b = b0 + errRight.b + errDown0[x + 1].b; + if (color2.b > 1) { + color3.b = 1; + } else if (color2.b < 0) { + color3.b = 0; + } else { + color3.b = color2.b; + } + pix = findColor(&color3, &actual); + err.r = (color2.r - actual.r) / 16; + err.g = (color2.g - actual.g) / 16; + err.b = (color2.b - actual.b) / 16; + errRight.r = 7 * err.r; + errRight.g = 7 * err.g; + errRight.b = 7 * err.b; + errDown1[x].r += 3 * err.r; + errDown1[x].g += 3 * err.g; + errDown1[x].b += 3 * err.b; + errDown1[x + 1].r += 5 * err.r; + errDown1[x + 1].g += 5 * err.g; + errDown1[x + 1].b += 5 * err.b; + errDown1[x + 2].r = err.r; + errDown1[x + 2].g = err.g; + errDown1[x + 2].b = err.b; + } else { + color2.r = r0; + color2.g = g0; + color2.b = b0; + pix = findColor(&color2, &actual); + } - // next column (line) - if (xFlip) - x -= dx; - else - x += dx; + // set pixel + //~ this should do a blend when 0 < alpha < 1 + if (alpha < 0.75) { + XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix); } } - - // next line (column) - if (yFlip) - y -= dy; - else - y += dy; } // blit the image into the pixmap - XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0); + XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch); // free memory delete imgStr; + if (oneBitMode) { + gfree(pixBuf1); + } else { + gfree(pixBuf); + } + if (maskColors) { + gfree(alphaBuf); + } gfree(image->data); image->data = NULL; XDestroyImage(image); - gfree(errRight); - gfree(errDown); -} - -Gulong XOutputDev::findColor(GfxColor *color) { - int r, g, b; - double gray; - Gulong pixel; - - if (trueColor) { - r = xoutRound(color->getR() * rMul); - g = xoutRound(color->getG() * gMul); - b = xoutRound(color->getB() * bMul); - pixel = ((Gulong)r << rShift) + - ((Gulong)g << gShift) + - ((Gulong)b << bShift); - } else if (numColors == 1) { - gray = color->getGray(); - if (gray < 0.5) - pixel = colors[0]; - else - pixel = colors[1]; - } else { - r = xoutRound(color->getR() * (numColors - 1)); - g = xoutRound(color->getG() * (numColors - 1)); - b = xoutRound(color->getB() * (numColors - 1)); -#if 0 //~ this makes things worse as often as better - // even a very light color shouldn't map to white - if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) { - if (color->getR() < 0.95) - --r; - if (color->getG() < 0.95) - --g; - if (color->getB() < 0.95) - --b; - } -#endif - pixel = colors[(r * numColors + g) * numColors + b]; - } - return pixel; + gfree(errDown0); + gfree(errDown1); } -GBool XOutputDev::findText(char *s, GBool top, GBool bottom, - int *xMin, int *yMin, int *xMax, int *yMax) { +GBool XOutputDev::findText(Unicode *s, int len, + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + int *xMin, int *yMin, + int *xMax, int *yMax) { double xMin1, yMin1, xMax1, yMax1; xMin1 = (double)*xMin; yMin1 = (double)*yMin; xMax1 = (double)*xMax; yMax1 = (double)*yMax; - if (text->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) { + if (text->findText(s, len, startAtTop, stopAtBottom, + startAtLast, stopAtLast, + &xMin1, &yMin1, &xMax1, &yMax1)) { *xMin = xoutRound(xMin1); *xMax = xoutRound(xMax1); *yMin = xoutRound(yMin1);