diff options
Diffstat (limited to 'fontconvert/main.c')
-rw-r--r-- | fontconvert/main.c | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/fontconvert/main.c b/fontconvert/main.c new file mode 100644 index 0000000..a50b311 --- /dev/null +++ b/fontconvert/main.c @@ -0,0 +1,747 @@ +/* fontconvert -- various operations on a bitmap font. + +Copyright (C) 1992 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 2, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "config.h" + +#include "charspec.h" +#include "cmdline.h" +#include "encoding.h" +#include "font.h" +#include "libfile.h" +#include "list.h" +#include "report.h" + +#include "filter.h" +#include "output-epsf.h" +#include "output-gf.h" +#include "output-tfm.h" +#include "random.h" + + +/* How much to adjust the baseline of output characters by. We rely on + this being initialized to all zeros by C. (-baseline-adjust) */ +int baseline_adjust[MAX_CHARCODE + 1]; + +/* If `column_split[N]' is non-NULL, we break character N into new + characters at each of the columns specified. We depend on this being + initialized to all zeros. (-column-split) */ +int *column_split[MAX_CHARCODE + 1]; + +/* The names of fonts to be concatenated onto the main font in the + output. (-concat) */ +list_type fontname_list; + +/* The resolution of the font we will read, in pixels per inch. We + don't deal with nonsquare pixels. (-dpi) */ +string dpi = "300"; + +/* The design size which will affect both the GF and TFM output, unless + the tfm-header `designsize' option overrides it. (-designsize) + `design_size_ratio' is the ratio of the input design size to the + output design size. */ +real design_size = 0.0; +static real design_size_ratio; + +/* The name of the encoding file specified by the user, and the + structure we parse it into. (-encoding) */ +static encoding_info_type *encoding_info = NULL; + +/* If `omit[n]' is true for an element N, we throw away that character + in that output. (-omit) */ +static boolean omit[MAX_CHARCODE + 1]; + +/* An explicitly specified output filename. (-output-file) */ +const_string output_name = NULL; + +/* Says which characters we should process. This is independent of the + ordering in the font file. (-range) */ +int starting_char = 0; +int ending_char = MAX_CHARCODE; + +/* The variable `translate' maps character codes in the input font to + character codes in the output; by default, maps everything to + themselves. (-remap) */ +charcode_type translate[MAX_CHARCODE + 1]; + +/* Says whether to output an EPS file for each character. (-epsf) */ +boolean wants_epsf = false; + +/* Says whether to output a GF file. (-gf) */ +boolean wants_gf = false; + +/* Says whether to output the font as plain text. (-text) */ +boolean wants_text = false; + +/* Says whether to output a TFM file. (-tfm) */ +boolean wants_tfm = false; + + + +static void append_concat_list (list_type *, string); +static bitmap_type column_extract (bitmap_type, unsigned, unsigned); +static void do_char (char_info_type); +static void do_split_char (char_info_type, int[]); +static string read_command_line (int, string []); +static void scan_baseline_adjust (string); +static void scan_column_split (string); +static void scan_omit_list (string); +static void scan_remap_list (string); + +/* Fontconvert provides a grabbag of operations: almost every + manipulation on bitmap fonts we found useful we threw here. */ + +int +main (int argc, string argv[]) +{ + int code; + bitmap_font_type f; + string *first_element; + string font_name; + unsigned this_font; + unsigned char_count = 0; + + /* Use Kpathsea path searching. */ + kpse_set_progname (argv[0]); + + /* Initialize the character code translation array before parsing the + command line, since we probably will not be told to remap every + character. */ + for (code = 0; code <= MAX_CHARCODE; code++) + translate[code] = code; + + /* Initialize the list of fonts to do to have one empty element at the + beginning, which we will fill in with the main font name. */ + fontname_list = list_init (); + first_element = LIST_TAPPEND (&fontname_list, string); + + /* Read all the arguments. */ + font_name = read_command_line (argc, argv); + + /* Put the main font name in the list. */ + *first_element = font_name; + + /* If no output name was specified on the command line, use the root + part of the input name. We've already removed the suffix in + `read_command_line'. */ + if (output_name == NULL) + output_name = basename (font_name); + + if (wants_gf && wants_tfm && find_suffix (output_name) != NULL) + FATAL ("The GF and TFM output files' suffixes can't be the same"); + + /* Open the main font specially, since we need to know some basic + information for most of the output formats. We don't need to close + it again, as opening a font twice does no harm. */ + f = get_bitmap_font (font_name, atou (dpi)); + + if (wants_epsf) + epsf_start_output (output_name); + if (wants_gf) + gf_start_output (font_name, output_name, dpi, + BITMAP_FONT_COMMENT (f)); + if (wants_tfm) + tfm_start_output (font_name, output_name, design_size, + BITMAP_FONT_DESIGN_SIZE (f)); + + + /* Have to do this after `tfm_start_output', as it needs to know if + design_size is set or not. */ + if (design_size == 0.0) + design_size = BITMAP_FONT_DESIGN_SIZE (f); + + design_size_ratio = BITMAP_FONT_DESIGN_SIZE (f) / design_size; + + /* Go through each input font. */ + for (this_font = 0; this_font < LIST_SIZE (fontname_list); this_font++) + { + string font_name = *(string *) LIST_ELT (fontname_list, this_font); + f = get_bitmap_font (font_name, atou (dpi)); + + if (wants_tfm) + tfm_start_font (font_name); + + REPORT1 ("(%s", BITMAP_FONT_FILENAME (f)); + + /* The main loop: convert each character. */ + for (code = starting_char; code <= ending_char; code++) + { + char_info_type *c = get_char (font_name, code); + + if (c == NULL) continue; + + REPORT2 ("%c[%u", ++char_count % 8 ? ' ' : '\n', code); + + if (omit[code]) + REPORT (" omit"); + else + { + /* If this character isn't supposed to split, just output it. + Otherwise, output each of the pieces after splitting. */ + if (column_split[code] == NULL) + do_char (*c); + else + do_split_char (*c, column_split[code]); + } + + REPORT ("]"); + } + + close_font (font_name); + REPORT (")\n"); + } + + if (wants_epsf) + epsf_finish_output (); + + /* We must finish the TFM output before GF, because `gf_finish_output' + might want to open a TFM file -- and we can only have one TFM file + open at a time. (Because we haven't rewritten the TFM library.) */ + if (wants_tfm) + tfm_finish_output (); + + if (wants_gf) + gf_finish_output (design_size, atof (dpi)); + + return 0; +} + +/* Output the character in whatever forms have been requested. */ + +static void +do_char (char_info_type c) +{ + static boolean char_done_p[MAX_CHARCODE + 1]; + + charcode_type original_code = CHARCODE (c); + + /* Do the character code translation. */ + charcode_type code = CHARCODE (c) = translate[original_code]; + + if (char_done_p[code]) + { + WARNING1 ("Character %d already output", CHARCODE (c)); + return; + } + + char_done_p[code] = true; + + if (filter_passes > 0) + filter_bitmap (CHAR_BITMAP (c)); + + if (random_max > 0.0) + { + bounding_box_type adjust_bb; + randomize_bitmap (&CHAR_BITMAP (c), &adjust_bb); + CHAR_MIN_ROW (c) -= MIN_ROW (adjust_bb); + CHAR_MAX_ROW (c) += MAX_ROW (adjust_bb); + CHAR_MIN_COL (c) -= MIN_COL (adjust_bb); + CHAR_MAX_COL (c) += MAX_COL (adjust_bb); + } + + /* If the `baseline_adjust' value is positive, the baseline should + move up, i.e., we subtract from the height and add to the depth. + But since the depth is represented as the minimum row, which can be + negative, we must subtract from it to make it ``larger''. */ + CHAR_MIN_ROW (c) -= baseline_adjust[code]; + CHAR_MAX_ROW (c) -= baseline_adjust[code]; + + /* Text output is already implemented in the library. */ + if (wants_text) + { + puts ("\f"); + print_char (stdout, c); + } + + if (wants_epsf) + epsf_output_char (c); + + if (wants_gf) + gf_output_char (c, design_size_ratio); + + if (wants_tfm) + tfm_output_char (c, atof (dpi)); + + if (original_code != code) + REPORT1 ("->%u", code); + + /* Free the space here, instead of in the caller, since we may + change the bitmap. */ + free_bitmap (&CHAR_BITMAP (c)); +} + + +/* Output the character C in pieces, the division points being the + columns in SPLIT. */ + +static void +do_split_char (char_info_type c, int split[]) +{ + unsigned last_col = 0; + unsigned n_split = 0; + + REPORT (" ("); + + /* We exit the loop after splitting the last character. */ + while (true) + { + int diff; + char_info_type split_char = c; + int this_col = *split++; + unsigned split_col = this_col != -1 + ? this_col : BITMAP_WIDTH (CHAR_BITMAP (c)); + + /* Change the character code of this piece. */ + CHARCODE (split_char) += n_split; + + REPORT1 ("%u", CHARCODE (split_char)); + + /* Take the chunk from the big bitmap. */ + CHAR_BITMAP (split_char) + = column_extract (CHAR_BITMAP (c), last_col, split_col); + + /* Move the right column over. */ + diff = BITMAP_WIDTH (CHAR_BITMAP (c)) + - BITMAP_WIDTH (CHAR_BITMAP (split_char)); + CHAR_MAX_COL (split_char) -= diff; + CHAR_SET_WIDTH (split_char) -= diff; + + /* Output it. */ + do_char (split_char); + + /* Exit the loop if we just output the last piece. */ + if (this_col == -1) + break; + + n_split++; + last_col = this_col; + REPORT (","); + } + + REPORT (")"); + free_bitmap (&CHAR_BITMAP (c)); +} + +/* Return the part of the bitmap SOURCE that lies between the columns + START and FINISH - 1, inclusive. */ + +static bitmap_type +column_extract (bitmap_type source, unsigned start, unsigned finish) +{ + unsigned this_row; + dimensions_type d = { BITMAP_HEIGHT (source), finish - start }; + bitmap_type answer = new_bitmap (d); + + /* Move to the given starting column. */ + BITMAP_BITS (source) += start; + + for (this_row = 0; this_row < BITMAP_HEIGHT (source); this_row++) + { + one_byte *answer_row = BITMAP_ROW (answer, this_row); + + memcpy (answer_row, BITMAP_BITS (source), BITMAP_WIDTH (answer)); + BITMAP_BITS (source) += BITMAP_WIDTH (source); + } + + return answer; +} + +/* Reading the options. */ + +/* This is defined in version.c. */ +extern string version_string; + +#define USAGE "Options: +<font_name> should be a filename, possibly with a resolution, e.g., + `cmr10' or `cmr10.300'.\n" \ + GETOPT_USAGE \ +"baseline-adjust <char1>:<integer1>,<char2>:<integer2>,...: move the baseline + of each <char> by the corresponding <integer>. A positive number + moves the baseline up, a negative one down. +column-split <char>@<column1>,...,<columnN>: split the character with + code <char> (before remapping) before each of the <column>s, producing n + new characters, with codes <char>, <char> + 1, ..., <char> + n, whose + bitmaps go from 0 to <column1> - 1 (inclusive), then <column1> to + <column2> - 1, ..., from <columnN> to the bitmap width. + Give the <column>s in bitmap coordinates, i.e., starting at zero. + To split more than one character, give this option for each. +concat <font_name>,<font_name>,...: concatenate the main input font with + the given <font_name>s; if a character code exists in more than one + font, it's the first occurrence that counts. +designsize <real>: use this as the design size for both the GF and TFM + output files, if any, unless overridden by `designsize' in the + `tfm-header' option. +dpi <unsigned>: use a resolution of <unsigned>; default is 300. +encoding <filename>: read encoding information for the character specs + from `<filename>.enc'; there is no default. Must come before any + options which use character specs. +epsf: output each character as an Encapsulated PostScript file named + <font_name>-<code>.eps, where <code> is the character code in decimal. +filter-passes <unsigned>: do the filtering this many times on each + character; default is 0. +filter-size <unsigned>: half the size of the filter cell, i.e., a side + is this number * 2 + 1; default is 1. +filter-threshold <real>: if the average of the pixels in the filter cell + is greater than this, change the pixel; default is .5. +fontdimens <fontdimen>:<real>,<fontdimen>:<real>,...: assign each <real> + to the corresponding <fontdimen> when outputting a TFM file. A + <fontdimen> can be either one of the standard names (in either upper + or lowercase), or a number between 1 and 30. Each <real> is taken to + be in points (except in the case of the <fontdimen> `slant' (parameter + 1), which is a dimensionless number). +gf: write a GF file to `<font_name>.<dpi>gf'. If this would overwrite the + input file, write to `x<font_name>.<dpi>gf' instead. +help: print this message. +omit <char1>,<char2>,...: omit the characters with the given codes or names + (before remapping) from the output. +output-file <filename>: use <filename> as the output filename if it has + a suffix, and as the base of the output files if it doesn't. It + cannot have a suffix if using the `epsf' option, or both the `gf' and the + `tfm' option. Default is the base part of the input font name. +random <real>: move each pixel a (uniformly) random distance between + -<real> and <real> in both x and y; default is 0. +random-threshold <real>: if randomizing, do not move pixels with + probability <real>; default is 0.2. +range <char1>-<char2>: only process characters between <char1> and + <char2> in the input font, inclusive. +remap <char1>:<char2>,<char1>:<char2>,...: for each pair, make the input + character with code <char1> have code <char2> in the output. +text: output the font to stdout as plain text, using `*'s and ` 's. +tfm: write a TFM file to `<font_name>.tfm'. If this would overwrite the + input file, write to `x<font_name>.tfm' instead. +tfm-header: <header-item>:<value>,<header-item>:<value>,...: assign each + <value> to the corresponding <header-item> when outputting a TFM file. + A <header-item> is one of `checksum', `designsize' or `codingscheme', + with casefolding. `checksum' requires an unsigned integer, + `designsize' a real, with 1.0 <= designsize < 2048, and `codingscheme' + a string of length less than 40 containing no parens or commas. +verbose: print brief progress reports on stdout. +version: print the version number of this program. +" + +/* We return the name of the font to process. */ + +static string +read_command_line (int argc, string argv[]) +{ + int g; /* `getopt' return code. */ + int option_index; + boolean explicit_dpi = false; + boolean printed_version = false; + struct option long_options[] + = { { "baseline-adjust", 1, 0, 0 }, + { "column-split", 1, 0, 0 }, + { "concat", 1, 0, 0 }, + { "designsize", 1, 0, 0 }, + { "dpi", 1, (int *) &explicit_dpi, 1 }, + { "encoding", 1, 0, 0 }, + { "epsf", 0, (int *) &wants_epsf, 1 }, + { "filter-passes", 1, 0, 0 }, + { "filter-size", 1, 0, 0 }, + { "filter-threshold", 1, 0, 0 }, + { "fontdimens", 1, 0, 0 }, + { "gf", 0, (int *) &wants_gf, 1 }, + { "help", 0, 0, 0 }, + { "omit", 1, 0, 0 }, + { "output-file", 1, 0, 0 }, + { "random", 1, 0, 0 }, + { "random-threshold", 1, 0, 0 }, + { "range", 1, 0, 0 }, + { "remap", 1, 0, 0 }, + { "text", 0, (int *) &wants_text, 1 }, + { "tfm", 0, (int *) &wants_tfm, 1 }, + { "tfm-header", 1, 0, 0 }, + { "verbose", 0, (int *) &verbose, 1 }, + { "version", 0, (int *) &printed_version, 1 }, + { 0, 0, 0, 0 } }; + + while (true) + { + g = getopt_long_only (argc, argv, "", long_options, &option_index); + + if (g == EOF) + break; + + if (g == '?') + exit (1); /* Unknown option. */ + + assert (g == 0); /* We have no short option names. */ + + if (ARGUMENT_IS ("baseline-adjust")) + scan_baseline_adjust (optarg); + + else if (ARGUMENT_IS ("column-split")) + scan_column_split (optarg); + + else if (ARGUMENT_IS ("concat")) + append_concat_list (&fontname_list, optarg); + + else if (ARGUMENT_IS ("designsize")) + { + design_size = atof (optarg); + TFM_CHECK_DESIGN_SIZE (design_size); + } + + else if (ARGUMENT_IS ("dpi")) + dpi = optarg; + + else if (ARGUMENT_IS ("encoding")) + { + if (encoding_info == NULL) + encoding_info = XTALLOC1 (encoding_info_type); + *encoding_info = read_encoding_file (optarg); + } + + else if (ARGUMENT_IS ("filter-passes")) + filter_passes = atou (optarg); + + else if (ARGUMENT_IS ("filter-size")) + filter_size = atou (optarg); + + else if (ARGUMENT_IS ("filter-threshold")) + { + filter_threshold = atof (optarg); + if (filter_threshold <= 0.0) + FATAL1 ("The filter threshold should be positive, not %f", + filter_threshold); + } + + else if (ARGUMENT_IS ("fontdimens")) + fontdimens = optarg; + + else if (ARGUMENT_IS ("help")) + { + fprintf (stderr, "Usage: %s [options] <font_name>.\n", argv[0]); + fprintf (stderr, USAGE); + exit (0); + } + + else if (ARGUMENT_IS ("omit")) + scan_omit_list (optarg); + + else if (ARGUMENT_IS ("output-file")) + output_name = optarg; + + else if (ARGUMENT_IS ("range")) + GET_RANGE (optarg, starting_char, ending_char); + + else if (ARGUMENT_IS ("random")) + random_max = atof (optarg); + + else if (ARGUMENT_IS ("random-threshold")) + random_threshold = atof (optarg); + + else if (ARGUMENT_IS ("remap")) + scan_remap_list (optarg); + + else if (ARGUMENT_IS ("text")) + report_file = stderr; + + else if (ARGUMENT_IS ("tfm-header")) + tfm_header = optarg; + + else if (ARGUMENT_IS ("version")) + printf ("%s.\n", version_string); + + /* Else it was just a flag; getopt has already done the assignment. */ + } + + FINISH_COMMAND_LINE (); +} + +/* The string S specifies baseline adjustments for individual + characters: `<charcode>:<adjustment>,...'. We set the element + <charcode> of the global array `baseline_adjust' to the <adjustment>. */ + +static void +scan_baseline_adjust (string s) +{ + string spec; + + for (spec = strtok (s, ARG_SEP); spec != NULL; spec = strtok (NULL, ARG_SEP)) + { + string code; + string adjust = strchr (spec, ':'); + + if (adjust == NULL) + FATAL1 ("Baseline adjustments look like `<code>:<integer>', not `%s'", + spec); + + code = substring (spec, 0, adjust - spec - 1); + + baseline_adjust[xparse_charspec (code, encoding_info)] + = atoi (adjust + 1); + } +} + + +/* The string S says how to split a single character into multiple + characters: `<charcode>@<column1>,...<columnN>'. We set the element + <charcode> of the global array `column_split' to the list of the + integers. */ + +static void +scan_column_split (string s) +{ + string code; + string column_list = strchr (s, '@'); + + if (column_list == NULL) + FATAL1 ("Column splits look like `<code>@<column>,<column>,...', not `%s'", + s); + + code = substring (s, 0, column_list - s - 1); + + column_split[xparse_charspec (code, encoding_info)] + = scan_unsigned_list (column_list + 1); +} + + +/* The string S is a list of font names, separated by commas. We append + each onto CONCAT_LIST. */ + +static void +append_concat_list (list_type *concat_list, string s) +{ + string name; + + /* The main routine must initialize CONCAT_LIST before we are called. */ + assert (concat_list != NULL && LIST_SIZE (*concat_list) > 0); + + for (name = strtok (s, ARG_SEP); name != NULL; name = strtok (NULL, ARG_SEP)) + { + string *new = LIST_TAPPEND (concat_list, string); + *new = name; + } +} + + +/* The string L is a list of character codes separated by commas; we + omit those characters in the output. Here, we parse the list and set + elements of the global array `omit' according to what we find. */ + +static void +scan_omit_list (string l) +{ + string map; + + for (map = strtok (l, ARG_SEP); map != NULL; map = strtok (NULL, ARG_SEP)) + { + charcode_type code = xparse_charspec (map, encoding_info); + omit[code] = true; + } +} + + +/* The string L is a list of remappings to apply, in the form + <code1>:<code2>,<code1>:<code2>,... where <code1> is a character + code in the original font, and <code2> is the character code to write + it as. No checking for remappings to or from the same character is + done here. + + We set elements of the global array `translate' according to what we + find. */ + +static void +scan_remap_list (string l) +{ + string map; /* A single remapping. */ + + for (map = strtok (l, ARG_SEP); map != NULL; map = strtok (NULL, ARG_SEP)) + { + one_byte original, target; + string original_str, target_str; + unsigned length = strlen (map); + + /* Ignore empty mappings, as in `a:b,,c:d'. */ + if (length == 0) + continue; + + else if (length < 3) + { + WARNING1 ("Mapping `%s' too short to be valid", map); + continue; + } + + else if (*map == ':') + { /* Must have form `::<code>'. */ + original_str = ":"; + if (*(map + 1) != ':') + { + WARNING1 ("Mapping `%s' doesn't have form `<code>:<code>'", map); + continue; + } + target_str = map + 2; + } + + else if (*(map + length - 1) == ':') + { /* Must have form `<code>::'. */ + target_str = ":"; + if (*(map + length - 2) != ':') + { + WARNING1 ("Mapping `%s' doesn't have form `<code>:<code>'", map); + continue; + } + original_str = substring (map, 0, length - 3); + } + + else + { /* Must have form `<code>:<code>'. */ + target_str = strchr (map, ':'); + if (target_str == NULL) + { + WARNING1 ("Mapping `%s' doesn't have form `<code>:<code>'", map); + continue; + } + target_str++; + original_str = substring (map, 0, target_str - 2 - map); + } + + original = xparse_charspec (original_str, encoding_info); + target = xparse_charspec (target_str, encoding_info); + translate[original] = target; + } +} + + + +/* xx use the macro instead */ +/*string +finish_command_line() +{ + do + { + if (printed_version && optind == argc) exit (0); + + if (optind + 1 == argc && *argv[optind] != 0) + { + FIND_CMDLINE_DPI (); + return MAYBE_REMOVE_SUFFIX (argv[optind]); + } + else + { + fprintf (stderr, "Usage: %s [options] <font_name>.\n", argv[0]); + fprintf (stderr, "(%s.)\n", optind == argc ? "Missing <font_name>" + : "Too many <font_name>s"); + fputs ("For more information, use ``-help''.\n", stderr); + exit (1); + } + return NULL; + } + while (0) +} +*/ |