% This program is not copyrighted and can be used freely. % Version 0 was implemented in May 1983. % Here is TeX material that gets inserted after \input webhdr \def\hang{\hangindent 3em\indent\ignorespaces} \def\TeX{T\hbox{\hskip-.1667em\lower.424ex\hbox{E}\hskip-.125em X}} \font\ninerm=amr9 \let\mc=\ninerm % medium caps for names like PASCAL \def\PASCAL{{\mc PASCAL}} \def\(#1){} % this is used to make module names sort themselves better \def\9#1{} % this is used for sort keys in the index \def\title{CH\lowercase{to}PX} \def\contentspagenumber{1} \def\topofcontents{\null \def\titlepage{F} % include headline on the contents page \def\rheader{\mainfont\hfil \contentspagenumber} \vfill \centerline{\titlefont The {\ttitlefont CHtoPX} processor} \vskip 15pt \centerline{(Version 1, October 1983)} \vfill} \def\botofcontents{\vfill \centerline{\hsize 5in\baselineskip9pt \vbox{\ninerm\noindent The preparation of this report was supported in part by the National Science Foundation under grants IST-8201926 and MCS-7723738, and by the System Development Foundation. `\TeX' is a trademark of the American Mathematical Society.}}} \pageno=\contentspagenumber \advance\pageno by 1 @* Introduction. The \.{CHtoPX} utility program converts \TeX\ raster pixel (``\.{CHR}'') files into character pixel (``\.{PXL}'') files. The \.{CHR} file input by this program can be edited with a normal text editor, and the result can be converted back to \.{PXL} format. The companion program \.{PXtoCH} will convert a \.{PXL} file into a \.{CHR} file. The first \.{CHtoPX} program was designed by Lynn Ruggles in the spring of 1983. The programs \.{TFtoPL} and \.{PLtoTF} had a significant effect on the evolution of the present code. A \.{CHR} file may contain raster information for as many as 128 characters and as few as 0 characters. An exception to these rules is planned for oriental fonts; such fonts, however, are not allowed by this version of \.{CHtoPX}. @^oriental characters@>@^Chinese characters@>@^Japanese characters@> The |banner| string defined here should be changed whenever \.{CHtoPX} gets modified. @d banner=='This is CHtoPX, Version 1' {printed when the program starts} @ This program is written entirely in standard \PASCAL, except that it occasionally has lower case letters in strings that are output. Such letters can be converted to upper case if necessary. The input is read from |chr_file|, and the output is written on |pxl_file|; error messages and other remarks are written on the |error_file|, which the user may choose to assign to the terminal if the system permits it. Each character ends with an |page_marker|, which is set to chr(12) (cntl-L). This character can be changed for systems which do not use cntl-L's in file formats. Whatever character is chosen should be one that will not appear at the beginning of a line. This restriction excludes all of the letters, and the symbols +, -, :, and blank. @^system dependencies@> The term |print| is used instead of |write| when this program writes on the |error_file|, so that all such output can be easily deflected. @d print(#)==write(error_file,#) @d print_ln(#)==write_ln(error_file,#) @p program CHtoPX(@!chr_file,@!pxl_file,@!error_file); const @@/ type @@/ var@?@@/ procedure initialize; begin @@/ print_ln(banner);@/ end_of_chr:=chr(12); end; @ Here are some macros for common programming idioms. @d incr(#) == #:=#+1 {increase a variable by unity} @d decr(#) == #:=#-1 {decrease a variable by unity} @d do_nothing == {empty statement} @d shift(#) == #:=#*2 {multiply by 2} @d shift_and_add(#) == #:=#*2+1 {multiply by 2 and add one} @* Font metric data. The \.{CHR} file is in a format which allows it to be edited with a text editor. Once it has been edited, it needs to be converted back into a \.{PXL} file which contains information that the \TeX\ typesetting routines can use. These \.{PXL} files store the information needed by \TeX\ in a more compact way. The character raster in the \.{CHR} file sits in an almost invisible bounding box. This box is |almost| invisible in that all of the positions in the box are represented by blank spaces, except for the position of the |x_offset|, the |y_offset|, and the |origin|. These three positions are represented respectively by a dash (-), a bar (?), and a plus sign (+). The character raster sits at the lower right corner of the bounding box, which means that there may be space above or to the left of the raster, but not to the right or below it. None of the dimensions in the following information include the bounding box, but instead refer only to the inside box containing the character raster. In the following information, the symbol (*) indicates that the information is used by the \.{PXL} file, a (+) indicates that it is information about the total dimensions of the character raster, and a (-) indicates that it is information about the character dimensions within the raster. Items marked with (*) should not be changed when editing a character. If after editing the height or width changes, the \.{CHtoPX} program will detect this and change the values before writing the new \.{PXL} file. Note also that there is a lot of redundant information. This may or may not be eliminated in later versions of the two programs \.{PXtoCH} and \.{CHtoPX}, once it is determined that the information is truly redundant or unnecessary. A \.{CHR} file contains:\par Font file information (see the \.{PXtoCH} documentation for more details of these fields): \yskip\hang |total_char|:total number of characters\par \hang |checksum| (*):matches the checksum in any \.{DVI} file that refers to this font.\par \hang |magnification| (*): 1000 times the magnification factor at which this font was produced.\par \hang |design_size| (*): matches the design size in the \.{TFM} file.\par \hang |directory_pointer| (*): pointer to the directory information. \par \hang |pixel_id| (*): currently $= 1001$ decimal.\par \hang |max_height|: not currently used, but available. The height of the tallest raster in the file.\par \hang |max_width|: also not used. The width of the widest raster in the file. \par \yskip\noindent Character information: \yskip\hang |chr_code|:ascii code of the character in octal.\par \hang |pxl_height| (*)(+): total height of the raster\par \hang |pxl_width| (*)(+): total width of the raster\par \hang |x_offset| (*): offset from upper left hand corner of bounding box\par \hang |y_offset| (*): offset from upper left hand corner of bounding box\par \hang |tfm_width| (*): width that the \.{TFM} file thinks the character is in \.{FIX}es\par \hang |right| (-): rightmost pixel in character \par \hang |left| (-): leftmost pixel in character\par \hang |depth| (-): number of pixels below baseline\par \hang |raster_width| (-): actual width of the raster from the x offset\par \hang |internal_width| (-): width of character from x offset\par \hang |right_kerning| (-): position where right kerning begins\par \hang |top| (-): top row of the character\par \hang |data_row_count| (-): height of the character within the raster\par \yskip\noindent Raster information: \yskip\hang + - origin (-1,-1)\par \hang - - x-offset\par \hang : - y-offset\par \hang . - white pixels in the raster\par \hang * - black pixels in the raster\par \yskip\noindent \hbox {Sample raster: \hskip 1in \vtop{\tt \hbox {+ - \hfill} \hbox { .......*........ \hfill} \hbox { ......*.*....... \hfill} \hbox { .....*...*...... \hfill} \hbox { ....*.....*..... \hfill} \hbox {:...*********.... \hfill} \hbox { .......*........ \hfill} \hbox { .......*........ \hfill} \hbox { .......*........ \hfill} }} @ @= @!font_rec=record@/ @!pxl_height:integer; @!pxl_width:integer; @!x_offset:integer; @!y_offset:integer; @!raster_addr:integer; @!tfm_width:integer; @!chr_code:integer; end; @!font_tbl_type= packed array [1..tbl_size] of font_rec; @ @= @!end_of_chr:char; {end of character marker} @!font_info:font_tbl_type; @!save_right:integer; {value read in from |chr_file|} @!save_left:integer; {value read in from |chr_file|} @!save_depth:integer; {value read in from |chr_file|} @!save_internal_width:integer; {value read in from |chr_file|} @!old_raster_addr:integer; {value read in from |chr_file|} @!save_kerning:integer; {value read in from |chr_file|} @!save_top:integer; {value read in from |chr_file|} @!save_bottom:integer; {value read in from |chr_file|} @!save_data_row_count:integer; {value read in from |chr_file|} @!save_raster_width:integer; {value read in from |chr_file|} @!total_char,@!checksum,@!magnification,@!design_size,@!directory_ptr, @!pixel_id,@!max_height,@!max_width:integer; @ Suffice it to say that a \.{CHR} file is an ordinary text file. The \.{CHR} format is almost self-explanatory when you see an example or two. @= @!chr_file:text; @!page_marker:char; @!file_ok:boolean; @ Additionally, an error file is provided which contains all error messages for each character. This file can be specified to be the terminal, in which case all the error messages will be displayed when the program is run. @= @!error_file:text; @ Detailed information about the |pxl_file| appears in the documentation of the companion program, \.{PXtoCH}, so it will not be repeated here. @= @!byte=0..255; @ @= @!pxl_file:packed file of byte; @ On some systems you may have to do something special to read a packed file of bytes. For example, the following code didn't work when it was first tried at Stanford, because packed files have to be opened with a special switch setting on the \PASCAL\ that was used. @= rewrite(pxl_file);@/ reset(chr_file);@/ rewrite(error_file); @^system dependencies@> @ The input may, of course, be all screwed up and not a \.{CHR} file at all, so we provide a means of escape. @d abort(#)==begin print_ln(#); print_ln('Sorry, but I can''t go on. Are you sure this is a CHR file?');@/ file_ok:=false; end @d error(#)==print_ln(#) @* Useful subroutines. @ |Max| returns the larger of two integers. @p function max (m,n:integer):integer; begin if m>n then max:=m else max:=n; end; @ |Min| returns the smaller of two integers. @p function min (m,n:integer):integer; begin if m>n then min:=n else min:=m; end; @ If we are not at the end of the row, read the next character from |chr_file|. If we are at the end of the row, set |ch| to a predefined |end_of_line| character. If this is the end of the character (and also the end of the page), set an |end_of_page| flag. If the character read in matches the |end_of_line| character, it is an error, so change it to a period and print out a message. @= @!end_of_page:boolean; {end of page flag} @!raster_found:boolean; {end of raster flag} @!ch:char; {next character read in} @ @p procedure get_next_ch (var ch:char); begin ch := end_of_line; if not eoln(chr_file) then read(chr_file,ch); if ch=end_of_chr then begin end_of_page:= true;@/ ch:=end_of_line;@/ end end; @ If the header information is missing for a character, or the raster image is too messed up to continue processing this character, then we will skip it. |Skip_to_next_page| prints an error message, then skips to the beginning of the next character in the file. Usually, each character appears on a separate page. @p procedure skip_to_next_pg; var @!ch:char; begin repeat read (chr_file,ch); until (ch=end_of_chr) or eof(chr_file); if eof(chr_file) then print_ln ('End of file.'); end; @ We want to write out the information to the \.{PXL} file in 8-bit bytes. Here we convert a 32 bit number into 4 eight-bit bytes and write them out in Big Endian order (highest byte first). @p procedure write_pxl(@!oneword:integer); var k,l,m,n:byte; begin k:= oneword div @'100000000;@/ l:=(oneword div @'200000) mod 256;@/ m:=(oneword div 256) mod 256;@/ n:=oneword mod 256;@/ write (pxl_file,k,l,m,n); end; @ Here we convert a 16 bit number into 2 eight-bit bytes and write them out in Big Endian order (highest byte first). @p procedure write_half_pxl(oneword:integer); var m,n:byte; begin if oneword < 0 then oneword:=(oneword + 65536); m:=(oneword div 256);@/ n:=(oneword mod 256);@/ write(pxl_file,m,n); end; @* Read font information from CHR file. The first step in processing the \.{CHR} file is to read in the font file information. Certain fields must be found or the processing can't continue. These fields originally came from the \.{PXL} file, and should not be modified in any way if the \.{CHR} file is changed. They include: |checksum|, |magnification|, |design_size|, and |pixel_id|. The other fields will be checked by \.{CHtoPX}, and if they don't match the current information in the file, will be updated when the new \.{PXL} file is written out. |Found_info| is a flag indicating whether a particular field was found, and |found_all| keeps track of whether the necessary fields were found. |File_ok| checks to see if any information was found at all. First we declare the constants, types and global variables we will need. There is nothing special about the length of |info_size|; sixteen was chosen simply because all of the information needed fit into that length. @= @!info_size = 16;@/ @!screen_width = 14; {max. no of char that will fit on one screen / 6 } @ @= @!infotype = packed array [1..info_size] of char; @ @= @!info : infotype; {header we're looking for} @!found_info,@!found_all,@!done : boolean; {flags to indicate what we found} @!num:integer; {numeric value associated with the current header} @!i,@!j:integer; {we always need these somewhere} @ @= file_ok:=false;@/ found_all:=true;@/ found_info:=true;@/ read_hdr('Font file info :',found_info);@/ read_info('Number of char :',total_char,found_info,file_ok);@/ read_info('Checksum :',checksum,found_info,file_ok);@/ found_all:=found_all and found_info;@/ read_info('Magnification :',magnification,found_info,file_ok);@/ found_all:=found_all and found_info;@/ read_info('Design size :',design_size,found_info,file_ok);@/ found_all:=found_all and found_info;@/ read_info('Directory ptr :',directory_ptr,found_info,file_ok);@/ found_all:=found_all and found_info;@/ read_info('Pixel Id :',pixel_id,found_info,file_ok);@/ found_all:=found_all and found_info;@/ read_info('Max height :',max_height,found_info,file_ok);@/ read_info('Max width :',max_width,found_info,file_ok);@/ @ @ Check to see if the needed information was found, and whether we can continue. If everything was ok, write out the |pixel_id| to the |pxl_file|. @= if not file_ok then abort ('No font header information found.') else if not found_all then abort ('Font header fields missing.') else begin@/ write_pxl(pixel_id);@/ skip_to_next_pg;@/ end; @ This procedure reads in the header information and the value of the field associated with the header. If the header information does not match the expected header, a switch is set so that the next time this procedure is called, the same field will be checked and no new one read in. This will work fine if there are lines missing; it will not work if there is an extra line or the lines are out of order. If the value is missing, zero is assumed. @p procedure read_info(info:infotype; var val:integer; var found_info:boolean; var ok:boolean); var i:integer; @!name:infotype; {header we're reading in} begin if found_info then begin for i := 1 to info_size do if not eoln (chr_file) then read(chr_file,name[i]) else name[i] := blank; if not eoln (chr_file) then readln (chr_file,num) else num:=0; end; found_info := name=info;@/ ok:=found_info or ok; if not found_info then begin val:=0; print(info);@/ error(' value missing. Substituting 0.'); end else val:=num; end; @ This procedure reads in the file header. If the header is missing, that's ok, but we'll print out an error message anyway. We also set |found| to false, and just in case this is really the next header, we will try to read in a number following the header, and save it for the next go round. @p procedure read_hdr(info:infotype;var found_info:boolean); var i:integer; @!name:infotype; {header we're reading in} begin for i := 1 to info_size do if not eoln (chr_file) then read(chr_file,name[i]) else name[i] := blank; if name <> info then begin error ('Font file header missing. Is this really a CHR file?'); error ('I will try to continue anyway.'); found_info:=false; if not eoln(chr_file) then read (chr_file,num) else num:=0; end; readln (chr_file); end; @* Read character header. Now, for each character, the character header is read in, then if the header is ok, the character raster is read, converted to binary, and written out. The information about the character is saved in |font_info|. If the header information is missing the character is skipped. @= @@/ if not chr_ok then begin error ('No character header information found.');@/ skip_to_next_pg; @/ end else @@/ @ @= @!chr_index,@!temp_chr:integer; @!chr_code:integer; {value read in from |chr_file|} @!chr_ok:boolean; @!chr_count:integer; {keeps track of whether we need to start a new line} @ Read in the character information using the same procedure as was used for the font information header. Note again, this will work if there are headers missing, but not if they are out of order or there are extra headers. The only field that is required in the header is the |chr_code|, because without it, we have no idea what character we are processing! If the |chr_code| is missing, we will skip this character and go on to the next. Note that the |chr_code| must be the first line of the header, or we assume that it isn't there. The other fields will be checked by \.{CHtoPX}, and if they don't match the current information in the file, then will be updated when the new \.{PXL} file is written out. If no value can be calculated, such as for |tfm_width|, it will be set to zero. |Found_info| is a flag indicating whether a particular field was found, and |chr_ok| checks to see if any information was found at all. @= chr_ok:=false;@/ found_info:=true;@/ read_info('chrcode :',chr_code,found_info,chr_ok);@/ if found_info then begin print('[',chr_code:1,']');@/ incr(chr_count); @/ if chr_count > screen_width then begin print_ln(' ');@/ chr_count := 0;@/ end; @@/ read_info('height :',font_info[chr_index].pxl_height,found_info, chr_ok); read_info('width :',font_info[chr_index].pxl_width,found_info,chr_ok); read_info('right :',save_right,found_info,chr_ok); read_info('left :',save_left,found_info,chr_ok); read_info('depth :',save_depth,found_info,chr_ok); read_info('x offset :',font_info[chr_index].x_offset,found_info,chr_ok); read_info('y offset :',font_info[chr_index].y_offset,found_info,chr_ok); read_info('tfm width :',font_info[chr_index].tfm_width,found_info,chr_ok); read_info('raster width :',save_raster_width,found_info,chr_ok); read_info('internal width :',save_internal_width,found_info,chr_ok);@/ read_info('right kerning :',save_kerning,found_info,chr_ok); read_info('top :',save_top,found_info,chr_ok); read_info('bottom :',save_bottom,found_info,chr_ok); read_info('data row count :',save_data_row_count,found_info,chr_ok); end else error ('Didn''t find chr_code.'); @ @= chr_index:=(chr_code div 100) *64;@/ temp_chr:=chr_code mod 100;@/ chr_index:=chr_index+(temp_chr div 10) * 8;@/ temp_chr:=temp_chr mod 10;@/ chr_index:=chr_index + temp_chr + 1; font_info[chr_index].chr_code := chr_code; @* Find raster image. The character raster sits inside a bounding box which contains information about the position of the character within the raster. This outside box may contain any number of rows or columns above or to the left of the character, any or all of which may be blank (but hopefully not, since we are looking for something in these rows and columns). Our first task, therefore, is to find out where the raster is inside this box, and at the same time, to save any information the outside box contains pertaining to the |x_offset|(indicated by a dash), the |y_offset|(a colon), or the |origin|(a plus) of the raster. @= @!tbl_size=128; {current maximum number of characters allowed in file} @!asterisk='*'; {represents a pixel that is `on', or black} @!blank=' '; {filler for bounding box} @!period='.'; {represents a pixel that is `off', or white} @!plus='+'; {the (0,0) position in the raster} @!dash='-'; {the |x_offset|} @!bar=':'; {the |y_offset|} @!end_of_line='!'; {marker to signal when we've reached the end of a line} @ @= @!found : (both,origin,x_off,none,letter); {what did we find?} @!raster_addr:integer; {keeps track of how many words have been written on the |pxl_file|} @!x_offset:integer; {position of |x_offset|} @!y_offset:integer; {position of |y_offset|} @!first_column:integer; {position of the first column of the character raster} @!bar_row:integer; {row where we found the bar} @!col_index:integer; {current column} @!row_index:integer; {current row} @!dash_column:integer; {column where dash found} @!plus_column:integer; {column where plus found} @!plus_row:integer; {row where plus found} @ It is here that things get sticky. There is no knowing whether either the offset information or the raster itself has been modified since being created by \.{PXtoCH}, and there could be all sorts of strange data in it. We will hope for the best, yet proceed with caution. @= begin end_of_page:=false;@/ row_index:=0;@/ col_index:=0;@/ found:=none;@/ @@/ if end_of_page then begin error ('No raster found.');@/ @@/ end else begin @@/ @@/ end; end; @ If no raster information was found, no character information will be saved, so all the fields for that character are zeroed out in the |font_info|. @= font_info[chr_index].pxl_height:=0;@/ font_info[chr_index].pxl_width:=0;@/ font_info[chr_index].x_offset:=0;@/ font_info[chr_index].y_offset:=0;@/ font_info[chr_index].raster_addr:=0;@/ font_info[chr_index].tfm_width:=0;@/ @ Search for the raster. Read |chr_file| until we reach the first period or asterisk, signalling the beginning of the character itself. Look for a plus, dash, or colon, which will indicate the position of the origin, x-offset, and y-offset, respectively. If no period or asterisk is found, the character doesn't exist, and no information will be written out to the |pxl_file|. @= dash_column:=-1;@/ plus_column:=-1;@/ plus_row:=-1;@/ bar_row:=-1;@/ x_offset:=-1;@/ y_offset:=-1;@/ first_column:=-1;@/ repeat get_next_ch(ch);@/ @@/ incr(col_index); until (end_of_page) or (found=letter); @ After we read in the next character, check it to see what it is. If it is a |plus|, it is the origin; if a |dash|, it is the x-offset; if it is a |colon|, it is the y-offset; if it is a |blank|, it is a place holder; if it is a |period| or |asterisk|, it is the beginning of the character raster. At the |end_of_line| we increment the row count and reset the column count. @= case ch of plus: begin if (found=origin) or (found=both) then error ('duplicate origin (+) found') else begin if found=x_off then found := both else found := origin; plus_column:=col_index;@/ plus_row:=row_index; end; end; dash: begin if (found=x_off) or (found=both) then error('duplicate x offset (-) found.') else begin if found=origin then found:=both else found :=x_off; dash_column:=col_index; end; end; bar: bar_row:=row_index; blank: do_nothing; asterisk, period: @; end_of_line: begin readln(chr_file);@/ incr(row_index);@/ col_index:=-1;@/ end; end; @ When we find the first period or asterisk, check to see what state we are in. If we have found the x-offset (dash) or y-offset (colon), save their position, relative to the origin. If the x-offset or origin wasn't found, assume that they are both zero. If the y-offset wasn't found, that's ok, since it could be that we just haven't gotten to it yet. There will be some number of spaces to the left of the raster (minimum of one) and we will save this number in |first_column|, so that when we start to process the raster image, we know how many spaces to skip before beginning processing. @= begin case found of both,letter:do_nothing; origin:dash_column:=plus_column; x_off: error('origin (+) not found. Substituting 0.');@/ none: error ('origin(+) and x offset (-) not found. Using 0 for both.');@/ end;@/ first_column:=col_index;@/ found:=letter;@/ x_offset:=dash_column-plus_column - 1;@/ y_offset:=bar_row-plus_row - 1;@/ end @* Read character raster. Here we read in each row of the character, and convert it into an equivalent binary number. This number must have four bytes of eight bits for a total of 32 positions, so if the row we are reading in does not have a multiple of 32 positions, we have to fill it with zeros so that it does. Each 8 bit number is then written out to the |pxl_file|. This process is repeated for each row in the character. Hopefully, each row will have the same number of characters in it; if they don't, then the raster is not rectangular, which could indicate a problem. The raster is processed as is, but a warning message is printed out. The character raster is read until either an |end_of_page| marker or a blank line is found. @d out(#) == write (pxl_file,(# mod 256));@/ @= @!row,@!save_byte,@!left,@!right,@!top,@!bottom,@!num_words:integer; @!bit:integer; @!first_line:boolean; {is this the first row of the raster?} @!last_col:boolean; {is this the last col of the raster?} @!top_found:boolean; {have we found the first asterisk?} @!line_width:integer; @ @= top_found:=false;@/ left:=max_width;@/ right:=0;@/ top:=0;@/ bottom:=0;@/ old_raster_addr:=raster_addr;@/ save_byte:=0;@/ end_of_page:=false;@/ row:=row_index-1;@/ line_width:=0;@/ last_col:=true;@/ first_line:=true;@/ repeat num_words:=0;@/ row := row + 1; repeat@/ incr(num_words); raster_found := false;@/ for i := 1 to 4 do {4 bytes of} begin for j := 1 to 8 do {8 bits each} @@/ if raster_found then begin out (save_byte);@/ save_byte:=0;@/ end; end;@/ if raster_found then incr(raster_addr);@/ until ch=end_of_line; if not end_of_page and raster_found then @@/ until end_of_page or not raster_found; if not raster_found then skip_to_next_pg; @ Read the next character and convert it. If it is a period, it becomes a zero; if it is an asterisk, it becomes a one; if it is anything else, it is treated as a period. What we are doing here is converting the raster made up of periods and asterisks into a binary number. This number will be saved by writing it out to the |pxl_file|. We are converting it bit by bit, so for each character we read in, we either add a zero or a one onto the end of the number we have calculated so far. When we get to the end of the line, we also check to see if the line width matches the width of the first line of the character. If it doesn't, then the character raster is not rectangular, so an error message is printed, but processing continues. @= begin bit:=(j+(i-1)*8)+(num_words-1)*32; case ch of asterisk: begin shift_and_add(save_byte);@/ raster_found := true; @ end; period: begin shift(save_byte);@/ raster_found := true; end; end_of_line: begin shift(save_byte); if first_line then begin first_line:=false;@/ last_col:=false;@/ line_width:=bit-1;@/ end else if last_col then begin if bit <> line_width then error('Character has uneven width. Processing as is.');@/ last_col:=false;@/ end; end; blank: shift(save_byte); others: begin error ('Unrecognized character. Assuming period.');@/ shift(save_byte); end end;@/ get_next_ch(ch);@/ end; @ We want to keep track of the size of the character within the raster, so we can compare it to the values read in in the header. |Left|, |right|, |top|, and |bottom| will be used for comparison. @= left := min(bit,left);@/ right :=max(bit,right);@/ if not top_found then begin top_found:=true;@/ top:=row;@/ end; bottom:=row;@/ @ Skip over the space to the left of the raster image which is part of the outside box. If a colon is found, save the row number as the |y_offset|. @= begin readln(chr_file);@/ for i := 0 to first_column do begin get_next_ch(ch); if ch = bar then y_offset:=row; end; end; @ Check to see if the two fields, |num1| and |num2|, match. If not, replace the second with the first and set a flag so we'll know a replacement has been done. @p procedure check_match (var num1:integer; var num2:integer; var flag:boolean); begin@/ if num1<> num2 then flag:=false;@/ num2:=num1;@/ end; @ @= @!width:integer; {width of the raster we just read in} @!height:integer; {height of the raster we just read in} @!all_match:boolean; {used to see if everything matched the values read in} @ Check to see if the values read in really match the raster. If not, save the raster values and print an error message. @= all_match:=true;@/ width:=right-left+1;@/ height:=bottom-top+1;@/ check_match (width,font_info[chr_index].pxl_width,all_match);@/ check_match (height,font_info[chr_index].pxl_height,all_match);@/ check_match (x_offset,font_info[chr_index].x_offset,all_match);@/ check_match (y_offset,font_info[chr_index].y_offset,all_match);@/ font_info[chr_index].raster_addr:=old_raster_addr;@/ if not all_match then begin @/ error (''); {skip to next line}@/ error ('Character raster does not match saved information.');@/ error ('Correct information has been saved.');@/ end@/ @* Write PXL file information. First, write out all of the font directory information to the |pxl_file|. @= for i := 1 to tbl_size do begin@/ write_half_pxl (font_info[i].pxl_width);@/ write_half_pxl (font_info[i].pxl_height);@/ write_half_pxl (font_info[i].x_offset);@/ write_half_pxl (font_info[i].y_offset);@/ write_pxl (font_info[i].raster_addr);@/ write_pxl (font_info[i].tfm_width);@/ end; @ Finally, write out the font information to the |pxl_file|, and we are done. @= write_pxl (checksum);@/ write_pxl (magnification);@/ write_pxl (design_size);@/ all_match:=true;@/ check_match(raster_addr,directory_ptr,all_match); if not all_match then error('Saved new directory pointer.'); write_pxl (directory_ptr);@/ write_pxl (pixel_id);@/ print_ln('All done.'); @* Main program. @ First read in the \.{CHR} file and process the characters. @= repeat@/ @@/ until eof(chr_file);@/ @ Then write out the \.{PXL} directory information and font information. @= @@/ @@/ @ Main program. Here's where we make it, or don't. @p begin@/ initialize;@/ @@/ raster_addr:=1;@/ chr_count := 0; if file_ok then begin @@/ @@/ end; end. @* System-dependent changes. This module should be replaced, if necessary, by changes to the program that are necessary to make \.{CHtoPX} work at a particular installation. It is usually best to design your change file so that all changes to previous modules preserve the module numbering; then everybody's version will be consistent with the printed program. More extensive changes, which introduce new modules, can be inserted here; then only the index itself will get a new module number. @^system dependencies@> @* Index. Pointers to error messages appear here together with the section numbers where each ident\-i\-fier is used.