From 2a393c134fe3fe8eb85bf818cb7ad6ae4396322a Mon Sep 17 00:00:00 2001 From: Martin Kretzschmar Date: Wed, 18 Sep 2002 22:20:42 +0000 Subject: Synched with Xpdf 1.01 --- (limited to 'pdf/xpdf/XOutputDev.cc') diff --git a/pdf/xpdf/XOutputDev.cc b/pdf/xpdf/XOutputDev.cc index 0913ed3..6f207d8 100644 --- a/pdf/xpdf/XOutputDev.cc +++ b/pdf/xpdf/XOutputDev.cc @@ -2,7 +2,7 @@ // // XOutputDev.cc // -// Copyright 1996 Derek B. Noonburg +// Copyright 1996-2002 Glyph & Cog, LLC // //======================================================================== @@ -10,6 +10,7 @@ #pragma implementation #endif +#include #include #include #include @@ -20,26 +21,28 @@ #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 HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H +#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); @@ -64,517 +67,79 @@ typedef char *XPointer; // drawing Bezier curves //------------------------------------------------------------------------ -// Parameters -//------------------------------------------------------------------------ - -GBool installCmap = gFalse; - -int rgbCubeSize = defaultRGBCube; - -#if HAVE_T1LIB_H -GString *t1libControl = NULL; -#endif - -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H -GString *freeTypeControl = 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 -#if CHINESE_GB_SUPPORT -GString *gb12Font = NULL; -#endif -#if CHINESE_CNS_SUPPORT -GString *cns13Font = NULL; -#endif - -//------------------------------------------------------------------------ -// Font map -//------------------------------------------------------------------------ - -struct FontMapEntry { - char *pdfFont; - char *xFont; - GString **t1Font; - FontEncoding *encoding; -}; - -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} -}; - -static FontMapEntry *userFontMap; - -//------------------------------------------------------------------------ // Font substitutions //------------------------------------------------------------------------ -struct FontSubst { - char *xFont; - GString **t1Font; +struct XOutFontSubst { + char *name; 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} -}; - -//------------------------------------------------------------------------ -// 16-bit fonts -//------------------------------------------------------------------------ - -#if JAPANESE_SUPPORT - -static char *japan12DefFont = - "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-jisx0208.1983-0"; - -// CID 0 .. 96 -static Gushort japan12Map[96] = { - 0x2121, 0x2121, 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 -}; - -// 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 -}; - -// 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 -}; - -static char *japan12Roman[10] = { - "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X" -}; - -static char *japan12Abbrev1[6] = { - "mm", "cm", "km", "mg", "kg", "cc" -}; - -#endif // JAPANESE_SUPPORT - -#if CHINESE_GB_SUPPORT - -static char *gb12DefFont = - "-*-fangsong ti-medium-r-normal-*-%s-*-*-*-*-*-gb2312.1980-0"; - -static Gushort gb12Map[940] = { - // 0 - 95 - 0x0000, 0x2121, 0x2321, 0x2322, 0x2323, 0x2167, 0x2325, 0x2326, - 0x2327, 0x2328, 0x2329, 0x232a, 0x232b, 0x232c, 0x232d, 0x232e, - 0x232f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, - 0x2337, 0x2338, 0x2339, 0x233a, 0x233b, 0x233c, 0x233d, 0x233e, - 0x233f, 0x2340, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, - 0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, - 0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, - 0x2357, 0x2358, 0x2359, 0x235a, 0x235b, 0x235c, 0x235d, 0x235e, - 0x235f, 0x2360, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, - 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, - 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, - 0x2377, 0x2378, 0x2379, 0x237a, 0x237b, 0x237c, 0x237d, 0x212b, - - // 96-355 - 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, - 0x2129, 0x212a, 0x212b, 0x212c, 0x212d, 0x212e, 0x212f, 0x2130, - 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, - 0x2139, 0x213a, 0x213b, 0x213c, 0x213d, 0x213e, 0x213f, 0x2140, - 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, - 0x2149, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f, 0x2150, - 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, - 0x2159, 0x215a, 0x215b, 0x215c, 0x215d, 0x215e, 0x215f, 0x2160, - 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, - 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2170, - 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, - 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x2231, 0x2232, - 0x2233, 0x2234, 0x2235, 0x2236, 0x2237, 0x2238, 0x2239, 0x223a, - 0x223b, 0x223c, 0x223d, 0x223e, 0x223f, 0x2240, 0x2241, 0x2242, - 0x2243, 0x2244, 0x2245, 0x2246, 0x2247, 0x2248, 0x2249, 0x224a, - 0x224b, 0x224c, 0x224d, 0x224e, 0x224f, 0x2250, 0x2251, 0x2252, - 0x2253, 0x2254, 0x2255, 0x2256, 0x2257, 0x2258, 0x2259, 0x225a, - 0x225b, 0x225c, 0x225d, 0x225e, 0x225f, 0x2260, 0x2261, 0x2262, - 0x2265, 0x2266, 0x2267, 0x2268, 0x2269, 0x226a, 0x226b, 0x226c, - 0x226d, 0x226e, 0x2271, 0x2272, 0x2273, 0x2274, 0x2275, 0x2276, - 0x2277, 0x2278, 0x2279, 0x227a, 0x227b, 0x227c, 0x2321, 0x2322, - 0x2323, 0x2324, 0x2325, 0x2326, 0x2327, 0x2328, 0x2329, 0x232a, - 0x232b, 0x232c, 0x232d, 0x232e, 0x232f, 0x2330, 0x2331, 0x2332, - 0x2333, 0x2334, 0x2335, 0x2336, 0x2337, 0x2338, 0x2339, 0x233a, - 0x233b, 0x233c, 0x233d, 0x233e, 0x233f, 0x2340, 0x2341, 0x2342, - 0x2343, 0x2344, 0x2345, 0x2346, 0x2347, 0x2348, 0x2349, 0x234a, - 0x234b, 0x234c, 0x234d, 0x234e, 0x234f, 0x2350, 0x2351, 0x2352, - 0x2353, 0x2354, 0x2355, 0x2356, 0x2357, 0x2358, 0x2359, 0x235a, - 0x235b, 0x235c, 0x235d, 0x235e, 0x235f, 0x2360, 0x2361, 0x2362, - 0x2363, 0x2364, 0x2365, 0x2366, 0x2367, 0x2368, 0x2369, 0x236a, - 0x236b, 0x236c, 0x236d, 0x236e, 0x236f, 0x2370, 0x2371, 0x2372, - 0x2373, 0x2374, 0x2375, 0x2376, 0x2377, 0x2378, 0x2379, 0x237a, - 0x237b, 0x237c, 0x237d, 0x237e, - - // 356-524 - 0x2421, 0x2422, 0x2423, 0x2424, - 0x2425, 0x2426, 0x2427, 0x2428, 0x2429, 0x242a, 0x242b, 0x242c, - 0x242d, 0x242e, 0x242f, 0x2430, 0x2431, 0x2432, 0x2433, 0x2434, - 0x2435, 0x2436, 0x2437, 0x2438, 0x2439, 0x243a, 0x243b, 0x243c, - 0x243d, 0x243e, 0x243f, 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, - 0x2445, 0x2446, 0x2447, 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, - 0x244d, 0x244e, 0x244f, 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, - 0x2455, 0x2456, 0x2457, 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, - 0x245d, 0x245e, 0x245f, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, - 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246a, 0x246b, 0x246c, - 0x246d, 0x246e, 0x246f, 0x2470, 0x2471, 0x2472, 0x2473, 0x2521, - 0x2522, 0x2523, 0x2524, 0x2525, 0x2526, 0x2527, 0x2528, 0x2529, - 0x252a, 0x252b, 0x252c, 0x252d, 0x252e, 0x252f, 0x2530, 0x2531, - 0x2532, 0x2533, 0x2534, 0x2535, 0x2536, 0x2537, 0x2538, 0x2539, - 0x253a, 0x253b, 0x253c, 0x253d, 0x253e, 0x253f, 0x2540, 0x2541, - 0x2542, 0x2543, 0x2544, 0x2545, 0x2546, 0x2547, 0x2548, 0x2549, - 0x254a, 0x254b, 0x254c, 0x254d, 0x254e, 0x254f, 0x2550, 0x2551, - 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, - 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, 0x255f, 0x2560, 0x2561, - 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, - 0x256a, 0x256b, 0x256c, 0x256d, 0x256e, 0x256f, 0x2570, 0x2571, - 0x2572, 0x2573, 0x2574, 0x2575, 0x2576, - - // 525-572 - 0x2621, 0x2622, 0x2623, - 0x2624, 0x2625, 0x2626, 0x2627, 0x2628, 0x2629, 0x262a, 0x262b, - 0x262c, 0x262d, 0x262e, 0x262f, 0x2630, 0x2631, 0x2632, 0x2633, - 0x2634, 0x2635, 0x2636, 0x2637, 0x2638, 0x2641, 0x2642, 0x2643, - 0x2644, 0x2645, 0x2646, 0x2647, 0x2648, 0x2649, 0x264a, 0x264b, - 0x264c, 0x264d, 0x264e, 0x264f, 0x2650, 0x2651, 0x2652, 0x2653, - 0x2654, 0x2655, 0x2656, 0x2657, 0x2658, - - // 573-601 - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, - - // 602-667 - 0x2721, 0x2722, 0x2723, 0x2724, 0x2725, 0x2726, - 0x2727, 0x2728, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e, - 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, - 0x2737, 0x2738, 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, - 0x273f, 0x2740, 0x2741, 0x2751, 0x2752, 0x2753, 0x2754, 0x2755, - 0x2756, 0x2757, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c, 0x275d, - 0x275e, 0x275f, 0x2760, 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, - 0x2766, 0x2767, 0x2768, 0x2769, 0x276a, 0x276b, 0x276c, 0x276d, - 0x276e, 0x276f, 0x2770, 0x2771, - - // 668-699 - 0x2821, 0x2822, 0x2823, 0x2824, - 0x2825, 0x2826, 0x2827, 0x2828, 0x2829, 0x282a, 0x282b, 0x282c, - 0x282d, 0x282e, 0x282f, 0x2830, 0x2831, 0x2832, 0x2833, 0x2834, - 0x2835, 0x2836, 0x2837, 0x2838, 0x2839, 0x283a, 0, 0, - 0, 0, 0, 0, - - // 700-737 - 0x2845, 0x2846, 0x2847, 0x2848, - 0x2849, 0x284a, 0x284b, 0x284c, 0x284d, 0x284e, 0x284f, 0x2850, - 0x2851, 0x2852, 0x2853, 0x2854, 0x2855, 0x2856, 0x2857, 0x2858, - 0x2859, 0x285a, 0x285b, 0x285c, 0x285d, 0x285e, 0x285f, 0x2860, - 0x2861, 0x2862, 0x2863, 0x2864, 0x2865, 0x2866, 0x2867, 0x2868, - 0x2869, 0, - - // 738-813 - 0x2924, 0x2925, 0x2926, 0x2927, 0x2928, 0x2929, - 0x292a, 0x292b, 0x292c, 0x292d, 0x292e, 0x292f, 0x2930, 0x2931, - 0x2932, 0x2933, 0x2934, 0x2935, 0x2936, 0x2937, 0x2938, 0x2939, - 0x293a, 0x293b, 0x293c, 0x293d, 0x293e, 0x293f, 0x2940, 0x2941, - 0x2942, 0x2943, 0x2944, 0x2945, 0x2946, 0x2947, 0x2948, 0x2949, - 0x294a, 0x294b, 0x294c, 0x294d, 0x294e, 0x294f, 0x2950, 0x2951, - 0x2952, 0x2953, 0x2954, 0x2955, 0x2956, 0x2957, 0x2958, 0x2959, - 0x295a, 0x295b, 0x295c, 0x295d, 0x295e, 0x295f, 0x2960, 0x2961, - 0x2962, 0x2963, 0x2964, 0x2965, 0x2966, 0x2967, 0x2968, 0x2969, - 0x296a, 0x296b, 0x296c, 0x296d, 0x296e, 0x296f, - - // 814-939 - 0x2321, 0x2322, - 0x2323, 0x2324, 0x2325, 0x2326, 0x2327, 0x2328, 0x2329, 0x232a, - 0x232b, 0x232c, 0x232d, 0x232e, 0x232f, 0x2330, 0x2331, 0x2332, - 0x2333, 0x2334, 0x2335, 0x2336, 0x2337, 0x2338, 0x2339, 0x233a, - 0x233b, 0x233c, 0x233d, 0x233e, 0x233f, 0x2340, 0x2341, 0x2342, - 0x2343, 0x2344, 0x2345, 0x2346, 0x2347, 0x2348, 0x2349, 0x234a, - 0x234b, 0x234c, 0x234d, 0x234e, 0x234f, 0x2350, 0x2351, 0x2352, - 0x2353, 0x2354, 0x2355, 0x2356, 0x2357, 0x2358, 0x2359, 0x235a, - 0x235b, 0x235c, 0x235d, 0x235e, 0x235f, 0x2360, 0x2361, 0x2362, - 0x2363, 0x2364, 0x2365, 0x2366, 0x2367, 0x2368, 0x2369, 0x236a, - 0x236b, 0x236c, 0x236d, 0x236e, 0x236f, 0x2370, 0x2371, 0x2372, - 0x2373, 0x2374, 0x2375, 0x2376, 0x2377, 0x2378, 0x2379, 0x237a, - 0x237b, 0x237c, 0x237d, 0x237e, 0x2821, 0x2822, 0x2823, 0x2824, - 0x2825, 0x2826, 0x2827, 0x2828, 0x2829, 0x282a, 0x282b, 0x282c, - 0x282d, 0x282e, 0x282f, 0x2830, 0x2831, 0x2832, 0x2833, 0x2834, - 0x2835, 0x2836, 0x2837, 0x2838, 0x2839, 0x283a, 0, 0, - 0, 0, 0, 0 -}; - -#endif // CHINESE_GB_SUPPORT - -#if CHINESE_CNS_SUPPORT - -static char *cns13DefFont = - "-*-fixed-medium-r-normal-*-%s-*-*-*-*-*-big5-0"; - -static Gushort cns13Map1[99] = { - // 0-98 - 0, 0xa140, 0xa149, 0xa1a8, 0xa1ad, 0xa243, 0xa248, 0xa1ae, - 0xa1a6, 0xa15d, 0xa15e, 0xa1af, 0xa1cf, 0xa141, 0xa1df, 0xa144, - 0xa241, 0xa2af, 0xa2b0, 0xa2b1, 0xa2b2, 0xa2b3, 0xa2b4, 0xa2b5, - 0xa2b6, 0xa2b7, 0xa2b8, 0xa147, 0xa146, 0xa1d5, 0xa1d7, 0xa1d6, - 0xa148, 0xa249, 0xa2cf, 0xa2d0, 0xa2d1, 0xa2d2, 0xa2d3, 0xa2d4, - 0xa2d5, 0xa2d6, 0xa2d7, 0xa2d8, 0xa2d9, 0xa2da, 0xa2db, 0xa2dc, - 0xa2dd, 0xa2de, 0xa2df, 0xa2e0, 0xa2e1, 0xa2e2, 0xa2e3, 0xa2e4, - 0xa2e5, 0xa2e6, 0xa2e7, 0xa2e8, 0xa165, 0xa242, 0xa166, 0xa173, - 0xa15a, 0xa1a5, 0xa2e9, 0xa2ea, 0xa2eb, 0xa2ec, 0xa2ed, 0xa2ee, - 0xa2ef, 0xa2f0, 0xa2f1, 0xa2f2, 0xa2f3, 0xa2f4, 0xa2f5, 0xa2f6, - 0xa2f7, 0xa2f8, 0xa2f9, 0xa2fa, 0xa2fb, 0xa2fc, 0xa2fd, 0xa2fe, - 0xa340, 0xa341, 0xa342, 0xa343, 0xa161, 0xa159, 0xa162, 0xa1e3, - 0, 0, 0xa14b -}; - -static Gushort cns13Map2[95] = { - // 13648-13742 - 0xa140, 0xa149, 0xa1a8, 0xa1ad, 0xa244, 0xa248, 0xa1ae, - 0xa1a6, 0xa15d, 0xa15e, 0xa1af, 0xa1cf, 0xa141, 0xa1df, 0xa144, - 0xa241, 0xa2af, 0xa2b0, 0xa2b1, 0xa2b2, 0xa2b3, 0xa2b4, 0xa2b5, - 0xa2b6, 0xa2b7, 0xa2b8, 0xa147, 0xa146, 0xa1d5, 0xa1d7, 0xa1d6, - 0xa148, 0xa249, 0xa2cf, 0xa2d0, 0xa2d1, 0xa2d2, 0xa2d3, 0xa2d4, - 0xa2d5, 0xa2d6, 0xa2d7, 0xa2d8, 0xa2d9, 0xa2da, 0xa2db, 0xa2dc, - 0xa2dd, 0xa2de, 0xa2df, 0xa2e0, 0xa2e1, 0xa2e2, 0xa2e3, 0xa2e4, - 0xa2e5, 0xa2e6, 0xa2e7, 0xa2e8, 0xa165, 0xa242, 0xa166, 0xa173, - 0xa15a, 0xa1a5, 0xa2e9, 0xa2ea, 0xa2eb, 0xa2ec, 0xa2ed, 0xa2ee, - 0xa2ef, 0xa2f0, 0xa2f1, 0xa2f2, 0xa2f3, 0xa2f4, 0xa2f5, 0xa2f6, - 0xa2f7, 0xa2f8, 0xa2f9, 0xa2fa, 0xa2fb, 0xa2fc, 0xa2fd, 0xa2fe, - 0xa340, 0xa341, 0xa342, 0xa343, 0xa161, 0xa159, 0xa162, 0xa1c3 -}; - -#endif - -//------------------------------------------------------------------------ -// Constructed characters -//------------------------------------------------------------------------ - -#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 +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} }; -// 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 -}; - -// 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 - //------------------------------------------------------------------------ // XOutputFont //------------------------------------------------------------------------ -XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12, - double m21, double m22, Display *display, - XOutputFontCache *cache) { - int code; - char *charName; - - 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 && - isxdigit(charName[1]) && isxdigit(charName[2]) && - ((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; - } else if ((strlen(charName) == 2) && - isxdigit(charName[0]) && isxdigit(charName[1]) && - ((charName[0] >= 'a' && charName[0] <= 'f') || - (charName[0] >= 'A' && charName[0] <= 'F') || - (charName[1] >= 'a' && charName[1] <= 'f') || - (charName[1] >= 'A' && charName[1] <= 'F'))) { - hex = gTrue; - break; - } - } - } - } +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; } XOutputFont::~XOutputFont() { } +void XOutputFont::getCharPath(GfxState *state, + CharCode c, Unicode *u, int ulen) { +} + #if HAVE_T1LIB_H //------------------------------------------------------------------------ // XOutputT1Font //------------------------------------------------------------------------ -XOutputT1Font::XOutputT1Font(GfxFont *gfxFont, GString *pdfBaseFont, - double m11, double m12, double m21, double m22, - Display *display, XOutputFontCache *cache): - XOutputFont(gfxFont, m11, m12, m21, m22, display, cache) +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) { - Ref embRef; double matrix[4]; - fontFile = NULL; - font = NULL; - - // 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; - } - - // load the font - if (!(fontFile = cache->getT1Font(gfxFont, pdfBaseFont))) { - return; - } + fontFile = fontFileA; // create the transformed instance matrix[0] = m11; @@ -598,47 +163,91 @@ void XOutputT1Font::updateGC(GC gc) { } void XOutputT1Font::drawChar(GfxState *state, Pixmap pixmap, int w, int h, - GC gc, double x, double y, int c) { - GfxRGB rgb; - - if (state->getRender() & 1) { - state->getStrokeRGB(&rgb); - } else { - state->getFillRGB(&rgb); - } + 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); + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, u[0]); +} + +void XOutputT1Font::getCharPath(GfxState *state, + CharCode c, Unicode *u, int uLen) { + font->getCharPath(c, u[0], state); } #endif // HAVE_T1LIB_H -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) //------------------------------------------------------------------------ -// XOutputTTFont +// XOutputFTFont //------------------------------------------------------------------------ -XOutputTTFont::XOutputTTFont(GfxFont *gfxFont, double m11, double m12, - double m21, double m22, Display *display, - XOutputFontCache *cache): - XOutputFont(gfxFont, m11, m12, m21, m22, display, cache) +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) { - Ref embRef; double matrix[4]; - fontFile = NULL; - font = NULL; + fontFile = fontFileA; - // we can only handle 8-bit, TrueType, with embedded font file - if (!(!gfxFont->is16Bit() && - gfxFont->getType() == fontTrueType && - gfxFont->getEmbeddedFontID(&embRef))) { - return; - } + // create the transformed instance + matrix[0] = m11; + matrix[1] = -m12; + matrix[2] = m21; + matrix[3] = -m22; + font = new FTFont(fontFile, matrix); +} - // load the font - if (!(fontFile = cache->getTTFont(gfxFont))) { - return; +XOutputFTFont::~XOutputFTFont() { + if (font) { + delete font; } +} + +GBool XOutputFTFont::isOk() { + return font != NULL; +} + +void XOutputFTFont::updateGC(GC gc) { +} + +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); +} + +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) +//------------------------------------------------------------------------ +// XOutputTTFont +//------------------------------------------------------------------------ + +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) +{ + double matrix[4]; + + fontFile = fontFileA; // create the transformed instance matrix[0] = m11; @@ -662,114 +271,211 @@ void XOutputTTFont::updateGC(GC gc) { } void XOutputTTFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h, - GC gc, double x, double y, int c) { - GfxRGB rgb; - - if (state->getRender() & 1) { - state->getStrokeRGB(&rgb); - } else { - state->getFillRGB(&rgb); - } + 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); + (int)(rgb->r * 65535), (int)(rgb->g * 65535), + (int)(rgb->b * 65535), c, u[0]); } -#endif // HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_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', or 'xx' 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 && - isxdigit(charName[0]) && isxdigit(charName[1])) { - sscanf(charName, "%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 (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]; - // adjust transform for the X transform convention - ntm12 = -ntm12; - ntm22 = -ntm22; + xUMap = xUMapA; + xUMap->incRefCnt(); + + // 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 && @@ -779,36 +485,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; } @@ -816,75 +524,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, int w, int h, - GC gc, double x, double y, int c) { - GfxFont *gfxFont; - Gushort c1; - char buf; - char *p; - int n, i; - double 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; - 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; + 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); } } @@ -892,32 +578,45 @@ void XOutputServerFont::drawChar(GfxState *state, Pixmap pixmap, int w, int h, // XOutputFontCache //------------------------------------------------------------------------ -XOutputFontCache::XOutputFontCache(Display *display, Guint depth) { - this->display = display; - this->depth = depth; +#if HAVE_T1LIB_H +XOutputT1FontFile::~XOutputT1FontFile() { + delete fontFile; +} +#endif + +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputFTFontFile::~XOutputFTFontFile() { + delete fontFile; +} +#endif + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) +XOutputTTFontFile::~XOutputTTFontFile() { + delete fontFile; +} +#endif + +XOutputFontCache::XOutputFontCache(Display *displayA, Guint depthA, + XOutputDev *xOutA, + FontRastControl t1libControlA, + FontRastControl freetypeControlA) { + display = displayA; + depth = depthA; + xOut = xOutA; #if HAVE_T1LIB_H t1Engine = NULL; - if (t1libControl) { - useT1lib = t1libControl->cmp("none") != 0; - t1libAA = t1libControl->cmp("plain") != 0; - t1libAAHigh = t1libControl->cmp("high") == 0; - } else { - useT1lib = gFalse; - t1libAA = gFalse; - t1libAAHigh = gFalse; - } + t1libControl = t1libControlA; #endif -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + ftEngine = NULL; +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) ttEngine = NULL; - if (freeTypeControl) { - useFreeType = freeTypeControl->cmp("none") != 0; - freeTypeAA = freeTypeControl->cmp("plain") != 0; - } else { - useFreeType = gFalse; - freeTypeAA = gFalse; - } +#endif +#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H + freetypeControl = freetypeControlA; #endif clear(); @@ -936,9 +635,12 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap, clear(); #if HAVE_T1LIB_H - if (useT1lib) { + if (t1libControl != fontRastNone) { t1Engine = new T1FontEngine(display, DefaultVisual(display, screenNum), - depth, colormap, t1libAA, t1libAAHigh); + depth, colormap, + t1libControl == fontRastAALow || + t1libControl == fontRastAAHigh, + t1libControl == fontRastAAHigh); if (t1Engine->isOk()) { if (trueColor) { t1Engine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); @@ -952,10 +654,31 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap, } #endif // HAVE_T1LIB_H -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - if (useFreeType) { +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + ftEngine = new FTFontEngine(display, DefaultVisual(display, screenNum), + 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, DefaultVisual(display, screenNum), - depth, colormap, freeTypeAA); + depth, colormap, + freetypeControl == fontRastAALow || + freetypeControl == fontRastAAHigh); if (ttEngine->isOk()) { if (trueColor) { ttEngine->useTrueColor(rMul, rShift, gMul, gShift, bMul, bShift); @@ -967,208 +690,166 @@ void XOutputFontCache::startDoc(int screenNum, Colormap colormap, ttEngine = NULL; } } -#endif +#endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) } void XOutputFontCache::delFonts() { int i; -#if HAVE_T1LIB_H - // delete Type 1 fonts - for (i = 0; i < nT1Fonts; ++i) { - delete t1Fonts[i]; - } - for (i = 0; i < t1FontFilesSize && t1FontFiles[i].num >= 0; ++i) { - delete t1FontFiles[i].fontFile; + for (i = 0; i < nFonts; ++i) { + delete fonts[i]; } - gfree(t1FontFiles); + +#if HAVE_T1LIB_H + // delete Type 1 font files + deleteGList(t1FontFiles, XOutputT1FontFile); if (t1Engine) { delete t1Engine; } #endif -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - // delete TrueType fonts - for (i = 0; i < nTTFonts; ++i) { - delete ttFonts[i]; - } - for (i = 0; i < ttFontFilesSize && ttFontFiles[i].num >= 0; ++i) { - delete ttFontFiles[i].fontFile; +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // delete FreeType font files + deleteGList(ftFontFiles, XOutputFTFontFile); + if (ftEngine) { + delete ftEngine; } - gfree(ttFontFiles); +#endif + +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + // delete TrueType fonts + deleteGList(ttFontFiles, XOutputTTFontFile); if (ttEngine) { delete ttEngine; } #endif - - // delete server fonts - for (i = 0; i < nServerFonts; ++i) { - delete serverFonts[i]; - } } void XOutputFontCache::clear() { int i; -#if HAVE_T1LIB_H - // clear Type 1 font cache - for (i = 0; i < t1FontCacheSize; ++i) { - t1Fonts[i] = NULL; + for (i = 0; i < xOutFontCacheSize; ++i) { + fonts[i] = NULL; } - nT1Fonts = 0; - t1FontFiles = NULL; - t1FontFilesSize = 0; + nFonts = 0; + +#if HAVE_T1LIB_H + // clear Type 1 font files + t1FontFiles = new GList(); #endif -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - // clear TrueType font cache - for (i = 0; i < ttFontCacheSize; ++i) { - ttFonts[i] = NULL; - } - nTTFonts = 0; - ttFontFiles = NULL; - ttFontFilesSize = 0; +#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 -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - XOutputTTFont *ttFont; -#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, TrueType, or server font? -#if HAVE_T1LIB_H - if (useT1lib && nT1Fonts > 0 && - t1Fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) { - return t1Fonts[0]; - } -#endif -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - if (useFreeType && nTTFonts > 0 && - ttFonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) { - return ttFonts[0]; - } -#endif - if (nServerFonts > 0 && serverFonts[0]->matches(gfxFont->getID(), - m11, m12, m21, m22)) { - return serverFonts[0]; + // is it the most recently used font? + if (nFonts > 0 && fonts[0]->matches(gfxFont->getID(), m11, m12, m21, m22)) { + return fonts[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 -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - // is it in the TrueType cache? - if (useFreeType) { - for (i = 1; i < nTTFonts; ++i) { - if (ttFonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) { - ttFont = ttFonts[i]; - for (j = i; j > 0; --j) { - ttFonts[j] = ttFonts[j-1]; - } - ttFonts[0] = ttFont; - return ttFont; - } + // 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); } - } #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; - } - } - - // 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; +#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 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; - case font16AdobeGB12: -#if CHINESE_GB_SUPPORT - xFontName = gb12Font ? gb12Font->getCString() : gb12DefFont; -#endif - break; - case font16AdobeCNS13: -#if CHINESE_CNS_SUPPORT - xFontName = cns13Font ? cns13Font->getCString() : cns13DefFont; -#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()) { @@ -1176,280 +857,502 @@ 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; - // 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; - m11 *= w1; - m12 *= w1; - m21 *= w1; - 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]); - m21 *= v; - m22 *= 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; + } + 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; } - m11 *= w1; - m12 *= w1; - m21 *= w1; - m22 *= w1; } } + + // 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; + } + font = tryGetFont(xref, dfp, gfxFont, m11, m12, m21, m22, + m11New, m12New, m21New, m22New, gTrue); } } -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H - // try to create a new TrueType font - if (useFreeType) { - ttFont = new XOutputTTFont(gfxFont, m11, m12, m21, m22, display, this); - if (ttFont->isOk()) { + // 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]"); + } + return NULL; + } - // insert in cache - if (nTTFonts == ttFontCacheSize) { - --nTTFonts; - delete ttFonts[nTTFonts]; - } - for (j = nTTFonts; j > 0; --j) { - ttFonts[j] = ttFonts[j-1]; + // 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; + + 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) { + font = tryGetT1FontFromFile(xref, dfp->t1.fileName, 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, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); } - ttFonts[0] = ttFont; - ++nTTFonts; + } +#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 ttFont; + case displayFontTT: +#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetFTFontFromFile(xref, dfp->tt.fileName, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); + } +#endif +#if !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) + if (freetypeControl != fontRastNone) { + font = tryGetTTFontFromFile(xref, dfp->tt.fileName, gfxFont, + m11Orig, m12Orig, m21Orig, m22Orig, + m11, m12, m21, m22, subst); } - delete ttFont; - } #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; + } + + return font; +} #if HAVE_T1LIB_H - // try to create a new Type 1 font - if (useT1lib) { - t1Font = new XOutputT1Font(gfxFont, t1FontName, m11, m12, m21, m22, - display, this); - if (t1Font->isOk()) { - - // insert in cache - if (nT1Fonts == t1FontCacheSize) { - --nT1Fonts; - delete t1Fonts[nT1Fonts]; - } - for (j = nT1Fonts; j > 0; --j) { - t1Fonts[j] = t1Fonts[j-1]; - } - t1Fonts[0] = t1Font; - ++nT1Fonts; +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; + Type1CFontFile *ff; + Object refObj, strObj; + int c; + int i; - return t1Font; + // 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; } - delete t1Font; } -#endif - // compute size and normalized transform matrix - size = sqrt(m21*m21 + m22*m22); - ntm11 = m11 / size; - ntm12 = m12 / size; - ntm21 = m21 / size; - ntm22 = m22 / size; - - // 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()) { + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { - // insert in cache - if (nServerFonts == serverFontCacheSize) { - --nServerFonts; - delete serverFonts[nServerFonts]; + // 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); + return NULL; + } + ff = new Type1CFontFile(fontBuf, fontLen); + ff->convertToType1(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(); } - for (j = nServerFonts; j > 0; --j) - serverFonts[j] = serverFonts[j-1]; - serverFonts[0] = serverFont; - ++nServerFonts; + fclose(f); + + // create the Font + font = tryGetT1FontFromFile(xref, fileName, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + // remove the temporary file + unlink(fileName->getCString()); + delete fileName; - return serverFont; + // check for an external font file + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetT1FontFromFile(xref, fileName, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; } - delete serverFont; - return NULL; + return font; } -#if HAVE_T1LIB_H -T1FontFile *XOutputFontCache::getT1Font(GfxFont *gfxFont, - GString *pdfBaseFont) { - Ref id; +XOutputFont *XOutputFontCache::tryGetT1FontFromFile(XRef *xref, + GString *fileName, + 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; + return NULL; + } + + // add to list + id = gfxFont->getID(); + t1FontFiles->append(new XOutputT1FontFile(id->num, id->gen, + subst, fontFile)); + + // 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 + +#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; - GString *tmpFileName; FILE *f; +#if 1 //~ need this until FT can handle fonts with missing tables char *fontBuf; int fontLen; - Type1CFontConverter *cvt; - Ref embRef; + TrueTypeFontFile *ff; +#endif Object refObj, strObj; int c; - int i, j; + int i; + // check the already available font files id = gfxFont->getID(); - - // check available fonts - fontFile = NULL; - for (i = 0; i < t1FontFilesSize && t1FontFiles[i].num >= 0; ++i) { - if (t1FontFiles[i].num == id.num && t1FontFiles[i].gen == id.gen) { - fontFile = t1FontFiles[i].fontFile; + 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; + } + return font; } } - // create a new font file - if (!fontFile) { - - // resize t1FontFiles if necessary - if (i == t1FontFilesSize) { - t1FontFiles = (XOutputT1FontFile *) - grealloc(t1FontFiles, - (t1FontFilesSize + 16) * sizeof(XOutputT1FontFile)); - for (j = 0; j < 16; ++j) { - t1FontFiles[t1FontFilesSize + j].num = -1; - } - t1FontFilesSize += 16; - } + // check for an embedded font + if (gfxFont->getEmbeddedFontID(&embRef)) { // create the font file - tmpFileName = NULL; - if (!gfxFont->is16Bit() && - (gfxFont->getType() == fontType1 || - gfxFont->getType() == fontType1C) && - gfxFont->getEmbeddedFontID(&embRef)) { - if (!openTempFile(&tmpFileName, &f, "wb", NULL)) { - error(-1, "Couldn't create temporary Type 1 font file"); + 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); return NULL; } - if (gfxFont->getType() == fontType1C) { - if (!(fontBuf = gfxFont->readEmbFontFile(&fontLen))) { - fclose(f); - return NULL; - } - 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); - } - strObj.streamClose(); - strObj.free(); - } - fclose(f); - fileName = tmpFileName; - } else if (!gfxFont->is16Bit() && - gfxFont->getType() == fontType1 && - gfxFont->getExtFontFile()) { - fileName = gfxFont->getExtFontFile(); + ff = new TrueTypeFontFile(fontBuf, fontLen); + ff->writeTTF(f); + delete ff; + gfree(fontBuf); } else { - fileName = pdfBaseFont; + 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 - fontFile = new T1FontFile(t1Engine, fileName->getCString(), - gfxFont->getEncoding()); - if (!fontFile->isOk()) { - error(-1, "Couldn't create t1lib font from '%s'", - fileName->getCString()); - delete fontFile; - return NULL; +#else + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); } - t1FontFiles[i].num = id.num; - t1FontFiles[i].gen = id.gen; - t1FontFiles[i].fontFile = fontFile; + strObj.streamClose(); + strObj.free(); +#endif + fclose(f); + + // create the Font + font = tryGetFTFontFromFile(xref, fileName, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); - // remove the font file - if (tmpFileName) { - unlink(tmpFileName->getCString()); - delete tmpFileName; + // remove the temporary file + unlink(fileName->getCString()); + delete fileName; + + // check for an external font file + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetFTFontFromFile(xref, fileName, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; + } + + return font; +} + +XOutputFont *XOutputFontCache::tryGetFTFontFromFile(XRef *xref, + GString *fileName, + GfxFont *gfxFont, + double m11Orig, + double m12Orig, + double m21Orig, + double m22Orig, + double m11, double m12, + double m21, double m22, + GBool subst) { + Ref *id; + FTFontFile *fontFile; + XOutputFont *font; + + // create the FreeType font file + if (gfxFont->isCIDFont()) { + if (gfxFont->getType() == fontCIDType2) { + fontFile = new FTFontFile(ftEngine, fileName->getCString(), + ((GfxCIDFont *)gfxFont)->getCIDToGID(), + ((GfxCIDFont *)gfxFont)->getCIDToGIDLen()); + } else { // fontCIDType0C + fontFile = new FTFontFile(ftEngine, fileName->getCString()); } + } else { + fontFile = new FTFontFile(ftEngine, fileName->getCString(), + ((Gfx8BitFont *)gfxFont)->getEncoding(), + ((Gfx8BitFont *)gfxFont)->getHasEncoding()); + } + if (!fontFile->isOk()) { + error(-1, "Couldn't create FreeType font from '%s'", + fileName->getCString()); + delete fontFile; + return NULL; } - return fontFile; + // add to list + id = gfxFont->getID(); + ftFontFiles->append(new XOutputFTFontFile(id->num, id->gen, + subst, fontFile)); + + // 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 +#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) -#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H -TTFontFile *XOutputFontCache::getTTFont(GfxFont *gfxFont) { - Ref id; - TTFontFile *fontFile; +#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; - Ref embRef; Object refObj, strObj; int c; - int i, j; + int i; + // check the already available font files id = gfxFont->getID(); - - // check available fonts - fontFile = NULL; - for (i = 0; i < ttFontFilesSize && ttFontFiles[i].num >= 0; ++i) { - if (ttFontFiles[i].num == id.num && ttFontFiles[i].gen == id.gen) { - fontFile = ttFontFiles[i].fontFile; + 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; } } - // create a new font file - if (!fontFile) { - - // resize ttFontFiles if necessary - if (i == ttFontFilesSize) { - ttFontFiles = (XOutputTTFontFile *) - grealloc(ttFontFiles, - (ttFontFilesSize + 16) * sizeof(XOutputTTFontFile)); - for (j = 0; j < 16; ++j) { - ttFontFiles[ttFontFilesSize + j].num = -1; - } - ttFontFilesSize += 16; - } + // 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; } - gfxFont->getEmbeddedFontID(&embRef); refObj.initRef(embRef.num, embRef.gen); - refObj.fetch(&strObj); + refObj.fetch(xref, &strObj); refObj.free(); strObj.streamReset(); while ((c = strObj.streamGetChar()) != EOF) { @@ -1459,33 +1362,226 @@ TTFontFile *XOutputFontCache::getTTFont(GfxFont *gfxFont) { strObj.free(); fclose(f); - // create the FreeType font file - fontFile = new TTFontFile(ttEngine, fileName->getCString()); - if (!fontFile->isOk()) { - error(-1, "Couldn't create FreeType font from '%s'", - fileName->getCString()); - delete fontFile; - return NULL; - } - ttFontFiles[i].num = id.num; - ttFontFiles[i].gen = id.gen; - ttFontFiles[i].fontFile = fontFile; + // create the Font + font = tryGetTTFontFromFile(xref, fileName, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); - // remove the font file + // remove the temporary file unlink(fileName->getCString()); delete fileName; + + } else if ((fileName = gfxFont->getExtFontFile())) { + font = tryGetTTFontFromFile(xref, fileName, gfxFont, + m11, m12, m21, m22, + m11, m12, m21, m22, gFalse); + + } else { + font = NULL; } - return fontFile; + return font; +} + +XOutputFont *XOutputFontCache::tryGetTTFontFromFile(XRef *xref, + GString *fileName, + 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; + return NULL; + } + + // add to list + id = gfxFont->getID(); + ttFontFiles->append(new XOutputTTFontFile(id->num, id->gen, + subst, fontFile)); + + // 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; } -#endif + +//------------------------------------------------------------------------ +// T3FontCache +//------------------------------------------------------------------------ + +struct T3FontCacheTag { + Gushort code; + Gushort mru; // valid bit (0x8000) and MRU index + double wx, wy; // untransformed glyph metrics +}; + +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; + + 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); +} + +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 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, Pixmap pixmapA, Guint depthA, + Colormap colormapA, GBool reverseVideoA, + unsigned long paperColor, GBool installCmap, + int rgbCubeSize) { XVisualInfo visualTempl; XVisualInfo *visualList; int nVisuals; @@ -1496,17 +1592,19 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, int r, g, b, n, m, i; GBool ok; + xref = NULL; + // get display/pixmap info - this->display = display; + display = displayA; screenNum = DefaultScreen(display); - this->pixmap = pixmap; - this->depth = depth; - this->colormap = colormap; + pixmap = pixmapA; + depth = depthA; + colormap = colormapA; // check for TrueColor visual trueColor = gFalse; if (depth == 0) { - this->depth = DefaultDepth(display, screenNum); + depth = DefaultDepth(display, screenNum); visualList = XGetVisualInfo(display, 0, &visualTempl, &nVisuals); for (i = 0; i < nVisuals; ++i) { if (visualList[i].visual == DefaultVisual(display, screenNum)) { @@ -1533,13 +1631,16 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, // 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; @@ -1553,6 +1654,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; } } @@ -1567,8 +1669,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; @@ -1577,21 +1680,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) { @@ -1602,6 +1710,9 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, } } + // reverse video mode + reverseVideo = reverseVideoA; + // allocate GCs gcValues.foreground = BlackPixel(display, screenNum); gcValues.background = WhitePixel(display, screenNum); @@ -1621,52 +1732,48 @@ XOutputDev::XOutputDev(Display *display, Pixmap pixmap, Guint depth, // 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; - // set up the font cache and fonts gfxFont = NULL; font = NULL; - fontCache = new XOutputFontCache(display, this->depth); + fontCache = new XOutputFontCache(display, depth, this, + globalParams->getT1libControl(), + globalParams->getFreeTypeControl()); + nT3Fonts = 0; + t3GlyphStack = NULL; // empty state stack save = NULL; // create text object - text = new TextPage(useEUCJP ? textOutASCII7 : textOutLatin1, gFalse); - - type3Warning = gFalse; + text = new TextPage(gFalse); } XOutputDev::~XOutputDev() { - gfree(userFontMap); + int i; + delete fontCache; + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } XFreeGC(display, strokeGC); XFreeGC(display, fillGC); XFreeGC(display, paperGC); - if (clipRegion) + if (clipRegion) { XDestroyRegion(clipRegion); + } delete text; } -void XOutputDev::startDoc() { +void XOutputDev::startDoc(XRef *xrefA) { + int i; + + xref = xrefA; fontCache->startDoc(screenNum, 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) { @@ -1896,6 +2003,11 @@ void XOutputDev::updateFillColor(GfxState *state) { 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)); } @@ -1903,6 +2015,11 @@ void XOutputDev::updateStrokeColor(GfxState *state) { 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)); } @@ -1913,20 +2030,20 @@ void XOutputDev::updateFont(GfxState *state) { 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); } - // look for Type 3 font - if (!type3Warning && gfxFont->getType() == fontType3) { - error(-1, "This document uses Type 3 fonts - some text may not be correctly displayed"); - type3Warning = gTrue; - } + text->updateFont(state); } void XOutputDev::stroke(GfxState *state) { @@ -2017,10 +2134,21 @@ void XOutputDev::doClip(GfxState *state, int rule) { n = convertPath(state, &points, &size, &numPoints, &lengths, gTrue); // construct union of subpath regions - region = XPolygonRegion(points, lengths[0], rule); + // (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) { - region2 = XPolygonRegion(points + j, lengths[i], rule); + if (lengths[i] > 2) { + region2 = XPolygonRegion(points + j, lengths[i], rule); + } else { + region2 = XCreateRegion(); + } XUnionRegion(region2, region, region); XDestroyRegion(region2); j += lengths[i] + 1; @@ -2103,7 +2231,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); } @@ -2115,6 +2243,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 < -pixmapW) { + (*points)[i].x = -pixmapW; + } else if ((*points)[i].x > 2 * pixmapW) { + (*points)[i].x = 2 * pixmapW; + } + if ((*points)[i].y < -pixmapH) { + (*points)[i].y = -pixmapH; + } else if ((*points)[i].y > 2 * pixmapH) { + (*points)[i].y = 2 * pixmapH; + } + } + // combine compound polygons if (fillHack) { i = j = k = 0; @@ -2133,13 +2276,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 @@ -2308,7 +2450,7 @@ 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->beginString(state); } void XOutputDev::endString(GfxState *state) { @@ -2316,418 +2458,428 @@ void XOutputDev::endString(GfxState *state) { } 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]; - text->addChar(state, x, y, dx, dy, c); + text->addChar(state, x, y, dx, dy, 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) == 3) { return; + } + x -= originX; + y -= originY; state->transform(x, y, &x1, &y1); + state->transformDelta(dx, dy, &dx1, &dy1); - font->drawChar(state, pixmap, pixmapW, pixmapH, - (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 | CHINESE_GB_SUPPORT | CHINESE_CNS_SUPPORT - int t1, t2; -#endif -#if JAPANESE_SUPPORT - double x2; - char *p; - int n, i; -#endif - - if (gfxFont) { - text->addChar16(state, x, y, dx, dy, c, gfxFont->getCharSet16()); + // 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); + } + + // 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); + } } - //~ assumes font is an XOutputServerFont - - if (!font) - return; +#if 0 //~ unimplemented: clipping to char path + // clip + if (render & 4) { + } +#endif +} - // 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 (!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; - } else { - c1 = 0; + t3FontCache[0] = t3Font; + break; } - } else { - c1 = 0; - } -#if 0 //~ - if (c1 == 0) - error(-1, "Unsupported Adobe-Japan1-2 character: %d", c); -#endif -#endif // JAPANESE_SUPPORT - break; - - // convert Adobe-GB1-2 to GB 2312-80 - case font16AdobeGB12: -#if CHINESE_GB_SUPPORT - if (c <= 939) { - c1 = gb12Map[c]; - } else if (c <= 4605) { - t1 = (c - 940) / 94; - t2 = (c - 940) % 94; - c1 = 0x3021 + (t1 << 8) + t2; - } else if (c <= 4694) { - c1 = c - 4606 + 0x5721; - } else if (c <= 7702) { - t1 = (c - 4695) / 94; - t2 = (c - 4695) % 94; - c1 = 0x5821 + (t1 << 8) + t2; - } else if (c == 7716) { - c1 = 0x2121; - } -#if 1 //~ - if (c1 == 0) { - error(-1, "Unsupported Adobe-GB1-2 character: %d", c); } -#endif -#endif // CHINESE_GB_SUPPORT - break; + if (i >= nT3Fonts) { - // convert Adobe-CNS1-3 to Big5 - case font16AdobeCNS13: -#if CHINESE_CNS_SUPPORT - if (c <= 98) { - c1 = cns13Map1[c]; - } else if (c <= 502) { - if (c == 247) { - c1 = 0xa1f7; - } else if (c == 248) { - c1 = 0xa1f6; - } else { - t1 = (c - 99) / 157; - t2 = (c - 99) % 157; - if (t2 <= 62) { - c1 = 0xa140 + (t1 << 8) + t2; - } else { - c1 = 0xa162 + (t1 << 8) + t2; - } + // 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]; } - } else if (c <= 505) { - c1 = 0xa3bd + (c - 503); - } else if (c <= 594) { - c1 = 0; - } else if (c <= 5995) { - if (c == 2431) { - c1 = 0xacfe; - } else if (c == 4308) { - c1 = 0xbe52; - } else if (c == 5221) { - c1 = 0xc2cb; - } else if (c == 5495) { - c1 = 0xc456; - } else if (c == 5550) { - c1 = 0xc3ba; - } else if (c == 5551) { - c1 = 0xc3b9; + ++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 { - if (c >= 2007 && c <= 2430) { - t1 = c - 594; - } else if (c >= 4309 && c <= 4695) { - t1 = c - 596; - } else if (c >= 5222 && c <= 5410) { - t1 = c - 596; - } else if (c >= 5496 && c <= 5641) { - t1 = c - 596; - } else { - t1 = c - 595; + 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; } - t2 = t1 % 157; - t1 /= 157; - if (t2 <= 62) { - c1 = 0xa440 + (t1 << 8) + t2; - } else { - c1 = 0xa462 + (t1 << 8) + t2; + 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 if (c <= 13645) { - if (c == 6039) { - c1 = 0xc9be; - } else if (c == 6134) { - c1 = 0xcaf7; - } else if (c == 8142) { - c1 = 0xdadf; - } else if (c == 8788) { - c1 = 0xd6cc; - } else if (c == 8889) { - c1 = 0xd77a; - } else if (c == 10926) { - c1 = 0xebf1; - } else if (c == 11073) { - c1 = 0xecde; - } else if (c == 11361) { - c1 = 0xf0cb; - } else if (c == 11719) { - c1 = 0xf056; - } else if (c == 12308) { - c1 = 0xeeeb; - } else if (c == 12526) { - c1 = 0xf4b5; - } else if (c == 12640) { - c1 = 0xf16b; - } else if (c == 12783) { - c1 = 0xf268; - } else if (c == 12900) { - c1 = 0xf663; - } else if (c == 13585) { - c1 = 0xf9c4; - } else if (c == 13641) { - c1 = 0xf9c6; - } else { - if (c >= 6006 && c <= 6038) { - t1 = c - 5995; - } else if (c >= 6088 && c <= 6133) { - t1 = c - 5995; - } else if (c >= 6302 && c <= 8250) { - t1 = c - 5995; - } else if (c >= 8251 && c <= 8888) { - t1 = c - 5994; - } else if (c >= 8890 && c <= 9288) { - t1 = c - 5995; - } else if (c >= 9289 && c <= 10925) { - t1 = c - 5994; - } else if (c >= 10927 && c <= 11072) { - t1 = c - 5995; - } else if (c >= 11362 && c <= 11477) { - t1 = c - 5997; - } else if (c >= 11615 && c <= 11718) { - t1 = c - 5995; - } else if (c >= 11942 && c <= 12139) { - t1 = c - 5995; - } else if (c >= 12140 && c <= 12221) { - t1 = c - 5994; - } else if (c >= 12222 && c <= 12307) { - t1 = c - 5993; - } else if (c >= 12309 && c <= 12316) { - t1 = c - 5994; - } else if (c >= 12317 && c <= 12469) { - t1 = c - 5993; - } else if (c >= 12470 && c <= 12525) { - t1 = c - 5992; - } else if (c >= 12527 && c <= 12639) { - t1 = c - 5993; - } else if (c >= 12641 && c <= 12782) { - t1 = c - 5994; - } else if (c >= 12784 && c <= 12828) { - t1 = c - 5995; - } else if (c >= 12829 && c <= 12899) { - t1 = c - 5994; - } else if (c >= 12901 && c <= 13094) { - t1 = c - 5995; - } else if (c >= 13095 && c <= 13584) { - t1 = c - 5994; - } else if (c >= 13586 && c <= 13628) { - t1 = c - 5995; - } else if (c == 13629) { - t1 = c - 5994; - } else if (c >= 13630 && c <= 13640) { - t1 = c - 5993; - } else if (c >= 13642 && c <= 13645) { - t1 = c - 5994; + 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, + DefaultVisual(display, screenNum), + depth, pixmap); + } + } + 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, + 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::endType3Char(GfxState *state) { + XImage *image; + Guchar *p; + int x, y; + Gulong pixel; + double alpha; + T3GlyphStack *t3gs; + + 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 { - t1 = c - 5996; + alpha = redMap[pixel & 0xff]; } - t2 = t1 % 157; - t1 /= 157; - if (t2 <= 62) { - c1 = 0xc940 + (t1 << 8) + t2; + 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 { - c1 = 0xc962 + (t1 << 8) + t2; + *p++ = 0; } } - } else if (c == 13646) { - c1 = 0xa14b; - } else if (c == 13647) { - c1 = 0xa1e3; - } else if (c <= 13742) { - c1 = cns13Map2[c - 13648]; - } else if (c <= 13746) { - c1 = 0xa159 + (c - 13743); - } else if (c <= 14055) { - c1 = 0; - } else if (c <= 14062) { - c1 = 0xf9d6 + (c - 14056); - } -#if 1 //~ - if (c1 == 0) { - error(-1, "Unsupported Adobe-CNS1-3 character: %d", c); } -#endif -#endif - break; + 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); + } + text->addChar(state, 0, 0, t3GlyphStack->wx, t3GlyphStack->wy, + t3GlyphStack->u, t3GlyphStack->uLen); + t3gs = t3GlyphStack; + t3GlyphStack = t3gs->next; + delete t3gs; +} + +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; + + // 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) { + x1 = -x0; + w0 += x0; + x0 = 0; + } + if (x0 + w0 > pixmapW) { + w0 = pixmapW - x0; + } + if (w0 <= 0) { + return; + } + if (y0 < 0) { + y1 = -y0; + h0 += y0; + y0 = 0; + } + if (y0 + h0 > pixmapH) { + h0 = pixmapH - y0; + } + if (h0 <= 0) { + return; } - 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); + 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); +} + +void XOutputDev::type3D0(GfxState *state, double wx, double wy) { + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; +} + +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; + int i, j; + + // allocate a cache entry + t3GlyphStack->cacheable = gTrue; + t3Font = t3GlyphStack->cache; + 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; + } + } + t3GlyphStack->wx = wx; + t3GlyphStack->wy = wy; + 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(); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + -t3GlyphStack->cache->glyphX, -t3GlyphStack->cache->glyphY); } inline Gulong XOutputDev::findColor(GfxRGB *x, GfxRGB *err) { @@ -2792,7 +2944,7 @@ Gulong XOutputDev::findColor(GfxRGB *rgb) { 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 +#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) @@ -2813,20 +2965,16 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, GBool inlineImg) { ImageStream *imgStr; XImage *image; - double xt, yt; + 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 hx, hy; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; int bx0, by0, bx1, by1, bw, bh; int cx0, cy0, cx1, cy1, cw, ch; - int dx, dy; - int dvx, dvdx, dvpx, dvqx, dvdx2, dvtx; - int dvy, dvdy, dvpy, dvqy, dvdy2, dvty; - int dhx, dhdx, dhpx, dhqx, dhdx2, dhtx, dhtx0; - int dhy, dhdy, dhpy, dhqy, dhdy2, dhty, dhty0; - int ivy, ivdy, ivpy, ivqy, ivty; - int ihx, ihdx, ihpx, ihqx, ihtx; - int vn, vi, hn, hi; - int bufy; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; GfxRGB rgb; Guchar *pixBuf; int imgPix; @@ -2837,80 +2985,97 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, double r0, g0, b0, r1, g1, b1; Gulong pix; Guchar *p; + int x, y, x1, y1, x2, y2; int n, m, i, j; - // corners in device space - state->transform(0, 0, &xt, &yt); - llx = xoutRound(xt); - lly = xoutRound(yt); - state->transform(0, 1, &xt, &yt); - ulx = xoutRound(xt); - uly = xoutRound(yt); - state->transform(1, 0, &xt, &yt); - lrx = xoutRound(xt); - lry = xoutRound(yt); - state->transform(1, 1, &xt, &yt); - urx = xoutRound(xt); - ury = xoutRound(yt); - - // horizontal traversal - hx = urx - ulx; - if (abs(lrx - llx) < abs(hx)) { - hx = lrx - llx; - } - hy = ury - uly; - if (abs(lry - lly) < abs(hy)) { - hy = lry - lly; + // 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; + } + + // 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]); + // 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 = bx1 = ulx; - if (llx < bx0) { - bx0 = llx; - } else if (llx > bx1) { - bx1 = llx; - } - if (urx < bx0) { - bx0 = urx; - } else if (urx > bx1) { - bx1 = urx; - } - if (lrx < bx0) { - bx0 = lrx; - } else if (lrx > bx1) { - bx1 = lrx; - } - by0 = by1 = uly; - if (lly < by0) { - by0 = lly; - } else if (lly > by1) { - by1 = lly; - } - if (ury < by0) { - by0 = ury; - } else if (ury > by1) { - by1 = ury; - } - if (lry < by0) { - by0 = lry; - } else if (lry > by1) { - by1 = lry; - } + 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 page pixmap - // (cx1, cy1) = upper-left corner of valid rectangle in image pixmap + // 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 - if (bx1 >= pixmapW) { - cw = pixmapW - bx0; - } else { - cw = bw; - } + // 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; @@ -2919,11 +3084,7 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, cx0 = bx0; cx1 = 0; } - if (by1 >= pixmapH) { - ch = pixmapH - by0; - } else { - ch = bh; - } + ch = (by1 >= pixmapH) ? pixmapH - by0 : bh; if (by0 < 0) { cy0 = 0; cy1 = -by0; @@ -2935,70 +3096,26 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, // check for tiny (zero width or height) images // and off-page images - if (cw <= 0 || ch <= 0) { + 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; } - // Bresenham parameters for vertical traversal - // (device coordinates and image coordinates) - dx = llx - ulx; - dy = lly - uly; - if (abs(dx) > abs(dy)) { - vn = abs(dx); - dvdx = dx > 0 ? 1 : -1; - dvpx = 0; - dvdx2 = 0; - dvdy = 0; - dvpy = abs(dy); - dvdy2 = dy > 0 ? 1 : -1; - } else { - vn = abs(dy); - dvdx = 0; - dvpx = abs(dx); - dvdx2 = dx > 0 ? 1 : -1; - dvdy = dy > 0 ? 1 : -1; - dvpy = 0; - dvdy2 = 0; - } - dvqx = dvqy = vn; - ivqy = vn + 1; - ivdy = height / ivqy; - ivpy = height % ivqy; - - // Bresenham parameters for horizontal traversal - // (device coordinates and image coordinates) - if (abs(hx) > abs(hy)) { - hn = abs(hx); - dhdx = hx > 0 ? 1 : -1; - dhpx = 0; - dhdx2 = 0; - dhdy = 0; - dhpy = abs(hy); - dhdy2 = hy > 0 ? 1 : -1; - } else { - hn = abs(hy); - dhdx = 0; - dhpx = abs(hx); - dhdx2 = hx > 0 ? 1 : -1; - dhdy = hy > 0 ? 1 : -1; - dhpy = 0; - dhdy2 = 0; - } - dhqx = dhqy = hn; - ihqx = hn + 1; - ihdx = width / ihqx; - ihpx = width % ihqx; + // compute Bresenham parameters for x and y scaling + yp = height / scaledHeight; + yq = height % scaledHeight; + xp = width / scaledWidth; + xq = width % scaledWidth; // allocate pixel buffer - n = ivdy + (ivpy > 0 ? 1 : 0); - pixBuf = (Guchar *)gmalloc(n * width * sizeof(Guchar)); + pixBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); // allocate XImage and read from page pixmap image = XCreateImage(display, DefaultVisual(display, screenNum), @@ -3009,6 +3126,11 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, // 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; @@ -3026,26 +3148,23 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, imgStr = new ImageStream(str, width, 1, 1); imgStr->reset(); - // traverse left edge of image - dvx = ulx; - dvtx = 0; - dvy = uly; - dvty = 0; - ivy = 0; - ivty = 0; - dhtx0 = 0; - dhty0 = 0; - n = 0; - bufy = -1; - for (vi = 0; vi <= vn; ++vi) { + // 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 - if (ivy > bufy) { - if (ivdy == 0) { - n = 1; - } else { - n = ivdy + (ivty + ivpy >= ivqy ? 1 : 0); - } + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { p = pixBuf; for (i = 0; i < n; ++i) { for (j = 0; j < width; ++j) { @@ -3056,26 +3175,44 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, ++p; } } - bufy = ivy; } + lastYStep = yStep; - // traverse a horizontal stripe - dhx = 0; - dhy = 0; - dhtx = dhtx0; - dhty = dhty0; - ihx = 0; - ihtx = 0; - for (hi = 0; hi <= hn; ++hi) { + // init x scale Bresenham + xt = 0; + xSrc = 0; - // compute filtered pixel value - imgPix = 0; - if (ihdx == 0) { - m = 1; + 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 { - m = ihdx + (ihtx + ihpx >= ihqx ? 1 : 0); + x2 = x1; + y2 = y1; } - p = pixBuf + ihx * sizeof(Guchar); + + // 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++; @@ -3083,9 +3220,12 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, p += width - m; } + // x scale Bresenham + xSrc += xStep; + // blend image pixel with background alpha = (double)imgPix / (double)(n * m); - xcolor.pixel = XGetPixel(image, dvx + dhx - bx0, dvy + dhy - by0); + xcolor.pixel = XGetPixel(image, tx + x2 - bx0, ty + y2 - by0); if (xcolor.pixel != lastPixel) { XQueryColor(display, colormap, &xcolor); r1 = (double)xcolor.red / 65535.0; @@ -3099,64 +3239,10 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, pix = findColor(&rgb2); // set pixel - XPutPixel(image, dvx + dhx - bx0, dvy + dhy - by0, pix); - - // Bresenham increment (horizontal stripe) - dhx += dhdx; - dhtx += dhpx; - if (dhtx >= dhqx) { - dhx += dhdx2; - dhtx -= dhqx; - } - dhy += dhdy; - dhty += dhpy; - if (dhty >= dhqy) { - dhy += dhdy2; - dhty -= dhqy; - } - ihx += ihdx; - ihtx += ihpx; - if (ihtx >= ihqx) { - ++ihx; - ihtx -= ihqx; - } - } - - // Bresenham increment (left edge) - dvx += dvdx; - dvtx += dvpx; - dhty0 += dvdx * dhdx * dhpy; - if (dvtx >= dvqx) { - dvx += dvdx2; - dvtx -= dvqx; - dhty0 += dvdx2 * dhdx * dhpy; - } - dvy += dvdy; - dvty += dvpy; - dhtx0 += dvdy * dhdy * dhpx; - if (dvty >= dvqy) { - dvy += dvdy2; - dvty -= dvqy; - dhtx0 += dvdy2 * dhdy * dhpx; - } - ivy += ivdy; - ivty += ivpy; - if (ivty >= ivqy) { - ++ivy; - ivty -= ivqy; - } - if (dhtx0 >= dhqy) { - dhtx0 -= dhqx; - } else if (dhtx0 < 0) { - dhtx0 += dhqx; - } - if (dhty0 >= dhqx) { - dhty0 -= dhqy; - } else if (dhty0 < 0) { - dhty0 += dhqy; + 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); @@ -3170,33 +3256,34 @@ void XOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - GBool inlineImg) { + int *maskColors, GBool inlineImg) { ImageStream *imgStr; XImage *image; int nComps, nVals, nBits; GBool dither; - double xt, yt; + 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 hx, hy; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; int bx0, by0, bx1, by1, bw, bh; int cx0, cy0, cx1, cy1, cw, ch; - int dx, dy; - int dvx, dvdx, dvpx, dvqx, dvdx2, dvtx; - int dvy, dvdy, dvpy, dvqy, dvdy2, dvty; - int dhx, dhdx, dhpx, dhqx, dhdx2, dhtx, dhtx0; - int dhy, dhdy, dhpy, dhqy, dhdy2, dhty, dhty0; - int ivy, ivdy, ivpy, ivqy, ivty; - int ihx, ihdx, ihpx, ihqx, ihtx; - int vn, vi, hn, hi; - int bufy; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; GfxRGB *pixBuf; + Guchar *alphaBuf; Guchar pixBuf2[gfxColorMaxComps]; GfxRGB color2, err, errRight; GfxRGB *errDown; - double r0, g0, b0; + double r0, g0, b0, alpha, mul; Gulong pix; GfxRGB *p; - int n, m, i, j; + Guchar *q; + GBool oneBitMode; + GfxRGB oneBitRGB[2]; + int x, y, x1, y1, x2, y2; + int n, m, i, j, k; // image parameters nComps = colorMap->getNumPixelComps(); @@ -3204,66 +3291,84 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, nBits = colorMap->getBits(); dither = nComps > 1 || nBits > 1; - // corners in device space - state->transform(0, 0, &xt, &yt); - llx = xoutRound(xt); - lly = xoutRound(yt); - state->transform(0, 1, &xt, &yt); - ulx = xoutRound(xt); - uly = xoutRound(yt); - state->transform(1, 0, &xt, &yt); - lrx = xoutRound(xt); - lry = xoutRound(yt); - state->transform(1, 1, &xt, &yt); - urx = xoutRound(xt); - ury = xoutRound(yt); - - // horizontal traversal - hx = urx - ulx; - if (abs(lrx - llx) < abs(hx)) { - hx = lrx - llx; - } - hy = ury - uly; - if (abs(lry - lly) < abs(hy)) { - hy = lry - lly; + // 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]); + // 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 = bx1 = ulx; - if (llx < bx0) { - bx0 = llx; - } else if (llx > bx1) { - bx1 = llx; - } - if (urx < bx0) { - bx0 = urx; - } else if (urx > bx1) { - bx1 = urx; - } - if (lrx < bx0) { - bx0 = lrx; - } else if (lrx > bx1) { - bx1 = lrx; - } - by0 = by1 = uly; - if (lly < by0) { - by0 = lly; - } else if (lly > by1) { - by1 = lly; - } - if (ury < by0) { - by0 = ury; - } else if (ury > by1) { - by1 = ury; - } - if (lry < by0) { - by0 = lry; - } else if (lry > by1) { - by1 = lry; - } + 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; @@ -3273,11 +3378,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // (cw, ch) = size of valid rectangle // These values will be used to transfer the XImage from/to the // Pixmap. - if (bx1 >= pixmapW) { - cw = pixmapW - bx0; - } else { - cw = bw; - } + cw = (bx1 >= pixmapW) ? pixmapW - bx0 : bw; if (bx0 < 0) { cx0 = 0; cx1 = -bx0; @@ -3286,11 +3387,7 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, cx0 = bx0; cx1 = 0; } - if (by1 >= pixmapH) { - ch = pixmapH - by0; - } else { - ch = bh; - } + ch = (by1 >= pixmapH) ? pixmapH - by0 : bh; if (by0 < 0) { cy0 = 0; cy1 = -by0; @@ -3302,69 +3399,30 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // check for tiny (zero width or height) images // and off-page images - if (cw <= 0 || ch <= 0) { + 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 for vertical traversal - // (device coordinates and image coordinates) - dx = llx - ulx; - dy = lly - uly; - if (abs(dx) > abs(dy)) { - vn = abs(dx); - dvdx = dx > 0 ? 1 : -1; - dvpx = 0; - dvdx2 = 0; - dvdy = 0; - dvpy = abs(dy); - dvdy2 = dy > 0 ? 1 : -1; - } else { - vn = abs(dy); - dvdx = 0; - dvpx = abs(dx); - dvdx2 = dx > 0 ? 1 : -1; - dvdy = dy > 0 ? 1 : -1; - dvpy = 0; - dvdy2 = 0; - } - dvqx = dvqy = vn; - ivqy = vn + 1; - ivdy = height / ivqy; - ivpy = height % ivqy; - - // Bresenham parameters for horizontal traversal - // (device coordinates and image coordinates) - if (abs(hx) > abs(hy)) { - hn = abs(hx); - dhdx = hx > 0 ? 1 : -1; - dhpx = 0; - dhdx2 = 0; - dhdy = 0; - dhpy = abs(hy); - dhdy2 = hy > 0 ? 1 : -1; - } else { - hn = abs(hy); - dhdx = 0; - dhpx = abs(hx); - dhdx2 = hx > 0 ? 1 : -1; - dhdy = hy > 0 ? 1 : -1; - dhpy = 0; - dhdy2 = 0; - } - dhqx = dhqy = hn; - ihqx = hn + 1; - ihdx = width / ihqx; - ihpx = width % ihqx; + // compute Bresenham parameters for x and y scaling + yp = height / scaledHeight; + yq = height % scaledHeight; + xp = width / scaledWidth; + xq = width % scaledWidth; // allocate pixel buffer - n = ivdy + (ivpy > 0 ? 1 : 0); - pixBuf = (GfxRGB *)gmalloc(n * width * sizeof(GfxRGB)); + pixBuf = (GfxRGB *)gmalloc((yp + 1) * width * sizeof(GfxRGB)); + if (maskColors) { + alphaBuf = (Guchar *)gmalloc((yp + 1) * width * sizeof(Guchar)); + } else { + alphaBuf = NULL; + } // allocate XImage image = XCreateImage(display, DefaultVisual(display, screenNum), @@ -3372,9 +3430,11 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, image->data = (char *)gmalloc(bh * image->bytes_per_line); // if the transform is anything other than a 0/90/180/270 degree - // rotation/flip, read the backgound pixmap to fill in the corners + // 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))) { + (uly == lly && ulx == urx)) || + maskColors) { XGetSubImage(display, pixmap, cx0, cy0, cw, ch, (1 << depth) - 1, ZPixmap, image, cx1, cy1); } @@ -3389,98 +3449,147 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, errDown = NULL; } + // optimize the one-bit-deep image case + if ((oneBitMode = nComps == 1 && nBits == 1)) { + 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(); - // traverse left edge of image - dvx = ulx; - dvtx = 0; - dvy = uly; - dvty = 0; - ivy = 0; - ivty = 0; - dhtx0 = 0; - dhty0 = 0; - n = 0; - bufy = -1; - for (vi = 0; vi <= vn; ++vi) { + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // initialize error diffusion accumulator + errRight.r = errRight.g = errRight.b = 0; + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } // read row(s) from image - if (ivy > bufy) { - if (ivdy == 0) { - n = 1; - } else { - n = ivdy + (ivty + ivpy >= ivqy ? 1 : 0); - } + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { p = pixBuf; + q = alphaBuf; for (i = 0; i < n; ++i) { for (j = 0; j < width; ++j) { imgStr->getPixel(pixBuf2); - colorMap->getRGB(pixBuf2, p); - ++p; + if (oneBitMode) { + *p++ = oneBitRGB[pixBuf2[0]]; + } else { + colorMap->getRGB(pixBuf2, p); + ++p; + } + if (q) { + *q = 1; + for (k = 0; k < nComps; ++k) { + if (pixBuf2[k] < maskColors[2*k] || + pixBuf2[k] > maskColors[2*k]) { + *q = 0; + break; + } + } + ++q; + } } } - bufy = ivy; } + lastYStep = yStep; - // clear error accumulator - errRight.r = errRight.g = errRight.b = 0; + // 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); - // traverse a horizontal stripe - dhx = 0; - dhy = 0; - dhtx = dhtx0; - dhty = dhty0; - ihx = 0; - ihtx = 0; - for (hi = 0; hi <= hn; ++hi) { - - // compute filtered pixel value - if (ihdx == 0) { - m = 1; + // y shear + y1 = ySign * y + xoutRound(yShear * x1); + + // rotation + if (rot) { + x2 = y1; + y2 = -x1; } else { - m = ihdx + (ihtx + ihpx >= ihqx ? 1 : 0); + x2 = x1; + y2 = y1; } - p = pixBuf + ihx * sizeof(Guchar); + + // 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; r0 = g0 = b0 = 0; + q = alphaBuf ? alphaBuf + xSrc : (Guchar *)NULL; + alpha = 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; } - r0 /= n * m; - g0 /= n * m; - b0 /= n * 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 + errDown[dvx + dhx - bx0].r; + color2.r = r0 + errRight.r + errDown[tx + x2 - bx0].r; if (color2.r > 1) { color2.r = 1; } else if (color2.r < 0) { color2.r = 0; } - color2.g = g0 + errRight.g + errDown[dvx + dhx - bx0].g; + color2.g = g0 + errRight.g + errDown[tx + x2 - bx0].g; if (color2.g > 1) { color2.g = 1; } else if (color2.g < 0) { color2.g = 0; } - color2.b = b0 + errRight.b + errDown[dvx + dhx - bx0].b; + color2.b = b0 + errRight.b + errDown[tx + x2 - bx0].b; if (color2.b > 1) { color2.b = 1; } else if (color2.b < 0) { color2.b = 0; } pix = findColor(&color2, &err); - errRight.r = errDown[dvx + dhx - bx0].r = err.r / 2; - errRight.g = errDown[dvx + dhx - bx0].g = err.g / 2; - errRight.b = errDown[dvx + dhx - bx0].b = err.b / 2; + errRight.r = errDown[tx + x2 - bx0].r = err.r / 2; + errRight.g = errDown[tx + x2 - bx0].g = err.g / 2; + errRight.b = errDown[tx + x2 - bx0].b = err.b / 2; } else { color2.r = r0; color2.g = g0; @@ -3489,77 +3598,29 @@ void XOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, } // set pixel - XPutPixel(image, dvx + dhx - bx0, dvy + dhy - by0, pix); - - // Bresenham increment (horizontal stripe) - dhx += dhdx; - dhtx += dhpx; - if (dhtx >= dhqx) { - dhx += dhdx2; - dhtx -= dhqx; - } - dhy += dhdy; - dhty += dhpy; - if (dhty >= dhqy) { - dhy += dhdy2; - dhty -= dhqy; - } - ihx += ihdx; - ihtx += ihpx; - if (ihtx >= ihqx) { - ++ihx; - ihtx -= ihqx; + //~ this should do a blend when 0 < alpha < 1 + if (alpha < 0.75) { + XPutPixel(image, tx + x2 - bx0, ty + y2 - by0, pix); } } - - // Bresenham increment (left edge) - dvx += dvdx; - dvtx += dvpx; - dhty0 += dvdx * dhdx * dhpy; - if (dvtx >= dvqx) { - dvx += dvdx2; - dvtx -= dvqx; - dhty0 += dvdx2 * dhdx * dhpy; - } - dvy += dvdy; - dvty += dvpy; - dhtx0 += dvdy * dhdy * dhpx; - if (dvty >= dvqy) { - dvy += dvdy2; - dvty -= dvqy; - dhtx0 += dvdy2 * dhdy * dhpx; - } - ivy += ivdy; - ivty += ivpy; - if (ivty >= ivqy) { - ++ivy; - ivty -= ivqy; - } - if (dhtx0 >= dhqy) { - dhtx0 -= dhqx; - } else if (dhtx0 < 0) { - dhtx0 += dhqx; - } - if (dhty0 >= dhqx) { - dhty0 -= dhqy; - } else if (dhty0 < 0) { - dhty0 += dhqy; - } } - + // blit the image into the pixmap XPutImage(display, pixmap, fillGC, image, cx1, cy1, cx0, cy0, cw, ch); // free memory delete imgStr; gfree(pixBuf); + if (maskColors) { + gfree(alphaBuf); + } gfree(image->data); image->data = NULL; XDestroyImage(image); gfree(errDown); } -GBool XOutputDev::findText(char *s, GBool top, GBool bottom, +GBool XOutputDev::findText(Unicode *s, int len, GBool top, GBool bottom, int *xMin, int *yMin, int *xMax, int *yMax) { double xMin1, yMin1, xMax1, yMax1; @@ -3567,7 +3628,7 @@ GBool XOutputDev::findText(char *s, GBool top, GBool bottom, yMin1 = (double)*yMin; xMax1 = (double)*xMax; yMax1 = (double)*yMax; - if (text->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) { + if (text->findText(s, len, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) { *xMin = xoutRound(xMin1); *xMax = xoutRound(xMax1); *yMin = xoutRound(yMin1); -- cgit v0.9.1