home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / fontutils-0.6-base.tgz / fontutils-0.6-base.tar / fsf / fontutils / bzrto / metafont.c < prev    next >
C/C++ Source or Header  |  1992-10-19  |  19KB  |  612 lines

  1. /* metafont.c: output the spline descriptions as a Metafont program.
  2.  
  3. Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include "bzr.h"
  22. #include "encoding.h"
  23. #include "filename.h"
  24. #include "font.h"
  25. #include "spline.h"
  26. #include "tfm.h"
  27. #include "vector.h"
  28.  
  29. #include "char.h"
  30. #include "input-ccc.h"
  31. #include "main.h"
  32. #include "metafont.h"
  33.  
  34. #include "ccc.h"
  35.  
  36.  
  37. /* Where the output goes.  */
  38. static FILE *mf_file;
  39. static string mf_filename;
  40.  
  41.  
  42. /* Macros for output to `mf_file'.  */
  43. #define OUT1(str, a)          fprintf (mf_file, str, a)
  44. #define OUT2(str, a1, a2)      fprintf (mf_file, str, a1, a2)
  45. #define OUT3(str, a1, a2, a3)      fprintf (mf_file, str, a1, a2, a3)
  46. #define OUT4(str, a1, a2, a3, a4) fprintf (mf_file, str, a1, a2, a3, a4)
  47.  
  48. #define OUT_LINE(str)      OUT1 ("%s\n", str)
  49. #define OUT_STRING(str)    OUT1 ("%s", str)
  50. #define OUT_STATEMENT(str) OUT1 ("%s;\n", str)
  51.  
  52. #define OUT_ZASSIGNMENT(lhs, c, l, s, pt)                \
  53.   do {                                    \
  54.     OUT3 (lhs " = ", c, l, s);                        \
  55.     OUT_POINT (pt);                            \
  56.     OUT_STRING (";\n");                            \
  57.   } while (0)
  58.  
  59. #define OUT_POINT(p) OUT2 ("(%.3fu,%.3fu)", p.x, p.y)
  60.  
  61. /* How to indent.  */
  62. #define INDENT "  "
  63.  
  64.  
  65. static string charcode_to_subr_name (charcode_type);
  66. static void out_bb_char (real_bounding_box_type *,
  67.                          real_coordinate_type *, bzr_char_type);
  68. static void out_move (real_coordinate_type *, real, real, boolean);
  69. static void output_bzr_body (bzr_char_type);
  70. static real_bounding_box_type output_ccc_body (ccc_type, char_type *[]);
  71. static void output_char_title (charcode_type);
  72. static void output_ligtable (list_type, char_type **);
  73. static void output_subr_start (charcode_type code);
  74. static tfm_char_type **read_tfm_files (list_type);
  75. static void read_tfm_file (tfm_char_type *[], string);
  76.  
  77. /* Start things off.  We want to write to a Metafont file, e.g.,
  78.    `cmss.mf', given a font name, e.g., `cmss10'.  */
  79.  
  80. void
  81. metafont_start_output (string output_name, bzr_preamble_type pre, 
  82.                tfm_global_info_type *info)
  83. {
  84.   unsigned this_param;
  85.   string current_time = now ();
  86.  
  87.   mf_filename = extend_filename (output_name, "mf");
  88.   mf_file = xfopen (mf_filename, "w");
  89.  
  90.   /* Output some identification.  */
  91.   OUT1 ("%% This file defines the Metafont font %s.\n", mf_filename);
  92.   OUT1 ("%% {%s}\n", BZR_COMMENT (pre));
  93.   OUT1 ("%% Generated %s.\n", current_time);
  94.   OUT_LINE ("% This font is in the public domain.");
  95.   
  96.   /* Output the size that our original data is based on.  The font can
  97.      be generated at sizes different than this.  */
  98.   OUT1 ("true_design_size# := %.3fpt#;\n", BZR_DESIGN_SIZE (pre));
  99.   
  100.   /* The real work is done in an auxiliary file.  */
  101.   OUT_STATEMENT ("input bzrsetup");
  102.  
  103.   /* Output the fontwide information given in the global `encoding_info'.  */
  104.   OUT1 ("font_coding_scheme := \"%s\";\n",
  105.         ENCODING_SCHEME_NAME (encoding_info));
  106.   OUT1 ("font_identifier := \"%s\";\n", output_name);
  107.  
  108.   if (info)
  109.     for (this_param = 1; this_param <= TFM_FONTDIMEN_COUNT (*info);
  110.          this_param++)
  111.       if (TFM_FONTDIMEN (*info, this_param) != 0.0)
  112.         OUT2 ("fontdimen %u: %.3fpt# * u#;\n", this_param,
  113.               TFM_FONTDIMEN (*info, this_param)); 
  114. }
  115.  
  116.  
  117. /* Used to output the character names as ``titles'', which Metafont
  118.    displays and puts on proofsheets in some circumstances.  */
  119.  
  120. static void
  121. output_char_title (charcode_type code)
  122. {
  123.   string title = ENCODING_CHAR_NAME (encoding_info, code);
  124.   
  125.   if (title == NULL)
  126.     title = concat ("character #", utoa (code));
  127.     
  128.   OUT1 (INDENT "\"%s\";\n", title);
  129. }
  130.  
  131. /* Output of subroutines.  The main part of the subroutine is done in
  132.    the same way as a character, it's just the adminstrative junk that's
  133.    different.  We have to remember some information in case this
  134.    subroutine later gets output as a character.  I'm not entirely sure
  135.    that we need it in all cases, but it seems cleaner to just always
  136.    save it.  */
  137.  
  138. void
  139. metafont_output_bzr_subr (bzr_char_type c)
  140. {
  141.   output_subr_start (CHARCODE (c));
  142.   output_bzr_body (c);
  143.   OUT_STATEMENT ("enddef");
  144. }
  145.  
  146.  
  147. void
  148. metafont_output_ccc_subr (ccc_type c, charcode_type code, char_type *subrs[])
  149. {
  150.   output_subr_start (code);
  151.   output_ccc_body (c, subrs);
  152.   OUT_STATEMENT ("enddef");
  153. }
  154.  
  155.  
  156. /* Output the common beginning of a subroutine.  The character name is a
  157.    Metafont ``title'' which gets output on proofsheets and sometimes on
  158.    the terminal.  */
  159.  
  160. static void
  161. output_subr_start (charcode_type code)
  162. {
  163.   OUT1 ("def %s = \n", charcode_to_subr_name (code));
  164.   output_char_title (code);
  165. }
  166.  
  167.  
  168. /* When we do need to output a subroutine, however, we have to choose a
  169.    name for it.  Since Metafont doesn't allow numerals in identifiers,
  170.    so we can't just use the decimal character code to distinguish the
  171.    subroutines.  Instead, we use the character name from the global
  172.    `encoding_info', or, if the encoding name is not strictly alphabetic
  173.    (e.g., `.notdef') a base-16 representation where zero is `a', ...,
  174.    fifteen is `o'.  */
  175.  
  176. #define OUT_CHAR_SUBR(c) OUT1 (INDENT "%s;\n", charcode_to_subr_name (c))
  177. #define SUBR_NAME_PREFIX "bzr_subr_"
  178.  
  179. static string
  180. charcode_to_subr_name (charcode_type code)
  181. {
  182.   string subr_name;
  183.   string name = ENCODING_CHAR_NAME (encoding_info, code);
  184.  
  185.   if (strspn (name, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
  186.       != strlen (name))
  187.     {
  188.       unsigned high = code >> 4;
  189.       unsigned low = code - (high << 4);
  190.       char alpha[3] = { high + 'a', low + 'a', 0 };
  191.  
  192.       subr_name = concat (SUBR_NAME_PREFIX, alpha);
  193.     }
  194.   else
  195.     subr_name = concat (SUBR_NAME_PREFIX, name);
  196.   
  197.   return subr_name;
  198. }
  199.  
  200. /* Output of characters.  Eventually we should do something about
  201.    italic corrections.  Also, it might be better to use a symbolic name
  202.    for the character code instead of hardwiring an integer constant.
  203.    Then different encodings could be selected at Metafont-time.  If the
  204.    character is already as a subroutine, just output a call.  */
  205.  
  206. void
  207. metafont_output_bzr_char (bzr_char_type c, char_type *subrs[])
  208. {
  209.   unsigned code = CHARCODE (c);
  210.   
  211.   OUT4 ("\nbeginchar (%d, %.3fu#, %.3fu#, %.3fu#);\n", 
  212.         code, CHAR_SET_WIDTH (c), CHAR_MAX_ROW (c), CHAR_DEPTH (c));
  213.   
  214.   if (subrs[code])
  215.     OUT_CHAR_SUBR (code);
  216.   else
  217.     {
  218.       output_char_title (code);
  219.       output_bzr_body (c);
  220.     }
  221.   
  222.   OUT_STATEMENT ("endchar");
  223. }
  224.  
  225.  
  226. void
  227. metafont_output_ccc_char (ccc_type c, charcode_type code, char_type *subrs[])
  228. {
  229.   real_bounding_box_type bb;
  230.   
  231.   if (subrs[code])
  232.     WARNING ("Sorry, CCC characters can't be subroutines yet");
  233.   else
  234.     {
  235.       OUT1 ("\nbegin_no_dimen_char (%d);\n", code);
  236.  
  237.       output_char_title (code);
  238.       bb = output_ccc_body (c, subrs);
  239.  
  240.       /* Set this character's dimensions: width, height, depth.  */
  241.       OUT3 (INDENT "set_char_dimens (%.3fu#, %.3fu#, %.3fu#);\n",
  242.             BB_WIDTH (bb), MAX_ROW (bb), -MIN_ROW (bb));
  243.       OUT_STATEMENT ("end_no_dimen_char");
  244.     }
  245. }
  246.  
  247. /* Output the shape in the BZR character C.  */
  248.  
  249. static void
  250. output_bzr_body (bzr_char_type c)
  251. {
  252.   unsigned this_list;
  253.   charcode_type code = CHARCODE (c);
  254.   spline_list_array_type shape = BZR_SHAPE (c);
  255.   boolean was_output = false;
  256.  
  257.   /* Go through the list of splines once, assigning the control points
  258.      to Metafont variables.  This allows us to produce more information
  259.      on proofsheets.  */
  260.  
  261.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (shape);
  262.        this_list++)
  263.     {
  264.       unsigned this_spline;
  265.       spline_list_type list = SPLINE_LIST_ARRAY_ELT (shape, this_list);
  266.  
  267.       if (SPLINE_LIST_LENGTH (list) == 0)
  268.         continue;
  269.         
  270.       was_output = true;
  271.       
  272.       OUT_ZASSIGNMENT (INDENT "z%u\\%u\\%us", code, this_list, 0,
  273.                        START_POINT (SPLINE_LIST_ELT (list, 0))); 
  274.  
  275.       for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (list);
  276.            this_spline++) 
  277.         {
  278.           spline_type s = SPLINE_LIST_ELT (list, this_spline);
  279.  
  280.           if (SPLINE_DEGREE (s) == CUBIC)
  281.             {
  282.               OUT_ZASSIGNMENT (INDENT "z%u\\%u\\%uc1", code, this_list,
  283.                    this_spline, CONTROL1 (s)); 
  284.               OUT_ZASSIGNMENT (INDENT "z%u\\%u\\%uc2", code, this_list,
  285.                    this_spline, CONTROL2 (s)); 
  286.             }
  287.  
  288.           /* The last point in the list is also the first point, and we
  289.              don't want to output two variables for the same point.  */
  290.           if (this_spline < SPLINE_LIST_LENGTH (list) - 1)
  291.             OUT_ZASSIGNMENT (INDENT "z%u\\%u\\%u", code, this_list,
  292.                      this_spline, END_POINT (s));
  293.         }
  294.     }
  295.  
  296.  
  297.   /* Go through the list of splines again, outputting the
  298.      path-construction commands.  */
  299.  
  300.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (shape);
  301.        this_list++)
  302.     {
  303.       unsigned this_spline;
  304.       spline_list_type list = SPLINE_LIST_ARRAY_ELT (shape, this_list);
  305.  
  306.       if (SPLINE_LIST_LENGTH (list) == 0)
  307.         continue;
  308.         
  309.       OUT3 (INDENT "fill_or_unfill z%u\\%u\\%us\n", code, this_list, 0);
  310.  
  311.       for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (list);
  312.            this_spline++)
  313.         {
  314.           spline_type s = SPLINE_LIST_ELT (list, this_spline);
  315.  
  316.           if (SPLINE_DEGREE (s) == LINEAR)
  317.             OUT_STRING (INDENT INDENT "--");
  318.           else
  319.             {
  320.               OUT_STRING (INDENT INDENT "..controls ");
  321.               OUT3 ("z%u\\%u\\%uc1 and ", code, this_list, this_spline);
  322.               OUT3 ("z%u\\%u\\%uc2..", code, this_list, this_spline);
  323.             }
  324.       
  325.           if (this_spline < SPLINE_LIST_LENGTH (list) - 1)
  326.             OUT3 ("z%u\\%u\\%u\n", code, this_list, this_spline);
  327.         }
  328.         
  329.       OUT_STRING ("cycle;\n");
  330.     }
  331.   
  332.   /* The plain Metafont `labels' command makes it possible to produce
  333.      proofsheets with all the points labeled.  We always want labels for
  334.      the starting and ending points, and possibly labels for the control
  335.      points on each spline, so we have our own command `proof_labels',
  336.      defined in `bzrsetup.mf'.  */
  337.      
  338.   if (was_output)
  339.     OUT_LINE (INDENT "proof_labels (");
  340.   
  341.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (shape);
  342.        this_list++)
  343.     {
  344.       unsigned this_spline;
  345.       spline_list_type list = SPLINE_LIST_ARRAY_ELT (shape, this_list);
  346.  
  347.       for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (list);
  348.            this_spline++)
  349.         OUT3 (INDENT INDENT "%u\\%u\\%u,\n", code, this_list, this_spline);
  350.     }
  351.  
  352.   if (was_output)
  353.     OUT_STRING (");\n");
  354. }
  355.  
  356.  
  357. /* Output the definition of CCC character CCC_CHAR.  All the subroutines
  358.    must be defined in SUBRS.  */
  359.  
  360. #define OUT_HMOVE(h, a) out_move (¤t, h, 0.0, a)
  361. #define OUT_VMOVE(v, a) out_move (¤t, 0.0, v, a)
  362.  
  363. static real_bounding_box_type
  364. output_ccc_body (ccc_type ccc_char, char_type *subrs[])
  365. {
  366.   unsigned this_cmd;
  367.   real_coordinate_type current = (real_coordinate_type) { 0.0, 0.0 };
  368.   real_bounding_box_type bb
  369.     = (real_bounding_box_type) { FLT_MAX, FLT_MIN, FLT_MAX, FLT_MIN };
  370.  
  371.   for (this_cmd = 0; this_cmd < LIST_SIZE (ccc_char); this_cmd++)
  372.     { 
  373.       ccc_cmd_type cmd = *(ccc_cmd_type *) LIST_ELT (ccc_char, this_cmd);
  374.       
  375.       switch (CCC_OPCODE (cmd))
  376.         {
  377.         case TR_SETCHAR:
  378.           {
  379.             char_type subr = *subrs[CCC_CHARCODE (cmd)];
  380.  
  381.             if (CHAR_CLASS (subr) == bzr_char_class)
  382.               {
  383.                 bzr_char_type bzr_char = *CHAR_BZR (subr);
  384.                 
  385.                 update_real_bounding_box (&bb, current);
  386.                 OUT_HMOVE (CHAR_LSB (bzr_char), false);
  387.  
  388.                 out_bb_char (&bb, ¤t, bzr_char);
  389.  
  390.                 OUT_HMOVE (CHAR_RSB (bzr_char), false);
  391.                 update_real_bounding_box (&bb, current);
  392.               }
  393.             else
  394.               /* We haven't implemented this yet since it doesn't seem
  395.                  all that necessary at the moment, and it would require
  396.                  figuring out a bounding box and possibly side bearings
  397.                  for CCC character -- certainly not impossible, but not
  398.                  trivial, either.  */
  399.               WARNING ("Sorry, I can't `setchar' a CCC char yet");
  400.           }
  401.           break;
  402.  
  403.         case TR_SETCHARBB:
  404.           {
  405.             char_type subr = *subrs[CCC_CHARCODE (cmd)];
  406.  
  407.             if (CHAR_CLASS (subr) == bzr_char_class)
  408.               out_bb_char (&bb, ¤t, *CHAR_BZR (subr));
  409.             else
  410.               WARNING ("Sorry, I can't `setcharbb' a CCC char yet");
  411.           }
  412.           break;
  413.  
  414.         case TR_HMOVE:
  415.           OUT_HMOVE (CCC_DIMEN_AMOUNT (cmd), CCC_DIMEN_ABSOLUTE (cmd));
  416.           break;
  417.  
  418.         case TR_VMOVE:
  419.           OUT_VMOVE (CCC_DIMEN_AMOUNT (cmd), CCC_DIMEN_ABSOLUTE (cmd));
  420.           break;
  421.  
  422.         default:
  423.           FATAL1 ("Bad command %d in CCC character", CCC_OPCODE (cmd));
  424.     }
  425.     }
  426.   
  427.   return bb;
  428. }
  429.  
  430.  
  431. /* Move by H horizontally and V vertically.  If ABSOLUTE is true, use
  432.    `pt' for units, otherwise `u'.  Update CURRENT.  */
  433.  
  434. static void
  435. out_move (real_coordinate_type *current, real h, real v, boolean absolute)
  436. {
  437.   string units = absolute ? "pt" : "u";
  438.   
  439.   OUT4 (INDENT "move (%.3f%s, %.3f%s);\n", (real) h, units, (real) v, units);
  440.   current->x += h;
  441.   current->y += v;
  442. }
  443.  
  444.  
  445. /* Output the character shape in BZR_CHAR, sans side bearings.  Update
  446.    CURRENT by the width of the shape, and update BB by the points before
  447.    and after the character.  We take into account the height of the
  448.    character when updating BB, even though we don't translate the
  449.    current point up there.  */
  450.  
  451. #define VOFFSET_POINT(voffset) \
  452.   ((real_coordinate_type) { current->x, current->y + (voffset) })
  453.  
  454. static void
  455. out_bb_char (real_bounding_box_type *bb, real_coordinate_type *current,
  456.              bzr_char_type bzr_char)
  457. {
  458.   /* Update BB with the current point.  */
  459.   update_real_bounding_box (bb, VOFFSET_POINT (CHAR_MIN_ROW (bzr_char)));
  460.  
  461.   /* Output the call to the character.  */
  462.   OUT_CHAR_SUBR (CHARCODE (bzr_char));
  463.   
  464.   /* Move by the width of the shape.  */
  465.   out_move (current, BB_WIDTH (CHAR_BB (bzr_char)), 0, false);
  466.  
  467.   /* Update the BB again with the new current point.  */
  468.   update_real_bounding_box (bb, VOFFSET_POINT (CHAR_MAX_ROW (bzr_char)));
  469. }
  470.  
  471. /* We use a macro `bye' to finish off the Metafont output, instead of the
  472.    primitive `end', for maximal flexibility.  (Although in practice it
  473.    makes no difference, since `end' can be redefined just like `bye'.)  */
  474.  
  475. void
  476. metafont_finish_output (list_type bzr_names, char_type **chars)
  477. {
  478.   output_ligtable (bzr_names, chars);
  479.   
  480.   OUT_LINE ("\nbye.");
  481.   xfclose (mf_file, mf_filename);
  482. }
  483.  
  484.  
  485. /* True if the character CODE exists in the array `tfm_chars'.  */
  486.  
  487. #define HAVE_CHAR_P(code) (tfm_chars[code] != NULL)
  488.  
  489. /* Start or continue the ligtable for the character CODE.  */
  490. #define LIGTABLE_ENTRY(code)                        \
  491.   if (output_something)                            \
  492.     OUT_LINE (",");                            \
  493.   else                                    \
  494.     {                                    \
  495.       output_something = true;                        \
  496.       OUT1 ("ligtable hex\"%x\":\n", code);                \
  497.     }
  498.  
  499.  
  500. /* Output the kerning and ligature information for this font.  Omit it
  501.    for any character which didn't get output.  */
  502.  
  503. static void
  504. output_ligtable (list_type bzr_names, char_type **chars)
  505. {
  506.   unsigned code;
  507.   tfm_char_type **tfm_chars = read_tfm_files (bzr_names);
  508.   
  509.   for (code = starting_char; code <= ending_char; code++)
  510.     {
  511.       list_type kern_list, ligature_list;
  512.       unsigned this_kern, this_lig;
  513.       tfm_char_type *c;
  514.       boolean output_something = false;
  515.       
  516.       /* If we don't have TFM information for CODE, don't do anything.  */
  517.       if (!HAVE_CHAR_P (code))
  518.         continue;
  519.  
  520.       c = tfm_chars[code];
  521.  
  522.       /* Output the kerns.  */
  523.       kern_list = c->kern;
  524.       for (this_kern = 0; this_kern < LIST_SIZE (kern_list); this_kern++)
  525.         {
  526.           tfm_kern_type *kern = LIST_ELT (kern_list, this_kern);
  527.  
  528.           if (HAVE_CHAR_P (kern->character))
  529.             {
  530.               LIGTABLE_ENTRY (code);
  531.               OUT2 ("hex\"%x\" kern %.3fpt#", kern->character, kern->kern);
  532.             }
  533.         }
  534.       
  535.       /* Output the ligatures.  */
  536.       ligature_list = c->ligature;
  537.       for (this_lig = 0; this_lig < LIST_SIZE (ligature_list); this_lig++)
  538.         {
  539.           tfm_ligature_type *lig = LIST_ELT (ligature_list, this_lig);
  540.  
  541.           if (HAVE_CHAR_P (lig->character) && HAVE_CHAR_P (lig->ligature))
  542.           {
  543.           LIGTABLE_ENTRY (code);
  544.             OUT2 ("hex\"%x\" =: hex\"%x\"", lig->character, lig->ligature);
  545.           }
  546.         }
  547.  
  548.       if (output_something)
  549.         OUT_LINE (";");
  550.     }
  551. }
  552.  
  553.  
  554. /* We want to retain kerning and ligature information from the original
  555.    TFM files, if such exist.  Our goal here is to read the TFM file
  556.    corresponding to each name in BZR_NAMES and return the union of all
  557.    the characters found as an array of `tfm_char_type's.  We do not
  558.    complain about nonexistent TFM files.  */
  559.  
  560. static tfm_char_type **
  561. read_tfm_files (list_type bzr_names)
  562. {
  563.   unsigned c, name;
  564.   tfm_char_type **tfm_chars = XTALLOC (MAX_CHARCODE + 1, tfm_char_type *);
  565.   
  566.   /* Assume we'll find nothing.  */
  567.   for (c = 0; c <= MAX_CHARCODE; c++)
  568.     tfm_chars[c] = NULL;
  569.  
  570.   /* Read all the files, updating the array as we go.  */
  571.   for (name = 0; name < LIST_SIZE (bzr_names); name++)
  572.     {
  573.       string *bzr_name = LIST_ELT (bzr_names, name);
  574.       
  575.       read_tfm_file (tfm_chars, *bzr_name);
  576.     }
  577.  
  578.   return tfm_chars;
  579. }
  580.  
  581.  
  582. /* If the TFM file corresponding to BZR_NAME exists, read it and update
  583.    TFM_CHARS.  We do not overwrite existing TFM characters, since
  584.    earlier files should override later files.  (The first file is the
  585.    main input file.)  Arguably multiple characters should be a warning,
  586.    but I'm not sure.  */
  587.  
  588. static void
  589. read_tfm_file (tfm_char_type *tfm_chars[], string bzr_name)
  590. {
  591.   string tfm_root = make_suffix (bzr_name, "tfm");
  592.   string tfm_name = find_tfm_filename (tfm_root);
  593.  
  594.   if (tfm_name != NULL && tfm_open_input_file (tfm_name))
  595.     {
  596.       unsigned code;
  597.       tfm_char_type *file_chars = tfm_get_chars ();
  598.       
  599.       for (code = 0; code <= MAX_CHARCODE; code++)
  600.         {
  601.           if (tfm_chars[code] == NULL && TFM_CHAR_EXISTS (file_chars[code]))
  602.             {
  603.               tfm_chars[code] = XTALLOC1 (tfm_char_type);
  604.               *tfm_chars[code] = file_chars[code];
  605.             }
  606.         }
  607.  
  608.       free (file_chars);
  609.       tfm_close_input_file ();
  610.     }
  611. }
  612.