beta-0.89.2
[luatex.git] / source / texk / kpathsea / tex-glyph.c
blob79b27f606f515e6942251a4b760628c4ab7a18bd
1 /* tex-glyph.c: search for GF/PK files.
3 Copyright 1993, 1994, 1995, 1996, 2008, 2009, 2011 Karl Berry.
4 Copyright 1997, 1998, 1999, 2005 Olaf Weber.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with this library; if not, see <http://www.gnu.org/licenses/>. */
19 #include <kpathsea/config.h>
21 #include <kpathsea/absolute.h>
22 #include <kpathsea/expand.h>
23 #include <kpathsea/fontmap.h>
24 #include <kpathsea/pathsearch.h>
25 #include <kpathsea/tex-glyph.h>
26 #include <kpathsea/tex-make.h>
27 #include <kpathsea/variable.h>
29 /* Routines are in bottom-up order. */
31 /* Support both cmr10.300pk and dpi300/cmr10.pk. (Use the latter
32 instead of dpi300\cmr10.pk since DOS supports /'s, but Unix doesn't
33 support \'s.) */
34 #define UNIX_BITMAP_SPEC "$KPATHSEA_NAME.$KPATHSEA_DPI$KPATHSEA_FORMAT"
35 #define DPI_BITMAP_SPEC "dpi$KPATHSEA_DPI/$KPATHSEA_NAME.$KPATHSEA_FORMAT"
36 #ifdef WIN32
37 #define DOS_BITMAP_SPEC "$KPATHSEA_DPI/$KPATHSEA_NAME.$KPATHSEA_FORMAT"
38 #endif
40 /* Look up font $KPATHSEA_NAME at resolution $KPATHSEA_DPI in PATH,
41 with filename suffix EXTENSION. Return file found or NULL. */
43 static string
44 try_format (kpathsea kpse, kpse_file_format_type format)
46 static const_string bitmap_specs[]
47 = { UNIX_BITMAP_SPEC, DPI_BITMAP_SPEC,
48 #ifdef WIN32
49 DOS_BITMAP_SPEC,
50 #endif
51 NULL };
52 const_string *spec;
53 boolean must_exist;
54 const_string *sfx;
55 string ret = NULL;
56 const_string path = kpse->format_info[format].path;
57 if (!path)
58 path = kpathsea_init_format (kpse, format);
60 /* Set the suffix on the name we'll be searching for. */
61 sfx = kpse->format_info[format].suffix;
62 if (sfx && *sfx)
63 kpathsea_xputenv (kpse, "KPATHSEA_FORMAT", *sfx);
65 /* OK, the limits on this for loop are a little hokey, but it saves
66 having to repeat the body. We want to do it once with `must_exist'
67 false to avoid looking on the disk for cmr10.600pk if
68 dpi600/cmr10.pk is in ls-R. (The time spent in the extra variable
69 expansions and db searches is negligible.) */
70 for (must_exist = false; !ret && must_exist <= true; must_exist++)
72 for (spec = bitmap_specs; !ret && *spec; spec++)
74 string name = kpathsea_var_expand (kpse, *spec);
75 ret = kpathsea_path_search (kpse, path, name, must_exist);
76 if (name != ret)
77 free (name);
81 return ret;
84 /* Look for FONTNAME at resolution DPI in format FORMAT. Search the
85 (entire) PK path first, then the GF path, if we're looking for both.
86 Return any filename found, and (if we succeeded) fill in GLYPH_FILE. */
88 static string
89 try_size (kpathsea kpse, const_string fontname, unsigned dpi,
90 kpse_file_format_type format,
91 kpse_glyph_file_type *glyph_file)
93 kpse_file_format_type format_found;
94 string ret;
95 boolean try_gf = format == kpse_gf_format || format == kpse_any_glyph_format;
96 boolean try_pk = format == kpse_pk_format || format == kpse_any_glyph_format;
98 kpathsea_xputenv_int (kpse, "KPATHSEA_DPI", dpi);
100 /* Look for PK first (since it's more likely to be found), then GF. */
101 ret = try_pk ? try_format (kpse, kpse_pk_format) : NULL;
102 format_found = kpse_pk_format;
104 if (ret == NULL && try_gf)
106 ret = try_format (kpse, kpse_gf_format);
107 format_found = kpse_gf_format;
110 if (ret != NULL && glyph_file)
111 { /* Success. Fill in the return info. */
112 glyph_file->name = fontname;
113 glyph_file->dpi = dpi;
114 glyph_file->format = format_found;
117 return ret;
120 /* Look for FONTNAME at resolution DPI, then at the resolutions within
121 KPSE_BITMAP_TOLERANCE of DPI. */
123 static string
124 try_resolution (kpathsea kpse, const_string fontname, unsigned dpi,
125 kpse_file_format_type format,
126 kpse_glyph_file_type *glyph_file)
128 string ret = try_size (kpse, fontname, dpi, format, glyph_file);
130 if (!ret) {
131 unsigned r;
132 unsigned tolerance = KPSE_BITMAP_TOLERANCE (dpi);
133 /* Cast to unsigned to shut up stupid compilers. */
134 unsigned lower_bound = (int) (dpi - tolerance) < 0
135 ? 0 : (unsigned)(dpi - tolerance);
136 unsigned upper_bound = (unsigned)(dpi + tolerance);
138 /* Prefer scaling up to scaling down, since scaling down can omit
139 character features (Tom did this in dvips). */
140 for (r = lower_bound; !ret && r <= upper_bound; r++)
141 if (r != dpi)
142 ret = try_size (kpse, fontname, r, format, glyph_file);
145 return ret;
148 /* Look up *FONTNAME_PTR in format FORMAT at DPI in the texfonts.map files
149 that we can find, returning the filename found and GLYPH_FILE. Also
150 set *FONTNAME_PTR to the real name corresponding to the alias found
151 or the first alias, if that is not an alias itself. (This allows
152 mktexpk to only deal with real names.) */
154 static string
155 try_fontmap (kpathsea kpse, const_string *fontname_ptr, unsigned dpi,
156 kpse_file_format_type format,
157 kpse_glyph_file_type *glyph_file)
159 const_string *mapped_names;
160 const_string fontname = *fontname_ptr;
161 string ret = NULL;
163 mapped_names = kpathsea_fontmap_lookup (kpse, fontname);
164 if (mapped_names) {
165 const_string mapped_name;
166 const_string first_name = *mapped_names;
167 while (!ret && (mapped_name = *mapped_names++)) {
168 kpathsea_xputenv (kpse, "KPATHSEA_NAME", mapped_name);
169 ret = try_resolution (kpse, mapped_name, dpi, format, glyph_file);
171 if (ret) {
172 /* If some alias succeeeded, return that alias. */
173 *fontname_ptr = xstrdup (mapped_name);
174 /* Return first alias name, unless that itself is an alias,
175 in which case do nothing. */
176 } else if (!kpathsea_fontmap_lookup (kpse, first_name)) {
177 *fontname_ptr = xstrdup (first_name);
181 return ret;
184 /* Look for FONTNAME in `kpse_fallback_resolutions', omitting DPI if we
185 happen across it. Return NULL if nothing found. Pass GLYPH_FILE
186 along as usual. Assume `kpse_fallback_resolutions' is sorted. */
188 static string
189 try_fallback_resolutions (kpathsea kpse,
190 const_string fontname, unsigned dpi,
191 kpse_file_format_type format,
192 kpse_glyph_file_type *glyph_file)
194 unsigned s;
195 int loc, max_loc;
196 int lower_loc, upper_loc;
197 unsigned lower_diff, upper_diff;
198 unsigned closest_diff = UINT_MAX;
199 string ret = NULL; /* In case the only fallback resolution is DPI. */
200 loc = 0; /* -Wall */
201 /* First find the fallback size closest to DPI, even including DPI. */
202 for (s = 0; kpse->fallback_resolutions[s] != 0; s++)
204 unsigned this_diff = abs (kpse->fallback_resolutions[s] - dpi);
205 if (this_diff < closest_diff)
207 closest_diff = this_diff;
208 loc = s;
211 if (s == 0)
212 return ret; /* If nothing in list, quit now. */
214 max_loc = s;
215 lower_loc = loc - 1;
216 upper_loc = loc + 1;
218 for (;;)
220 unsigned fallback = kpse->fallback_resolutions[loc];
221 /* Don't bother to try DPI itself again. */
222 if (fallback != dpi)
224 ret = try_resolution (kpse, fontname, fallback, format, glyph_file);
225 if (ret)
226 break;
229 /* That didn't work. How far away are the locs above or below? */
230 lower_diff = lower_loc > -1
231 ? dpi - kpse->fallback_resolutions[lower_loc] : INT_MAX;
232 upper_diff = upper_loc < max_loc
233 ? kpse->fallback_resolutions[upper_loc] - dpi : INT_MAX;
235 /* But if we're at the end in both directions, quit. */
236 if (lower_diff == INT_MAX && upper_diff == INT_MAX)
237 break;
239 /* Go in whichever direction is closest. */
240 if (lower_diff < upper_diff)
242 loc = lower_loc;
243 lower_loc--;
245 else
247 loc = upper_loc;
248 upper_loc++;
252 return ret;
255 /* See the .h file for description. This is the entry point. */
257 string
258 kpathsea_find_glyph (kpathsea kpse,
259 const_string passed_fontname, unsigned dpi,
260 kpse_file_format_type format,
261 kpse_glyph_file_type *glyph_file)
263 string ret;
264 kpse_glyph_source_type source;
265 const_string fontname = passed_fontname;
267 /* Start the search: try the name we're given. */
268 source = kpse_glyph_source_normal;
269 kpathsea_xputenv (kpse, "KPATHSEA_NAME", fontname);
270 ret = try_resolution (kpse, fontname, dpi, format, glyph_file);
272 /* Try all the various possibilities in order of preference. */
273 if (!ret) {
274 /* Maybe FONTNAME was an alias. */
275 source = kpse_glyph_source_alias;
276 ret = try_fontmap (kpse, &fontname, dpi, format, glyph_file);
278 /* If not an alias, try creating it on the fly with mktexpk,
279 unless FONTNAME is absolute or explicitly relative. */
280 if (!ret && !kpathsea_absolute_p (kpse, fontname, true)) {
281 source = kpse_glyph_source_maketex;
282 /* `try_resolution' leaves the envvar set randomly. */
283 kpathsea_xputenv_int (kpse, "KPATHSEA_DPI", dpi);
284 ret = kpathsea_make_tex (kpse, format, fontname);
287 /* If mktex... succeeded, set return struct. Doesn't make sense for
288 `kpse_make_tex' to set it, since it can only succeed or fail,
289 unlike the other routines. */
290 if (ret && glyph_file) {
291 KPSE_GLYPH_FILE_DPI (*glyph_file) = dpi;
292 KPSE_GLYPH_FILE_NAME (*glyph_file) = fontname;
295 /* If mktex... failed, try any fallback resolutions. */
296 else {
297 if (kpse->fallback_resolutions)
298 ret = try_fallback_resolutions (kpse, fontname, dpi, format, glyph_file);
300 /* We're down to the font of last resort. */
301 if (!ret && kpse->fallback_font) {
302 const_string name = kpse->fallback_font;
303 source = kpse_glyph_source_fallback;
304 kpathsea_xputenv (kpse, "KPATHSEA_NAME", name);
306 /* As before, first try it at the given size. */
307 ret = try_resolution (kpse, name, dpi, format, glyph_file);
309 /* The fallback font at the fallback resolutions. */
310 if (!ret && kpse->fallback_resolutions)
311 ret = try_fallback_resolutions (kpse, name, dpi, format, glyph_file);
316 /* If RET is null, then the caller is not supposed to look at GLYPH_FILE,
317 so it doesn't matter if we assign something incorrect. */
318 if (glyph_file)
319 KPSE_GLYPH_FILE_SOURCE (*glyph_file) = source;
321 /* FIXME: fontname may have been allocated, but (worse) it may also
322 have been assigned to struct that's passed out of this function.
323 if (fontname != passed_fontname)
324 free (fontname);
327 return ret;
330 #if defined (KPSE_COMPAT_API)
331 string
332 kpse_find_glyph (const_string passed_fontname, unsigned dpi,
333 kpse_file_format_type format,
334 kpse_glyph_file_type *glyph_file)
336 return kpathsea_find_glyph (kpse_def, passed_fontname, dpi, format,
337 glyph_file);
339 #endif
342 /* The tolerances change whether we base things on DPI1 or DPI2. */
344 boolean
345 kpathsea_bitmap_tolerance (kpathsea kpse, double dpi1, double dpi2)
347 unsigned tolerance = KPSE_BITMAP_TOLERANCE (dpi2);
348 unsigned lower_bound = (int) (dpi2 - tolerance) < 0 ? 0 : dpi2 - tolerance;
349 unsigned upper_bound = dpi2 + tolerance;
350 (void)kpse; /* currenty not used */
352 return lower_bound <= dpi1 && dpi1 <= upper_bound;
355 #if defined (KPSE_COMPAT_API)
356 boolean
357 kpse_bitmap_tolerance (double dpi1, double dpi2)
359 return kpathsea_bitmap_tolerance (kpse_def, dpi1, dpi2);
361 #endif
364 #ifdef TEST
366 void
367 test_find_glyph (kpathsea kpse, const_string fontname, unsigned dpi)
369 string answer;
370 kpse_glyph_file_type ret;
372 printf ("\nSearch for %s@%u:\n\t", fontname, dpi);
374 answer = kpathsea_find_glyph (kpse, fontname, dpi,
375 kpse_any_glyph_format, &ret);
376 if (answer)
378 string format = ret.format == kpse_pk_format ? "pk" : "gf";
379 if (!ret.name)
380 ret.name = "(nil)";
381 printf ("%s\n\t(%s@%u, %s)\n", answer, ret.name, ret.dpi, format);
383 else
384 puts ("(nil)");
389 main (int argc, char **argv)
391 kpathsea kpse = xcalloc(1,sizeof(kpathsea_instance));
392 kpathsea_set_program_name(kpse, argv[0], NULL);
393 test_find_glyph (kpse, "/usr/local/lib/tex/fonts/cm/cmr10", 300); /* abs. */
394 test_find_glyph (kpse, "cmr10", 300); /* normal */
395 test_find_glyph (kpse, "logo10", 300); /* find gf */
396 test_find_glyph (kpse, "cmr10", 299); /* find 300 */
397 test_find_glyph (kpse, "circle10", 300); /* in fontmap */
398 test_find_glyph (kpse, "none", 300); /* do not find */
399 kpse->kpse_fallback_font = "cmr10";
400 test_find_glyph (kpse, "fallback", 300); /* find fallback font cmr10 */
401 kpathsea_init_fallback_resolutions (kpse, "KPATHSEA_TEST_SIZES");
402 test_find_glyph (kpse, "fallbackdpi", 759); /* find fallback cmr10@300 */
404 kpathsea_xputenv (kpse,"GFFONTS", ".");
405 test_find_glyph (kpse, "cmr10", 300); /* different GFFONTS/TEXFONTS */
407 return 0;
410 #endif /* TEST */
414 Local variables:
415 standalone-compile-command: "gcc -g -I. -I.. -DTEST tex-glyph.c kpathsea.a"
416 End: