dvitomp fix from Akira
[mplib] / src / texk / kpathsea / tex-glyph.c
1 /* tex-glyph.c: search for GF/PK files.
2
3    Copyright 1993, 1994, 1995, 1996, 2008 Karl Berry.
4    Copyright 1997, 1998, 1999, 2005 Olaf Weber.
5
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.
10
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.
15
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/>.  */
18
19 #include <kpathsea/config.h>
20
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>
28
29 /* Routines are in bottom-up order.  */
30 \f
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
37 /* Look up FONTNAME at resolution DPI in PATH, with filename suffix
38    EXTENSION.  Return file found or NULL.  */
39
40 static string
41 try_format P3C(const_string, fontname,  unsigned, dpi,
42                kpse_file_format_type,  format)
43 {
44   static const_string bitmap_specs[]
45     = { UNIX_BITMAP_SPEC, DPI_BITMAP_SPEC, NULL };
46   const_string *spec;
47   boolean must_exist;
48   string ret = NULL;
49   const_string path = kpse_format_info[format].path;
50   const_string *sfx;
51   if (!path)
52     path = kpse_init_format (format);
53   
54   /* Set the suffix on the name we'll be searching for.  */
55   sfx = kpse_format_info[format].suffix;
56   if (sfx && *sfx) 
57     xputenv ("KPATHSEA_FORMAT", *sfx);
58
59   /* OK, the limits on this for loop are a little hokey, but it saves
60      having to repeat the body.  We want to do it once with `must_exist'
61      false to avoid looking on the disk for cmr10.600pk if
62      dpi600/cmr10.pk is in ls-R.  (The time spent in the extra variable
63      expansions and db searches is negligible.)  */
64   for (must_exist = false; !ret && must_exist <= true; must_exist++)
65     {
66       for (spec = bitmap_specs; !ret && *spec; spec++)
67         {
68           string name = kpse_var_expand (*spec);
69           ret = kpse_path_search (path, name, must_exist);
70           if (name != ret)
71             free (name);
72         }
73     }
74     
75   return ret;
76 }
77 \f
78 /* Look for FONTNAME at resolution DPI in format FORMAT.  Search the
79    (entire) PK path first, then the GF path, if we're looking for both.
80    Return any filename found, and (if we succeeded) fill in GLYPH_FILE.  */
81
82 static string
83 try_size P4C(const_string, fontname,  unsigned, dpi,
84              kpse_file_format_type, format,
85              kpse_glyph_file_type *, glyph_file)
86 {
87   kpse_file_format_type format_found;
88   string ret;
89   boolean try_gf = format == kpse_gf_format || format == kpse_any_glyph_format;
90   boolean try_pk = format == kpse_pk_format || format == kpse_any_glyph_format;
91
92   xputenv_int ("KPATHSEA_DPI", dpi);
93   
94   /* Look for PK first (since it's more likely to be found), then GF.  */
95   ret = try_pk ? try_format (fontname, dpi, kpse_pk_format) : NULL;
96
97   if (ret != NULL)
98     format_found = kpse_pk_format;
99   else
100     {
101       if (try_gf)
102         {
103           ret = try_format (fontname, dpi, kpse_gf_format);
104           format_found = kpse_gf_format;
105         }
106     }
107   
108   if (ret != NULL && glyph_file)
109     { /* Success.  Fill in the return info.  Discard const.  */
110       glyph_file->name = (string) fontname;
111       glyph_file->dpi = dpi;
112       glyph_file->format = format_found;
113     }
114     
115   return ret;
116 }
117 \f
118 /* Look for FONTNAME at resolution DPI, then at the resolutions within
119    KPSE_BITMAP_TOLERANCE of DPI.  */
120
121 static string
122 try_resolution P4C(const_string, fontname,  unsigned, dpi,
123                    kpse_file_format_type, format,
124                    kpse_glyph_file_type *, glyph_file)
125 {
126   string ret = try_size (fontname, dpi, format, glyph_file);
127   
128   if (!ret)
129     {
130       unsigned r;
131       unsigned tolerance = KPSE_BITMAP_TOLERANCE (dpi);
132       /* Cast to unsigned to shut up stupid compilers. */
133       unsigned lower_bound = (int) (dpi - tolerance) < 0 ? 0 : (unsigned)(dpi - tolerance);
134       unsigned upper_bound = (unsigned)(dpi + tolerance);
135       
136       /* Prefer scaling up to scaling down, since scaling down can omit
137          character features (Tom did this in dvips).  */
138       for (r = lower_bound; !ret && r <= upper_bound; r++)
139         if (r != dpi)
140           ret = try_size (fontname, r, format, glyph_file);
141     }
142   
143   return ret;
144 }
145 \f
146 /* Look up *FONTNAME_PTR in format FORMAT at DPI in the texfonts.map files
147    that we can find, returning the filename found and GLYPH_FILE.  Also
148    set *FONTNAME_PTR to the real name corresponding to the alias found
149    or the first alias, if that is not an alias itself.  (This allows
150    mktexpk to only deal with real names.)  */
151
152 static string
153 try_fontmap P4C(string *, fontname_ptr,  unsigned, dpi,
154                 kpse_file_format_type, format,
155                 kpse_glyph_file_type *, glyph_file)
156 {
157   string *mapped_names;
158   string fontname = *fontname_ptr;
159   string ret = NULL;
160
161   mapped_names = kpse_fontmap_lookup (fontname);
162   if (mapped_names) {
163     string mapped_name;
164     string first_name = *mapped_names;
165     while (!ret && (mapped_name = *mapped_names++)) {
166       xputenv ("KPATHSEA_NAME", mapped_name);
167       ret = try_resolution (mapped_name, dpi, format, glyph_file);
168     }
169     if (ret) {
170       /* If some alias succeeeded, return that alias.  */
171       *fontname_ptr = xstrdup (mapped_name);
172     /* Return first alias name, unless that itself is an alias,
173        in which case do nothing.  */
174     } else if (!kpse_fontmap_lookup (first_name)) {
175       *fontname_ptr = xstrdup (first_name);
176     }
177   } 
178
179   return ret;
180 }
181 \f
182 /* Look for FONTNAME in `kpse_fallback_resolutions', omitting DPI if we
183    happen across it.  Return NULL if nothing found.  Pass GLYPH_FILE
184    along as usual.  Assume `kpse_fallback_resolutions' is sorted.  */
185
186 static string
187 try_fallback_resolutions P4C(const_string, fontname,  unsigned, dpi,
188                              kpse_file_format_type, format,
189                              kpse_glyph_file_type *, glyph_file)
190 {
191   unsigned s;
192   int loc, max_loc;
193   int lower_loc, upper_loc;
194   unsigned lower_diff, upper_diff;
195   unsigned closest_diff = UINT_MAX;
196   string ret = NULL; /* In case the only fallback resolution is DPI.  */
197
198   /* First find the fallback size closest to DPI, even including DPI.  */
199   for (s = 0; kpse_fallback_resolutions[s] != 0; s++)
200     {
201       unsigned this_diff = abs (kpse_fallback_resolutions[s] - dpi);
202       if (this_diff < closest_diff)
203         {
204           closest_diff = this_diff;
205           loc = s;
206         }
207     }
208   if (s == 0)
209     return ret; /* If nothing in list, quit now.  */
210   
211   max_loc = s;
212   lower_loc = loc - 1;
213   upper_loc = loc + 1;
214   
215   for (;;)
216     {
217       unsigned fallback = kpse_fallback_resolutions[loc];
218       /* Don't bother to try DPI itself again.  */
219       if (fallback != dpi)
220         {
221           ret = try_resolution (fontname, fallback, format, glyph_file);
222           if (ret)
223             break;
224         }
225       
226       /* That didn't work. How far away are the locs above or below?  */
227       lower_diff = lower_loc > -1
228                    ? dpi - kpse_fallback_resolutions[lower_loc] : INT_MAX;
229       upper_diff = upper_loc < max_loc
230                    ? kpse_fallback_resolutions[upper_loc] - dpi : INT_MAX;
231       
232       /* But if we're at the end in both directions, quit.  */
233       if (lower_diff == INT_MAX && upper_diff == INT_MAX)
234         break;
235       
236       /* Go in whichever direction is closest.  */
237       if (lower_diff < upper_diff)
238         {
239           loc = lower_loc;
240           lower_loc--;
241         }
242       else
243         {
244           loc = upper_loc;
245           upper_loc++;
246         }
247     }
248
249   return ret;
250 }
251 \f
252 /* See the .h file for description.  This is the entry point.  */
253
254 string
255 kpse_find_glyph P4C(const_string, passed_fontname,  unsigned, dpi,
256                     kpse_file_format_type, format,
257                     kpse_glyph_file_type *, glyph_file)
258 {
259   string ret;
260   kpse_glyph_source_type source;
261   string fontname = (string) passed_fontname; /* discard const */
262   
263   /* Start the search: try the name we're given.  */
264   source = kpse_glyph_source_normal;
265   xputenv ("KPATHSEA_NAME", fontname);
266   ret = try_resolution (fontname, dpi, format, glyph_file);
267   
268   /* Try all the various possibilities in order of preference.  */
269   if (!ret) {
270     /* Maybe FONTNAME was an alias.  */
271     source = kpse_glyph_source_alias;
272     ret = try_fontmap (&fontname, dpi, format, glyph_file);
273
274     /* If not an alias, try creating it on the fly with mktexpk,
275        unless FONTNAME is absolute or explicitly relative.  */
276     if (!ret && !kpse_absolute_p (fontname, true)) {
277       source = kpse_glyph_source_maketex;
278       /* `try_resolution' leaves the envvar set randomly.  */
279       xputenv_int ("KPATHSEA_DPI", dpi);
280       ret = kpse_make_tex (format, fontname);
281     }
282
283     /* If mktex... succeeded, set return struct.  Doesn't make sense for
284        `kpse_make_tex' to set it, since it can only succeed or fail,
285        unlike the other routines.  */
286     if (ret && glyph_file) {
287       KPSE_GLYPH_FILE_DPI (*glyph_file) = dpi;
288       KPSE_GLYPH_FILE_NAME (*glyph_file) = fontname;
289     }
290
291     /* If mktex... failed, try any fallback resolutions.  */
292     else {
293       if (kpse_fallback_resolutions)
294         ret = try_fallback_resolutions (fontname, dpi, format, glyph_file);
295
296       /* We're down to the font of last resort.  */
297       if (!ret && kpse_fallback_font) {
298         const_string name = kpse_fallback_font;
299         source = kpse_glyph_source_fallback;
300         xputenv ("KPATHSEA_NAME", name);
301
302         /* As before, first try it at the given size.  */
303         ret = try_resolution (name, dpi, format, glyph_file);
304
305         /* The fallback font at the fallback resolutions.  */
306         if (!ret && kpse_fallback_resolutions)
307           ret = try_fallback_resolutions (name, dpi, format, glyph_file);
308       }
309     }
310   }
311   
312   /* If RET is null, then the caller is not supposed to look at GLYPH_FILE,
313      so it doesn't matter if we assign something incorrect.  */
314   if (glyph_file)
315     KPSE_GLYPH_FILE_SOURCE (*glyph_file) = source;
316
317   /* FIXME: fontname may have been allocated, but (worse) it may also
318      have been assigned to struct that's passed out of this function.
319   if (fontname != passed_fontname)
320     free (fontname);
321   */
322   
323   return ret;
324 }
325 \f
326 /* The tolerances change whether we base things on DPI1 or DPI2.  */
327
328 boolean
329 kpse_bitmap_tolerance P2C(double, dpi1,  double, dpi2)
330 {
331   unsigned tolerance = KPSE_BITMAP_TOLERANCE (dpi2);
332   unsigned lower_bound = (int) (dpi2 - tolerance) < 0 ? 0 : dpi2 - tolerance;
333   unsigned upper_bound = dpi2 + tolerance;
334
335   return lower_bound <= dpi1 && dpi1 <= upper_bound;
336 }
337 \f
338 #ifdef TEST
339
340 void
341 test_find_glyph (const_string fontname, unsigned dpi)
342 {
343   string answer;
344   kpse_glyph_file_type ret;
345   
346   printf ("\nSearch for %s@%u:\n\t", fontname, dpi);
347
348   answer = kpse_find_glyph_format (fontname, dpi,
349                                    kpse_any_glyph_format, &ret);
350   if (answer)
351     {
352       string format = ret.format == kpse_pk_format ? "pk" : "gf";
353       if (!ret.name)
354         ret.name = "(nil)";
355       printf ("%s\n\t(%s@%u, %s)\n", answer, ret.name, ret.dpi, format);
356     }
357   else
358     puts ("(nil)");
359 }
360
361
362 int
363 main ()
364 {
365   test_find_glyph ("/usr/local/lib/tex/fonts/cm/cmr10", 300); /* absolute */
366   test_find_glyph ("cmr10", 300);     /* normal */
367   test_find_glyph ("logo10", 300);    /* find gf */
368   test_find_glyph ("cmr10", 299);     /* find 300 */
369   test_find_glyph ("circle10", 300);  /* in fontmap */
370   test_find_glyph ("none", 300);      /* do not find */
371   kpse_fallback_font = "cmr10";
372   test_find_glyph ("fallback", 300);  /* find fallback font cmr10 */
373   kpse_init_fallback_resolutions ("KPATHSEA_TEST_SIZES");
374   test_find_glyph ("fallbackdpi", 759); /* find fallback font cmr10@300 */
375   
376   xputenv ("GFFONTS", ".");
377   test_find_glyph ("cmr10", 300);     /* different GFFONTS/TEXFONTS */
378   
379   return 0;
380 }
381
382 #endif /* TEST */
383
384
385 /*
386 Local variables:
387 test-compile-command: "gcc -g -I. -I.. -DTEST tex-glyph.c kpathsea.a"
388 End:
389 */