1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 /* This file contains the Plotter-specific _retrieve_font method, which is
20    called by the _pl_g_set_font() function, which in turn is invoked by the
21    API functions alabel() and flabelwidth().  It is called when the
22    font_name, font_size, and textangle fields of the current drawing state
23    have been filled in.  It retrieves the specified font, and fills in the
24    font_type, typeface_index, font_index, font_is_iso8858, true_font_size,
25    and font_ascent, and font_descent fields of the drawing state. */
26 
27 /* This version is for XDrawablePlotters (and XPlotters).  It also fills in
28    the x_font_struct and x_font_pixel_size fields of the drawing state,
29    which are X-specific.
30 
31    This version operates by retrieving an 8-bit core X font from the X
32    display, as follows.  First, it looks at the x_label field of the
33    drawing state, which may contain a "hint": a string passed down from a
34    higher level, containing the characters that will eventually be rendered
35    in the font.  This is for efficiency.  If possible, only a subset of the
36    font will be retrieved (most X displays support this).
37 
38    Nearly all core X fonts have XLFD (X Logical Font Description) names.
39    So we run through various possibilities: first we try to interpret the
40    font_name parameter as the name of a PS font in libplot's hardcoded font
41    database (e.g. "Times-Roman"), in which case the database will supply a
42    corresponding base XLFD name (e.g., "times-medium-r-normal"); then we
43    try to interpret font_name as a base XLFD name which is not in the
44    hardcoded database (e.g. "charter-medium-r-normal"); then finally we try
45    to interpret it as a non-XLFD font name (e.g., "fixed" or "9x15").  In
46    each of these cases, we try to retrieve only a subset of the font (as
47    specified by the x_label field, just mentioned); if that fails, an
48    attempt is made to retrieve the entire font.
49 
50    Text strings, including rotated and affinely transformed text strings,
51    will eventually be rendered in x_text.c, using the XAffText module in
52    x_afftext.c.  So here, all we do is retrieve a reasonable pixel size for
53    the font, which will be scaled, etc., as needed.  (Note that the choice
54    we make for the pixel size does not depend on the `textangle' drawing
55    parameter, which is the rotation angle of the text in the user frame;
56    though the XAffText module will pay attention to that parameter.)  The
57    XAffText module will take care of scaling, so we set the true_font_size
58    parameter to be the same as the user-specified font_size.
59 
60    Note: For speed, we maintain a linked-list cache of previously
61    rasterized-and-retrieved fonts.  The linked list is accessible via the
62    x_font_list member of the XDrawablePlotter (or XPlotter).  An
63    ever-growing linked list is probably good enough if there aren't huge
64    numbers of font or font size changes.  It's deallocated when the Plotter
65    is destroyed; see x_defplot.c. */
66 
67 #include "sys-defines.h"
68 #include "extern.h"
69 #include "g_her_metr.h"
70 
71 /* max length user-level font name we'll accept; this may be either an XLFD
72    base name (a string with exactly three hyphens in it), or something else
73    (an alias, a pre-XLFD font name, etc.). */
74 #define MAX_USER_FONT_NAME_LENGTH 200
75 
76 /* length of buffer for constructing an X font name; must be large enough
77    to handle XFLD template, user-level font name */
78 #define MAX_FONT_NAME_LENGTH 255
79 
80 /* XLFD templates, with holes into which we can punch the base XLFD name (a
81    string with exactly three hyphens in it) and the integer size in terms
82    of pixels.  We need a Latin-1 one and a generic one, since e.g. the
83    Symbol font, which we support, has "adobe-fontspecific" as its last two
84    fields. */
85 static const char * const xlfd_template_latin_1 = "-*-%s-*-%d-*-*-*-*-*-iso8859-1";
86 static const char * const xlfd_template_generic = "-*-%s-*-%d-*-*-*-*-*-*-*";
87 
88 /* length of buffer for constructing an X11R6-style list-of-charset-ranges
89    string, e.g. "[32 48_57 65_90]" would represent the non-contiguous set
90    of characters [ 0-9A-Z]. */
91 #define MAX_CHARSET_SUBSET_LIST_LENGTH 767
92 
93 #define PL_NUM_XLFD_FIELDS 14
94 #define XLFD_FIELD_FOUNDRY 0
95 #define XLFD_FIELD_FAMILY 1
96 #define XLFD_FIELD_WEIGHT 2
97 #define XLFD_FIELD_SLANT 3
98 #define XLFD_FIELD_SET_WIDTH 4
99 #define XLFD_FIELD_ADDITIONAL_STYLE 5
100 #define XLFD_FIELD_PIXELS 6
101 #define XLFD_FIELD_POINTS 7
102 #define XLFD_FIELD_HORIZONTAL_RESOLUTION 8
103 #define XLFD_FIELD_VERTICAL_RESOLUTION 9
104 #define XLFD_FIELD_SPACING 10
105 #define XLFD_FIELD_AVERAGE_WIDTH 11
106 #define XLFD_FIELD_CHARACTER_SET_MAJOR 12
107 #define XLFD_FIELD_CHARACTER_SET_MINOR 13 /* in X11R6 may include char subset */
108 
109 /* forward references */
110 static bool is_a_subset (unsigned char set1[32], unsigned char set2[32]);
111 static char *xlfd_field (const char *name, int field);
112 static double min_sing_val (double m[4]);
113 static void print_bitvector (unsigned char v[32], char *s);
114 static void set_font_dimensions (Display *dpy, plXFontRecord *fptr);
115 static void string_to_bitvector (const unsigned char *s, unsigned char v[8]);
116 static plXFontRecord *select_x_font (Display *dpy, plXFontRecord **x_fontlist_ptr, const char *name, const unsigned char *s, bool subsetting);
117 
118 /* _pl_x_retrieve_font() attempts to retrieve a core X font specified by a
119    triple, namely {name, size, rotation}.  The rotation parameter is
120    ignored, since the XAffText module will be used to rotate or transform
121    any rendered string (see x_text.c, and x_afftext.c for the module).
122    Four possible font retrievals are attempted, in order.
123 
124    1. `name' is taken to be an alias for an XLFD base name, as listed in
125    libplot's hardcoded font database in g_fontdb.c.  (Aliases for the 35
126    standard font names appear there.  E.g., name="times-roman" or
127    "Times-Roman" is an alias for the three-hyphen XLFD base name
128    "times-roman-r-normal".)
129 
130    2. `name' is taken to be an XLFD base name, of the form
131    foundry-family-weight-slant-width, with exactly four hyphens.  E.g.,
132    name="adobe-times-roman-r-normal" or
133    name="bitstream-courier-medium-r-normal".  NOT YET IMPLEMENTED.
134 
135    3. `name' is taken to be an XLFD base name, of the form
136    family-weight-slant-width, with exactly three hyphens.  E.g.,
137    name="grotesk-bold-r-normal", or "times-medium-r-normal".
138 
139    4. `name' is taken to be a full font name, in which case `size' is
140    ignored.  E.g., name="fixed" or name="9x15" or
141    name="-dec-terminal-bold-r-normal--14-140-75-75-c-80-iso8859-1".  This
142    option is mostly to support ancient core X fonts without proper XLFD
143    names, such as "9x15".
144 
145    If a core X font is successfully retrieved (which will set the fields
146    true_font_size, font_ascent, font_descent, and font_is_iso8859_1 of the
147    drawing state, and the X-specific fields x_font_struct,
148    x_font_pixel_size), then this function fills in the font_type field
149    (PL_F_POSTSCRIPT [or PL_F_PCL] in case 1, PL_F_OTHER in cases 2, 3 and
150    4), and returns true.  If a font is not successfully retrieved, this
151    function returns false.
152 
153    The typeface_index and font_index fields are also filled in, in case 1.
154    In the other cases (PL_F_OTHER) it's hardly worth it to fill them in, since
155    switching to other fonts in the middle of a text string, except for a
156    symbol font (`font #0') won't be supported.  See g_cntrlify.c. */
157 
158 bool
_pl_x_retrieve_font(S___ (Plotter * _plotter))159 _pl_x_retrieve_font (S___(Plotter *_plotter))
160 {
161   const char *name, *true_name = ""; /* keep compiler happy */
162   bool matched_builtin = false;	/* font name matches name of a font in db? */
163   bool success;			/* font retrieved from cache or server? */
164   const char *name_p;
165   const char *x_name = NULL, *x_name_alt = NULL; /* from db */
166   const char *x_name_alt2 = NULL, *x_name_alt3 = NULL; /* from db */
167   int typeface_index = 0, font_index = 0; /* from db */
168   int font_type = PL_F_POSTSCRIPT; /* from db */
169   int i, hyphen_count;
170 
171   name = _plotter->drawstate->font_name;
172 
173 #ifdef DEBUG
174   fprintf (stderr, "----------------------------------------------------------------------\n");
175   fprintf (stderr, "_pl_x_retrieve_font(): name=\"%s\", size=%g, x_label=%s\n",
176 		   name, _plotter->drawstate->font_size, _plotter->drawstate->x_label);
177 #endif
178 
179   if (strlen (name) > MAX_USER_FONT_NAME_LENGTH) /* avoid buffer overflow */
180     return false;
181 
182   if (_plotter->drawstate->font_size == 0.0)
183     /* don't try to retrieve zero-size fonts */
184     return false;
185 
186   /* Search null-terminated table of recognized PS fonts, in g_fontdb.c,
187      for a name matching the passed name.  We support either PS-style names
188      (e.g. "Times-Roman") or shortened XLFD-style names
189      (e.g. "times-medium-r-normal").  Alternative versions of latter are
190      supported because some X servers use "zapfdingbats" instead of "itc
191      zapf dingbats", etc. */
192   i = -1;
193   while (_pl_g_ps_font_info[++i].ps_name) /* array ends in NULL */
194     {
195       if ((strcasecmp (_pl_g_ps_font_info[i].ps_name, name) == 0)
196 	  /* check alternative ps font name if any */
197 	  || (_pl_g_ps_font_info[i].ps_name_alt
198 	      && strcasecmp (_pl_g_ps_font_info[i].ps_name_alt, name) == 0)
199 	  /* check 2nd alternative ps font name if any */
200 	  || (_pl_g_ps_font_info[i].ps_name_alt2
201 	      && strcasecmp (_pl_g_ps_font_info[i].ps_name_alt2, name) == 0)
202 	  /* check X font name */
203 	  || (strcasecmp (_pl_g_ps_font_info[i].x_name, name) == 0)
204 	  /* check alternative X font name if any */
205 	  || (_pl_g_ps_font_info[i].x_name_alt
206 	      && strcasecmp (_pl_g_ps_font_info[i].x_name_alt, name) == 0)
207 	  /* check 2nd alternative X font name if any */
208 	  || (_pl_g_ps_font_info[i].x_name_alt2
209 	      && strcasecmp (_pl_g_ps_font_info[i].x_name_alt2, name) == 0)
210 	  /* check 3rd alternative X font name if any */
211 	  || (_pl_g_ps_font_info[i].x_name_alt3
212 	      && strcasecmp (_pl_g_ps_font_info[i].x_name_alt3, name) == 0))
213 	break;
214     }
215 
216   if (_pl_g_ps_font_info[i].ps_name) /* matched name of a PS font in database */
217     {
218       matched_builtin = true;
219       true_name = _pl_g_ps_font_info[i].ps_name;
220       x_name = _pl_g_ps_font_info[i].x_name;
221       x_name_alt = _pl_g_ps_font_info[i].x_name_alt;
222       x_name_alt2 = _pl_g_ps_font_info[i].x_name_alt2;
223       x_name_alt3 = _pl_g_ps_font_info[i].x_name_alt3;
224       font_type = PL_F_POSTSCRIPT;
225       typeface_index = _pl_g_ps_font_info[i].typeface_index;
226       font_index = _pl_g_ps_font_info[i].font_index;
227     }
228 
229 #ifdef USE_LJ_FONTS_IN_X
230   if (matched_builtin == false)	/* PS match failed, so try PCL fonts too */
231     {
232       i = -1;
233       while (_pl_g_pcl_font_info[++i].ps_name) /* array ends in NULL */
234 	{
235 	  if ((strcasecmp (_pl_g_pcl_font_info[i].ps_name, name) == 0)
236 	      /* check alternative ps font name if any */
237 	      || (_pl_g_pcl_font_info[i].ps_name_alt
238 		  && strcasecmp (_pl_g_pcl_font_info[i].ps_name_alt, name) == 0)
239 	      /* check X font name */
240 	      || (strcasecmp (_pl_g_pcl_font_info[i].x_name, name) == 0))
241 	    break;
242 	}
243 
244       if (_pl_g_pcl_font_info[i].ps_name) /* matched name of a PCL font in db */
245 	{
246 	  matched_builtin = true;
247 	  true_name = _pl_g_pcl_font_info[i].ps_name;
248 	  x_name = _pl_g_pcl_font_info[i].x_name;
249 	  x_name_alt = NULL;
250 	  font_type = PL_F_PCL;
251 	  typeface_index = _pl_g_pcl_font_info[i].typeface_index;
252 	  font_index = _pl_g_pcl_font_info[i].font_index;
253 	}
254     }
255 #endif /* USE_LJ_FONTS_IN_X */
256 
257 #ifdef DEBUG
258   fprintf (stderr, "Matched database font %s = %s = %s = %s\n",
259 	   x_name, x_name_alt, x_name_alt2, x_name_alt3);
260 #endif
261 
262   if (matched_builtin)
263     {
264       /* user passed the name of a PS or PCL font in libplot's database */
265       success = _pl_x_select_xlfd_font_carefully (R___(_plotter) x_name,
266 						  x_name_alt, x_name_alt2,
267 						  x_name_alt3);
268       if (success)
269 	/* RETRIEVAL TYPE #1: we've retrieved a core X font, the XLFD name
270            of which was listed in libplot's hardcoded database; and have
271            filled in X-specific fields */
272 	{
273 	  free ((char *)_plotter->drawstate->true_font_name);
274 	  _plotter->drawstate->true_font_name =
275 	    (const char *)_pl_xmalloc (strlen (true_name) + 1);
276 	  strcpy ((char *)_plotter->drawstate->true_font_name, true_name);
277 
278 	  _plotter->drawstate->font_type = font_type;
279 	  _plotter->drawstate->typeface_index = typeface_index;
280 	  _plotter->drawstate->font_index = font_index;
281 
282 #ifdef DEBUG
283 	  fprintf (stderr, "_pl_x_retrieve_font(): retrieved \"%s\" as \"%s\", type=%d\n", name, _plotter->drawstate->true_font_name, _plotter->drawstate->font_type);
284 	  fprintf (stderr, "_pl_x_retrieve_font(): font_size=%g, true_font_size=%g, font_ascent=%g, font_descent=%g, font_is_iso8859_1=%d, x_font_pixel_size=%d\n", _plotter->drawstate->font_size, _plotter->drawstate->true_font_size, _plotter->drawstate->font_ascent, _plotter->drawstate->font_descent, _plotter->drawstate->font_is_iso8859_1, _plotter->drawstate->x_font_pixel_size);
285 #endif
286 	  return true;
287 	}
288     }
289 
290   /* User-specified font name didn't match either of the names of any PS
291      [or PCL] font in libplot's database, so first handle the possibility
292      that it's an XLFD base name for some other core X font
293      (e.g. "charter-medium-r-normal"), with exactly three hyphens. */
294   name_p = name;
295   hyphen_count = 0;
296   while (*name_p)
297     hyphen_count += (*name_p++ == '-' ? 1 : 0);
298 
299   if (hyphen_count == 3)
300     /* treat as base of an XLFD name */
301     {
302       success = _pl_x_select_xlfd_font_carefully (R___(_plotter) name,
303 						  NULL, NULL, NULL);
304 
305       if (success)
306 	/* RETRIEVAL TYPE #3: we've retrieved a core X font, the base XLFD
307            name of which was passed by the user, that isn't one of the
308            fonts in libplot's hardcoded database; and have filled in
309            X-specific fields */
310 	{
311 	  free ((char *)_plotter->drawstate->true_font_name);
312 	  _plotter->drawstate->true_font_name =
313 	    (const char *)_pl_xmalloc (strlen (name) + 1);
314 	  strcpy ((char *)_plotter->drawstate->true_font_name, name);
315 
316 	  _plotter->drawstate->font_type = PL_F_OTHER;
317 	  /* these two fields are irrelevant because we don't support
318 	     switching among fonts not in libplot's internal database */
319 	  _plotter->drawstate->typeface_index = 0;
320 	  _plotter->drawstate->font_index = 1;
321 
322 #ifdef DEBUG
323 	  fprintf (stderr, "_pl_x_retrieve_font(): retrieved \"%s\" as \"%s\", type=%d\n", name, _plotter->drawstate->true_font_name, _plotter->drawstate->font_type);
324 	  fprintf (stderr, "_pl_x_retrieve_font(): font_size=%g, true_font_size=%g, font_ascent=%g, font_descent=%g, font_is_iso8859_1=%d, x_font_pixel_size=%d\n", _plotter->drawstate->font_size, _plotter->drawstate->true_font_size, _plotter->drawstate->font_ascent, _plotter->drawstate->font_descent, _plotter->drawstate->font_is_iso8859_1, _plotter->drawstate->x_font_pixel_size);
325 #endif
326 	  return true;
327 	}
328     }
329 
330   /* User-passed name didn't have exactly 3 hyphens, so try treating it as
331      the full name of a core X font; ignore size.  This a kludge, included
332      partly to support pre-XLFD fonts, e.g. "9x15", and aliases for XLFD
333      fonts, e.g. "fixed".  Most of the latter are really pre-XLFD names. */
334 
335     {
336       double det;
337 
338       det = _plotter->drawstate->transform.m[0] * _plotter->drawstate->transform.m[3]
339 	- _plotter->drawstate->transform.m[1] * _plotter->drawstate->transform.m[2];
340       if (det == 0.0)
341 	/* singular user-space -> device-space map; bail */
342 	return false;
343 
344       /* Try to retrieve font from server or cache list, given its full
345 	 name; and for the moment, ignore the preferred pixel size we just
346 	 computed.  3rd argument `false' requests entire font.  A GOOD IDEA? */
347       success = _pl_x_select_font_carefully (R___(_plotter) name,
348 					 _plotter->drawstate->x_label,
349 					 false);
350 
351       if (success)
352 	/* RETRIEVAL TYPE #4: we've retrieved a core X font, the full name
353            of which was passed by the user, that isn't one of the fonts in
354            libplot's hardcoded database */
355 	{
356 	  free ((char *)_plotter->drawstate->true_font_name);
357 	  _plotter->drawstate->true_font_name =
358 	    (const char *)_pl_xmalloc (strlen (name) + 1);
359 	  strcpy ((char *)_plotter->drawstate->true_font_name, name);
360 
361 	  _plotter->drawstate->font_type = PL_F_OTHER;
362 	  /* these two fields are irrelevant because we don't support
363 	     switching among `other' fonts */
364 	  _plotter->drawstate->typeface_index = 0;
365 	  _plotter->drawstate->font_index = 1;
366 
367 	  if (_plotter->drawstate->x_font_pixel_size == 0) /* paranoia */
368 	    return false;
369 
370 #ifdef DEBUG
371 	  fprintf (stderr, "_pl_x_retrieve_font(): retrieved \"%s\" as \"%s\", type=%d\n", name, _plotter->drawstate->true_font_name, _plotter->drawstate->font_type);
372 #endif
373 	  return true;
374 	}
375     }
376 
377   /* couldn't retrieve a matching X font, so declare failure; this will
378      lead (at a higher level; see g_retrieve.c) either to the retrieval of
379      a default substitute X font, or a builtin Hershey font */
380 #ifdef DEBUG
381 	  fprintf (stderr, "_pl_x_retrieve_font(): FAILURE, couldn't retrieve \"%s\"\n", name);
382 #endif
383   return false;
384 }
385 
386 /* _pl_x_select_xlfd_font_carefully() is a helper function that
387    x_retrieve_font() above uses.  It constructs a full XLFD name of a core
388    X11 font from each of several specified base XLFD names, such as
389    "helvetica-medium-r-normal", and attempts to retrieve them in order,
390    until a successful retrieval occurs.  The inclusion of several
391    alternatives is useful, since each of the built-in fonts in libplot's
392    database (see g_fontdb.c) is associated with several possible base XLFD
393    names.  That's because there is no standardization of names across
394    vendors, even for the commonly used `Adobe 35' fonts, i.e., libplot's
395    supported `Postscript fonts'.
396 
397    For each alternative, a fontname ending in -iso8859-1 (indicating the
398    ISO-Latin-1 encoding) is tried, and if that doesn't work, a fontname
399    ending in -*-* (the encoding being left up to the server).  The
400    lower-level function _pl_x_select_font_carefully() does the retrieval.
401    It is passed the `x_label' field of the drawing state, which is a hint
402    indicating which characters are needed.  A proper subset of the font
403    will be retrieved, if possible, to save time.  The proper subset is
404    indicated to the server, as usual, by a suffix on the font name.  E.g.,
405    ....-iso8859-1[88 89] consists only of the letters `X' and `Y'
406    (characters 88 and 89).
407 
408    This code, when requesting the retrieval, must place an integer pixel
409    size in the XLFD name.  The pixel size it chooses is based on (1) the
410    font_size in user coordinates and (2) the transformation matrix, which
411    takes user-space to device-space, i.e., to X11 pixel space.
412 
413    When any label is drawn, the retrieved font will in general be scaled,
414    by XAffDrawString(); see the code in x_text.c.  So this code cleverly
415    chooses an integer pixel size which, if it weren't for rounding to the
416    closest integer, wouldn't require any scaling of the glyph bitmaps at
417    all, provided that the user-space -> device-space is uniform (not
418    "anamorphic"), and doesn't involve a rotation. */
419 
420 bool
_pl_x_select_xlfd_font_carefully(R___ (Plotter * _plotter)const char * x_name,const char * x_name_alt,const char * x_name_alt2,const char * x_name_alt3)421 _pl_x_select_xlfd_font_carefully (R___(Plotter *_plotter) const char *x_name, const char *x_name_alt, const char *x_name_alt2, const char *x_name_alt3)
422 {
423   char *x_name_buf;		/* buffer for creating font name */
424   bool success = false;
425   int integer_font_size_in_pixels;
426   double det, font_size_in_pixels;
427 
428   det = _plotter->drawstate->transform.m[0] * _plotter->drawstate->transform.m[3]
429     - _plotter->drawstate->transform.m[1] * _plotter->drawstate->transform.m[2];
430 
431   if (det == 0.0)
432     /* singular user-space -> device-space map; bail */
433     return false;
434 
435   /* Compute preferred pixel size for the core X font: the user-space font
436      size, multiplied by a measure of the size of the user-space to
437      device-space transformation matrix.  The "measure" we choose is the
438      minimum of the matrix's two singular values.  (There are other
439      possible choices for this measure.) */
440 
441   font_size_in_pixels =
442     min_sing_val (_plotter->drawstate->transform.m) *_plotter->drawstate->font_size;
443   if (font_size_in_pixels == 0.0)
444     /* preferred device-space font size, in terms of pixels, is zero; bail */
445     return false;
446 
447   /* quantize to an integer pixel size: round downward */
448   integer_font_size_in_pixels = (int)font_size_in_pixels;
449 
450   if (font_size_in_pixels == 0)
451     /* integer device-space size, in terms of pixels, is zero; bail */
452     return false;
453 
454   /* prepare buffer for font name assemblage */
455   x_name_buf = (char *)_pl_xmalloc ((MAX_FONT_NAME_LENGTH + 1) * sizeof (char));
456 
457   /* try to retrieve font from server or cache list, after punching the
458      pixel size into the appropriate XLFD fontname template */
459 
460   /* try Latin-1 fontname, i.e. fontname ending in -iso8859-1 */
461 
462   sprintf (x_name_buf, xlfd_template_latin_1, x_name, integer_font_size_in_pixels);
463   success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
464 					 _plotter->drawstate->x_label,
465 					 true);
466 #ifdef DEBUG
467   fprintf (stderr, "_pl_x_select_xlfd_font_carefully(): retrieval begins with %s\n",
468 	   x_name_buf);
469 #endif
470 
471   if (success == false)
472     /* try fontname ending in -*-* */
473     {
474       sprintf (x_name_buf, xlfd_template_generic,
475 	       x_name, integer_font_size_in_pixels);
476       success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
477 					     _plotter->drawstate->x_label,
478 					     true);
479     }
480 
481   if (x_name_alt)
482     /* alternative base XLFD name was supplied, so try it too */
483     {
484       if (success == false)
485 	/* try Latin-1 fontname, i.e. fontname ending in -iso8859-1 */
486 	{
487 	  sprintf (x_name_buf, xlfd_template_latin_1,
488 		   x_name_alt, integer_font_size_in_pixels);
489 	  success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
490 						 _plotter->drawstate->x_label,
491 						 true);
492 	}
493       if (success == false)
494 	/* try fontname ending in -*-* */
495 	{
496 	  sprintf (x_name_buf, xlfd_template_generic,
497 		   x_name_alt, integer_font_size_in_pixels);
498 	  success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
499 						 _plotter->drawstate->x_label,
500 						 true);
501 	}
502     }
503 
504   if (x_name_alt2)
505     /* 2nd alternative base XLFD name was supplied, so try it too */
506     {
507       if (success == false)
508 	/* try Latin-1 fontname, i.e. fontname ending in -iso8859-1 */
509 	{
510 	  sprintf (x_name_buf, xlfd_template_latin_1,
511 		   x_name_alt2, integer_font_size_in_pixels);
512 	  success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
513 						 _plotter->drawstate->x_label,
514 						 true);
515 	}
516       if (success == false)
517 	/* try fontname ending in -*-* */
518 	{
519 	  sprintf (x_name_buf, xlfd_template_generic,
520 		   x_name_alt2, integer_font_size_in_pixels);
521 	  success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
522 						 _plotter->drawstate->x_label,
523 						 true);
524 	}
525     }
526 
527   if (x_name_alt3)
528     /* 3rd alternative base XLFD name was supplied, so try it too */
529     {
530       if (success == false)
531 	/* try Latin-1 fontname, i.e. fontname ending in -iso8859-1 */
532 	{
533 	  sprintf (x_name_buf, xlfd_template_latin_1,
534 		   x_name_alt3, integer_font_size_in_pixels);
535 	  success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
536 						 _plotter->drawstate->x_label,
537 						 true);
538 	}
539       if (success == false)
540 	/* try fontname ending in -*-* */
541 	{
542 	  sprintf (x_name_buf, xlfd_template_generic,
543 		   x_name_alt3, integer_font_size_in_pixels);
544 	  success = _pl_x_select_font_carefully (R___(_plotter) x_name_buf,
545 						 _plotter->drawstate->x_label,
546 						 true);
547 	}
548     }
549 
550   if (success)
551     /* A clever hack.  Slightly alter the true_font_size field (and
552        font_ascent/font_descent/font_cap_height for consistency), in a way
553        that will permit any text string to be rendered in this font by
554        XDrawString(), rather than by our bitmap-scaling code in
555        x_afftext.c.  Provided, that is, the text string isn't rotated, and
556        the user-space -> device-space transformation is a uniform scaling
557        that respects coordinate axes.
558 
559        The reason this works is that the rendering code in x_text.c calls
560        our bitmap-scaling function XAffDrawString() with a certain
561        transformation matrix a[] as an argument.  Under the above
562        circumstances, this matrix will turn out to be the identity matrix,
563        due to the slight modification performed below.  That being the
564        case, XAffDrawString() will simply call XDrawString rather than
565        performing any bitmap scaling.  The result: in the above
566        circumstances, which are very common, the glyphs in the text string
567        won't undergo a slight scaling traceable to the integer quantization
568        of font pixel size, and will look better. */
569     {
570       double factor = integer_font_size_in_pixels / font_size_in_pixels;
571 
572       /* scale by this factor, quite close to unity */
573       _plotter->drawstate->true_font_size *= factor;
574       _plotter->drawstate->font_ascent *= factor;
575       _plotter->drawstate->font_descent *= factor;
576       _plotter->drawstate->font_cap_height *= factor;
577     }
578 
579   return success;
580 }
581 
582 /* _pl_x_select_font_carefully() is a wrapper around select_x_font() below.
583    It attempts to retrieve the font NAME from the X server or from the
584    per-Plotter font cache of already-retrieved fonts.  The character subset
585    desired (if any) is specified by the string S, and whether an attempt
586    should actually be made to retrieve a subset rather than the entire is
587    specified by the parameter SUBSETTING.  The return value indicates
588    whether the retrieval succeeded.
589 
590    A NULL value for S means the font is being retrieved only to look at its
591    metrics; in which case select_x_font() will attempt to retrieve the
592    character 'X' only, to permit the cap-height metric to be determined;
593    and the space character, for good measure.  This is arranged in the
594    low-level function string_to_bitvector() below; we _always_ include the
595    characters 'X' and ' ' among those we ask the server to rasterize.
596 
597    The inclusion of 'X' incidentally works around a bug reported by Georgy
598    Salnikov <sge@nmr.nioch.nsc.ru>, in XFree86-3.2.  (We previously tried
599    to retrieve not 'X', but rather the space character.  But if an XLFD
600    font name ends in *-*[32], i.e. font contains only a single space,
601    XFree86 reports an error retrieving the font, and any executable linked
602    with this code client will terminate.)
603 
604    If the font is successfully retrieved (either from the X server or from
605    the cache), this function fills in most font-related fields of the
606    drawing state.  That includes the generic fields true_font_size,
607    font_ascent, font_descent, font_cap_height, font_is_iso8859_1, and the
608    X-specific fields x_font_struct and x_font_pixel_size.
609 
610    The true_font_size is simply set equal to font_size (which is whatever
611    the user passed to libplot by invoking the fontname() function in the
612    libplot API).  That is because in principle, libplot's X driver can
613    match exactly any user-specified font size, by scaling bitmaps.  See
614    x_text.c and x_afftext.c for the code that renders text strings. */
615 
616 bool
_pl_x_select_font_carefully(R___ (Plotter * _plotter)const char * name,const unsigned char * s,bool subsetting)617 _pl_x_select_font_carefully (R___(Plotter *_plotter) const char *name, const unsigned char *s, bool subsetting)
618 {
619   plXFontRecord *fptr;
620 
621   if (s == (unsigned char *)NULL)
622     s = (unsigned char *)"";	/* "" is effectively "X " */
623 
624   /* attempt to retrieve the specified (subset of the) font */
625   fptr = select_x_font (_plotter->x_dpy, &(_plotter->x_fontlist),
626 			   name, s, subsetting);
627 
628 #ifdef DEBUG
629   fprintf (stderr, "_pl_x_select_font_carefully(): select_x_font() returns %p\n",
630 	   fptr);
631 #endif
632 
633   if (subsetting && (fptr == (plXFontRecord *)NULL))
634     /* failure; so try to retrieve entire font instead of a subset,
635        ignoring the passed hint string S (perhaps server doesn't support
636        subsetting?) */
637     fptr = select_x_font (_plotter->x_dpy, &(_plotter->x_fontlist),
638 			   name, s, false);
639 
640   if (fptr == (plXFontRecord *)NULL)
641     /* couldn't retrieve font from cache or from server */
642     return false;
643   else
644     /* Success, so fill in fields of the drawing state from the returned
645        font record.  Most are generic rather than X-specific.  Ascent,
646        descent and cap_height are user-space quantities; so are scaled by
647        the font_size, which is expressed in user-space units. */
648     {
649       if (fptr->x_font_pixel_size <= 0) /* paranoia */
650 	return false;
651 
652 #ifdef DEBUG
653       fprintf (stderr, "fontname = %s, min_char_or_byte2 = %u, max_char_or_byte2 = %u, ascent = %d, descent = %d, default_char = %u\n", name, fptr->x_font_struct->min_char_or_byte2, fptr->x_font_struct->max_char_or_byte2, fptr->x_font_struct->ascent, fptr->x_font_struct->descent, fptr->x_font_struct->default_char);
654 #endif
655 
656       /* set generic fields */
657       _plotter->drawstate->true_font_size = _plotter->drawstate->font_size;
658       _plotter->drawstate->font_ascent =
659 	((fptr->x_font_struct->ascent * _plotter->drawstate->font_size)
660 	 / fptr->x_font_pixel_size);
661       _plotter->drawstate->font_descent =
662 	((fptr->x_font_struct->descent * _plotter->drawstate->font_size)
663 	 / fptr->x_font_pixel_size);
664       _plotter->drawstate->font_cap_height =
665 	((fptr->x_font_cap_height * _plotter->drawstate->font_size)
666 	 / fptr->x_font_pixel_size);
667 
668       _plotter->drawstate->font_is_iso8859_1 = fptr->x_font_is_iso8859_1;
669 
670       /* set X-specific fields */
671       _plotter->drawstate->x_font_struct = fptr->x_font_struct;
672       _plotter->drawstate->x_font_pixel_size = fptr->x_font_pixel_size;
673 
674       return true;
675     }
676 }
677 
678 /* Attempt to retrieve a core X font, as a plXFontRecord.  The font is
679    specified by NAME, and a hint as to which characters will need to be
680    rendered is passed as the non-null string S.  This permits the retrieval
681    of a proper subset of the font, if desired.  The SUBSETTING parameter
682    indicates whether the retrieval of an appropriate subset of the font
683    should first be attempted, before retrieval of the entire font.
684 
685    The X_FONTLIST_PTR argument passes [by reference!] a pointer to a font
686    cache, a linked list of plXFontRecords that will be searched.  If the
687    font isn't found in the cache but can be successfully retrieved from the
688    X display server instead, a new record is added to the head of this
689    list; and if it can't be, a null (invalid) record is added to the head
690    of the list; in both cases, to speed up later retrieval attempts.
691 
692    Return value: a pointer to the font record, if a font was found in the
693    cache or newly added to it; otherwise NULL.  */
694 
695 static plXFontRecord *
select_x_font(Display * dpy,plXFontRecord ** x_fontlist_ptr,const char * name,const unsigned char * s,bool subsetting)696 select_x_font (Display *dpy, plXFontRecord **x_fontlist_ptr, const char *name, const unsigned char *s, bool subsetting)
697 {
698   bool found = false;
699   unsigned char bitvec[32];
700   plXFontRecord *x_fontlist, *fptr;
701 
702 #ifdef DEBUG
703   fprintf (stderr, "select_x_font (name=\"%s\", subset=\"%s\", subsetting=%d)\n",
704 	   name, (const char *)s, subsetting);
705 #endif
706 
707   if (subsetting)
708     /* construct 256-bit vector specifying charset subset */
709     string_to_bitvector (s, bitvec);
710 
711   /* get head of linked-list cache */
712   x_fontlist = *x_fontlist_ptr;
713 
714   /* attempt to find font in cache */
715   for (fptr = x_fontlist; fptr; fptr = fptr->next)
716     {
717 #ifdef DEBUG
718       fprintf (stderr, "select_x_font(): cache entry: name=\"%s\", subset=%d\n",
719 	       fptr->x_font_name, fptr->subset);
720 #endif
721 
722       if (strcmp (name, fptr->x_font_name) == 0)
723 	{
724 	  if ((subsetting && fptr->subset
725 	       && is_a_subset (bitvec, fptr->subset_vector))
726 	      || (subsetting && (fptr->subset == false))
727 	      || (subsetting == false && fptr->subset == false))
728 	    {
729 	      found = true;
730 	      break;
731 	    }
732 	}
733     }
734 
735   if (found)
736     {
737       if (fptr->x_font_struct)
738 	/* found record was a genuine one */
739 	{
740 #ifdef DEBUG
741 	  fprintf (stderr, "select_x_font(): font cache HIT on name=%s, s=\"%s\"\n", name, s);
742 #endif
743 	  return fptr;
744 	}
745       else
746 	{
747 #ifdef DEBUG
748 	  fprintf (stderr, "select_x_font(): font cache HIT (fake) on name=\"%s\", s=\"%s\"\n", name, s);
749 #endif
750 	  /* invalid record: an earlier retrieval attempt must have failed */
751 	  return (plXFontRecord *)NULL;
752 	}
753     }
754 
755 #ifdef DEBUG
756   fprintf (stderr, "select_x_font(): font cache miss on name=\"%s\", s=\"%s\"\n", name, s);
757 #endif
758 
759   /* no record in cache, so try to retrieve font from X server */
760   {
761     char *tmpname, *tmpname_perm, *_charset_subset_list = NULL;
762     int extra = 0;
763 
764     /* allocate space for new record, update pointer to cache to include it */
765     fptr =
766       (plXFontRecord *)_pl_xmalloc (sizeof (plXFontRecord));
767     fptr->next = *x_fontlist_ptr;
768     *x_fontlist_ptr = fptr;
769 
770     if (subsetting)
771       {
772 	_charset_subset_list =
773 	  (char *)_pl_xmalloc ((MAX_CHARSET_SUBSET_LIST_LENGTH + 1) * sizeof (char));
774 	print_bitvector (bitvec, _charset_subset_list);
775 	extra = strlen (_charset_subset_list);
776       }
777     tmpname_perm = (char *)_pl_xmalloc (1 + strlen (name));
778     strcpy (tmpname_perm, name);
779     tmpname = (char *)_pl_xmalloc (1 + strlen (name) + extra);
780     strcpy (tmpname, name);
781     if (subsetting)
782       {
783 	/* append X11R6 list-of-ranges to name to be sent to server */
784 	strcat (tmpname, _charset_subset_list);
785 	free (_charset_subset_list);
786       }
787 
788 #ifdef DEBUG
789     fprintf (stderr, "select_x_font(): trying to invoke XLoadQueryFont on \"%s\", subsetting=%d\n", tmpname, subsetting);
790 #endif
791 
792     /* attempt to retrieve font from server; return value from
793        XLoadQueryFont() equalling NULL indicates failure */
794     fptr->x_font_struct =
795       XLoadQueryFont (dpy, tmpname);
796     free (tmpname);
797 
798     /* whether or not there was success, fill in some add'l fields of record */
799     fptr->x_font_name = tmpname_perm; /* don't include subset in stored name */
800     fptr->subset = subsetting;
801     if (subsetting)
802       memcpy (fptr->subset_vector, bitvec, 32 * sizeof (unsigned char));
803 
804     /* handle a special case: retrieval from server succeeded, but the
805        retrieved font wasn't an 8-bit font, so we can't use it */
806 
807     if (fptr->x_font_struct
808 	&& (fptr->x_font_struct->min_byte1 != 0
809 	    || fptr->x_font_struct->max_byte1 != 0))
810       /* treat as if retrieval failed */
811       {
812 	XFreeFont (dpy, fptr->x_font_struct);
813 	fptr->x_font_struct = (XFontStruct *)NULL;
814       }
815 
816     if (fptr->x_font_struct)
817       /* retrieval succeeded */
818       {
819 #ifdef DEBUG
820 	fprintf (stderr, "select_x_font(): loaded font \"%s\"\n", name);
821 #endif
822 	/* fill in, as well, the x_font_pixel_size, x_font_cap_height,
823 	   x_font_iso_8859_1 fields of the font record */
824 	set_font_dimensions (dpy, fptr);
825 
826 	return fptr;		/* X font selected */
827       }
828     else
829       /* retrieval failed */
830       {
831 #ifdef DEBUG
832 	fprintf (stderr, "select_x_font(): failed to load font \"%s\"\n", name);
833 #endif
834 	return (plXFontRecord *)NULL;
835       }
836   }
837 }
838 
839 /* Complete the filling in of an plXFontRecord, by filling in the fields
840    x_font_pixel_size, x_font_cap_height, and x_font_is_iso8859_1, on the
841    basis of the x_font_struct field.  Called by preceding function.  */
842 
843 static void
set_font_dimensions(Display * dpy,plXFontRecord * fptr)844 set_font_dimensions (Display *dpy, plXFontRecord *fptr)
845 {
846   unsigned long retval;
847   char *name, *pixel_field;
848   char *charset_major_field, *charset_minor_field;
849 
850 #ifdef DEBUG2
851   {
852     int i;
853 
854     for (i = 0; i < fptr->x_font_struct->n_properties; i++)
855       fprintf (stderr, "\tproperty %s [atom %lu] is %ld\n",
856 	       XGetAtomName(dpy, fptr->x_font_struct->properties[i].name),
857 	       fptr->x_font_struct->properties[i].name,
858 	       fptr->x_font_struct->properties[i].card32);
859   }
860 #endif
861 
862   if (XGetFontProperty (fptr->x_font_struct, XA_FONT, &retval))
863     /* this font has a FONT property, as any well behaved font should */
864     {
865       /* Extract relevant fields from this property (i.e. from X server's
866 	 idea of the font name).  This will work if it's an XLFD name. */
867       name = XGetAtomName (dpy, retval);
868 
869 #ifdef DEBUG
870       fprintf (stderr, "set_font_dimensions(): FONT property is \"%s\"\n", name);
871 #endif
872       pixel_field = xlfd_field (name, XLFD_FIELD_PIXELS);
873       charset_major_field = xlfd_field (name, XLFD_FIELD_CHARACTER_SET_MAJOR);
874       charset_minor_field = xlfd_field (name, XLFD_FIELD_CHARACTER_SET_MINOR);
875       XFree (name);
876 
877       /* determine whether font encoding is ISO-Latin-1 */
878       if ((charset_major_field != NULL) && (charset_minor_field != NULL)
879 	  && strcasecmp (charset_major_field, "iso8859") == 0
880 	  && (charset_minor_field[0] == (char)'1'
881 	      && (charset_minor_field[1] == (char)0 /* string terminator */
882 		  || charset_minor_field[1] == (char)'[')))
883 	  fptr->x_font_is_iso8859_1 = true;
884       else
885 	  fptr->x_font_is_iso8859_1 = false;
886 
887       if (charset_major_field)
888 	free (charset_major_field);
889       if (charset_minor_field)
890 	free (charset_minor_field);
891 
892       if (pixel_field != NULL)
893 	/* font presumably has an XLFD name, since it has a pixel field */
894 	{
895 	  /* extract x_font_pixel_size from the pixel field */
896 
897 	  unsigned int size;
898 
899 	  sscanf (pixel_field, "%u", &size);
900 	  fptr->x_font_pixel_size = size;
901 	  free (pixel_field);
902 
903 	  /* fill in the font_{cap_height} field; we get it from the ascent
904 	     of the `X' character, if it exists */
905 
906 	  if ('X' >= fptr->x_font_struct->min_char_or_byte2
907 	      && 'X' <= fptr->x_font_struct->max_char_or_byte2
908 	      && fptr->x_font_struct->per_char)
909 	    /* have `X' char in the font, and have per-char data */
910 	    {
911 	      int X = 'X' - fptr->x_font_struct->min_char_or_byte2;
912 
913 	      fptr->x_font_cap_height
914 		= fptr->x_font_struct->per_char[X].ascent;
915 	    }
916 	  else		/* do our best */
917 	      fptr->x_font_cap_height
918 		= fptr->x_font_struct->min_bounds.ascent;
919 
920 	  /* we've set all fields, so we can return */
921 	  return;
922 	}
923 #ifdef DEBUG2
924       fprintf (stderr, "FONT property does not exist\n");
925 #endif
926     }
927   else
928   /* font doesn't have an XLFD name (so no pixel size field), or there's no
929      FONT property at all (a bad situation) */
930     {
931       Atom pixel_size_atom, resolution_y_atom;
932       unsigned long point_size, resolution_y;
933 
934       fptr->x_font_is_iso8859_1 = false; /* assumed (worst case) */
935 
936       pixel_size_atom = XInternAtom (dpy, "PIXEL_SIZE", (Bool)false);
937 
938       if (XGetFontProperty (fptr->x_font_struct, pixel_size_atom, &retval))
939 	/* there's a PIXEL_SIZE property, so use it to compute font size */
940 	{
941 #ifdef DEBUG2
942 	  fprintf (stderr, "PIXEL_SIZE property is \"%lu\"\n", retval);
943 #endif
944 	  fptr->x_font_pixel_size = retval;
945 	}
946       else
947 	/* no PIXEL_SIZE, so try to compute pixel size from POINT_SIZE and
948 	   RESOLUTION_Y properties */
949 	{
950 #ifdef DEBUG2
951 	  fprintf (stderr, "PIXEL_SIZE property does not exist\n");
952 #endif
953 	  resolution_y_atom = XInternAtom (dpy, "RESOLUTION_Y", (Bool)false);
954 	  if (XGetFontProperty (fptr->x_font_struct, XA_POINT_SIZE, &point_size)
955 	      && (XGetFontProperty (fptr->x_font_struct,
956 				    resolution_y_atom, &resolution_y)))
957 	    {
958 #ifdef DEBUG2
959 	      fprintf (stderr, "POINT_SIZE property is \"%lu\"\n",
960 		       point_size);
961 	      fprintf (stderr, "RESOLUTION_Y property is \"%lu\"\n",
962 		       resolution_y);
963 #endif
964 	      fptr->x_font_pixel_size =
965 		IROUND(((double)point_size * (double)resolution_y / 722.7));
966 	    }
967 	  else
968 	    /* we can't compute the font size legitimately, so estimate it
969 	       from the XFontStruct (may not be reliable) */
970 	    {
971 #ifdef DEBUG2
972 	      fprintf (stderr, "POINT_SIZE and/or RESOLUTION_Y properties do not exist\n");
973 #endif
974 	      fptr->x_font_pixel_size = fptr->x_font_struct->ascent + fptr->x_font_struct->descent;
975 	    }
976 	}
977 
978       fptr->x_font_cap_height
979 	= fptr->x_font_struct->per_char['X' - fptr->x_font_struct->min_char_or_byte2].ascent;
980     }
981 }
982 
983 /* Extract a field from an XLFD name string, by number, and return it, via
984    a call to malloc.  If `name' doesn't appear to be an XLFD name, NULL is
985    returned. */
986 
987 static char *
xlfd_field(const char * name,int field)988 xlfd_field(const char *name, int field)
989 {
990   const char *p;
991   const char *fields[PL_NUM_XLFD_FIELDS];
992   char *retstring;
993   int len[PL_NUM_XLFD_FIELDS];
994   int i, n, m;
995   /* split into fields at hyphens */
996   for (p = name, i = 0, n = 0, m = 0;
997        *p && (i < PL_NUM_XLFD_FIELDS);
998        p++, n++, m++)
999     {
1000       if (*p == '-')
1001 	{
1002 	  if (i > 0)
1003 	    len[i-1] = n;
1004 	  n = 0;
1005 	  fields[i++] = p;
1006 	}
1007     }
1008   if (i < PL_NUM_XLFD_FIELDS)
1009     return NULL;
1010 
1011   len[PL_NUM_XLFD_FIELDS - 1] = strlen (name) - (m - 1); /* final field exhausts string */
1012 
1013   /* for len[] and fields[], each field includes initial hyphen */
1014   retstring = (char *)_pl_xmalloc (len[field] * sizeof(char));
1015   strncpy (retstring, fields[field] + 1,
1016 	   (unsigned int)(len[field] - 1)); /* skip initial - */
1017   retstring[len[field] - 1] = '\0';
1018 
1019   return retstring;
1020 }
1021 
1022 /* Prepare a bit vector (length 256 bits, i.e. 32 bytes) indicating which
1023    characters in the range 1..255 are used in string S.  We always include
1024    the character 'X', even if it isn't present in the string.  `X' is
1025    special because we can subsequently use it to retrieve the cap height.
1026    For backward compatibility (not necessary?) we also include the space
1027    character. */
1028 
1029 static void
string_to_bitvector(const unsigned char * s,unsigned char v[32])1030 string_to_bitvector (const unsigned char *s, unsigned char v[32])
1031 {
1032   unsigned char c;
1033   unsigned int i, j;
1034   int k;
1035 
1036   for (k = 0; k < 32; k++)
1037     v[k] = 0;
1038 
1039   /* include the X character */
1040   c = 'X';
1041   i = c / 8;
1042   j = c % 8;
1043   v[i] |= (1 << j);
1044 
1045   /* include the space character too */
1046   c = ' ';
1047   i = c / 8;
1048   j = c % 8;
1049   v[i] |= (1 << j);
1050 
1051   /* include all characters in the passed string */
1052   while ((c = *s) != (unsigned char)'\0')
1053     {
1054       i = c / 8;
1055       j = c % 8;
1056 #ifdef DEBUG2
1057       fprintf (stderr, "saw char %d (i.e. %c), stored as %d,%d\n", c, c, i, j);
1058 #endif
1059       v[i] |= (1 << j);
1060       s++;
1061     }
1062 }
1063 
1064 /* This writes a bitvector as a string, in the form used in X11R6-style
1065    charset subsetting.  Each range of chars may require the writing of up
1066    to 8 bytes, e.g. " 160_180". The list of ranges is contained within
1067    brackets. */
1068 
1069 static void
print_bitvector(unsigned char v[32],char * s)1070 print_bitvector (unsigned char v[32], char *s)
1071 {
1072   int i, num_ranges_output = 0, num_chars_output = 0;
1073   int start_of_range = 0;
1074   bool used;
1075   bool in_range = false;
1076 
1077   *s++ = '[';
1078   for (i = 0; i <= 256; i++)
1079     {
1080       if (i == 256)
1081 	used = false;
1082       else
1083 	used = (v[i / 8] & (1 << (i % 8))) ? true : false;
1084 
1085 #ifdef DEBUG2
1086       if (used)
1087 	fprintf (stderr, "stored char %d (i.e. %c), from %d,%d\n", i, i, i/8, i%8);
1088 #endif
1089 
1090       if (used && in_range == false)
1091 	/* begin new range */
1092 	{
1093 	  start_of_range = i;
1094 	  in_range = true;
1095 	}
1096       else if (used == false && in_range)
1097 	/* end of range, so output the range */
1098 	{
1099 	  int hundreds, tens, ones;
1100 	  bool hundreds_output;
1101 
1102 	  if (num_chars_output > MAX_CHARSET_SUBSET_LIST_LENGTH - 8)
1103 	    break;		/* abort to avoid buffer overrun */
1104 
1105 	  if (num_ranges_output > 0)
1106 	    /* use space as separator */
1107 	    {
1108 	      *s++ = ' ';
1109 	      num_chars_output++;
1110 	    }
1111 
1112 #ifdef DEBUG2
1113 	  fprintf (stderr, "outputting character range %d..%d, i.e. %c..%c\n",
1114 		   start_of_range, i-1, start_of_range, i-1);
1115 #endif
1116 	  if (start_of_range < (i - 1))
1117 	    /* have a genuine range, start..(i-1), not a singleton */
1118 	    {
1119 	      /* output start of range, followed by underscore */
1120 	      hundreds = start_of_range / 100;
1121 	      tens = (start_of_range - hundreds * 100) / 10;
1122 	      ones = start_of_range % 10;
1123 	      hundreds_output = false;
1124 	      if (hundreds > 0)
1125 		{
1126 		  *s++ = (char)'0' + hundreds;
1127 		  hundreds_output = true;
1128 		  num_chars_output++;
1129 		}
1130 	      if (hundreds_output || tens > 0)
1131 		{
1132 		  *s++ = (char)'0' + tens;
1133 		  num_chars_output++;
1134 		}
1135 	      *s++ = (char)'0' + ones;
1136 	      num_chars_output++;
1137 	      *s++ = (char)'_';
1138 	      num_chars_output++;
1139 	    }
1140 
1141 	  /* output end of range, which is i-1 */
1142 	  hundreds = (i-1) / 100;
1143 	  tens = ((i-1) - hundreds * 100) / 10;
1144 	  ones = (i-1) % 10;
1145 	  hundreds_output = false;
1146 	  if (hundreds > 0)
1147 	    {
1148 	      *s++ = (char)'0' + hundreds;
1149 	      hundreds_output = true;
1150 	      num_chars_output++;
1151 	    }
1152 	  if (hundreds_output || tens > 0)
1153 	    {
1154 	      *s++ = (char)'0' + tens;
1155 	      num_chars_output++;
1156 	    }
1157 	  *s++ = (char)'0' + ones;
1158 	  num_chars_output++;
1159 
1160 	  /* no longer in range */
1161 	  in_range = false;
1162 	  num_ranges_output++;
1163 	}
1164     }
1165   *s++ = ']';
1166   /* add final null */
1167   *s = '\0';
1168 }
1169 
1170 static bool
is_a_subset(unsigned char set1[32],unsigned char set2[32])1171 is_a_subset (unsigned char set1[32], unsigned char set2[32])
1172 {
1173   int i;
1174   bool retval = true;
1175 
1176   for (i = 0; i < 32; i++)
1177     if (set1[i] & ~(set2[i]))
1178       {
1179 	retval = false;
1180 	break;
1181       }
1182 
1183   return retval;
1184 }
1185 
1186 /* Compute the minimum of the two singular values of a 2x2 matrix.  Used
1187    for computing our favored pixel size, at which to retrieve a font; the
1188    matrix is the user-space -> device-space transformation matrix. */
1189 
1190 static double
min_sing_val(double m[4])1191 min_sing_val (double m[4])
1192 {
1193   double mm[4], mprod[4];
1194   double mag, max_mag = 0.0;
1195   double trace, det, b2_4ac, min_sing_val_squared, min_sing_val;
1196   int i;
1197 
1198   /* scale the elements of m so that the largest has magnitude unity, to
1199      reduce the chance of floating point roundoff error; this scaling will
1200      be undone at the end */
1201 
1202   for (i = 0; i < 4; i++)
1203     {
1204       mag = fabs (m[i]);
1205       if (mag > max_mag)
1206 	max_mag = mag;
1207     }
1208   if (max_mag <= 0.0)
1209     return 0.0;
1210   for (i = 0; i < 4; i++)
1211     mm[i] = m[i] / max_mag;
1212 
1213   /* Compute M times the transpose of M.  In the absence of floating-point
1214      rounding error, this product matrix, which is symmetric, will be
1215      "non-negative", i.e., its eigenvalues will be non-negative.  The
1216      singular values of M are (square roots of) its eigenvalues. */
1217 
1218   mprod[0] = mm[0]*mm[0] + mm[1]*mm[1];
1219   mprod[1] = mm[0]*mm[2] + mm[1]*mm[3];
1220   mprod[2] = mm[2]*mm[0] + mm[3]*mm[1];
1221   mprod[3] = mm[2]*mm[2] + mm[3]*mm[3];
1222 
1223   trace = mprod[0] + mprod[3];
1224   det = mprod[0] * mprod[3] - mprod[1] * mprod[2];
1225 
1226   if (det < 0.0)		/* rare rounding error problem */
1227     return 0.0;
1228 
1229   /* sing vals are (square roots of) solns of x^2 - trace * x + det = 0 */
1230 
1231   b2_4ac = trace * trace - 4 * det;
1232 
1233   if (b2_4ac < 0.0)
1234     /* a common, innocuous rounding error problem */
1235     b2_4ac = 0.0;
1236 
1237   min_sing_val_squared = 0.5 * (trace - sqrt (b2_4ac));
1238   if (min_sing_val_squared < 0.0) /* rare rounding error problem */
1239     return 0.0;
1240 
1241   min_sing_val = sqrt (min_sing_val_squared);
1242 
1243   /* return minimum singular value, not forgetting to undo the useful
1244      scaling with which we began */
1245   return min_sing_val * max_mag;
1246 }
1247