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