diff options
Diffstat (limited to 'Build/source/texk/cjkutils/cjkutils-4.8.2/hbf2gf/hbf2gf.w')
-rw-r--r-- | Build/source/texk/cjkutils/cjkutils-4.8.2/hbf2gf/hbf2gf.w | 2808 |
1 files changed, 2808 insertions, 0 deletions
diff --git a/Build/source/texk/cjkutils/cjkutils-4.8.2/hbf2gf/hbf2gf.w b/Build/source/texk/cjkutils/cjkutils-4.8.2/hbf2gf/hbf2gf.w new file mode 100644 index 00000000000..f96282776f1 --- /dev/null +++ b/Build/source/texk/cjkutils/cjkutils-4.8.2/hbf2gf/hbf2gf.w @@ -0,0 +1,2808 @@ +% This is the cweb file hbf2gf.w of the CJK package ver. 4.8.2 29-Dec-2008 + +% Copyright (C) 1994-2008 Werner Lemberg <wl@@gnu.org> +% +% 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 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 in doc/COPYING; if not, write to the Free +% Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +% MA 02110-1301 USA + +% To print this CWEB file you should (but not must) use the CWEAVE of the +% c2cweb-package (found at the CTAN archives, e.g. ftp.dante.de) and then say +% +% cweave +ai hbf2gf.w +% +% This (fully compatible) CWEAVE can transform CWEB-files with alternative +% output rules (look at the position of braces below!) the author (it's me +% too :-) prefer. Otherwise this file will be formatted traditionally. + +\font\meta=logo10 +\def\mf{{\meta META}\-{\meta FONT}} +\def\Om{\char "0A} % Omega symbol + +\def\title{hbf2gf (CJK Version 4.8.2)} + +\def\topofcontents{ + \null\vfill + \centerline{\titlefont The {\ttitlefont hbf2gf} program} + \vskip 20pt + \centerline{(CJK Version 4.8.2)} + \vfill} + +\def\botofcontents{ + \vfill + \noindent + Copyright \copyright~1996-1999 by Werner Lemberg + \bigskip\noindent + Permission is granted to make and distribute verbatim copies of this + document provided that the copyright notice and this permission notice are + preserved on all copies. + + \smallskip\noindent + Permission is granted to copy and distribute modified versions of this + document under the conditions for verbatim copying, provided that the + entire resulting derived work is distributed under the terms of a + permission notice identical to this one.} + +\pageno=\contentspagenumber \advance\pageno by 1 +\let\maybe=\iftrue +\fullpageheight=240mm +\pageheight=223mm +\pagewidth=158mm +\setpage +\frenchspacing + + +\def\msdos{\.{msdos}} +@s msdos TeX + +@s HBF int +@s HBF_CHAR int +@s HBF_BBOX int + +@s __inline__ int + + +@* Introduction. +This is the \.{hbf2gf} program by Werner Lemberg +(\.{wl@@gnu.org}). + +The ``banner line'' defined here should be changed whenever \.{hbf2gf} is +modified. + +@d banner +"hbf2gf (CJK ver. 4.8.2)" + + +@ +\.{hbf2gf} is intended to convert Hanzi Bitmap Fonts (HBF) into \TeX\ +generic font files (\.{GF} files) according to the \\{CJK} package, which +\.{hbf2gf} is part of. + +The outline of \.{hbf2gf} is simple: a CJK (Chinese/Japanese/Korean) bitmap +file will be scaled and written in at most |nmb_files| \.{GF} files, each +file containing |256|~characters (except the last and possibly the first +one). In the normal case it's not necessary to compute the right value of +|nmb_files| because \.{hbf2gf} will do this; you should use |-1| instead to +indicate this. See the last section for an example. + +Alternatively you can call \.{hbf2gf} similar to \mf, i.e., the program will +compute one font on demand. This mode will be used if two or three input +parameters instead of one are given: the font name, the horizontal +resolution, and optionally a vertical scaling factor or resolution to allow +modes for e.g.\ $300\times600\,\hbox{dpi}$ printers. \.{hbf2gf} will extract +the configuration file name from the font name; if this file isn't found, +the program exits with error code~|2| (this is useful for scripts like +\.{mktexpk}). If the configuration file is found but an error occurs while +computing the font, error code~|1| is returned. In case of success, the exit +code is zero. + +The characters in the input font files are completely described by the HBF +header file. This program uses the HBF API implementation of Ross Paterson +(\.{ross@@soi.city.ac.uk}; with small extensions). You will find a +description of the HBF standard at \.{ftp.ifcss.org}. + +A batch file created by \.{hbf2gf} too (if the program computes a whole set +of subfonts) will convert the \.{GF} files to \.{PK} files using +\.{GFtoPK}, a part of every \TeX\ package. + +@d TRUE 1 +@d FALSE 0@# + +@d STRING_LENGTH 255 + /* the maximal length of an input string in the configuration file */ +@d FILE_NAME_LENGTH 1024 + /* the maximal length (including the path) of a filename */@# + +@<Global variables@>= +int nmb_files = -1; /* create all files by default */ +int unicode = FALSE; /* whether a Unicode font should be processed */@# + +int testing = FALSE; /* whether we test only the font name */@# + +int mf_like = FALSE; /* whether we are in the \mf-like mode */ +int file_number = 0; /* the subfont number */@# + +double x_resolution = 0.0; /* the second and third input parameter */ +double y_scale = 1.0;@# + +int pk_files = TRUE; /* command line options */ +int tfm_files = TRUE; +int long_extension = TRUE; +int quiet = FALSE;@# + +char config_file[FILE_NAME_LENGTH + 4 + 1]; + /* we probably must add `\.{.cfg}' */ +char output_name[STRING_LENGTH + 1];@# + +FILE *config, *out; +HBF *hbf;@# + +#ifdef msdos /* if we compile under DOS or OS/2 */ +#define WRITE_BIN "wb" +#define WRITE_TXT "wt" +#define READ_BIN "rb" +#define READ_TXT "rt" +#else +#define WRITE_BIN "w" +#define WRITE_TXT "w" +#define READ_BIN "r" +#define READ_TXT "r" +#endif@# + +int end_of_file = FALSE; + + +@ +One \.{PL} file will be created additionally, which describes the font +metrics in a readable way. Because all CJK characters have identical +bounding boxes, one metrics file is enough---the batch job created by +\.{hbf2gf} calls \.{PLtoTF} to produce this \.{TFM} file and then copies +it into |nmb_files| metrics files. There usually will be a discrepancy +between the number of characters in the last \.{GF} file and the +\.{TFM} file, but this does not harm. + +If you specify the \.{ofm\_file} option in the configuration file, an +extended virtual property file (such files have the extension \.{.ovp}) for +the \Om\ system is written; this will be then converted with +\.{ovp2ovf} into an \.{OFM} and an \.{OVF} file to map all the subfonts +into one large virtual font. + + + +@* The main routine. +The main routine takes |file_name|, |x_resolution|, and |y_scale| as command +line parameters if in \mf-like mode, otherwise |config_file| as the only +argument. |read_config()| scans the configuration file and fills the global +variables, |write_file()| writes the \.{GF} files, |write_pl()| and +|write_ovp()| write the \.{PL} and \.{OVP} files respectively, and +|write_job()| the batch file. + +@c +@<Include files@>@; +@<Prototypes@>@; +@<Global variables@>@;@# + + +int main(int argc, char *argv[]) + {char *p; + + @<Initialize \TeX\ file searching@>@;@# + + @<Scan options@>@;@# + + if(!quiet) + printf("\n%s\n\n", banner);@# + + strncpy(config_file, argv[1], FILE_NAME_LENGTH); + config_file[FILE_NAME_LENGTH] = '\0';@# + + if(argc > 2 || testing) + {int l = strlen(config_file); + + if(l > 2) + config_file[l - 2] = '\0'; + /* strip subfont number from file name */ + else + {if(!quiet) + printf("`%s' can't be a subfont created by hbf2gf\n", + config_file); + exit(2); + }@# + + mf_like = TRUE; + }@# + + read_config(); /* will call |exit(1)| on errors */@# + + if(mf_like) + @<Check other arguments@>@;@# + + @<Initialize variables@>@;@# + + @<Write files@>@;@# + + if(tfm_files) + write_pl(); + if(ofm_file) + write_ovp(); + if(!mf_like) + write_job();@# + + hbfClose(hbf);@# + + exit(0); + return 0; /* never reached */ + } + + +@ +@d VERSION @/ +"\n"@/ +"Copyright (C) 1996-1999 Werner Lemberg.\n"@/ +"There is NO warranty. You may redistribute this software\n"@/ +"under the terms of the GNU General Public License\n"@/ +"and the HBF library copyright.\n"@/ +"\n"@/ +"For more information about these matters, see the files\n"@/ +"named COPYING and hbf.c.\n"@/ +"\n" + +@<Print version@>= + {printf("\n"); + printf(banner); + printf(" (%s)\n", TeX_search_version()); + printf(VERSION); + exit(0); + } + + +@ +@d USAGE @/ +"\n"@/ +"Usage: hbf2gf [-q] configuration_file[.cfg]\n"@/ +" hbf2gf [options] font_name x_resolution [y_scale | y_resolution]\n"@/ +" hbf2gf -t [-q] font_name\n"@/ +"\n"@/ +" Convert a font in HBF format to TeX's GF resp. PK format.\n"@/ +"\n"@/ +" -q be silent\n"@/ +" -p don't produce a PL file\n"@/ +" -g don't produce a GF file\n"@/ +" -n use no resolution in extension (only `.gf')\n"@/ +" -t test for font_name (returns 0 on success)\n"@/ +" --help print this message and exit\n"@/ +" --version print version number and exit\n"@/ +"\n" + +@<Print help information@>= + {printf(USAGE); + exit(0); + } + + +@ +Three options can be specified to the program (\.{-p}, \.{-g}, and \.{-n}) +if in \mf-like mode to suppress creation of a \.{PL} resp.\ a +\.{GF} file, and to force a `\.{.gf}' extension (instead of +e.g.\ `\.{.300gf}'). The corresponding setting of a particular switch in the +configuration file is ignored then. + +Additionally, the option \.{-t} tests whether the specified subfont name +leads to an \.{hbf2gf} configuration file. It returns 0 on success and prints +out the name of that configuration file (provided the \.{-q} switch isn't +set). This test isn't a thorough one; it only removes the last two characters +and checks whether a configuration file with that name exists. + +@<Scan options@>= + if(argc == 2) + {if(strcmp(argv[1], "--help") == 0) + @<Print help information@>@; + else if(strcmp(argv[1], "--version") == 0) + @<Print version@>@; + }@# + + while(argc > 1) + {p = argv[1]; + if(p[0] != '-') + break; + if(p[1] == 'p') + tfm_files = FALSE; + else if(p[1] == 'g') + pk_files = FALSE; + else if(p[1] == 'n') + long_extension = FALSE; + else if(p[1] == 'q') + quiet = TRUE; + else if(p[1] == 't') + testing = TRUE;@# + + argv++; + argc--; + }@# + + if(testing) + {if(argc != 2) + {fprintf(stderr, "Need exactly one parameter for `-t' option.\n"); + fprintf(stderr, "Try `hbf2gf --help' for more information.\n"); + exit(1); + } + } + else if(argc < 2 || argc > 4) + {fprintf(stderr, "Invalid number of parameters.\n"); + fprintf(stderr, "Try `hbf2gf --help' for more information.\n"); + exit(1); + }@# + + +@ +If the (optional) argument is larger than~|10|, we treat it as a value for +the vertical resolution (in dpi), otherwise as a vertical scaling factor. + +@<Check other arguments@>= + {if(unicode) + file_number = (int)strtol(&argv[1][strlen(argv[1]) - 2], + (char **)NULL, 16); + else + file_number = atoi(&argv[1][strlen(argv[1]) - 2]);@# + + x_resolution = atof(argv[2]); + if(x_resolution < PRINTER_MIN_RES_X) + {fprintf(stderr, "Invalid horizontal resolution\n"); + exit(1); + }@# + + if(argc > 3) + {y_scale = atof(argv[3]); + if(y_scale < 0.01) + {fprintf(stderr, + "Invalid vertical scaling factor or resolution\n"); + exit(1); + } + if(y_scale > 10.0) + y_scale = (double)x_resolution / y_scale; + } + } + + +@ +If |unicode| is |TRUE|, the start value of the running number appended to +the base name of the output font files is taken from the HBF header file, +otherwise it starts with~`\.{01}'. |min_char| represents the lower bound of +the code range. + +If we are in \mf-like mode, |file_number| is taken from the command line, +and |max_numb| will be set to~|1|. + +@<Write files@>= + {int j, max_numb; + + if(!mf_like) + {file_number = (unicode == TRUE ? (min_char >> 8) : 1); + if(nmb_files == -1) + max_numb = (unicode == TRUE ? 0x100 : 100); + else + max_numb = nmb_files; + } + else + max_numb = 1;@# + + for(j = 0; (j < max_numb) && !end_of_file; file_number++, j++) + write_file();@# + + nmb_files = j; /* the real number of output font files */ + } + + +@ +@<Include files@>= +#ifdef HAVE_CONFIG_H +#include <c-auto.h> +#endif +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef TM_IN_SYS_TIME +#include <sys/time.h> +#endif +#include "hbf.h" + + + +@* The functions. +The first function to be described is |write_file()|. Each \.{GF} file +consists of three sections: a preamble, a data section, and a postamble. The +functions |write_pre()|, |write_data()|, and |write_post()| handle this. + +@<Prototypes@>= +void write_file(void); + + +@ +In \mf-like mode we create font file name extensions similar to \mf\ if +the \.{-n} option isn't specified; otherwise only `\.{.gf}' will be appended. + +@c +void write_file(void) + {char output_file[FILE_NAME_LENGTH + 1]; + + + if(pk_files) + {if(mf_like) + {if(unicode) + sprintf(output_file, "%s%02x.%.0igf",@/ + output_name, file_number, + long_extension ? (int)(x_resolution + 0.5) : 0); + else + sprintf(output_file, "%s%02i.%.0igf",@/ + output_name, file_number, + long_extension ? (int)(x_resolution + 0.5) : 0); + } + else + {if(unicode) + sprintf(output_file, "%s%02x.gf", output_name, file_number); + else + sprintf(output_file, "%s%02i.gf", output_name, file_number); + } + if(!(out = fopen(output_file, WRITE_BIN))) + {fprintf(stderr, "Couldn't open `%s'\n", output_file); + exit(1); + } + if(!quiet) + printf("Writing `%s' ", output_file);@# + + write_pre(); + write_data(); + write_post(); + fclose(out);@# + + if(!quiet) + printf("\n"); + } + else + write_data(); + } + + +@ +The preamble has two bytes at the very beginning, |PRE| and |GF_ID|. |PRE| +starts the preamble, and |GF_ID| is the Generic Font Identity Byte. The next +bytes are a string in Pascal format containing a header, the date, and the +time. Strings in Pascal format start with the length of the string and have +no terminating NULL byte. + +@d GF_ID 131 +@d PRE 247@# + +@d header " hbf2gf output " + +@<Prototypes@>= +void write_pre(void); + + +@ +@s tm int + +@c +void write_pre(void) + {char out_s[40], s[20]; + time_t secs_now; + struct tm *time_now; + + + strcpy(out_s, header);@# + + secs_now = time(NULL); /* system date and time */ + time_now = localtime(&secs_now); + strftime(s, 20, "%Y.%m.%d:%H.%M", time_now); + strcat(out_s, s);@# + + fputc(PRE, out); + fputc(GF_ID, out); + fputc(strlen(out_s), out); + fputs(out_s, out); + } + + +@ +|write_data()| produces the middle part of the \.{GF} file. It first sets +|char_adr_p| equal to the address of |char_adr[]| which will contain file +offsets of the compressed characters. + +|input_size_x| and |input_size_y| reflect the original dimensions of the +bitmap font, |pk_output_size_x| and |pk_output_size_y| contain the width and +height of the output character box (in pixels), |pk_offset_x| and +|pk_offset_y| define the baseline of the font. The same names starting with +`\\{tfm\_}' instead of `\\{pk\_}' are used for \.{TFM} files (values are +multiples of design size). |mag_x| and |mag_y| hold the scaling factors +which are needed to reach |design_size|. |slant| defines the slant ($\Delta +x / \Delta y$), |target_size_x| and |target_size_y| will be the final +dimensions; |magstep_x = target_size_x / design_size| is \TeX's +\.{\\magstep}. + +The \CEE/ standard specifies that all global values will be automatically +set to~|0| if no initialization value is given. + +@<Global...@>= +long char_adr[256]; +long *char_adr_p;@# + +int pk_offset_x; + /* horizontal offset (increase character width a bit; will be applied on + both the left and the right side) */ +double tfm_offset_x; +int pk_offset_y; + /* vertical offset (must be configured to desired font size) */ +double tfm_offset_y;@# + +int input_size_x; +int input_size_y; +const char *font_encoding; /* taken from the HBF file */ +int pk_width; /* without slant */ +int pk_output_size_x; + /* the output character box dimensions without offsets */ +double tfm_output_size_x; +int pk_output_size_y; +double tfm_output_size_y;@# + +double design_size = 10.0; /* in points */ +double target_size_x; /* in points */ +double target_size_y; +double magstep_x; +double magstep_y; +double slant; +int rotation;@# + +double mag_x; /* horizontal and vertical magnification values */ +double mag_y;@# + +int empty_char; /* a flag whether the character does not exist or is empty */ +int last_char; /* the last valid character in a \.{GF} file */@# + +int dot_count; /* this counts the processed characters; + every ten characters a dot is output to the screen */ + + +@ +@<Prototypes@>= +void write_data(void); + + +@ +@c +void write_data(void) + {dot_count = 0; + char_adr_p = char_adr;@# + + for(last_char = 0; (last_char < 256) && !end_of_file; last_char++) + @<Write character@>@; + } + + +@ +The code in this section saves the current file position first and calls +|make_pixel_array()|, which expands and scales the character bitmap. + +|BOC| (and |BOC1|), the Begin Of Character command byte, must be followed by +the character code and the dimensions of the character as explained in +``\mf---the program'' (corrected by vertical and horizontal offsets). + +|write_coding()| compresses and outputs the bitmap; |EOC| (End Of Character) +finishes the current character. + +@d BOC 67 +@d BOC1 68 /* simplified version of |BOC| */ +@d EOC 69 + +@<Write character@>= + {if(dot_count++ % 10 == 0) /* a progress report for impatient users */ + if(pk_files && !quiet) + {printf("."); + fflush(stdout); + }@# + + empty_char = FALSE; + make_pixel_array(); + if(end_of_file) + return;@# + + if(pk_files) + {*char_adr_p = ftell(out); + char_adr_p++;@# + + if(empty_char) + {fputc(BOC1, out); + fputc((unsigned char)last_char, out); + fputc(0, out); + fputc(0, out); + fputc(0, out); + fputc(0, out); + fputc(EOC, out); + } + else + {fputc(BOC, out); + fputl(last_char, out); + fputl(-1L, out); + fputl(pk_offset_x, out); + fputl(pk_output_size_x + pk_offset_x, out); + fputl(pk_offset_y, out); + fputl(pk_output_size_y + pk_offset_y, out);@# + + write_coding();@# + + fputc(EOC, out); + } + } + } + + +@ +The current \.{GF} file will be completed with data written by +|write_post()|. The end consists of three sections: ``special'', ``post'', +and ``postpost''. The first contains material not used by \TeX\ itself but +which can be used by other programs like \.{GFtoDVI} or for documentary +purposes (|coding[]| and |comment[]|). The second describes the font as a +whole, and the last marks the end of the file. + +|pk_total_min_x| up to |pk_total_max_y| define the greatest bounding box of +this file (including offsets); the horizontal character escapement after +drawing the character is |pk_dx|. |tfm_width| is the width in multiples of +the design size ignoring the target size. + +@d _2_16 65536.0 /* $2^{16}$ */ +@d _2_20 1048576.0 /* $2^{20}$ */ + +@<Global...@>= +char coding[STRING_LENGTH + 1]; /* a comment describing the font encoding */ +char comment[STRING_LENGTH + 1]; /* a comment describing the font */@# + +unsigned long checksum;@# + +long pk_total_min_x; +long pk_total_max_x; +long pk_total_min_y; +long pk_total_max_y;@# + +int dpi_x; /* printer resolution */ +int dpi_y; +double ppp_x; /* pixels per point */ +double ppp_y; + + +@ +To clarify the meaning of these values see the sections about the metrics +and configuration file also. + +\TeX\ defines that 72.27~points are exactly 1~inch. + +@<Prototypes@>= +void write_post(void); + + +@ +@c +void write_post(void) + {long special_adr; + long post_adr;@# + + long designsize = design_size * _2_20; /* design size $* 2^{20}$ */@# + + int pk_dx; + long tfm_width;@# + + int i; + long temp; + + + ppp_x = dpi_x / 72.27 * magstep_x; + ppp_y = dpi_y / 72.27 * magstep_y;@# + + pk_total_min_x = pk_offset_x; + pk_total_max_x = pk_output_size_x + 2 * pk_offset_x; + pk_total_min_y = pk_offset_y; + pk_total_max_y = pk_output_size_y + pk_offset_y;@# + + pk_dx = pk_width + 2 * pk_offset_x; /* no slant */ + tfm_width = (tfm_output_size_x + 2 * tfm_offset_x) * _2_20; + /* width in multiples of design size $* 2^{20}$ */@# + + @<Special section@>@; + @<Post section@>@; + @<Postpost section@>@; + } + + +@ +\.{XXXn} will be followed by n~bytes representing the length of a string +which follows immediately. |YYY| is a 32~bit integer which is normally +connected with the preceding string (but not used here). |special_adr| +contains the address of the ``special section''. All items here are +optional. + +@d XXX1 239 /* these are all special command bytes */ +@d XXX2 240 /* not used */ +@d XXX3 241 /* not used */ +@d XXX4 242 /* not used */@# + +@d YYY 243 /* not used */ + +@<Special section@>= + special_adr = ftell(out);@# + + if(*coding) + {fputc(XXX1, out); /* |XXX1| implies a string length |< 256| */ + fputc(strlen(coding), out); + fputs(coding, out); + }@# + + if(*comment) + {fputc(XXX1, out); + fputc(strlen(comment), out); + fputs(comment, out); + } + + +@ +All character offsets collected in |char_adr| will be written to the output +file. |fputl()| writes a 32~bit integer into a file. + +|CHAR_LOC0| (and |CHAR_LOC|) is the first byte of a character locator +(i.e., offset, character code, and width information). |POST| starts the +postamble, and |post_adr| points to the beginning byte of the postamble. + +@d POST 248@# + +@d CHAR_LOC 245 +@d CHAR_LOC0 246 /* simplified version of |CHAR_LOC| */ + +@<Post section@>= + post_adr = ftell(out); + fputc(POST, out); + fputl(special_adr, out);@# + + fputl(designsize, out); + fputl(checksum, out); + fputl(ppp_x * _2_16, out); + fputl(ppp_y * _2_16, out); + fputl(pk_total_min_x, out); + fputl(pk_total_max_x, out); + fputl(pk_total_min_y, out); + fputl(pk_total_max_y, out);@# + + char_adr_p = char_adr;@# + + if(pk_dx < 256) + {for(i = 0; i < last_char; i++) /* the character locators */ + {fputc(CHAR_LOC0, out); + fputc(i, out); + fputc(pk_dx, out); + fputl(tfm_width, out); + fputl(*char_adr_p++, out); + } + } + else /* will only happen if |MAX_CHAR_SIZE >= 256| */ + {for(i = 0; i < last_char; i++) + {fputc(CHAR_LOC, out); + fputc(i, out); + fputl(pk_dx * _2_16, out); + fputl(0, out); + fputl(tfm_width, out); + fputl(*char_adr_p++, out); + } + } + + +@ +|POSTPOST| starts the section after the postamble. To get all information in +a \.{GF} file, you must start here. The very last bytes of the file have +the value |POSTPOST_ID| (the file is filled with at least 4 of these bytes +until a file length of a multiple of~4 is reached). Going backwards a +|GF_ID| will be next, then comes the address of the postamble section. + +Jumping to the postamble, a |POST| byte comes first, then the address of +the special section, and afterwards all character offsets. These offsets and +addresses describe the whole file. + +@d POSTPOST 249 +@d POSTPOST_ID 223 + +@<Postpost section@>= + fputc(POSTPOST, out); + fputl(post_adr, out); + fputc(GF_ID, out); + temp = ftell(out); + i = (int)(temp % 4) + 4; + while(i--) + fputc(POSTPOST_ID, out); + + +@ +\TeX\ wants the most significant byte first. + +@<Prototypes@>= +void fputl(long, FILE *); + + +@ +@c +void fputl(long num, FILE *f) + {fputc(num >> 24, f); + fputc(num >> 16, f); + fputc(num >> 8, f); + fputc(num, f); + } + + +@ +|make_pixel_array()| scales a character into the array |out_char[]| where +each \\{byte} represents one pixel, contrary to the input file where each +\\{bit} is used to store the character bitmap. |BLACK| indicates a black +pixel. + +The scaling routine was modeled after the program \.{pnmscale} of the +\.{pbmplus} package. \.{pbmplus} was designed to handle arbitrary pictures, +and bitmaps are only a special case of a graymap with values from~0 for +white up to |PIXEL_MAXVAL = 255| for black. + +If |EOF| is encountered, |end_of_file| is set and the function returns +immediately. + +@d BLACK 1 +@d WHITE 0@# + +@d PIXEL_MAXVAL 255 +@d SCALE 4096 +@d HALFSCALE 2048@# + +@d MAX_CHAR_SIZE 1023 + +@<Global...@>= +HBF_CHAR code; +const unsigned char *bitmap; + /* a proper input bitmap array will be allocated by the HBF API */ +const unsigned char *bP;@# + +unsigned char out_char[MAX_CHAR_SIZE * MAX_CHAR_SIZE + 1]; + /* the output bitmap array */ +unsigned char *out_char_p;@# + +unsigned char pixelrow[MAX_CHAR_SIZE]; +unsigned char temp_pixelrow[MAX_CHAR_SIZE]; +unsigned char new_pixelrow[MAX_CHAR_SIZE + 1]; + /* we need space to append a white pixel */@# +int curr_row; /* for |read_row()| if we access the glyph rotated */ + +long grayrow[MAX_CHAR_SIZE];@# + +long s_mag_x, s_mag_y, s_slant; + + +@ +We need to initialize the |grayrow[]| array together with some other +variables. + +Two steps are necessary to compute the |code| if we are in the \mf-like +mode. Assuming that we search the code \.{0xXXYY}, we first set |code| to +the value \.{0xXXAA}, where \.{0xAA} is equal to |min_2_byte| (getting +|offset| as the number of remaining characters to reach the first character +in our given subfont), then we increment |code| (and decrement |offset|) +until |offset| equals~0. + +@<Initialize variables@>= + {int col, offset; + + if(rotation) + {int tmp; + + + tmp = input_size_x; + input_size_x = input_size_y; + input_size_y = tmp; + }@# + + if(mf_like) + {target_size_x = design_size * (x_resolution / dpi_x); + target_size_y = design_size * (x_resolution * y_scale / dpi_y); + } + else + target_size_x = target_size_y = design_size; + magstep_x = target_size_x / design_size; + magstep_y = target_size_y / design_size;@# + + pk_offset_x = offset_x * magstep_x + 0.5; + pk_offset_y = offset_y * magstep_y + 0.5; + tfm_offset_x = offset_x / (dpi_x / 72.27) / design_size; + tfm_offset_y = offset_y / (dpi_y / 72.27) / design_size;@# + + pk_width = input_size_x * mag_x * magstep_x + 0.5; /* without slant */ + pk_output_size_x = input_size_x * mag_x * magstep_x + + input_size_y * mag_y * magstep_y * slant + 0.5; + pk_output_size_y = input_size_y * mag_y * magstep_y + 0.5; + tfm_output_size_x = input_size_x * mag_x / + (dpi_x / 72.27) / design_size; + tfm_output_size_y = input_size_y * mag_y / + (dpi_y / 72.27) / design_size; + if(pk_output_size_x > MAX_CHAR_SIZE) + {fprintf(stderr, "Output character box width too big\n"); + exit(1); + } + if(pk_output_size_y > MAX_CHAR_SIZE) + {fprintf(stderr, "Output character box height too big\n"); + exit(1); + }@# + + for(col = 0; col < input_size_x; ++col) + grayrow[col] = HALFSCALE;@# + + if(!mf_like) + code = (min_char & 0xFF00) + min_2_byte; + else + {if((file_number < (unicode ? 0 : 1)) || (file_number >= 0x100)) + {fprintf(stderr, "Invalid subfile number\n"); + exit(1); + }@# + + if(unicode) + {offset = 0; + code = file_number * 0x100; + } + else + {offset = (file_number - 1) * 256 % nmb_2_bytes; + code = (min_char & 0xFF00) + min_2_byte + + (file_number - 1) * 256 / nmb_2_bytes * 0x100; + }@# + + while(offset--) + while(!b2_codes[code++ & 0xFF]) + /* eliminate invalid |b2_codes| */ + ;@# + + if(code > max_char) + {fprintf(stderr, "Invalid subfile number\n"); + exit(1); + } + } + + s_mag_x = mag_x * magstep_x * SCALE; + s_mag_y = mag_y * magstep_y * SCALE; + s_slant = slant * SCALE; + } + + +@ +All arrays of the \\{pixelrow} family contain gray values. While scaling +with non-integer values a pixel of the input bitmap will normally not align +with the pixel grid of the output bitmap (geometrically spoken). In this +case we first compute the fractions of input pixel rows scaled vertically +and add the corresponding gray values until a temporary row is produced. +Then we repeat this procedure horizontally pixel by pixel and write the +result into an output array. + + +@<Prototypes@>= +void make_pixel_array(void); + + +@ +@c +void make_pixel_array(void) + {unsigned char *prP; + unsigned char *temp_prP; + unsigned char *new_prP; + long *grP;@# + + register unsigned char *xP; + register unsigned char *nxP;@# + + register int row, col; + int rows_read = 0; + register int need_to_read_row = 1;@# + + long frac_row_to_fill = SCALE; + long frac_row_left = s_mag_y;@# + + int no_code = FALSE; + + + prP = pixelrow; + temp_prP = temp_pixelrow; + new_prP = new_pixelrow; + grP = grayrow; + out_char_p = out_char; /* will be increased by |write_row()| */@# + +again: + if(b2_codes[code & 0xFF]) /* a valid second byte? */ + {if(pk_files) + {bitmap = hbfGetBitmap(hbf, code); + bP = bitmap; + /* will be increased by |read_row()| */@# + + if(!bitmap) + empty_char = TRUE; + else + @<Scale row by row@>@; + } + } + else + no_code = TRUE;@# + + if((code & 0xFF) == max_2_byte) + code += 0xFF - (max_2_byte - min_2_byte); /* go to next plane */ + if(code >= max_char) + {end_of_file = TRUE; + return; + }@# + + code++;@# + + if(no_code) + {no_code = FALSE; + goto again; + } + } + + +@ +@<Scale row by row@>= + {if(pk_output_size_y == input_size_y) /* shortcut Y~scaling if possible */ + temp_prP = prP;@# + + curr_row = input_size_y - 1; /* only needed for rotated glyphs */ + for(row = 0; row < pk_output_size_y; ++row) + {@<Scale Y from |pixelrow[]| into |temp_pixelrow[]|@>@; + @<Scale X from |temp_pixelrow[]| into |new_pixelrow[]| + and write it into |out_char[]|@>@; + } + } + + +@ +@<Scale Y from |pixelrow[]| into |temp_pixelrow[]|@>= + if(pk_output_size_y == input_size_y) + /* shortcut Y~scaling if possible */ + read_row(prP); + else + {while(frac_row_left < frac_row_to_fill) + {if(need_to_read_row) + if(rows_read < input_size_y) + {read_row(prP); + ++rows_read; + }@# + + for(col = 0, xP = prP; col < input_size_x; ++col, ++xP) + grP[col] += frac_row_left * (*xP);@# + + frac_row_to_fill -= frac_row_left; + frac_row_left = s_mag_y; + need_to_read_row = 1; + }@# + + @<Produce a temporary row@>@; + } + + +@ +Now |frac_row_left >= frac_row_to_fill|, so we can produce a row. + +@<Produce a temporary row@>= + if(need_to_read_row) + if(rows_read < input_size_y) + {read_row(prP); + ++rows_read; + need_to_read_row = 0; + }@# + + for(col = 0, xP = prP, nxP = temp_prP; + col < input_size_x; ++col, ++xP, ++nxP) + {register long g; + + + g = grP[col] + frac_row_to_fill * (*xP); + g /= SCALE; + if(g > PIXEL_MAXVAL) + g = PIXEL_MAXVAL;@# + + *nxP = g; + grP[col] = HALFSCALE; + }@# + + frac_row_left -= frac_row_to_fill; + if(frac_row_left == 0) + {frac_row_left = s_mag_y; + need_to_read_row = 1; + } + frac_row_to_fill = SCALE; + + +@ +To implement the slant we move the starting point |nxP| to the right +according to the corresponding |y|~value. To simplify life only positive +shift values are allowed. + +We always append a white pixel to avoid artefacts at the end of the line +produced by the last line. This rule sets the second condition that the +slant must not be greater than~|1|---such a large slant would be unusable +anyway for typesetting purposes. + +@<Scale X from |temp_pixelrow[]| into |new_pixelrow[]| + and write it into |out_char[]|@>= + if(pk_width == input_size_x && s_slant == 0) + /* shortcut X~scaling if possible */ + write_row(temp_prP); + else + {register long g = HALFSCALE; + register long frac_col_to_fill = SCALE; + register long frac_col_left; + register int need_col = 0; + + + nxP = new_prP;@# + + frac_col_left = (pk_output_size_y - row) * s_slant; + while(frac_col_left >= frac_col_to_fill) + {*(nxP++) = 0; + frac_col_left -= frac_col_to_fill; + }@# + + if(frac_col_left > 0) + frac_col_to_fill -= frac_col_left;@# + + for(col = 0, xP = temp_prP; col < input_size_x; ++col, ++xP) + {frac_col_left = s_mag_x; + while(frac_col_left >= frac_col_to_fill) + {if(need_col) + {++nxP; + g = HALFSCALE; + }@# + + g += frac_col_to_fill * (*xP); + g /= SCALE; + if(g > PIXEL_MAXVAL) + g = PIXEL_MAXVAL;@# + + *nxP = g; + frac_col_left -= frac_col_to_fill; + frac_col_to_fill = SCALE; + need_col = 1; + }@# + + if(frac_col_left > 0) + {if(need_col) + {++nxP; + g = HALFSCALE; + need_col = 0; + }@# + + g += frac_col_left * (*xP); + frac_col_to_fill -= frac_col_left; + } + }@# + + @<Write out a row@>@; + } + + +@ +@<Write out a row@>= + if(frac_col_to_fill > 0) + {--xP; + g += frac_col_to_fill * (*xP); + }@# + + if(!need_col) + {g /= SCALE; + if(g > PIXEL_MAXVAL) + g = PIXEL_MAXVAL; + *nxP = g; + }@# + + *(++nxP) = 0; /* append a blank pixel */@# + + write_row(new_prP); + + +@ +|read_row()| reads a row from |bitmap[]| and converts it into a graymap row. +If the |rotation| flag has been set, we get the proper column instead (note +that in this case |input_size_x| already reflects the width of the rotated +glyph). + +@<Prototypes@>= +#ifdef __GNUC__ +__inline__ +#endif +void read_row(unsigned char *); + + +@ +@c +#ifdef __GNUC__ +__inline__ +#endif +void read_row(unsigned char *pixelrow) + {register int col, bitshift, offset; + register unsigned char *xP; + register unsigned char item = 0; + + if(rotation) + {bitshift = 7 - (curr_row % 8); + offset = (input_size_y + 7) / 8; + bP = bitmap + curr_row / 8; + for(col = 0, xP = pixelrow; col < input_size_x; ++col, ++xP) + {*xP = ((*bP >> bitshift) & 1) == 1 ? PIXEL_MAXVAL : 0; + bP += offset; + } + curr_row--; + } + else + {bitshift = -1; + for(col = 0, xP = pixelrow; col < input_size_x; ++col, ++xP) + {if(bitshift == -1) + {item = *(bP++); /* increase input bitmap pointer */ + bitshift = 7; + } + *xP = ((item >> bitshift) & 1) == 1 ? PIXEL_MAXVAL : 0; + --bitshift; + } + } + } + + +@ +|write_row()| converts the graymap back into a bitmap using a simple +threshold. + +@<Global...@>= +int threshold = 128; + + +@ +@<Prototypes@>= +#ifdef __GNUC__ +__inline__ +#endif +void write_row(unsigned char *); + + +@ +@c +#ifdef __GNUC__ +__inline__ +#endif +void write_row(unsigned char *pixelrow) + {register int col; + register unsigned char *xP; + + for(col = 0, xP = pixelrow; col < pk_output_size_x; ++col, ++xP) + *(out_char_p++) = (*xP >= threshold) ? 1 : 0; + /* increase output bitmap pointer */ + } + + +@ +Now comes the most interesting routine. The pixel array will be compressed +in sequences of black and white pixels. + +|SKIP0|, |SKIP1|, and |SKIP2| indicate how many blank lines will be skipped. +|PAINT_(x)| means that the next x~pixels will have the same color, then the +color changes. |NEW_ROW_(x)| is the first black pixel in the next row. + +An example: the pixel sequence 111100011001 [new row] 000111011110 will be +output as 4 3 2 2 1 77 3 1 4 1. + +Commands with an ending~`n' in its name indicate that the next n~bytes +should be read as the counter. Example: |SKIP1|~26 means `skip the next +26~rows'. + +For further details please refer to ``\mf---the program''. + +@d PAINT_(x) (x) /* $0 \le x \le 63$ */ +@d PAINT1 64 +@d PAINT2 65 +@d PAINT3 66 /* not used */@# + +@d SKIP0 70 +@d SKIP1 71 +@d SKIP2 72 +@d SKIP3 73 /* not used */@# + +@d NEW_ROW_(x) ((x) + 74) /* $0 \le x \le 164$ */@# + +@d NOOP 244 /* not used */ + +@<Prototypes@>= +void write_coding(void); + + +@ +The |goto start| instruction causes some compilers to complain about +``Unreachable code $\ldots$'' or something similar. + +@c +void write_coding(void) + {register int count, skip; + register unsigned char paint; + register int x, y; + register unsigned char *cp; + + x = 0; + y = 0; + cp = out_char + y * pk_output_size_x + x; + count = skip = 0; + paint = WHITE; + goto start;@# + + while (y < pk_output_size_y) + {@<Search blank lines@>@; +start: + @<Process rest of line@>@; + y++; + } + }@# + + +@ +@<Search blank lines@>= + count = 0; + x = 0; + cp = out_char + y * pk_output_size_x + x;@# + + while(x < pk_output_size_x) + {if(*cp == paint) + count++; + else + {if(skip == 0) + {if(count <= 164) + fputc(NEW_ROW_(count), out); + else + {fputc(SKIP0, out);@# + + if(count < 256) + {fputc(PAINT1, out); + fputc(count, out); + } + else + {fputc(PAINT2, out); + fputc(count >> 8, out); + fputc(count & 0xFF, out); + } + } + } + else + {if(skip == 1) + fputc(SKIP0, out); + else + {if(skip < 256) + {fputc(SKIP1, out); + fputc(skip, out); + } + else + {fputc(SKIP2, out); + fputc(skip >> 8, out); + fputc(skip & 0xFF, out); + } + } + skip = 0; + if(count < 64) + fputc(PAINT_(count), out); + else if(count < 256) + {fputc(PAINT1, out); + fputc(count, out); + } + else + {fputc(PAINT2, out); + fputc(count >> 8, out); + fputc(count & 0xFF, out); + } + } + count = 0; + paint = BLACK; + break; + } + x++; + cp++; + } + if(x >= pk_output_size_x) + {skip++; + y++; + continue; + } + + +@ +@<Process rest of line@>= + while(x < pk_output_size_x) + {if(*cp == paint) + count++; + else + {if(count < 64) + fputc(PAINT_(count), out); + else if(count < 256) + {fputc(PAINT1, out); + fputc(count, out); + } + else + {fputc(PAINT2, out); + fputc(count >> 8, out); + fputc(count & 0xFF, out); + } + count = 1; + paint = BLACK - paint; + } + x++; + cp++; + } + if(paint == BLACK) + {if(count < 64) + fputc(PAINT_(count), out); + else if(count < 256) + {fputc(PAINT1, out); + fputc(count, out); + } + else + {fputc(PAINT2, out); + fputc(count >> 8, out); + fputc(count & 0xFF, out); + } + paint = WHITE; + } + + + +@* The font metrics file. +This routine creates one \.{PL} file with the font properties. None of the +\TeX\ font dimensions are needed because you never will use CJK fonts +directly, and intercharacter stretching is handled by the \\{CJK} macro +\.{\\CJKglue}. (Other packages may define similar commands.) + +The name of the \.{PL} file will contain the running two digits in +\mf-like mode only. + +It makes sense \\{not} to compute the check sum automatically for two +reasons. Firstly, since \TeX's checksum algorithm is based on the character +width, the number of valid characters, and the designsize, there is a much +higher chance that two subfonts from different HBF fonts have the same check +sum than it is for ordinary fonts, because all characters have the same +width, usually 256~characters in a subfont, and very often the same design +size. Secondly, and this is more important, we create just one +\.{TFM} file for all subfonts regardless of the real number of characters +in a particular subfont. + +To have an identification string in the \.{TFM} file, we split it into +single bytes and use the \.{HEADER} command repeatedly. + +@<Prototypes@>= +void write_pl(void); + + +@ +@c +void write_pl(void) + {int i, pos; + char output_file[FILE_NAME_LENGTH + 1]; + long t, sc; + char *s; + char tfm_header[] = "Created by hbf2gf"; + + file_number--; /* for \mf-like mode */@# + + if(mf_like) + {if(unicode) + sprintf(output_file, "%s%02x.pl", output_name, file_number); + else + sprintf(output_file, "%s%02i.pl", output_name, file_number); + } + else + sprintf(output_file, "%s.pl", output_name);@# + + if(!(out = fopen(output_file, WRITE_TXT))) + {fprintf(stderr, "Couldn't open `%s'\n", output_file); + exit(1); + } + if(!quiet) + printf("\nWriting `%s'\n", output_file);@# + + fprintf(out,@/ + "\n(FAMILY %s%d)"@/ + "\n(CODINGSCHEME CJK-%s)", + output_name, file_number, font_encoding);@# + + fprintf(out,@/ + "\n(DESIGNSIZE R %.6f)"@/ + "\n(COMMENT DESIGNSIZE IS IN POINTS)"@/ + "\n(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)"@/ + "\n(CHECKSUM O %lo)"@/ + "\n(FONTDIMEN"@/ + "\n (SLANT R %.6f)"@/ + "\n (SPACE R 0.0)"@/ + "\n (STRETCH R 0.0)"@/ + "\n (SHRINK R 0.0)"@/ + "\n (XHEIGHT R 1.0)"@/ + "\n (QUAD R 1.0)"@/ + "\n (EXTRASPACE R 0.0)"@/ + "\n )", design_size, checksum, slant);@# + + s = tfm_header; + i = strlen(s); + t = ((long)i) << 24; + sc = 16; + pos = 18;@# + + fprintf(out, "\n"); + while(i > 0) + {t |= ((long)(*(unsigned char *)s++)) << sc; + sc -= 8; + if(sc < 0) + {fprintf(out, "\n(HEADER D %d O %lo)", pos, t); + t = 0; + sc = 24; + pos++; + } + i--; + } + if(t) + fprintf(out, "\n(HEADER D %d O %lo)", pos, t); + fprintf(out, "\n");@# + + for(i = 0; i < 256; i++) + {fprintf(out,@/ + "\n(CHARACTER O %o"@/ + "\n (CHARWD R %.6f)"@/ + "\n (CHARHT R %.6f)"@/ + "\n (CHARDP R %.6f)"@/ + "\n (CHARIC R %.6f)"@/ + "\n )",@/ + i, + tfm_output_size_x + 2 * tfm_offset_x, + tfm_output_size_y + tfm_offset_y, + -tfm_offset_y, + slant * (tfm_output_size_y + tfm_offset_y)); + }@# + + fclose(out); + } + + + +@* The extended virtual font file for \Om. +The following is very similar to |write_pl()|; we simply map the glyphs +of the subfonts back to the original encoding positions. + +@<Prototypes@>= +void write_ovp(void); + + +@ +@c +void write_ovp(void) + {int c, i, nmb_subfonts, remainder, count, pos; + char output_file[FILE_NAME_LENGTH + 1]; + long t, sc; + char *s; + char ofm_header[] = "Created by hbf2gf"; + + nmb_subfonts = ((max_char - (min_char & 0xFF00)) / 256 * nmb_2_bytes) + / 256 + 1; + remainder = ((max_char - (min_char & 0xFF00)) / 256 * nmb_2_bytes) + % 256;@# + + /* correction for the last incomplete second byte range */ + for(count = 0; count < (max_char & 0xFF); count++) + if(b2_codes[count]) + remainder++; + if(remainder >= 256) + nmb_subfonts++;@# + + sprintf(output_file, "%s.ovp", output_name);@# + + if(!(out = fopen(output_file, WRITE_TXT))) + {fprintf(stderr, "Couldn't open `%s'\n", output_file); + exit(1); + } + if(!quiet) + printf("\nWriting `%s'\n", output_file);@# + + fprintf(out,@/ + "\n(VTITLE Omega virtual font created by hbf2gf)"@/ + "\n(DESIGNSIZE R %.6f)"@/ + "\n(COMMENT DESIGNSIZE IS IN POINTS)"@/ + "\n(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)"@/ + "\n(CHECKSUM O %lo)"@/ + "\n(FONTDIMEN"@/ + "\n (SLANT R %.6f)"@/ + "\n (SPACE R 0.0)"@/ + "\n (STRETCH R 0.0)"@/ + "\n (SHRINK R 0.0)"@/ + "\n (XHEIGHT R 1.0)"@/ + "\n (QUAD R 1.0)"@/ + "\n (EXTRASPACE R 0.0)"@/ + "\n )", design_size, checksum, slant);@# + + s = ofm_header; + i = strlen(s); + t = ((long)i) << 24; + sc = 16; + pos = 18;@# + + fprintf(out, "\n"); + while(i > 0) + {t |= ((long)(*(unsigned char *)s++)) << sc; + sc -= 8; + if(sc < 0) + {fprintf(out, "\n(HEADER D %d O %lo)", pos, t); + t = 0; + sc = 24; + pos++; + } + i--; + } + if(t) + fprintf(out, "\n(HEADER D %d O %lo)", pos, t); + fprintf(out, "\n");@# + + for(i = 0; i < nmb_subfonts; i++) + {fprintf(out,@/ + "\n(MAPFONT D %i"@/ + "\n (FONTNAME %s%02i)"@/ + "\n (FONTCHECKSUM O %lo)"@/ + "\n (FONTAT R 1.0)"@/ + "\n (FONTDSIZE R %.6f)"@/ + "\n )", i, output_name, i + 1, checksum, design_size); + }@# + + for(c = min_char, i = 0, count = 0; c <= max_char; c++) + {if(b2_codes[c & 0xFF] == VALID_SUBCODE) + {fprintf(out,@/ + "\n(CHARACTER O %o"@/ + "\n (CHARWD R %.6f)"@/ + "\n (CHARHT R %.6f)"@/ + "\n (CHARDP R %.6f)"@/ + "\n (CHARIC R %.6f)"@/ + "\n (MAP"@/ + "\n (SELECTFONT D %i)"@/ + "\n (SETCHAR O %o)"@/ + "\n )"@/ + "\n )",@/ + c, + tfm_output_size_x + 2 * tfm_offset_x, + tfm_output_size_y + tfm_offset_y, + -tfm_offset_y, + slant * (tfm_output_size_y + tfm_offset_y), + i, + count);@# + + count++; + if(count == 256) + {count = 0; + i++; + } + } + else + continue; + }@# + + fclose(out); + } + + + +@* The job file. +This routine is the most system specific one. If your operating system needs +a different outline, make appropriate changes here. + +You have to call this batch file after \.{hbf2gf} has finished (if not in +\mf-like mode). It will transform the \.{GF} files into \.{PK} files and +delete the now unnecessary \.{GF} files, then transform the \.{PL} file into +a \.{TFM} file and copy it |nmb_files| times. The name of the job file is +|output_name|. + +@d EXTENSION_LENGTH 8 /* the maximal length of a file extension */@# + +@d GFTOPK_NAME "gftopk" +@d PLTOTF_NAME "pltotf" +@d OVP2OVF_NAME "ovp2ovf" + +@<Global...@>= +char job_extension[EXTENSION_LENGTH + 1]; +char rm_command[STRING_LENGTH + 1]; +char cp_command[STRING_LENGTH + 1]; +char pk_directory[STRING_LENGTH + 1]; +char tfm_directory[STRING_LENGTH + 1];@# + +int ofm_file = FALSE; + + +@ +@<Prototypes@>= +void write_job(void); + + +@ +@c +void write_job(void) + {FILE *out; + int i, j; + char buffer[FILE_NAME_LENGTH + 1]; + + + strcpy(buffer, output_name); + strcat(buffer, job_extension); + if(!(out = fopen(buffer, WRITE_TXT))) + {fprintf(stderr, "Couldn't open `%s'\n", buffer); + exit(1); + } + if(!quiet) + printf("\nWriting `%s'\n", buffer);@# + + if(pk_files) + {if(unicode) + {for(i = (min_char >> 8), j = 0; j < nmb_files; i++, j++) + fprintf(out,@/ + "%s %s%02x.gf %s%s%02x.%.0ipk\n"@/ + "%s %s%02x.gf\n",@/ + GFTOPK_NAME, output_name, i,@/ + pk_directory, output_name, i, + long_extension ? (int)(dpi_x * magstep_x + 0.5) : 0,@/ + rm_command, output_name, i); + } + else + {for(i = 1; i <= nmb_files; i++) + fprintf(out,@/ + "%s %s%02i.gf %s%s%02i.%.0ipk\n"@/ + "%s %s%02i.gf\n",@/ + GFTOPK_NAME, output_name, i,@/ + pk_directory, output_name, i, + long_extension ? (int)(dpi_x * magstep_x + 0.5) : 0,@/ + rm_command, output_name, i); + } + }@# + + if(tfm_files) + {fprintf(out,@/ + "\n"@/ + "%s %s.pl %s.tfm\n"@/ + "%s %s.pl\n"@/ + "\n",@/ + PLTOTF_NAME, output_name, output_name,@/ + rm_command, output_name);@# + + if(unicode) + {for(i = (min_char >> 8), j = 0; j < nmb_files; i++, j++) + fprintf(out,@/ + "%s %s.tfm %s%s%02x.tfm\n",@/ + cp_command, output_name, + tfm_directory, output_name, i); + } + else + {for(i = 1; i <= nmb_files; i++) + fprintf(out,@/ + "%s %s.tfm %s%s%02i.tfm\n",@/ + cp_command, output_name, + tfm_directory, output_name, i); + }@# + + fprintf(out,@/ + "\n"@/ + "%s %s.tfm",@/ + rm_command, output_name); + }@# + + if(ofm_file) + {fprintf(out,@/ + "\n"@/ + "%s %s.ovp %s.ovf %s.ofm\n"@/ + "%s %s.ovp\n"@/ + "\n",@/ + OVP2OVF_NAME, output_name, output_name, output_name,@/ + rm_command, output_name); + }@# + + fclose(out); + } + + + +@* The configuration file. +Here is a list with all necessary keywords (and parameters): +\medskip +\halign{\quad\.{#}\hfil&\quad#\hfil\cr + hbf\_header & the HBF header file name of the input font(s). \cr + output\_name & the name stem of the output files. \cr + & Should be equal to the name of the configuration file + in most cases.\cr + & A running two digit decimal number starting with + \.{01} will be appended. \cr + & (For Unicode fonts see the keyword \.{unicode} + below.) \cr + } +\bigskip +And now all optional keywords: +\medskip +\halign{\quad\.{#}\hfil&\quad#\hfil\cr + x\_offset & increases the character width. \cr + & Will be applied on both sides; \cr + & default is the value given in the HBF header + (\.{HBF\_BITMAP\_BOUNDING\_BOX}) \cr + & scaled to \\{design\/}size (in pixels). \cr + y\_offset & shifts all characters up or down; \cr + & default is the value given in the HBF header + (\.{HBF\_BITMAP\_BOUNDING\_BOX}) \cr + & scaled to \\{design\/}size (in pixels). \cr + design\_size & the design size (in points) of the font. \cr + & \.{x\_offset} and \.{y\_offset} refer to this + size. \cr + & Default is |10.0| \cr + target\_size & This command is obsolete now and will be + ignored.\cr + & \cr + slant & the slant of the font (given as $\Delta x / + \Delta y$). \cr + & Only values in the range |0 <= slant <= 1| + are allowed. \cr + & Default is |0.0| \cr + rotation & if set to `yes'. the glyphs are rotated + 90~degrees counter-clockwise. \cr + & The default offsets as given in the HBF header + will be ignored (and set to~|0|). \cr + & Default is `no'. \cr + & \cr + mag\_x & \cr + mag\_y & scaling values of the characters to reach + design size. \cr + & If only one magnification is given, x and y + values are assumed to be equal. \cr + & Default is \.{mag\_x} $=$ \.{mag\_y} $=$ |1.0| + \cr + threshold & A value between |1| and |254| defining a + threshold for converting the internal \cr + & graymap into the output bitmap; lower values + cut more pixels. \cr + & Default value is |128|. \cr + & \cr + comment & a comment describing the font; \cr + & default is none. \cr + & \cr + nmb\_fonts & the number of the fonts. \cr + & Default value is~|-1| for creating all + fonts. \cr + unicode & if `yes', a two digit hexadecimal number will + be used as a running number, \cr + & starting with the value of the first byte of + the first code range. \cr + & Default is `no'. \cr + min\_char & the minimum of the encoding range. \cr + & Specify this value if it is not identical to + the lowest code value \cr + & in the HBF file (to which it defaults). \cr + & \cr + dpi\_x & \cr + dpi\_y & the horizontal and vertical resolution (in + dpi) of the printer. \cr + & If only one resolution is given, x and y + values are assumed to be equal. \cr + & Default is |300|. \cr + checksum & a checksum to identify the \.{GF} files + with the appropriate \.{TFM} files. \cr + & The default of this 32~bit unsigned integer + is |0|. \cr + coding & a comment describing the coding scheme; \cr + & default is none. \cr + & \cr + pk\_directory & the destination directory of the + \.{PK} files; \cr + & default: none. \cr + & Attention! The batch file will not check + whether this directory exists. \cr + tfm\_directory & the destination directory of the + \.{TFM} files; \cr + & default: none. \cr + & Attention! The batch file will not check + whether this directory exists. \cr + pk\_files & whether to create \.{PK} files or not; \cr + & default is `yes'. \cr + tfm\_files & whether to create \.{TFM} files or not; \cr + & default is `yes'. \cr + ofm\_file & whether to create an \.{OFM} and an + \.{OVF} file or not; \cr + & default is `no'. \cr + long\_extension & if `yes', \.{PK} files will include the + resolution in the extension \cr + & (e.g. \.{gsso1201.300pk}). \cr + & This affects the batch file only (default is + `yes'). \cr + rm\_command & this shell command removes files; \cr + & default: `rm'. \cr + cp\_command & this shell command copies files; \cr + & default: `cp'. \cr + job\_extension & the extension of the batch file which calls + \.{GFtoPK} and \.{PLtoTF} \cr + & to convert the \.{GF} and the + \.{PL} files into \.{PK} and + \.{TFM} files; \cr + & default is none. \cr + } +\bigskip + +The searching algorithm (for the keywords) of \.{hbf2gf} is case +insensitive; it makes no difference whether you write for example \.{comment} +or \.{CommenT}. The keywords must start a line (be in the first column), and +the corresponding parameters must be on the same line with the keyword and +separated by at least one space or tabulator stop. Lines starting not with a +keyword are ignored. + +Key values \\{are} case sensitive (except \.{yes} and \.{no}). + +The default system dependent values are for \UNIX/-like operating systems; +if you use for example DOS, you must write +\medskip +\halign{\quad\.{#}\hfil&\quad\.{#}\hfil\cr + long\_extension & no \cr + rm\_command & del \cr + cp\_command & copy \cr + job\_extension & .bat \cr + } +\medskip + +Both the values |pk_output_size_x| and |pk_output_size_y| must not exceed +|MAX_CHAR_SIZE|; \.{x\_offset} and \.{y\_offset} are related to the +design size (and not to the input size). + +In \mf-like mode, one \.{GF} file and one \.{PL} file will be computed +(depending on the command line options \.{-g} and \.{-p}), taking +|x_resolution| and |y_scale| from the command line. \.{nmb\_fonts} will +always be set to~|1|; no job file will be created. + + +@ +@d PRINTER_MIN_RES_X 50 +@d PRINTER_MIN_RES_Y 50 + +@<Global...@>= +char Buffer[STRING_LENGTH + 1]; + + +@ +@<Prototypes@>= +void read_config(void); + + +@ +If |config_file| isn't found in \mf-like mode we assume that the font +isn't a HBF font at all. + +@c +void read_config(void) + {HBF_BBOX *boxp; + char *real_config_file; + + + @<Handle extension@>@;@# + + real_config_file = TeX_search_cfg_file(config_file); + if(!real_config_file) + {if(mf_like) + {if(!quiet) + printf("Couldn't find `%s'\n", config_file); + exit(2); + } + else + {fprintf(stderr, "Couldn't find `%s'\n", config_file); + exit(1); + } + }@# + + if(!(config = fopen(real_config_file, READ_TXT))) + {if(!testing) + {fprintf(stderr, "Couldn't open `%s'\n", config_file); + exit(1); + } + else /* We reach this point only if no searching library is used */ + {if(!quiet) + fprintf(stderr, "Couldn't find or open `%s'\n", config_file); + exit(2); + } + }@# + + if(testing) + {if(!quiet) + printf("%s\n", real_config_file); + exit(0); + } + + @<Necessary parameters@>@; + @<Optional parameters@>@;@# + + @<Get code range@>@; + @<Get sub code range@>@;@# + + fclose(config); + } + + +@ +Here we check whether we have to add an extension. + +@<Handle extension@>= + {int i, lastext = -1; + + for(i = 0; config_file[i]; i++) + if(config_file[i] == '.') + lastext = i; + else if (config_file[i] == '/' || + config_file[i] == ':' || + config_file[i] == '\\') + lastext = -1;@# + + if(lastext == -1) + strcat(config_file, ".cfg"); + } + + +@ +@<Necess...@>= + {char hbf_header[STRING_LENGTH + 1]; + char *real_hbf_header; + + if(!fsearch("hbf_header")) + config_error("hbf_header"); + else + strcpy(hbf_header, Buffer);@# + + real_hbf_header = TeX_search_hbf_file(hbf_header); + if(!real_hbf_header) + {fprintf(stderr, "Couldn't find `%s'\n", hbf_header); + exit(1); + }@# + + hbfDebug = 1; /* we activate error messages of the HBF API + while scanning the HBF header file */@# + + if(!(hbf = hbfOpen(real_hbf_header))) + exit(1);@# + + hbfDebug = 0;@# + + boxp = hbfBitmapBBox(hbf); + input_size_x = boxp->hbf_height; /* will be checked later for rotation */ + input_size_y = boxp->hbf_width; + font_encoding = hbfProperty(hbf, "HBF_CODE_SCHEME");@# + + if(!fsearch("output_name")) + config_error("output_name"); + else + strcpy(output_name, Buffer); + } + + +@ +@<Global...@>= +int offset_x; +int offset_y;@# + +HBF_CHAR user_min_char; +int have_min_char = FALSE; + +@ +@<Opt...@>= + {if(fsearch("nmb_files")) + nmb_files = atoi(Buffer); + if(fsearch("unicode")) + if(Buffer[0] == 'y' || Buffer[0] == 'Y') + unicode = TRUE; + if(fsearch("min_char")) + {user_min_char = strtoul(Buffer, (char **)NULL, 0); + have_min_char = TRUE; + }@# + + if(!mf_like) + {if(fsearch("pk_files")) + if(Buffer[0] == 'n' || Buffer[0] == 'N') + pk_files = FALSE; + if(fsearch("tfm_files")) + if(Buffer[0] == 'n' || Buffer[0] == 'N') + tfm_files = FALSE; + if(fsearch("ofm_file")) + if(Buffer[0] == 'y' || Buffer[0] == 'Y') + ofm_file = TRUE; + if(fsearch("long_extension")) + if(Buffer[0] == 'n' || Buffer[0] == 'N') + long_extension = FALSE; + }@# + + if(fsearch("slant")) + slant = atof(Buffer); + if(slant < 0.0 || slant > 1.0) + {fprintf(stderr, "Invalid slant\n"); + exit(1); + } + if(fsearch("rotation")) + if(Buffer[0] == 'y' || Buffer[0] == 'Y') + rotation = TRUE;@# + + if(fsearch("mag_x")) + mag_x = atof(Buffer); + if(fsearch("mag_y")) + mag_y = atof(Buffer); + if(!mag_x && !mag_y) + {mag_x = 1.0; + mag_y = 1.0; + } + if(mag_x && !mag_y) + mag_y = mag_x; + if(mag_y && !mag_x) + mag_x = mag_y; + if(mag_x <= 0.0) + {fprintf(stderr, "Invalid horizontal magnification\n"); + exit(1); + } + if(mag_y <= 0.0) + {fprintf(stderr, "Invalid vertical magnification\n"); + exit(1); + }@# + + if(fsearch("dpi_x")) + dpi_x = atoi(Buffer); + if(fsearch("dpi_y")) + dpi_y = atoi(Buffer); + if(!dpi_x && !dpi_y) + {dpi_x = 300; + dpi_y = 300; + } + if(dpi_x && !dpi_y) + dpi_y = dpi_x; + if(dpi_y && !dpi_x) + dpi_x = dpi_y; + if(dpi_x <= PRINTER_MIN_RES_X) + {fprintf(stderr, "Invalid horizontal printer resolution\n"); + exit(1); + } + if(dpi_y <= PRINTER_MIN_RES_Y) + {fprintf(stderr, "Invalid vertical printer resolution\n"); + exit(1); + }@# + + if(fsearch("design_size")) + design_size = atof(Buffer);@# + + if(fsearch("x_offset")) + offset_x = atoi(Buffer); + else + offset_x = rotation ? 0 : (boxp->hbf_xDisplacement * mag_x + 0.5); + if(fsearch("y_offset")) + offset_y = atoi(Buffer); + else + offset_y = rotation ? 0 : (boxp->hbf_yDisplacement * mag_y + 0.5); + if(!fsearch("comment")) + comment[0] = '\0'; + else + strcpy(comment, Buffer);@# + + if(fsearch("threshold")) + threshold = atoi(Buffer); + if(threshold <= 0 || threshold >= 255) + {fprintf(stderr, "Invalid threshold\n"); + exit(1); + }@# + + if(!fsearch("checksum")) + checksum = 0; + else + checksum = strtoul(Buffer, (char **)NULL, 0);@# + + if(!fsearch("coding")) + coding[0] = '\0'; + else + strcpy(coding, Buffer);@# + + if(!fsearch("pk_directory")) + pk_directory[0] = '\0'; + else + strcpy(pk_directory, Buffer);@# + + if(!fsearch("tfm_directory")) + tfm_directory[0] = '\0'; + else + strcpy(tfm_directory, Buffer);@# + + if(fsearch("rm_command")) + strcpy(rm_command, Buffer); + else + strcpy(rm_command, "rm");@# + + if(fsearch("cp_command")) + strcpy(cp_command, Buffer); + else + strcpy(cp_command, "cp");@# + + if(!fsearch("job_extension")) + job_extension[0] = '\0'; + else + {strncpy(job_extension, Buffer, EXTENSION_LENGTH); + job_extension[EXTENSION_LENGTH] = '\0'; + } + } + + +@ +The function |hbfGetCodeRange()| is an extension to the HBF API. + +Successive calls return the code ranges in ascending order; we only need the +extrema of the whole code range. + +In case |min_char| has been supplied in the configuration file, we use that +value instead. + +@<Global...@>= +HBF_CHAR min_char, max_char; + + +@ +@<Get code range@>= + {const void *cp; + HBF_CHAR dummy; + + cp = hbfGetCodeRange(hbf, NULL, &min_char, &max_char); + for(; cp != NULL; cp = hbfGetCodeRange(hbf, cp, &dummy, &max_char)) + ;@# + + if(have_min_char) + min_char = user_min_char; + } + + +@ +The function |hbfGetByte2Range()| is an extension to the HBF API. + +Successive calls return the byte~2 ranges in ascending order. We raise +|VALID_SUBCODE| in the array |b2_codes[]| for all characters in subcode +ranges. + +@d VALID_SUBCODE 1 + +@<Global...@>= +char b2_codes[256]; +unsigned char min_2_byte, max_2_byte; +int nmb_2_bytes = 0; + + +@ +@<Get sub code range@>= + {const void *b2r; + unsigned char dummy; + int i; + + for(i = 0; i < 256; i++) + b2_codes[i] = 0;@# + + b2r = hbfGetByte2Range(hbf, NULL, &min_2_byte, &max_2_byte); + dummy = min_2_byte; + for(; b2r != NULL; b2r = hbfGetByte2Range(hbf, b2r, &dummy, &max_2_byte)) + {for(i = dummy; i <= max_2_byte; i++) + b2_codes[i] = VALID_SUBCODE; + }@# + + for(i = 0; i < 256; i++) /* compute the number of valid |b2_codes| */ + if(b2_codes[i] == VALID_SUBCODE) + nmb_2_bytes++; + } + + +@ +This search routine is case insignificant. Each keyword must start a line; +the function checks whether the character before the keyword is a newline +character (|'\n'|). It also checks the presence of a parameter and fills +|Buffer| if existent. |fsearch()| returns~1 on success. + +@<Prototypes@>= +int fsearch(const char *); + + +@ +@c +int fsearch(const char *search_string) + {char *P, p; + const char *Q; + char temp_buffer[STRING_LENGTH + 1]; + char env_name[STRING_LENGTH + 1]; + char *env_p; + char *env_value; + char *Buf_p; + int Ch, ch, old_ch = '\n'; + int count = STRING_LENGTH; + + rewind(config); /* we start at offset~0 */@# + + do + {Q = search_string; + p = tolower(*Q); + Ch = fgetc(config); + ch = tolower(Ch); + while(!(ch == p && old_ch == '\n') && Ch != EOF) + /* search first character of |search_string|; + |'\n'| must be the character before */ + {old_ch = ch; + Ch = fgetc(config); + ch = tolower(Ch); + }@# + + for(;;) + {if(*(++Q) == '\0') + if((Ch = fgetc(config)) == ' ' || Ch == '\t') + /* there must be a space or a tab stop after the keyword */ + goto success; + Ch = fgetc(config); + if(tolower(Ch) != tolower(*Q)) + break; + } + } + while(Ch != EOF);@# + + return 0;@# + +success: + P = temp_buffer;@# + + while((Ch = fgetc(config)) == ' ' || Ch == '\t') + /* remove leading blanks and tabs */ + ; + while(Ch != '\n' && --count > 0 && Ch != EOF) /* fill |Buffer| */ + {*P++ = Ch; + Ch = fgetc(config); + } + *P = '\0';@# + + if (*temp_buffer) + @<Check for environment variables@>@; + else + *Buffer = '\0';@# + + return (*Buffer) ? 1 : 0; /* is there something in the buffer? */ + } + + +@ +To make the configuration file more flexible we allow environment variables +in the arguments. We scan the parameter stored in |temp_buffer| whether it +contains a `\.{\$}' character. If yes, the following code fragment tries to +get an environment variable name whose value will be then fetched with +|getenv()|. An environment variable name recognized by \.{hbf2gf} must start +with a letter or underscore; the other characters may be alphanumeric or an +underscore. You can surround the environment variable name with braces to +indicate where the name ends, e.g. \.{\$\{FOO\}}. The interpolated +configuration parameter will be truncated to |STRING_LENGTH| characters. If +you want to have `\.{\$}' you must write `\.{\$\$}'. + +Note that you should avoid to use such environment variables for specifying +the location of the configuration file in case you have support for a file +searching library like kpathsea. Its primary aim is to specify the target +directories for the \.{pk\_directory} and the \.{tfm\_directory} keywords. + +@<Check for environment variables@>= + {P = temp_buffer; + Buf_p = Buffer; + count = STRING_LENGTH - 1;@# + + while(*P && count > 0) + {env_p = env_name;@# + + if(*P == '$') + {P++; + if(*P == '$') + {*(Buf_p++) = *(P++); + count--; + continue; + } + while(*P == '{') @q } @> + P++; + if(!(isalpha(*P) || *P == '_')) + {fprintf(stderr,@/ + "Invalid environment variable name in configuration file\n"); + exit(1); + } + *(env_p++) = *(P++); + while(*P) + {if(isalnum(*P) || *P == '_') + *(env_p++) = *(P++); + else + {@q { @> while(*P == '}') + P++; + *env_p = '\0'; + break; + } + }@# + + env_value = getenv(env_name); + if(env_value) /* append the environment value to |Buffer| */ + {while(*env_value && count > 0) + {*(Buf_p++) = *(env_value++); + count--; + } + } + } + else + {*(Buf_p++) = *(P++); + count--; + } + } + *Buf_p = '\0'; + } + + +@ +If an error occurs, |config_error()| will leave the program with an error +message. + +@<Prototypes@>= +void config_error(const char *); + + +@ +@c +void config_error(const char *message) + {fprintf(stderr, "Couldn't find `%s' entry in configuration file\n", + message); + exit(1); + } + + + +@* File searching. + +We support three searching engines: emtexdir, kpathsea, and MiKTeX (which is +a Win32 port of kpathsea). For emtexdir, define |HAVE_EMTEXDIR| while +compiling. For kpathsea, define |HAVE_LIBKPATHSEA|. For MikTeX, define +|HAVE_MIKTEX|. If none of these macros is defined, a simple |fopen()| will +be used instead. + +@<Include files@>= +#if defined(HAVE_LIBKPATHSEA) +#include "kpathsea/kpathsea.h" + +#elif defined(HAVE_EMTEXDIR) +#include "emtexdir.h" + +#elif defined(HAVE_MIKTEX) +#include "miktex.h" +#endif + +@ +@<Global variables@>= +#if defined(HAVE_EMTEXDIR) +char emtex_version_string[] = "emTeXdir"; +#elif !defined(HAVE_MIKTEX) +char no_version_string[] = "no search library"; +#endif + + +@ +@<Prototypes@>= +const char *TeX_search_version(void); + + +@ +@c +const char *TeX_search_version(void) + { +#if defined(HAVE_LIBKPATHSEA) + return kpathsea_version_string; +#elif defined(HAVE_EMTEXDIR) + return emtex_version_string; +#elif defined(HAVE_MIKTEX) + char buf[200]; + + strcpy(buf, "MiKTeX "); + miktex_get_miktex_version_string_ex(buf + 7, sizeof (buf) - 7); + + return buf; +#else + return no_version_string; +#endif + } + + +@ +@s emtex_dir int + +@<Global variables@>= +#ifdef HAVE_EMTEXDIR +struct emtex_dir cfg_path, hbf_path; +#endif + + +@ +@<Prototypes@>= +#ifdef HAVE_EMTEXDIR +extern int setup_list(struct emtex_dir *, char *, const char *, unsigned); +int dir_setup(struct emtex_dir *, const char *, const char *, unsigned); +char *file_find(char *, struct emtex_dir *); +#endif + + +@ +We slightly modify |emtex_dir_setup()| (from the file \.{emtexdir.c}) to +output a warning in case the environment variable |env| isn't set properly. + +@c +#ifdef HAVE_EMTEXDIR +int dir_setup(ed, env, dir, flags) + struct emtex_dir *ed; + const char *env; + const char *dir; + unsigned flags; + + {const char *val; + char path[260]; + + ed->alloc = 0; + ed->used = 0; + ed->list = NULL;@# + + if(env != NULL && (val = getenv(env)) != NULL) + return setup_list(ed, path, val, flags); + else + fprintf(stderr, + "Environment variable `%s' not set; use current directory\n", + env);@# + + return TRUE; + } + + +@ +@<Global variables@>= +char name_buffer[FILE_NAME_LENGTH + 1]; + + +@ +@c +char *file_find(name, list) + char *name; + struct emtex_dir *list; + + {if(emtex_dir_find(name_buffer, sizeof(name_buffer), list, name, EDF_CWD)) + return name_buffer;@# + + return NULL; + } +#endif + + +@ +For emtexdir we use the environment variables \.{HBFCFG} and \.{HBFONTS} for +configuration resp.\ HBF header files. + +@<Initialize \TeX\ file searching@>= +#if defined(HAVE_LIBKPATHSEA) + kpse_set_program_name(argv[0], "hbf2gf"); + kpse_init_prog("HBF2GF", 300, "cx", "cmr10");@# + +#elif defined(HAVE_EMTEXDIR) + if(!dir_setup(&cfg_path, "HBFCFG", NULL, EDS_BANG)) + {fprintf(stderr, + "Couldn't setup search path for configuration files\n"); + exit(1); + } + if(!dir_setup(&hbf_path, "HBFONTS", NULL, EDS_BANG)) + {fprintf(stderr, + "Couldn't setup search path for HBF header files\n"); + exit(1); + } +#endif + + +@ +Finally, here are the searching routines. A special format in the kpathsea +library for fonts which are neither PostScript nor TrueType (\.{MISCFONTS}) +is available with version~3.3 and newer. For older versions we use the path +for PostScript fonts (\.{T1FONTS}) to find HBF files. Configuration files +are searched in the path specified within \.{TEXCONFIG} for old kpathsea +versions, and within \.{HBF2GFINPUTS} for new versions. + +@<Prototypes@>= +char *TeX_search_cfg_file(char *); +char *TeX_search_hbf_file(char *); + + +@ +@c +#if defined(HAVE_LIBKPATHSEA) +char *TeX_search_cfg_file(char *name) + { + return kpse_find_file(name, kpse_program_text_format, TRUE); + }@# + + +char *TeX_search_hbf_file(char *name) + { + return kpse_find_file(name, kpse_miscfonts_format, TRUE); + }@# + + +#elif defined(HAVE_EMTEXDIR) +char *TeX_search_cfg_file(char *name) + {return file_find(name, &cfg_path); + }@# + + +char *TeX_search_hbf_file(char *name) + {return file_find(name, &hbf_path); + }@# + + +#elif defined(HAVE_MIKTEX) +char *TeX_search_cfg_file(char *name) + {char result[_MAX_PATH]; + + if (!miktex_find_input_file("hbf2gf", *name, result)) + return 0; + return strdup(result); + }@# + + +char *TeX_search_hbf_file(char *name) + {char result[_MAX_PATH]; + + + if (!miktex_find_miscfont_file(*name, result)) + return 0; + return strdup(result); + }@# + + +#else +char *TeX_search_cfg_file(char *name) + {return name; + }@# + + +char *TeX_search_hbf_file(char *name) + {return name; + } +#endif + + + +@* An example. +This is the example configuration file \.{b5so12.cfg} (for use with DOS or +OS/2 and the emtexdir searching engine): +\bigskip +\halign{\quad\.{#}\hfil&\quad\.{#}\hfil\cr + hbf\_header & et24.hbf \cr + mag\_x & 2.076 \cr + x\_offset & 3 \cr + y\_offset & -8 \cr + comment & fanti songti 24x24 pixel font scaled and + adapted to 12 pt \cr + & \cr + design\_size & 12.0 \cr + & \cr + nmb\_fonts & -1 \cr + & \cr + output\_name & b5so12 \cr + & \cr + dpi\_x & 300 \cr + checksum & 123456789 \cr + coding & codingscheme Big 5 encoded TeX text \cr + & \cr + long\_extension & no \cr + job\_extension & .cmd \cr + rm\_command & del \cr + cp\_command & copy \cr + pk\_directory & \$HBF\_TARGET\\pk\\360dpi\\ \cr + tfm\_directory & \$HBF\_TARGET\\tfm\\ \cr + } +\bigskip +If you say e.g. +\medskip +\quad \.{set HBF\_TARGET=c:\\emtex\\texfonts} +\medskip +on your DOS prompt (or in your \.{autoexec.bat} file), then the interpolated +value of the \.{tfm\_directory} keyword is \.{c:\\emtex\\texfonts\\tfm\\}. +The HBF header file \.{et24.hbf} will be searched in the path specified by +the \.{HBFCFG} environment variable. +\bigskip +The call +\medskip +\quad \.{hbf2gf b5so12.cfg} +\medskip +creates the files +\medskip +\quad \.{b5so1201.gf}, \.{b5so1202.gf}, $\ldots$ , \.{b5so1255.gf}, +\.{b5so12.pl}, and \.{b5so12.cmd} +\bigskip +After calling +\medskip +\quad \.{b5so12.cmd} +\medskip +you will find the \.{PK} files in the +\.{c:\\emtex\\texfonts\\pk\\360dpi} directory and the \.{TFM} files in +the \.{c:\\emtex\\texfonts\\tfm} directory; all \.{GF} files and +\.{b5so12.pl} will be deleted. +\bigskip +The call +\medskip +\quad \.{hbf2gf -n b5so1220 417} +\medskip +creates two files: +\medskip +\quad \.{b5so1220.gf} and \.{b5so1220.pl} +\medskip +using the configuration file \.{b5so12.cfg}. The \.{GF} file would be +named \.{b5so1220.417gf} if the flag \.{-n} had not been used. + +\medskip + +It is possible to convert bitmap fonts to \.{PK} files almost +automatically. The HBF header file already has the entry +\.{HBF\_BITMAP\_BOUNDING\_BOX} which defines vertical and horizontal offsets +(in pixels), but these values are not in all cases optimal. If you omit +\.{x\_offset} and \.{y\_offset} in the configuration file, the third and +fourth parameter of \.{HBF\_BITMAP\_BOUNDING\_BOX} is used, scaled to design +size (to say it in other words: \.{x\_offset} and \.{y\_offset} will always +apply to the design size to be synchronous with the \.{TFM} files). + +Don't confuse scaling and magnification: Scaling here means that you choose +a (arbitrary) design size and compute scaling values (\.{mag\_x} and +\.{mag\_y}) which scales the bitmap to this particular design size at a +certain (arbitrarily chosen) resolution (\.{dpi\_x} and \.{dpi\_y}). +Magnification means that the scaled bitmap will be then magnified to a +certain target size while still using the font parameters (i.e., the +\.{TFM} file) of the design size. + +In the sample, you have a $24 \times 24$ bitmap font which will be scaled to +$12\,\hbox{pt}$ having a resolution of $300\,\hbox{dpi}$: +\medskip +\quad $1\,\hbox{pt}$ are $300 / 72.27 = 4.1511$ pixel; + +\quad $12\,\hbox{pt}$ are $4.1511 * 12 = 49.813$ pixel; + +\quad thus the theoretical scaling value is $49.813 / 24 = 2.076$. + +\medskip + +But especially for small sizes, this may not be the best value if the font +should harmonize with, say, Knuth's Computer Modern fonts. I recommend to +compute, say, 5~\.{PK} fonts, then check the CJK font with different \TeX\ +fonts to see whether the offsets and/or the scaling value is good. The +greater the design size the finer you can control the offsets---as an +example you could use a design size of $30\,\hbox{pt}$ (nevertheless there +is a compile-time constant |MAX_CHAR_SIZE| which limits the maximal +character size; default is 255~pixels). + +If you have found optimal offsets, you can produce many different +magnifications of the CJK font using the same set of \.{TFM} files +analogous to ordinary \TeX\ fonts; as a simplification, we assume that +\.{PK} files with a resolution of $300\,\hbox{dpi}$ and a design size of +$10\,\hbox{pt}$ have the extension `\.{.300pk}' (respectively come into a +`\.{300dpi}' subdirectory)---this is the reason why in the above example for +the $12\,\hbox{pt}$ design size a `\.{360dpi}' target directory has been +used. Now we can use the following formula: + + $$ \\{needed\_dpi} = \\{your\_horizontal\_resolution} * + {\\{your\_target\_size} \over 10.0} $$ + +Example: assuming that your printer has a resolution of +$300\times400\,\hbox{dpi}$, and you want $14.4\,\hbox{pt}$: + + $$ 300 * {14.4 \over 10.0} = 432 $$ + +The vertical scaling value is $400 / 300 = 1.3333$. Use these values now to +call \.{hbf2gf} in \mf-like mode: + +\quad \.{hbf2gf b5so1220 432 1.3333} + + + +@* Index. |