% This is the cweb file hbf2gf.w of the CJK package ver. 4.8.5 16-Oct-2021 % Copyright (C) 1994-2021 Werner Lemberg % % 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.5)} \def\topofcontents{ \null\vfill \centerline{\titlefont The {\ttitlefont hbf2gf} program} \vskip 20pt \centerline{(CJK Version 4.8.5)} \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.5)" @ \.{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 */@# @= 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 @@; @@; @@;@# int main(int argc, char *argv[]) {char *p; @@;@# @@;@# 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) @@;@# @@;@# @@;@# 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" @= {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" @= {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. @= if(argc == 2) {if(strcmp(argv[1], "--help") == 0) @@; else if(strcmp(argv[1], "--version") == 0) @@; }@# 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. @= {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|. @= {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 */ } @ @= #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef TM_IN_SYS_TIME #include #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. @= static 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 static 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 " @= static void write_pre(void); @ @s tm int @c static 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. @= 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 */ @ @= static void write_data(void); @ @c static void write_data(void) {dot_count = 0; char_adr_p = char_adr;@# for(last_char = 0; (last_char < 256) && !end_of_file; last_char++) @@; } @ 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 @= {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}$ */ @= 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. @= static void write_post(void); @ @c static 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}$ */@# @@; @@; @@; } @ \.{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_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_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 @= 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. @= static void fputl(long, FILE *); @ @c static 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 @= 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. @= {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. @= static void make_pixel_array(void); @ @c static 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 @@; } } 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; } } @ @= {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) {@@; @@; } } @ @= 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; }@# @@; } @ Now |frac_row_left >= frac_row_to_fill|, so we can produce a 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. @= 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; } }@# @@; } @ @= 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). @= static #ifdef __GNUC__ __inline__ #endif void read_row(unsigned char *); @ @c static #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. @= int threshold = 128; @ @= static #ifdef __GNUC__ __inline__ #endif void write_row(unsigned char *); @ @c static #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 */ @= static void write_coding(void); @ The |goto start| instruction causes some compilers to complain about ``Unreachable code $\ldots$'' or something similar. @c static 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) {@@; start: @@; y++; } }@# @ @= 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; } @ @= 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. @= static void write_pl(void); @ @c static 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. @= static void write_ovp(void); @ @c static 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" @= 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; @ @= static void write_job(void); @ @c static 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 @= char Buffer[STRING_LENGTH + 1]; @ @= static 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 static void read_config(void) {HBF_BBOX *boxp; char *real_config_file; @@;@# 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); } @@; @@;@# @@; @@;@# fclose(config); } @ Here we check whether we have to add an 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"); } @ @= {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); } @ @= int offset_x; int offset_y;@# HBF_CHAR user_min_char; int have_min_char = FALSE; @ @= {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. @= HBF_CHAR min_char, max_char; @ @= {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 @= char b2_codes[256]; unsigned char min_2_byte, max_2_byte; int nmb_2_bytes = 0; @ @= {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. @= static int fsearch(const char *); @ @c static 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((unsigned char)*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((unsigned char)*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) @@; 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. @= {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((unsigned char)*P) || *P == '_')) {fprintf(stderr,@/ "Invalid environment variable name in configuration file\n"); exit(1); } *(env_p++) = *(P++); while(*P) {if(isalnum((unsigned char)*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. @= static void config_error(const char *); @ @c static 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. @= #if defined(HAVE_LIBKPATHSEA) #include "kpathsea/kpathsea.h" #elif defined(HAVE_EMTEXDIR) #include "emtexdir.h" #elif defined(HAVE_MIKTEX) #include "miktex.h" #endif @ @= #if defined(HAVE_EMTEXDIR) char emtex_version_string[] = "emTeXdir"; #elif !defined(HAVE_MIKTEX) char no_version_string[] = "no search library"; #endif @ @= static const char *TeX_search_version(void); @ @c static 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 @= #ifdef HAVE_EMTEXDIR struct emtex_dir cfg_path, hbf_path; #endif @ @= #ifdef HAVE_EMTEXDIR extern int setup_list(struct emtex_dir *, char *, const char *, unsigned); static int dir_setup(struct emtex_dir *, const char *, const char *, unsigned); static 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 static 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; } #endif @ @= #ifdef HAVE_EMTEXDIR char name_buffer[FILE_NAME_LENGTH + 1]; #endif @ @c #ifdef HAVE_EMTEXDIR static 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. @= #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. @= static char *TeX_search_cfg_file(char *); static char *TeX_search_hbf_file(char *); @ @c #if defined(HAVE_LIBKPATHSEA) static char *TeX_search_cfg_file(char *name) { return kpse_find_file(name, kpse_program_text_format, TRUE); }@# static char *TeX_search_hbf_file(char *name) { return kpse_find_file(name, kpse_miscfonts_format, TRUE); }@# #elif defined(HAVE_EMTEXDIR) static char *TeX_search_cfg_file(char *name) {return file_find(name, &cfg_path); }@# static char *TeX_search_hbf_file(char *name) {return file_find(name, &hbf_path); }@# #elif defined(HAVE_MIKTEX) static char *TeX_search_cfg_file(char *name) {char result[_MAX_PATH]; if (!miktex_find_input_file("hbf2gf", *name, result)) return 0; return strdup(result); }@# static char *TeX_search_hbf_file(char *name) {char result[_MAX_PATH]; if (!miktex_find_miscfont_file(*name, result)) return 0; return strdup(result); }@# #else static char *TeX_search_cfg_file(char *name) {return name; }@# static 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.