/* # output.c: output # # Copyright (C) 1992, 2011 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # */ #include "config.h" #include "filename.h" #include "libfile.h" #include "report.h" #include "main.h" #include "output.h" /* The assignments to the TFM fontdimen values. (-fontdimens) */ string fontdimens = NULL; /* The name of the output file specified by the user. (-output-file) */ string output_name = NULL; /* The character code to use for the x-height. (-xheight-char) */ charcode_type xheight_char = 120; /* `x' in ASCII. */ static void finalize_kern_list (tfm_char_type *, char_type *[]); static void finalize_lig_list (tfm_char_type *, char_type *[]); static tfm_global_info_type find_tfm_info (char_type *[]); static gf_char_type make_gf_char (char_info_type); static void update_tfm_info_from_symtab (tfm_global_info_type *); static void write_gf (bitmap_font_type, char_type *[]); static void write_tfm (char_type *[]); static boolean write_tfm_char (charcode_type, char_type *[]); /* Output a TFM and (optionally) GF file with the characters in CHARS. Use the characters from BITMAP_FONT for the shapes. We use an existing TFM file, the encoding file, and command line arguments to determine the metric information. */ void output_font (bitmap_font_type bitmap_font, char_type *chars[]) { write_tfm (chars); if (!no_gf) write_gf (bitmap_font, chars); } /* Write a TFM file with CHARS. */ static void write_tfm (char_type *chars[]) { unsigned this_char; unsigned char_count = 0; string pl_output_name = extend_filename (output_name, "pl"); tfm_global_info_type tfm_info = find_tfm_info (chars); /* If `have_tfm', we've already opened the input file. */ string input_tfm_name = have_tfm ? tfm_input_filename () : NULL; /* We remove the suffix before making the PL name because if the user said `-output-file=foo.bar', it's the TFM file which should be named `foo.bar'; the PL file is still named `foo.pl'. */ string output_root = remove_suffix (output_name); string pl_name = extend_filename (output_root, "pl"); string output_tfm_name = extend_filename (output_name, "tfm"); /* If we'd overwrite the input, change the name. */ if (input_tfm_name != NULL && same_file_p (input_tfm_name, output_tfm_name)) { pl_name = make_prefix ("x", pl_name); output_tfm_name = make_prefix ("x", output_tfm_name); } /* Let's write the TFM file first, but to a PL file, which is easier. */ if (!tfm_open_pl_output_file (pl_output_name)) FATAL_PERROR (pl_output_name); REPORT1 ("\nWriting PL file `%s':\n", pl_output_name); /* Write the global information we just figured out. */ tfm_put_global_info (tfm_info); /* Only look between `starting_char' and `ending_char', i.e., the user's -range. */ for (this_char = starting_char; this_char <= ending_char; this_char++) { if (write_tfm_char (this_char, chars)) REPORT2 ("[%d]%c", this_char, ++char_count % 13 == 0 ? '\n' : ' '); } if (char_count % 13 != 0) REPORT ("\n"); tfm_convert_pl (output_tfm_name, verbose); tfm_close_pl_output_file (); } /* Return the global TFM information structure: we read from an existing TFM file, if the global `have_tfm' is true, then from the encoding information in the global `encoding_info', then from the symbol table, and finally from the command line (i.e., the global `fontdimens). */ static tfm_global_info_type find_tfm_info (char_type *chars[]) { tfm_global_info_type tfm_info; if (have_tfm) { tfm_info = tfm_get_global_info (); TFM_CHECKSUM (tfm_info) = 0; /* Can't use the old checksum. */ } else { /* No TFM file to be found, so initialize the structure with the defaults. */ tfm_info = tfm_init_global_info (); TFM_DESIGN_SIZE (tfm_info) = get_designsize_in_points (); } /* `encoding_info' has been set at this point, one way or another; use whatever codingscheme it says. */ TFM_CODING_SCHEME (tfm_info) = ENCODING_SCHEME_NAME (encoding_info); /* The `fontsize' fontdimen is typically the same as the designsize. (I can't imagine when it would be anything else, but it seems prudent to let the user-specified values in the symbol table or commandline override this.) */ tfm_set_fontsize (&tfm_info); /* Use the bitmap height of `xheight_char', if it exists, for the default x-height. If not, guess .5 of the designsize. */ TFM_FONTDIMEN (tfm_info, TFM_XHEIGHT_PARAMETER) = chars[xheight_char] != NULL ? PIXELS_TO_POINTS ( CHAR_HEIGHT (*CHAR_BITMAP_INFO (*chars[xheight_char])), dpi_real) : .5 * TFM_DESIGN_SIZE (tfm_info); /* If any fontdimens were set in the CMI files, use them. */ update_tfm_info_from_symtab (&tfm_info); /* Finally, update the fontdimens from the command line. */ tfm_set_fontdimens (fontdimens, &tfm_info); return tfm_info; } /* It is not an error for the user to use a fontdimen name for something else, so we do not complain if we cannot resolve such a name. */ static void update_tfm_info_from_symtab (tfm_global_info_type *tfm_info) { unsigned p; for (p = 1; p <= TFM_MAX_FONTDIMENS; p++) { string name = tfm_fontdimen_name (p); symval_type *sv = name ? symtab_lookup (name) : NULL; if (sv != NULL) { if (symval_resolve (sv)) tfm_set_fontdimen (tfm_info, p, PIXELS_TO_POINTS (SYMVAL_REAL (*sv), dpi_real)); } } } /* Return true if we have TFM info for CODE in CHARS. */ static boolean have_tfm_char (charcode_type code, char_type *chars[]) { boolean ok; /* If we have no `char_type' for CODE, have to give up. */ if (chars[code] == NULL) ok = false; /* If we have no TFM info in the `char_type', have to give up. */ else if (CHAR_TFM_INFO (*chars[code]) == NULL) ok = false; /* xx what about _EXISTS? */ else ok = true; return ok; } /* Write the single character CODE from CHARS to the TFM file (assume the latter is open), if we have all the information. Return true if we wrote it, else false. */ static boolean write_tfm_char (charcode_type code, char_type *chars[]) { boolean ok; if (have_tfm_char (code, chars)) { tfm_char_type c = *CHAR_TFM_INFO (*chars[code]); /* The remaining thing to check is that the kerns and ligatures refer only to (other) characters which will be output. */ finalize_kern_list (&c, chars); finalize_lig_list (&c, chars); /* Write the character. */ tfm_put_char (c); ok = true; } else ok = false; return ok; } /* Look through the kern list in C for kerns which refer to characters in CHARS we won't be outputting. Remove such. */ static void finalize_kern_list (tfm_char_type *c, char_type *chars[]) { unsigned e; list_type good_list = list_init (); for (e = 0; e < LIST_SIZE (TFM_KERN (*c)); e++) { tfm_kern_type *k = LIST_ELT (TFM_KERN (*c), e); if (have_tfm_char (k->character, chars)) { tfm_kern_type *new_kern = LIST_TAPPEND (&good_list, tfm_kern_type); *new_kern = *k; } } /* Throw away the old list. */ list_free (&TFM_KERN (*c)); /* Insert the good list. */ TFM_KERN (*c) = good_list; } /* Routine for ligatures analogous to `finalize_kern_list'. */ static void finalize_lig_list (tfm_char_type *c, char_type *chars[]) { unsigned e; list_type good_list = list_init (); for (e = 0; e < LIST_SIZE (TFM_LIGATURE (*c)); e++) { tfm_ligature_type *lig = LIST_ELT (TFM_LIGATURE (*c), e); if (have_tfm_char (lig->character, chars) && have_tfm_char (lig->ligature, chars)) { tfm_ligature_type *new_lig = LIST_TAPPEND (&good_list, tfm_ligature_type); *new_lig = *lig; } } /* Throw away the old list. */ list_free (&TFM_LIGATURE (*c)); /* Insert the good list. */ TFM_LIGATURE (*c) = good_list; } /* Write a GF file using BITMAP_FONT and CHARS. Don't overwrite an existing file named `BITMAP_FONT_FILENAME (FONT)'. */ static void write_gf (bitmap_font_type font, char_type *chars[]) { unsigned this_char; unsigned char_count = 0; /* We depend upon `output_name' being set when we are called. */ string gf_output_name = extend_filename (output_name, concat (dpi, "gf")); if (same_file_p (BITMAP_FONT_FILENAME (font), gf_output_name)) gf_output_name = make_prefix ("x", gf_output_name); if (!gf_open_output_file (gf_output_name)) FATAL_PERROR (gf_output_name); REPORT1 ("\nWriting GF file `%s':\n", gf_output_name); /* Write the first part of the file. */ gf_put_preamble (concat4 ("charspace output ", now () + 4, " from ", BITMAP_FONT_COMMENT (font))); for (this_char = starting_char; this_char <= ending_char; this_char++) { if (chars[this_char] != NULL && CHAR_BITMAP_INFO (*chars[this_char]) != NULL) { gf_put_char (make_gf_char (*CHAR_BITMAP_INFO (*chars[this_char]))); REPORT2 ("[%d]%c", this_char, ++char_count % 13 == 0 ? '\n' : ' '); } } if (char_count % 13 != 0) REPORT ("\n"); /* Write the last part of the file. */ gf_put_postamble (real_to_fix (get_designsize_in_points ()), dpi_real, dpi_real); gf_close_output_file (); } /* Turn BITMAP_CHAR (the generic structure) into a `gf_char_type'. We assume there are no real changes to be made in BITMAP_CHAR, i.e., that we need only copy the relevant fields. */ static gf_char_type make_gf_char (char_info_type bitmap_char) { gf_char_type gf_char; GF_CHARCODE (gf_char) = CHARCODE (bitmap_char); GF_BITMAP (gf_char) = CHAR_BITMAP (bitmap_char); GF_CHAR_BB (gf_char) = CHAR_BB (bitmap_char); GF_H_ESCAPEMENT (gf_char) = CHAR_SET_WIDTH (bitmap_char); GF_TFM_WIDTH (gf_char) = CHAR_TFM_WIDTH (bitmap_char); return gf_char; }