From 5e4835f8b6cb6c475a7be59c15b46246e0bdff84 Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 18 Feb 2001 18:29:34 +0000 Subject: [PATCH] *** empty log message *** --- pandatool/src/egg-mkfont/Sources.pp | 6 +- pandatool/src/egg-mkfont/T1-WGL4.enc | 128 +++ pandatool/src/egg-mkfont/charBitmap.I | 11 - pandatool/src/egg-mkfont/charBitmap.h | 8 - pandatool/src/egg-mkfont/eggMakeFont.cxx | 995 +++-------------------- pandatool/src/egg-mkfont/eggMakeFont.h | 32 +- pandatool/src/egg-mkfont/fontFile.I | 78 ++ pandatool/src/egg-mkfont/fontFile.cxx | 54 ++ pandatool/src/egg-mkfont/fontFile.h | 52 ++ pandatool/src/egg-mkfont/pkFontFile.cxx | 909 +++++++++++++++++++++ pandatool/src/egg-mkfont/pkFontFile.h | 51 ++ pandatool/src/egg-mkfont/ttf2egg | 94 +++ pandatool/src/progbase/programBase.cxx | 193 +++-- pandatool/src/progbase/programBase.h | 4 + 14 files changed, 1632 insertions(+), 983 deletions(-) create mode 100644 pandatool/src/egg-mkfont/T1-WGL4.enc create mode 100644 pandatool/src/egg-mkfont/fontFile.I create mode 100644 pandatool/src/egg-mkfont/fontFile.cxx create mode 100644 pandatool/src/egg-mkfont/fontFile.h create mode 100644 pandatool/src/egg-mkfont/pkFontFile.cxx create mode 100644 pandatool/src/egg-mkfont/pkFontFile.h create mode 100755 pandatool/src/egg-mkfont/ttf2egg diff --git a/pandatool/src/egg-mkfont/Sources.pp b/pandatool/src/egg-mkfont/Sources.pp index cb241676ee..7d43ec919f 100644 --- a/pandatool/src/egg-mkfont/Sources.pp +++ b/pandatool/src/egg-mkfont/Sources.pp @@ -14,7 +14,11 @@ charBitmap.I charBitmap.cxx charBitmap.h \ charLayout.cxx charLayout.h \ charPlacement.I charPlacement.cxx charPlacement.h \ - eggMakeFont.cxx eggMakeFont.h + eggMakeFont.cxx eggMakeFont.h \ + fontFile.I fontFile.cxx fontFile.h \ + pkFontFile.cxx pkFontFile.h #end bin_target +#define INSTALL_SCRIPTS ttf2egg +#define INSTALL_DATA T1-WGL4.enc diff --git a/pandatool/src/egg-mkfont/T1-WGL4.enc b/pandatool/src/egg-mkfont/T1-WGL4.enc new file mode 100644 index 0000000000..064c723a8f --- /dev/null +++ b/pandatool/src/egg-mkfont/T1-WGL4.enc @@ -0,0 +1,128 @@ +% T1-WGL4.enc +% +% +% This is LaTeX T1 encoding for WGL4 encoded TrueType fonts +% (e.g. from Windows 95) +% +% +% Note that /hyphen appears twice (for the T1 code points `hyphen' 0x2d +% and `hyphenchar' 0x7f). +% +% +% LIGKERN space l =: lslash ; +% LIGKERN space L =: Lslash ; +% LIGKERN question quoteleft =: questiondown ; +% LIGKERN exclam quoteleft =: exclamdown ; +% LIGKERN hyphen hyphen =: endash ; +% LIGKERN endash hyphen =: emdash ; +% LIGKERN quoteleft quoteleft =: quotedblleft ; +% LIGKERN quoteright quoteright =: quotedblright ; +% LIGKERN comma comma =: quotedblbase ; +% LIGKERN less less =: guillemotleft ; +% LIGKERN greater greater =: guillemotright ; +% +% LIGKERN f i =: fi ; +% LIGKERN f l =: fl ; +% LIGKERN f f =: ff ; +% LIGKERN ff i =: ffi ; +% LIGKERN ff l =: ffl ; +% +% We blow away kerns to and from spaces (TeX doesn't have a +% space) and also remove any kerns from the numbers. +% +% LIGKERN space {} * ; * {} space ; +% LIGKERN zero {} * ; * {} zero ; +% LIGKERN one {} * ; * {} one ; +% LIGKERN two {} * ; * {} two ; +% LIGKERN three {} * ; * {} three ; +% LIGKERN four {} * ; * {} four ; +% LIGKERN five {} * ; * {} five ; +% LIGKERN six {} * ; * {} six ; +% LIGKERN seven {} * ; * {} seven ; +% LIGKERN eight {} * ; * {} eight ; +% LIGKERN nine {} * ; * {} nine ; + +/T1Encoding [ % now 256 chars follow +% 0x00 + /grave /acute /circumflex /tilde + /dieresis /hungarumlaut /ring /caron + /breve /macron /dotaccent /cedilla + /ogonek /quotesinglbase /guilsinglleft /guilsinglright +% 0x10 + /quotedblleft /quotedblright /quotedblbase /guillemotleft + /guillemotright /endash /emdash /compwordmark + /perthousandzero /dotlessi /dotlessj /ff + /fi /fl /ffi /ffl +% 0x20 + /visualspace /exclam /quotedbl /numbersign + /dollar /percent /ampersand /quoteright + /parenleft /parenright /asterisk /plus + /comma /hyphen /period /slash +% 0x30 + /zero /one /two /three + /four /five /six /seven + /eight /nine /colon /semicolon + /less /equal /greater /question +% 0x40 + /at /A /B /C + /D /E /F /G + /H /I /J /K + /L /M /N /O +% 0x50 + /P /Q /R /S + /T /U /V /W + /X /Y /Z /bracketleft + /backslash /bracketright /asciicircum /underscore +% 0x60 + /quoteleft /a /b /c + /d /e /f /g + /h /i /j /k + /l /m /n /o +% 0x70 + /p /q /r /s + /t /u /v /w + /x /y /z /braceleft + /bar /braceright /asciitilde /hyphen +% 0x80 + /Abreve /Aogonek /Cacute /Ccaron + /Dcaron /Ecaron /Eogonek /Gbreve + /Lacute /Lcaron /Lslash /Nacute + /Ncaron /Eng /Odblacute /Racute +% 0x90 + /Rcaron /Sacute /Scaron /Scedilla + /Tcaron /Tcedilla /Udblacute /Uring + /Ydieresis /Zacute /Zcaron /Zdot + /IJ /Idot /dmacron /section +% 0xA0 + /abreve /aogonek /cacute /ccaron + /dcaron /ecaron /eogonek /gbreve + /lacute /lcaron /lslash /nacute + /ncaron /eng /odblacute /racute +% 0xB0 + /rcaron /sacute /scaron /scedilla + /tcaron /tcedilla /udblacute /uring + /ydieresis /zacute /zcaron /zdot + /ij /exclamdown /questiondown /sterling +% 0xC0 + /Agrave /Aacute /Acircumflex /Atilde + /Adieresis /Aring /AE /Ccedilla + /Egrave /Eacute /Ecircumflex /Edieresis + /Igrave /Iacute /Icircumflex /Idieresis +% 0xD0 + /Eth /Ntilde /Ograve /Oacute + /Ocircumflex /Otilde /Odieresis /OE + /Oslash /Ugrave /Uacute /Ucircumflex + /Udieresis /Yacute /Thorn /Germandbls +% 0xE0 + /agrave /aacute /acircumflex /atilde + /adieresis /aring /ae /ccedilla + /egrave /eacute /ecircumflex /edieresis + /igrave /iacute /icircumflex /idieresis +% 0xF0 + /eth /ntilde /ograve /oacute + /ocircumflex /otilde /odieresis /oe + /oslash /ugrave /uacute /ucircumflex + /udieresis /yacute /thorn /germandbls +] def + +% eof diff --git a/pandatool/src/egg-mkfont/charBitmap.I b/pandatool/src/egg-mkfont/charBitmap.I index 0cfa16f7bc..c50cf54d39 100644 --- a/pandatool/src/egg-mkfont/charBitmap.I +++ b/pandatool/src/egg-mkfont/charBitmap.I @@ -23,14 +23,3 @@ INLINE int CharBitmap:: get_height() const { return _block.size(); } - - -//////////////////////////////////////////////////////////////////// -// Function: SortCharBitmap::Function Operator -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE bool SortCharBitmap:: -operator() (const CharBitmap *c1, const CharBitmap *c2) const { - return (c1->get_height() > c2->get_height()); -} diff --git a/pandatool/src/egg-mkfont/charBitmap.h b/pandatool/src/egg-mkfont/charBitmap.h index de39e2bbbe..8e1426ab19 100644 --- a/pandatool/src/egg-mkfont/charBitmap.h +++ b/pandatool/src/egg-mkfont/charBitmap.h @@ -38,14 +38,6 @@ public: unsigned int _x, _y; }; -// An STL function object to sort the characters in order from tallest -// to shortest. This provides a more optimal packing into the -// resulting image. -class SortCharBitmap { -public: - INLINE bool operator() (const CharBitmap *c1, const CharBitmap *c2) const; -}; - #include "charBitmap.I" #endif diff --git a/pandatool/src/egg-mkfont/eggMakeFont.cxx b/pandatool/src/egg-mkfont/eggMakeFont.cxx index 8be176e742..4213e01150 100644 --- a/pandatool/src/egg-mkfont/eggMakeFont.cxx +++ b/pandatool/src/egg-mkfont/eggMakeFont.cxx @@ -6,6 +6,7 @@ #include "eggMakeFont.h" #include "charBitmap.h" #include "charPlacement.h" +#include "pkFontFile.h" #include #include @@ -17,480 +18,6 @@ #include -/******************************************************************** - -I found the following in a source file for pftype called pftype.web. -It's some nutty TeX-based Pascal program, and the documentation is a -little hard to read because of the embedded TeX formatting controls. -But it describes quite thoroughly the format of the pk file. - -********************************************************************* - -@* Packed file format. -The packed file format is a compact representation of the data contained in a -\.{GF} file. The information content is the same, but packed (\.{PK}) files -are almost always less than half the size of their \.{GF} counterparts. They -are also easier to convert into a raster representation because they do not -have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be -separately interpreted. In addition, the \.{PK} format expressedly forbids -\&{special} commands within a character. The minimum bounding box for each -character is explicit in the format, and does not need to be scanned for as in -the \.{GF} format. Finally, the width and escapement values are combined with -the raster information into character ``packets'', making it simpler in many -cases to process a character. - -A \.{PK} file is organized as a stream of 8-bit bytes. At times, these bytes -might be split into 4-bit nybbles or single bits, or combined into multiple -byte parameters. When bytes are split into smaller pieces, the `first' piece -is always the most significant of the byte. For instance, the first bit of -a byte is the bit with value 128; the first nybble can be found by dividing -a byte by 16. Similarly, when bytes are combined into multiple byte -parameters, the first byte is the most significant of the parameter. If the -parameter is signed, it is represented by two's-complement notation. - -The set of possible eight-bit values is separated into two sets, those that -introduce a character definition, and those that do not. The values that -introduce a character definition range from 0 to 239; byte values -above 239 are interpreted as commands. Bytes that introduce character -definitions are called flag bytes, and various fields within the byte indicate -various things about how the character definition is encoded. Command bytes -have zero or more parameters, and can never appear within a character -definition or between parameters of another command, where they would be -interpeted as data. - -A \.{PK} file consists of a preamble, followed by a sequence of one or more -character definitions, followed by a postamble. The preamble command must -be the first byte in the file, followed immediately by its parameters. -Any number of character definitions may follow, and any command but the -preamble command and the postamble command may occur between character -definitions. The very last command in the file must be the postamble. - -@ The packed file format is intended to be easy to read and interpret by -device drivers. The small size of the file reduces the input/output overhead -each time a font is loaded. For those drivers that load and save each font -file into memory, the small size also helps reduce the memory requirements. -The length of each character packet is specified, allowing the character raster -data to be loaded into memory by simply counting bytes, rather than -interpreting each command; then, each character can be interpreted on a demand -basis. This also makes it possible for a driver to skip a particular -character quickly if it knows that the character is unused. - -@ First, the command bytes will be presented; then the format of the -character definitions will be defined. Eight of the possible sixteen -commands (values 240 through 255) are currently defined; the others are -reserved for future extensions. The commands are listed below. Each command -is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte, -and any parameters. The parameters are followed by a bracketed number -telling how many bytes they occupy, with the number preceded by a plus sign if -it is a signed quantity. (Four byte quantities are always signed, however.) - -\yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|. This command is undefined in general; -it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading -programs are being used. \MF\ generates \\{xxx} commands when encountering -a \&{special} string. It is recommended that |x| be a string having the form -of a keyword followed by possible parameters relevant to that keyword. - -\yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|. Like |pk_xxx1|, but |0<=k<65536|. - -\yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|. Like |pk_xxx1|, but -|0<=k<@t$2^{24}$@>|. \MF\ uses this when sending a \&{special} string whose -length exceeds~255. - -\yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|. Like |pk_xxx1|, but |k| can be -ridiculously large; |k| musn't be negative. - -\yskip\hang|pk_yyy| 244 |y[4]|. This command is undefined in general; it -functions as a five-byte \\{no\_op} unless special \.{PK} reading programs -are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a result of -\&{numspecial} commands; the intent is to provide numeric parameters to -\\{xxx} commands that immediately precede. - -\yskip\hang|pk_post| 245. Beginning of the postamble. This command is -followed by enough |pk_no_op| commands to make the file a multiple -of four bytes long. Zero through three bytes are usual, but any number -is allowed. -This should make the file easy to read on machines that pack four bytes to -a word. - -\yskip\hang|pk_no_op| 246. No operation, do nothing. Any number of -|pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be -inserted between a command and its parameters, between two parameters, or -inside a character definition. - -\yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]| -|vppp[4]|. Preamble command. Here, |i| is the identification byte of the -file, currently equal to 89. The string |x| is merely a comment, usually -indicating the source of the \.{PK} file. The parameters |ds| and |cs| are -the design size of the file in $1/2^{20}$ points, and the checksum of the -file, respectively. The checksum should match the \.{TFM} file and the -\.{GF} files for this font. Parameters |hppp| and |vppp| are the ratios -of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they -can be used to correlate the font with specific device resolutions, -magnifications, and ``at sizes''. Usually, the name of the \.{PK} file is -formed by concatenating the font name (e.g., cmr10) with the resolution at -which the font is prepared in pixels per inch multiplied by the magnification -factor, and the letters \.{pk}. For instance, cmr10 at 300 dots per inch -should be named \.{cmr10.300pk}; at one thousand dots per inch and magstephalf, -it should be named \.{cmr10.1095pk}. - -@ We put a few of the above opcodes into definitions for symbolic use by -this program. - -@d pk_id = 89 {the version of \.{PK} file described} -@d pk_xxx1 = 240 {\&{special} commands} -@d pk_yyy = 244 {\&{numspecial} commands} -@d pk_post = 245 {postamble} -@d pk_no_op = 246 {no operation} -@d pk_pre = 247 {preamble} -@d pk_undefined == 248, 249, 250, 251, 252, 253, 254, 255 - -@ The \.{PK} format has two conflicting goals: to pack character raster and -size information as compactly as possible, while retaining ease of translation -into raster and other forms. A suitable compromise was found in the use of -run-encoding of the raster information. Instead of packing the individual -bits of the character, we instead count the number of consecutive `black' or -`white' pixels in a horizontal raster row, and then encode this number. Run -counts are found for each row from left to right, traversing rows from the -top to bottom. This is essentially the way the \.{GF} format works. -Instead of presenting each row individually, however, we concatenate all -of the horizontal raster rows into one long string of pixels, and encode this -row. With knowledge of the width of the bit-map, the original character glyph -can easily be reconstructed. In addition, we do not need special commands to -mark the end of one row and the beginning of the next. - -Next, we place the burden of finding the minimum bounding box on the part -of the font generator, since the characters will usually be used much more -often than they are generated. The minimum bounding box is the smallest -rectangle that encloses all `black' pixels of a character. We also -eliminate the need for a special end of character marker, by supplying -exactly as many bits as are required to fill the minimum bounding box, from -which the end of the character is implicit. - -Let us next consider the distribution of the run counts. Analysis of several -dozen pixel files at 300 dots per inch yields a distribution peaking at four, -falling off slowly until ten, then a bit more steeply until twenty, and then -asymptotically approaching the horizontal. Thus, the great majority of our -run counts will fit in a four-bit nybble. The eight-bit byte is attractive for -our run-counts, as it is the standard on many systems; however, the wasted four -bits in the majority of cases seem a high price to pay. Another possibility -is to use a Huffman-type encoding scheme with a variable number of bits for -each run-count; this was rejected because of the overhead in fetching and -examining individual bits in the file. Thus, the character raster definitions -in the \.{PK} file format are based on the four-bit nybble. - -@ An analysis of typical pixel files yielded another interesting statistic: -Fully 37\char`\%\ -of the raster rows were duplicates of the previous row. Thus, the \.{PK} -format allows the specification of repeat counts, which indicate how many times -a horizontal raster row is to be repeated. These repeated rows are taken out -of the character glyph before individual rows are concatenated into the long -string of pixels. - -For elegance, we disallow a run count of zero. The case of a null raster -description should be gleaned from the character width and height being equal -to zero, and no raster data should be read. No other zero counts are ever -necessary. Also, in the absence of repeat counts, the repeat value is set to -be zero (only the original row is sent.) If a repeat count is seen, it takes -effect on the current row. The current row is defined as the row on which the -first pixel of the next run count will lie. The repeat count is set back to -zero when the last pixel in the current row is seen, and the row is sent out. - -This poses a problem for entirely black and entirely white rows, however. Let -us say that the current row ends with four white pixels, and then we have five -entirely empty rows, followed by a black pixel at the beginning of the next -row, and the character width is ten pixels. We would like to use a repeat -count, but there is no legal place to put it. If we put it before the white -run count, it will apply to the current row. If we put it after, it applies -to the row with the black pixel at the beginning. Thus, entirely white or -entirely black repeated rows are always packed as large run counts (in this -case, a white run count of 54) rather than repeat counts. - -@ Now we turn our attention to the actual packing of the run counts and -repeat counts into nybbles. There are only sixteen possible nybble values. -We need to indicate run counts and repeat counts. Since the run counts are -much more common, we will devote the majority of the nybble values to them. -We therefore indicate a repeat count by a nybble of 14 followed by a packed -number, where a packed number will be explained later. Since the repeat -count value of one is so common, we indicate a repeat one command by a single -nybble of 15. A 14 followed by the packed number 1 is still legal for a -repeat one count. The run counts are coded directly as packed -numbers. - -For packed numbers, therefore, we have the nybble values 0 through 13. We -need to represent the positive integers up to, say, $2^{31}-1$. We would -like the more common smaller numbers to take only one or two nybbles, and -the infrequent large numbers to take three or more. We could therefore -allocate one nybble value to indicate a large run count taking three or more -nybbles. We do this with the value 0. - -@ We are left with the values 1 through 13. We can allocate some of these, say -|dyn_f|, to be one-nybble run counts. -These will work for the run counts |1..dyn_f|. For subsequent run -counts, we will use a nybble greater than |dyn_f|, followed by a second nybble, -whose value can run from 0 through 15. Thus, the two-nybble values will -run from |dyn_f+1..(13-dyn_f)*16+dyn_f|. We have our definition of large run -count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|. - -We can analyze our several dozen pixel files and determine an optimal value of -|dyn_f|, and use this value for all of the characters. Unfortunately, values -of |dyn_f| that pack small characters well tend to pack the large characters -poorly, and values that pack large characters well are not efficient for the -smaller characters. Thus, we choose the optimal |dyn_f| on a character basis, -picking the value that will pack each individual character in the smallest -number of nybbles. Legal values of |dyn_f| run from 0 (with no one-nybble run -counts) to 13 (with no two-nybble run counts). - -@ Our only remaining task in the coding of packed numbers is the large run -counts. We use a scheme suggested by D.~E.~Knuth -@^Knuth, Donald Ervin@> -that simply and elegantly represents arbitrarily large values. The -general scheme to represent an integer |i| is to write its hexadecimal -representation, with leading zeros removed. Then we count the number of -digits, and prepend one less than that many zeros before the hexadecimal -representation. Thus, the values from one to fifteen occupy one nybble; -the values sixteen through 255 occupy three, the values 256 through 4095 -require five, etc. - -For our purposes, however, we have already represented the numbers one -through |(13-dyn_f)*16+dyn_f|. In addition, the one-nybble values have -already been taken by our other commands, which means that only the values -from sixteen up are available to us for long run counts. Thus, we simply -normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and -adding 16, and then we represent the result according to the scheme above. - -@ The final algorithm for decoding the run counts based on the above scheme -looks like this, assuming that a procedure called \\{pk\_nyb} is available -to get the next nybble from the file, and assuming that the global -|repeat_count| indicates whether a row needs to be repeated. Note that this -routine is recursive, but since a repeat count can never directly follow -another repeat count, it can only be recursive to one level. - -@= -function pk_packed_num : integer ; -var i, @!j : integer ; -begin - i := get_nyb ; - if i = 0 then begin - repeat j := get_nyb ; incr(i) ; until j <> 0 ; - while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ; - pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ; - end else if i <= dyn_f then - pk_packed_num := i - else if i < 14 then - pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1 - else begin - if repeat_count <> 0 then abort('Second repeat count for this row!') ; -@.Second repeat count...@> - repeat_count := 1; {prevent recursion more than one level} - if i = 14 then repeat_count := pk_packed_num; - send_out(true, repeat_count) ; - pk_packed_num := pk_packed_num ; - end ; -end ; - -@ For low resolution fonts, or characters with `gray' areas, run encoding can -often make the character many times larger. Therefore, for those characters -that cannot be encoded efficiently with run counts, the \.{PK} format allows -bit-mapping of the characters. This is indicated by a |dyn_f| value of -14. The bits are packed tightly, by concatenating all of the horizontal raster -rows into one long string, and then packing this string eight bits to a byte. -The number of bytes required can be calculated by |(width*height+7) div 8|. -This format should only be used when packing the character by run counts takes -more bytes than this, although, of course, it is legal for any character. -Any extra bits in the last byte should be set to zero. - -@ At this point, we are ready to introduce the format for a character -descriptor. It consists of three parts: a flag byte, a character preamble, -and the raster data. The most significant four bits of the flag byte -yield the |dyn_f| value for that character. (Notice that only values of -0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character; -thus, the flag bytes do not conflict with the command bytes, whose upper nybble -is always 15.) The next bit (with weight 8) indicates whether the first run -count is a black count or a white count, with a one indicating a black count. -For bit-mapped characters, this bit should be set to a zero. The next bit -(with weight 4) indicates whether certain later parameters (referred to as size -parameters) are given in one-byte or two-byte quantities, with a one indicating -that they are in two-byte quantities. The last two bits are concatenated on to -the beginning of the packet-length parameter in the character preamble, -which will be explained below. - -However, if the last three bits of the flag byte are all set (normally -indicating that the size parameters are two-byte values and that a 3 should be -prepended to the length parameter), then a long format of the character -preamble should be used instead of one of the short forms. - -Therefore, there are three formats for the character preamble; the one that -is used depends on the least significant three bits of the flag byte. If the -least significant three bits are in the range zero through three, the short -format is used. If they are in the range four through six, the extended short -format is used. Otherwise, if the least significant bits are all set, then -the long form of the character preamble is used. The preamble formats are -explained below. - -\yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]| -|h[1]| |hoff[+1]| |voff[+1]|. -If this format of the character preamble is used, the above -parameters must all fit in the indicated number of bytes, signed or unsigned -as indicated. Almost all of the standard \TeX\ font characters fit; the few -exceptions are fonts such as \.{cminch}. - -\yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]| -|w[2]| |h[2]| |hoff[+2]| |voff[+2]|. Larger characters use this extended -format. - -\yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]| -|w[4]| |h[4]| |hoff[4]| |voff[4]|. This is the general format that -allows all of the -parameters of the \.{GF} file format, including vertical escapement. -\vskip\baselineskip -The |flag| parameter is the flag byte. The parameter |pl| (packet length) -contains the offset -of the byte following this character descriptor, with respect to the beginning -of the |tfm| width parameter. This is given so a \.{PK} reading program can, -once it has read the flag byte, packet length, and character code (|cc|), skip -over the character by simply reading this many more bytes. For the two short -forms of the character preamble, the last two bits of the flag byte should be -considered the two most-significant bits of the packet length. For the short -format, the true packet length might be calculated as |(flag mod 4)*256+pl|; -for the short extended format, it might be calculated as -|(flag mod 4)*65536+pl|. - -The |w| parameter is the width and the |h| parameter is the height in pixels -of the minimum bounding box. The |dx| and |dy| parameters are the horizontal -and vertical escapements, respectively. In the short formats, |dy| is assumed -to be zero and |dm| is |dx| but in pixels; -in the long format, |dx| and |dy| are both -in pixels multiplied by $2^{16}$. The |hoff| is the horizontal offset from the -upper left pixel to the reference pixel; the |voff| is the vertical offset. -They are both given in pixels, with right and down being positive. The -reference pixel is the pixel that occupies the unit square in \MF; the -\MF\ reference point is the lower left hand corner of this pixel. (See the -example below.) - -@ \TeX\ requires all characters that have the same character codes -modulo 256 to have also the same |tfm| widths and escapement values. The \.{PK} -format does not itself make this a requirement, but in order for the font to -work correctly with the \TeX\ software, this constraint should be observed. -(The standard version of \TeX\ cannot output character codes greater -than 255, but extended versions do exist.) - -Following the character preamble is the raster information for the -character, packed by run counts or by bits, as indicated by the flag byte. -If the character is packed by run counts and the required number of nybbles -is odd, then the last byte of the raster description should have a zero -for its least significant nybble. - -@ As an illustration of the \.{PK} format, the character \char4\ from the font -amr10 at 300 dots per inch will be encoded. This character was chosen -because it illustrates some -of the borderline cases. The raster for the character looks like this (the -row numbers are chosen for convenience, and are not \MF's row numbers.) - -\vskip\baselineskip -{\def\smbox{\vrule height 7pt width 7pt depth 0pt \hskip 3pt}% -\catcode`\*=\active \let*=\smbox -\centerline{\vbox{\baselineskip=10pt -\halign{\hfil#\quad&&\hfil#\hfil\cr -0& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -1& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -2& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -3& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -4& & &*&*& & & & & & & & & & & & & & & & &*&*\cr -5& & &*&*& & & & & & & & & & & & & & & & &*&*\cr -6& & &*&*& & & & & & & & & & & & & & & & &*&*\cr -7\cr -8\cr -9& & & & &*&*& & & & & & & & & & & & &*&*& & \cr -10& & & & &*&*& & & & & & & & & & & & &*&*& & \cr -11& & & & &*&*& & & & & & & & & & & & &*&*& & \cr -12& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr -13& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr -14& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr -15& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr -16& & & & &*&*& & & & & & & & & & & & &*&*& & \cr -17& & & & &*&*& & & & & & & & & & & & &*&*& & \cr -18& & & & &*&*& & & & & & & & & & & & &*&*& & \cr -19\cr -20\cr -21\cr -22& & &*&*& & & & & & & & & & & & & & & & &*&*\cr -23& & &*&*& & & & & & & & & & & & & & & & &*&*\cr -24& & &*&*& & & & & & & & & & & & & & & & &*&*\cr -25& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -26& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -27& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -28&+& &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr -&\hphantom{*}&\hphantom{*}\cr -}}}} -The width of the minimum bounding box for this character is 20; its height -is 29. The `+' represents the reference pixel; notice how it lies outside the -minimum bounding box. The |hoff| value is $-2$, and the |voff| is~28. - -The first task is to calculate the run counts and repeat counts. The repeat -counts are placed at the first transition (black to white or white to black) -in a row, and are enclosed in brackets. White counts are enclosed in -parentheses. It is relatively easy to generate the counts list: -\vskip\baselineskip -\centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]} -\centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82} -\vskip\baselineskip -Note that any duplicated rows that are not all white or all black are removed -before the run counts are calculated. The rows thus removed are rows 5, 6, -10, 11, 13, 14, 15, 17, 18, 23, and 24. - -@ The next step in the encoding of this character is to calculate the optimal -value of |dyn_f|. The details of how this calculation is done are not -important here; suffice it to say that there is a simple algorithm that can -determine the best value of |dyn_f| in one pass over the count list. For this -character, the optimal value turns out to be 8 (atypically low). Thus, all -count values less than or equal to 8 are packed in one nybble; those from -nine to $(13-8)*16+8$ or 88 are packed in two nybbles. The run encoded values -now become (in hex, separated according to the above list): -\vskip\baselineskip -\centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3} -\centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9} -\vskip\baselineskip\noindent -which comes to 36 nybbles, or 18 bytes. This is shorter than the 73 bytes -required for the bit map, so we use the run count packing. - -@ The short form of the character preamble is used because all of the -parameters fit in their respective lengths. The packet length is therefore -18 bytes for the raster, plus -eight bytes for the character preamble parameters following the character -code, or 26. The |tfm| width for this character is 640796, or {\tt 9C71C} in -hexadecimal. The horizontal escapement is 25 pixels. The flag byte is -88 hex, indicating the short preamble, the black first count, and the -|dyn_f| value of 8. The final total character packet, in hexadecimal, is: -\vskip\baselineskip -$$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr -Flag byte&88\cr -Packet length&1A\cr -Character code&04\cr -|tfm| width&09&C7&1C\cr -Horizontal escapement (pixels)&19\cr -Width of bit map&14\cr -Height of bit map&1D\cr -Horizontal offset (signed)&FE\cr -Vertical offset&1C\cr -Raster data&D9&E2&97\cr -&2B&1E&22\cr -&93&24&E3\cr -&97&4E&22\cr -&93&2C&5E\cr -&22&97&D9\cr}}$$ -********************************************************************/ - - -#define PK_XXX1 240 -#define PK_XXX2 241 -#define PK_XXX3 242 -#define PK_XXX4 243 -#define PK_YYY 244 -#define PK_POST 245 -#define PK_NO_OP 246 -#define PK_PRE 247 - - //////////////////////////////////////////////////////////////////// // Function: EggMakeFont::Constructor // Access: Public @@ -530,6 +57,21 @@ EggMakeFont() : EggWriter(true, false) { "on -d, below. The default is 1.", &EggMakeFont::dispatch_int, NULL, &_output_zsize); + add_option + ("fg", "r,g,b[,a]", 0, + "Specifies the foreground color of the generated texture map. The " + "default is white: 1,1,1,1, which leads to the most flexibility " + "as the color can be modulated at runtime to any suitable color.", + &EggMakeFont::dispatch_color, NULL, &_fg[0]); + + add_option + ("bg", "r,g,b[,a]", 0, + "Specifies the background color of the generated texture map. The " + "default is transparent black: 0,0,0,0, which allows the text to be " + "visible against any color background by placing a polygon of a " + "suitable color behind it.", + &EggMakeFont::dispatch_color, NULL, &_bg[0]); + add_option ("d", "x,y[,c]", 0, "Dimensions in pixels of the texture image, with an optional number of " @@ -581,6 +123,8 @@ EggMakeFont() : EggWriter(true, false) { "as the first or last character it loses its special meaning.", &EggMakeFont::dispatch_string, NULL, &_only_chars); + _fg.set(1.0, 1.0, 1.0, 1.0); + _bg.set(0.0, 0.0, 0.0, 0.0); _output_xsize = 256; _output_ysize = 256; _output_zsize = 1; @@ -607,7 +151,7 @@ handle_args(ProgramBase::Args &args) { return false; } - _input_pk_filename = args[0]; + _input_font_filename = args[0]; args.pop_front(); return EggWriter::handle_args(args); } @@ -678,7 +222,7 @@ get_uv(double x, double y) { //////////////////////////////////////////////////////////////////// LPoint2d EggMakeFont:: get_xy(double x, double y) { - return LPoint2d(x / (_hppp * _ppu), -y / (_vppp * _ppu)); + return LPoint2d(x / (_font->get_hppp() * _ppu), -y / (_font->get_vppp() * _ppu)); } //////////////////////////////////////////////////////////////////// @@ -712,7 +256,8 @@ copy_character(const CharPlacement &pl) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (bm->_block[y][x]) { - _output_image.set_alpha(xp + x, yp + y, 1.0); + _output_image.set_xel(xp + x, yp + y, _fg[0], _fg[1], _fg[2]); + _output_image.set_alpha(xp + x, yp + y, _fg[3]); } } } @@ -720,7 +265,7 @@ copy_character(const CharPlacement &pl) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (bm->_block[y][x]) { - _output_image.set_xel(xp + x, yp + y, 1.0, 1.0, 1.0); + _output_image.set_xel(xp + x, yp + y, _fg[0], _fg[1], _fg[2]); } } } @@ -793,10 +338,10 @@ consider_scale_factor(double scale_factor) { _layout.reset(_working_xsize, _working_ysize, _working_buffer_pixels); - Chars::iterator ci; bool ok = true; - for (ci = _chars.begin(); ci != _chars.end() && ok; ++ci) { - ok = _layout.place_character(*ci); + int num_chars = _font->get_num_chars(); + for (int i = 0; i < num_chars; i++) { + ok = _layout.place_character(_font->get_char(i)); if (!ok) { // Out of room. return false; @@ -926,389 +471,26 @@ choose_image_size() { } -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::fetch_nibble -// Access: Private -// Description: Returns the next 4-bit nibble from the pk stream. -//////////////////////////////////////////////////////////////////// -unsigned int EggMakeFont:: -fetch_nibble() { - assert(_p < (int)_pk.size()); - if (_high) { - _high = false; - return _pk[_p] >> 4; - } else { - _high = true; - return _pk[_p++] & 0xf; - } -} - //////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::fetch_packed_int -// Access: Private -// Description: Returns the next packed integer from the pk stream. -//////////////////////////////////////////////////////////////////// -unsigned int EggMakeFont:: -fetch_packed_int() { - int i = fetch_nibble(); - if (i == 0) { - int j; - do { - j = fetch_nibble(); - i++; - } while (j == 0); - while (i > 0) { - j = (j << 4) | fetch_nibble(); - i--; - } - return j - 15 + (13 - _dyn_f)*16 + _dyn_f; - - } else if (i <= _dyn_f) { - return i; - - } else if (i < 14) { - return (i - _dyn_f - 1)*16 + fetch_nibble() + _dyn_f + 1; - - } else { - _repeat_count = 1; - if (i == 14) { - _repeat_count = fetch_packed_int(); - } - // nout << "[" << _repeat_count << "]"; - return fetch_packed_int(); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::fetch_byte -// Access: Private -// Description: Returns the next 8-bit unsigned byte from the pk -// stream. -//////////////////////////////////////////////////////////////////// -unsigned int EggMakeFont:: -fetch_byte() { - assert(_high); - assert(_p < (int)_pk.size()); - return _pk[_p++]; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::fetch_int -// Access: Private -// Description: Returns the next n-byte unsigned int from -// the pk stream. -//////////////////////////////////////////////////////////////////// -unsigned int EggMakeFont:: -fetch_int(int n) { - assert(_high); - - unsigned int result = 0; - for (int i = 0; i < n; i++) { - assert(_p < (int)_pk.size()); - result = (result << 8) | _pk[_p]; - _p++; - } - - return result; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::fetch_signed_int -// Access: Private -// Description: Returns the next n-byte signed int from -// the pk stream. -//////////////////////////////////////////////////////////////////// -int EggMakeFont:: -fetch_signed_int(int n) { - assert(_high); - - assert(_p < (int)_pk.size()); - int result = (signed char)_pk[_p]; - _p++; - for (int i = 1; i < n; i++) { - assert(_p < (int)_pk.size()); - result = (result << 8) | _pk[_p]; - _p++; - } - - return result; -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::do_character -// Access: Private -// Description: Reads a single character from the pk file and -// processes it. Returns true if successful, false if -// something bad happened. -//////////////////////////////////////////////////////////////////// -bool EggMakeFont:: -do_character(int flag_byte) { - // int start_p = _p - 1; - _dyn_f = (flag_byte >> 4); - bool first_black = ((flag_byte & 0x8) != 0); - int bsize = (flag_byte & 0x4) ? 2 : 1; - int prepend_length = (flag_byte & 0x3); - - bool use_long_form = ((flag_byte & 0x7) == 0x7); - - unsigned int pl, cc, itfm, w, h; - int hoff, voff; - unsigned int idx = 0; - unsigned int idy = 0; - int next_p; - - if (use_long_form) { - pl = fetch_int(); - cc = fetch_int(); - next_p = _p + pl; - itfm = fetch_int(); - idx = fetch_int(); - idy = fetch_int(); - w = fetch_int(); - h = fetch_int(); - hoff = fetch_signed_int(); - voff = fetch_signed_int(); - } else { - pl = fetch_int(bsize) | (prepend_length << bsize*8); - cc = fetch_byte(); - next_p = _p + pl; - itfm = fetch_int(3); - idx = fetch_int(bsize) << 16; - w = fetch_int(bsize); - h = fetch_int(bsize); - hoff = fetch_signed_int(bsize); - voff = fetch_signed_int(bsize); - } - - // double tfm = (double)itfm / (double)(1 << 24); - double dx = (double)idx / (double)(1 << 16); - double dy = (double)idy / (double)(1 << 16); - // double di_width = tfm * _ppu * _hppp / _vppp; - - if (_get_all || - ((cc >= 33 && cc <= 127) && - (_only_chars.empty() || _only_chars.find((char)cc) != string::npos))) { - nout << " " << cc; - - CharBitmap *bm = new CharBitmap(cc, w, h, hoff, voff, dx, dy); - - if (_dyn_f == 14) { - // A bitmapped character: this character has the actual w x h - // bits stored directly in the pk file. This kind of character - // is quite rare, and the code is therefore untested. - if (h > 0 && w > 0) { - nout - << "\nA rare bitmapped character encountered! You are now running\n" - << "untested code. If this works, change this line in the program\n" - << "to indicate that the code is actually tested!\n\n"; - } - - unsigned int bit = 0; - unsigned int byte = 0; - for (unsigned int y = 0; y < h; y++) { - for (unsigned int x = 0; x < w; x++) { - if (bit == 0) { - bit = 0x80; - byte = fetch_byte(); - } - bm->_block[y][x] = ((byte & bit)!=0); - bit >>= 1; - } - } - - } else { - // A normal, rle character. This character has sequences of - // black and white runs stored in the pk file. Most characters - // will be stored this way. - bool black = first_black; - _repeat_count = 0; - - int count = fetch_packed_int(); - while (bm->paint(black, count, _repeat_count)) { - /* - if (black) { - nout << count; - } else { - nout << "(" << count << ")"; - } - */ - black = !black; - count = fetch_packed_int(); - } - // nout << "\n"; - } - - _chars.push_back(bm); - - /* - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - nout << (bm->_block[y][x] ? ' ' : '*'); - } - nout << "\n"; - } - */ - - if (!_high) { - _p++; - _high = true; - } - - if (_p != next_p) { - nout << "Expected p == " << next_p << " got " << _p << "\n"; - } - - } else { - nout << " (" << cc << ")"; - } - - _p = next_p; - return true; -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::do_xxx -// Access: Private -// Description: The xxx1 .. xxx4 series of commands specify an -// embedded comment or some such silliness in the pk -// file that must be skipped. +// Function: EggMakeFont::unsmooth_rgb +// Access: Public +// Description: Make the image jaggy in RGB (but leave it antialiased +// in alpha). This will result in correct antialiasing +// when the text is loaded in the player. //////////////////////////////////////////////////////////////////// void EggMakeFont:: -do_xxx(int num_bytes) { - _p += fetch_int(num_bytes); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::do_yyy -// Access: Private -// Description: The yyy command is an encoded number which might have -// meaning to a preceding xxx block, but means nothing -// to us. -//////////////////////////////////////////////////////////////////// -void EggMakeFont:: -do_yyy() { - _p += 4; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::do_post -// Access: Private -// Description: The beginning of the postamble. -//////////////////////////////////////////////////////////////////// -void EggMakeFont:: -do_post() { - _post = true; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::do_pre -// Access: Private -// Description: The preamble. -//////////////////////////////////////////////////////////////////// -void EggMakeFont:: -do_pre() { - int id = fetch_byte(); - if (id != 89) { - nout << "Warning: PK file had an unexpected ID, " << id << "\n"; - } - - int comment_len = fetch_byte(); - - assert(_p + comment_len <= (int)_pk.size()); - nout.write(&_pk[_p], comment_len); - nout << "\n"; - _p += comment_len; - - int ds = fetch_int(); - fetch_int(); // cs - int hppp = fetch_int(); // hppp - int vppp = fetch_int(); // vppp - - _ds = (double)ds / (double)(1 << 20); - _hppp = (double)hppp / (double)(1 << 16); - _vppp = (double)vppp / (double)(1 << 16); - - nout << "Font size is " << _ds << " points, rasterized at " - << floor(_vppp * 72.27 + 0.5) << " DPI.\n"; -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggMakeFont::read_pk -// Access: Private -// Description: -//////////////////////////////////////////////////////////////////// -void EggMakeFont:: -read_pk() { - if (_p >= (int)_pk.size()) { - nout << "PK file is empty.\n"; - exit(1); - } - unsigned int cmd = fetch_byte(); - if (cmd != PK_PRE) { - nout << "Not a PK file.\n"; - exit(1); - } - do_pre(); - - nout << "Characters:"; - - while (_p < (int)_pk.size()) { - unsigned int cmd = fetch_byte(); - if (_post && !_post_warning && cmd != PK_NO_OP) { - _post_warning = true; - nout << "\nWarning: postamble was not the last command.\n"; - } - if (cmd < 240) { - if (!do_character(cmd)) { - return; - } - } else { - switch (cmd) { - case PK_XXX1: - do_xxx(1); - break; - - case PK_XXX2: - do_xxx(2); - break; - - case PK_XXX3: - do_xxx(3); - break; - - case PK_XXX4: - do_xxx(4); - break; - - case PK_YYY: - do_yyy(); - break; - - case PK_POST: - do_post(); - break; - - case PK_NO_OP: - break; - - default: - nout << "\nUnexpected command " << cmd << " encountered in PK file\n"; - exit(1); +unsmooth_rgb(PNMImage &image) { + for (int y = 0; y < image.get_y_size(); y++) { + for (int x = 0; x < image.get_x_size(); x++) { + double alpha = image.get_alpha(x, y); + if (alpha != 0.0) { + image.set_xel(x, y, image.get_xel(x, y) / alpha); } } } - nout << "\n"; - - if (!_post) { - nout << "Warning: did not encounter postamble.\n"; - } } - //////////////////////////////////////////////////////////////////// // Function: EggMakeFont::expand_hyphen // Access: Public @@ -1322,7 +504,7 @@ expand_hyphen(const string &str) { string result; size_t last = 0; - size_t hyphen = str.find('-', 1); + size_t hyphen = str.find('-', last + 1); while (hyphen < str.length() - 1) { size_t ap = hyphen - 1; size_t zp = hyphen + 1; @@ -1334,8 +516,8 @@ expand_hyphen(const string &str) { result += i; } - last = zp; - hyphen = str.find('-', last); + last = zp + 1; + hyphen = str.find('-', last + 1); } result += str.substr(last); @@ -1364,31 +546,19 @@ run() { nout << "Extracting only characters: " << _only_chars << "\n"; } - _input_pk_filename.set_binary(); - ifstream pk_file; - if (!_input_pk_filename.open_read(pk_file)) { - nout << "Unable to read " << _input_pk_filename << "\n"; + _font = new PkFontFile(); + if (!_font->read(_input_font_filename, _get_all, _only_chars)) { + nout << "Unable to read " << _input_font_filename << ".\n"; exit(1); } - - unsigned char c = pk_file.get(); - while (pk_file && !pk_file.eof()) { - _pk.push_back(c); - c = pk_file.get(); - } - _p = 0; - _high = true; - _post = false; - _post_warning = false; - read_pk(); - - nout << "Placing " << _chars.size() << " letters.\n"; + nout << "Placing " << _font->get_num_chars() << " letters.\n"; // Now that we've collected all the characters, sort them in order // from tallest to shortest so we will hopefully get a more optimal // packing. - sort(_chars.begin(), _chars.end(), SortCharBitmap()); + _font->sort_chars_by_height(); + // Choose a suitable image size if the user didn't specify one. if (!_got_output_size) { @@ -1403,17 +573,14 @@ run() { _working_poly_pixels = _poly_pixels * _scale_factor; _output_image.clear(_working_xsize, _working_ysize, _output_zsize); - if (_output_image.has_alpha()) { - // If we're generating an image with an alpha channel, the font - // detail goes entirely into the alpha channel, and the color - // channel(s) are pure white. - _output_image.alpha_fill(0.0); - _output_image.fill(1.0, 1.0, 1.0); + // If we specified 1.0 for both foreground and background alpha, we + // don't really want to use alpha. + _use_alpha = _output_image.has_alpha() && + (_fg[3] != 1.0 || _bg[3] != 1.0); - } else { - // If we're generating an image with no alpha channel, the font - // detail goes into the color channel(s). - _output_image.fill(0.0, 0.0, 0.0); + _output_image.fill(_bg[0], _bg[1], _bg[2]); + if (_output_image.has_alpha()) { + _output_image.alpha_fill(_bg[3]); } _group = new EggGroup(); @@ -1423,6 +590,34 @@ run() { _vpool = new EggVertexPool("vpool"); _group->add_child(_vpool); + // Set the appropriate flags on the texture. + EggTexture::Format format = EggTexture::F_unspecified; + if (_use_alpha) { + switch (_output_zsize) { + case 1: + format = EggTexture::F_alpha; + break; + case 2: + format = EggTexture::F_luminance_alpha; + break; + case 4: + format = EggTexture::F_rgba; + break; + } + } else { + switch (_output_zsize) { + case 1: + case 2: + format = EggTexture::F_luminance; + break; + case 3: + case 4: + format = EggTexture::F_rgb; + break; + } + } + _tref->set_format(format); + // Make the group a sequence, as a convenience. If we view the // egg file directly we can see all the characters one at a time. _group->set_switch_flag(true); @@ -1431,7 +626,7 @@ run() { if (_ppu <= 0.0) { // If the user set ppu to 0, it means to normalize the character // height. - _ppu = _ds; + _ppu = _font->get_ds(); } // Now we can copy all the characters onto the actual image. @@ -1450,20 +645,40 @@ run() { } // Also create an egg group indicating the font's design size. - PT(EggGroup) ds_group = new EggGroup("ds"); - EggVertex *vtx = _vpool->make_new_vertex(LPoint3d(0.0, _ds / _ppu, 0.0)); + EggGroup *ds_group = new EggGroup("ds"); + _group->add_child(ds_group); + EggVertex *vtx = _vpool->make_new_vertex(LPoint3d(0.0, _font->get_ds() / _ppu, 0.0)); EggPoint *point = new EggPoint; ds_group->add_child(point); point->add_vertex(vtx); + if (_use_alpha && _bg[3] == 0.0) { + // If we have a transparent background, then everything in the + // color channels is pointless--the color information is + // completely replaced. Might as well fill it white. + _output_image.fill(_fg[0], _fg[1], _fg[2]); + + } else if (_use_alpha && _bg[3] == 1.0) { + // Similarly if we have a transparent foreground. + _output_image.fill(_bg[0], _bg[1], _bg[2]); + } + // All done! Write everything out. nout << "Scale factor is " << _scale_factor << "\n"; PNMImage small(_output_xsize, _output_ysize, _output_zsize); small.gaussian_filter_from(_gaussian_radius, _output_image); + // Fix antialiasing, if required. + if (_use_alpha && _bg[3] != 0.0 && _bg[3] != 1.0) { + // If we have some non-transparent background, we need to + // compensate for the antialiasing. + unsmooth_rgb(small); + } + + nout << "Generating " << _output_xsize << " by " << _output_ysize << " by " << _output_zsize << " image: " - << _output_image_filename << "\n"; + << _output_image_filename << "\n"; small.write(_output_image_filename); _data.write_egg(get_output()); diff --git a/pandatool/src/egg-mkfont/eggMakeFont.h b/pandatool/src/egg-mkfont/eggMakeFont.h index 943ac20448..b6ef4df84b 100644 --- a/pandatool/src/egg-mkfont/eggMakeFont.h +++ b/pandatool/src/egg-mkfont/eggMakeFont.h @@ -19,6 +19,7 @@ class CharBitmap; class EggGroup; class EggTexture; class EggVertexPool; +class FontFile; //////////////////////////////////////////////////////////////////// // Class : EggMakeFont @@ -47,18 +48,7 @@ private: bool choose_scale_factor(); void choose_image_size(); - unsigned int fetch_nibble(); - unsigned int fetch_packed_int(); - unsigned int fetch_byte(); - unsigned int fetch_int(int n = 4); - int fetch_signed_int(int n = 4); - bool do_character(int flag_byte); - void do_xxx(int num_bytes); - void do_yyy(); - void do_post(); - void do_pre(); - void read_pk(); - + void unsmooth_rgb(PNMImage &image); string expand_hyphen(const string &str); public: @@ -66,8 +56,10 @@ public: private: Filename _output_image_filename; - Filename _input_pk_filename; + Filename _input_font_filename; bool _got_output_size; + Colord _fg, _bg; + bool _use_alpha; int _output_xsize, _output_ysize, _output_zsize; double _buffer_pixels; double _poly_pixels; @@ -77,19 +69,7 @@ private: bool _get_all; string _only_chars; - double _ds; - double _vppp; - double _hppp; - - bool _post; - bool _post_warning; - int _p; - bool _high; - int _dyn_f; - int _repeat_count; - vector _pk; - typedef vector Chars; - Chars _chars; + FontFile *_font; typedef map EggDefs; EggDefs _egg_defs; diff --git a/pandatool/src/egg-mkfont/fontFile.I b/pandatool/src/egg-mkfont/fontFile.I new file mode 100644 index 0000000000..03c57168e3 --- /dev/null +++ b/pandatool/src/egg-mkfont/fontFile.I @@ -0,0 +1,78 @@ +// Filename: fontFile.I +// Created by: drose (18Feb01) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::get_ds +// Access: Public +// Description: Returns the designed size of the font in points. +// This is the basic height of a line of text in the +// given font. +//////////////////////////////////////////////////////////////////// +INLINE double FontFile:: +get_ds() const { + return _ds; +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::get_dpi +// Access: Public +// Description: Returns the resolution of the rasterized font in dots +// per inch. This is mainly useful for user +// information; use get_vppp() and get_hppp() for more +// precises measurements. +//////////////////////////////////////////////////////////////////// +INLINE double FontFile:: +get_dpi() const { + return floor(_vppp * 72.27 + 0.5); +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::get_vppp +// Access: Public +// Description: Returns the vertical pixels per point of the +// rasterized font characters. This is the number of +// pixels high each bitmap characters is per point of +// font. +//////////////////////////////////////////////////////////////////// +INLINE double FontFile:: +get_vppp() const { + return _vppp; +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::get_hppp +// Access: Public +// Description: Returns the horizontal pixels per point of the +// rasterized font characters. This is the number of +// pixels wide each bitmap characters is per point of +// font. +//////////////////////////////////////////////////////////////////// +INLINE double FontFile:: +get_hppp() const { + return _hppp; +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::get_num_chars +// Access: Public +// Description: Returns the number of characters (glyphs) read from +// the font. +//////////////////////////////////////////////////////////////////// +INLINE int FontFile:: +get_num_chars() const { + return _chars.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::get_char +// Access: Public +// Description: Returns the nth character (glyph) read from the font. +//////////////////////////////////////////////////////////////////// +INLINE CharBitmap *FontFile:: +get_char(int n) const { + nassertr(n >= 0 && n < (int)_chars.size(), (CharBitmap *)NULL); + return _chars[n]; +} diff --git a/pandatool/src/egg-mkfont/fontFile.cxx b/pandatool/src/egg-mkfont/fontFile.cxx new file mode 100644 index 0000000000..72c521c9c6 --- /dev/null +++ b/pandatool/src/egg-mkfont/fontFile.cxx @@ -0,0 +1,54 @@ +// Filename: fontFile.cxx +// Created by: drose (18Feb01) +// +//////////////////////////////////////////////////////////////////// + +#include "fontFile.h" +#include "charBitmap.h" + +#include + + +// An STL function object to sort the characters in order from tallest +// to shortest. This provides a more optimal packing into the +// resulting image. +class SortCharBitmap { +public: + bool operator() (const CharBitmap *c1, const CharBitmap *c2) const { + return (c1->get_height() > c2->get_height()); + } +}; + + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +FontFile:: +FontFile() { + // A standard design size for font types that don't specify + // otherwise. + _ds = 12.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +FontFile:: +~FontFile() { +} + +//////////////////////////////////////////////////////////////////// +// Function: FontFile::sort_chars_by_height +// Access: Public +// Description: Sorts the array of characters in the font as returned +// by get_char() so that the tallest ones appear first +// in the list. +//////////////////////////////////////////////////////////////////// +void FontFile:: +sort_chars_by_height() { + sort(_chars.begin(), _chars.end(), SortCharBitmap()); +} diff --git a/pandatool/src/egg-mkfont/fontFile.h b/pandatool/src/egg-mkfont/fontFile.h new file mode 100644 index 0000000000..c1e23de02e --- /dev/null +++ b/pandatool/src/egg-mkfont/fontFile.h @@ -0,0 +1,52 @@ +// Filename: fontFile.h +// Created by: drose (18Feb01) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FONTFILE_H +#define FONTFILE_H + +#include + +#include +#include + +#include + +class CharBitmap; + +//////////////////////////////////////////////////////////////////// +// Class : FontFile +// Description : This is an abstract base class that provides the +// interface to reading and/or rasterizing a particular +// font file. +//////////////////////////////////////////////////////////////////// +class FontFile { +public: + FontFile(); + virtual ~FontFile(); + + virtual bool read(const Filename &filename, + bool extract_all, const string &extract_only)=0; + + INLINE double get_ds() const; + INLINE double get_dpi() const; + INLINE double get_vppp() const; + INLINE double get_hppp() const; + + void sort_chars_by_height(); + INLINE int get_num_chars() const; + INLINE CharBitmap *get_char(int n) const; + +protected: + double _ds; + double _vppp; + double _hppp; + + typedef vector Chars; + Chars _chars; +}; + +#include "fontFile.I" + +#endif diff --git a/pandatool/src/egg-mkfont/pkFontFile.cxx b/pandatool/src/egg-mkfont/pkFontFile.cxx new file mode 100644 index 0000000000..4ce2d66628 --- /dev/null +++ b/pandatool/src/egg-mkfont/pkFontFile.cxx @@ -0,0 +1,909 @@ +// Filename: pkFontFile.cxx +// Created by: drose (18Feb01) +// +//////////////////////////////////////////////////////////////////// + +#include "pkFontFile.h" +#include "charBitmap.h" + +/******************************************************************** + +I found the following in a source file for pftype called pftype.web. +It's some nutty TeX-based Pascal program, and the documentation is a +little hard to read because of the embedded TeX formatting controls. +But it describes quite thoroughly the format of the pk file. + +********************************************************************* + +@* Packed file format. +The packed file format is a compact representation of the data contained in a +\.{GF} file. The information content is the same, but packed (\.{PK}) files +are almost always less than half the size of their \.{GF} counterparts. They +are also easier to convert into a raster representation because they do not +have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be +separately interpreted. In addition, the \.{PK} format expressedly forbids +\&{special} commands within a character. The minimum bounding box for each +character is explicit in the format, and does not need to be scanned for as in +the \.{GF} format. Finally, the width and escapement values are combined with +the raster information into character ``packets'', making it simpler in many +cases to process a character. + +A \.{PK} file is organized as a stream of 8-bit bytes. At times, these bytes +might be split into 4-bit nybbles or single bits, or combined into multiple +byte parameters. When bytes are split into smaller pieces, the `first' piece +is always the most significant of the byte. For instance, the first bit of +a byte is the bit with value 128; the first nybble can be found by dividing +a byte by 16. Similarly, when bytes are combined into multiple byte +parameters, the first byte is the most significant of the parameter. If the +parameter is signed, it is represented by two's-complement notation. + +The set of possible eight-bit values is separated into two sets, those that +introduce a character definition, and those that do not. The values that +introduce a character definition range from 0 to 239; byte values +above 239 are interpreted as commands. Bytes that introduce character +definitions are called flag bytes, and various fields within the byte indicate +various things about how the character definition is encoded. Command bytes +have zero or more parameters, and can never appear within a character +definition or between parameters of another command, where they would be +interpeted as data. + +A \.{PK} file consists of a preamble, followed by a sequence of one or more +character definitions, followed by a postamble. The preamble command must +be the first byte in the file, followed immediately by its parameters. +Any number of character definitions may follow, and any command but the +preamble command and the postamble command may occur between character +definitions. The very last command in the file must be the postamble. + +@ The packed file format is intended to be easy to read and interpret by +device drivers. The small size of the file reduces the input/output overhead +each time a font is loaded. For those drivers that load and save each font +file into memory, the small size also helps reduce the memory requirements. +The length of each character packet is specified, allowing the character raster +data to be loaded into memory by simply counting bytes, rather than +interpreting each command; then, each character can be interpreted on a demand +basis. This also makes it possible for a driver to skip a particular +character quickly if it knows that the character is unused. + +@ First, the command bytes will be presented; then the format of the +character definitions will be defined. Eight of the possible sixteen +commands (values 240 through 255) are currently defined; the others are +reserved for future extensions. The commands are listed below. Each command +is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte, +and any parameters. The parameters are followed by a bracketed number +telling how many bytes they occupy, with the number preceded by a plus sign if +it is a signed quantity. (Four byte quantities are always signed, however.) + +\yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|. This command is undefined in general; +it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading +programs are being used. \MF\ generates \\{xxx} commands when encountering +a \&{special} string. It is recommended that |x| be a string having the form +of a keyword followed by possible parameters relevant to that keyword. + +\yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|. Like |pk_xxx1|, but |0<=k<65536|. + +\yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|. Like |pk_xxx1|, but +|0<=k<@t$2^{24}$@>|. \MF\ uses this when sending a \&{special} string whose +length exceeds~255. + +\yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|. Like |pk_xxx1|, but |k| can be +ridiculously large; |k| musn't be negative. + +\yskip\hang|pk_yyy| 244 |y[4]|. This command is undefined in general; it +functions as a five-byte \\{no\_op} unless special \.{PK} reading programs +are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a result of +\&{numspecial} commands; the intent is to provide numeric parameters to +\\{xxx} commands that immediately precede. + +\yskip\hang|pk_post| 245. Beginning of the postamble. This command is +followed by enough |pk_no_op| commands to make the file a multiple +of four bytes long. Zero through three bytes are usual, but any number +is allowed. +This should make the file easy to read on machines that pack four bytes to +a word. + +\yskip\hang|pk_no_op| 246. No operation, do nothing. Any number of +|pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be +inserted between a command and its parameters, between two parameters, or +inside a character definition. + +\yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]| +|vppp[4]|. Preamble command. Here, |i| is the identification byte of the +file, currently equal to 89. The string |x| is merely a comment, usually +indicating the source of the \.{PK} file. The parameters |ds| and |cs| are +the design size of the file in $1/2^{20}$ points, and the checksum of the +file, respectively. The checksum should match the \.{TFM} file and the +\.{GF} files for this font. Parameters |hppp| and |vppp| are the ratios +of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they +can be used to correlate the font with specific device resolutions, +magnifications, and ``at sizes''. Usually, the name of the \.{PK} file is +formed by concatenating the font name (e.g., cmr10) with the resolution at +which the font is prepared in pixels per inch multiplied by the magnification +factor, and the letters \.{pk}. For instance, cmr10 at 300 dots per inch +should be named \.{cmr10.300pk}; at one thousand dots per inch and magstephalf, +it should be named \.{cmr10.1095pk}. + +@ We put a few of the above opcodes into definitions for symbolic use by +this program. + +@d pk_id = 89 {the version of \.{PK} file described} +@d pk_xxx1 = 240 {\&{special} commands} +@d pk_yyy = 244 {\&{numspecial} commands} +@d pk_post = 245 {postamble} +@d pk_no_op = 246 {no operation} +@d pk_pre = 247 {preamble} +@d pk_undefined == 248, 249, 250, 251, 252, 253, 254, 255 + +@ The \.{PK} format has two conflicting goals: to pack character raster and +size information as compactly as possible, while retaining ease of translation +into raster and other forms. A suitable compromise was found in the use of +run-encoding of the raster information. Instead of packing the individual +bits of the character, we instead count the number of consecutive `black' or +`white' pixels in a horizontal raster row, and then encode this number. Run +counts are found for each row from left to right, traversing rows from the +top to bottom. This is essentially the way the \.{GF} format works. +Instead of presenting each row individually, however, we concatenate all +of the horizontal raster rows into one long string of pixels, and encode this +row. With knowledge of the width of the bit-map, the original character glyph +can easily be reconstructed. In addition, we do not need special commands to +mark the end of one row and the beginning of the next. + +Next, we place the burden of finding the minimum bounding box on the part +of the font generator, since the characters will usually be used much more +often than they are generated. The minimum bounding box is the smallest +rectangle that encloses all `black' pixels of a character. We also +eliminate the need for a special end of character marker, by supplying +exactly as many bits as are required to fill the minimum bounding box, from +which the end of the character is implicit. + +Let us next consider the distribution of the run counts. Analysis of several +dozen pixel files at 300 dots per inch yields a distribution peaking at four, +falling off slowly until ten, then a bit more steeply until twenty, and then +asymptotically approaching the horizontal. Thus, the great majority of our +run counts will fit in a four-bit nybble. The eight-bit byte is attractive for +our run-counts, as it is the standard on many systems; however, the wasted four +bits in the majority of cases seem a high price to pay. Another possibility +is to use a Huffman-type encoding scheme with a variable number of bits for +each run-count; this was rejected because of the overhead in fetching and +examining individual bits in the file. Thus, the character raster definitions +in the \.{PK} file format are based on the four-bit nybble. + +@ An analysis of typical pixel files yielded another interesting statistic: +Fully 37\char`\%\ +of the raster rows were duplicates of the previous row. Thus, the \.{PK} +format allows the specification of repeat counts, which indicate how many times +a horizontal raster row is to be repeated. These repeated rows are taken out +of the character glyph before individual rows are concatenated into the long +string of pixels. + +For elegance, we disallow a run count of zero. The case of a null raster +description should be gleaned from the character width and height being equal +to zero, and no raster data should be read. No other zero counts are ever +necessary. Also, in the absence of repeat counts, the repeat value is set to +be zero (only the original row is sent.) If a repeat count is seen, it takes +effect on the current row. The current row is defined as the row on which the +first pixel of the next run count will lie. The repeat count is set back to +zero when the last pixel in the current row is seen, and the row is sent out. + +This poses a problem for entirely black and entirely white rows, however. Let +us say that the current row ends with four white pixels, and then we have five +entirely empty rows, followed by a black pixel at the beginning of the next +row, and the character width is ten pixels. We would like to use a repeat +count, but there is no legal place to put it. If we put it before the white +run count, it will apply to the current row. If we put it after, it applies +to the row with the black pixel at the beginning. Thus, entirely white or +entirely black repeated rows are always packed as large run counts (in this +case, a white run count of 54) rather than repeat counts. + +@ Now we turn our attention to the actual packing of the run counts and +repeat counts into nybbles. There are only sixteen possible nybble values. +We need to indicate run counts and repeat counts. Since the run counts are +much more common, we will devote the majority of the nybble values to them. +We therefore indicate a repeat count by a nybble of 14 followed by a packed +number, where a packed number will be explained later. Since the repeat +count value of one is so common, we indicate a repeat one command by a single +nybble of 15. A 14 followed by the packed number 1 is still legal for a +repeat one count. The run counts are coded directly as packed +numbers. + +For packed numbers, therefore, we have the nybble values 0 through 13. We +need to represent the positive integers up to, say, $2^{31}-1$. We would +like the more common smaller numbers to take only one or two nybbles, and +the infrequent large numbers to take three or more. We could therefore +allocate one nybble value to indicate a large run count taking three or more +nybbles. We do this with the value 0. + +@ We are left with the values 1 through 13. We can allocate some of these, say +|dyn_f|, to be one-nybble run counts. +These will work for the run counts |1..dyn_f|. For subsequent run +counts, we will use a nybble greater than |dyn_f|, followed by a second nybble, +whose value can run from 0 through 15. Thus, the two-nybble values will +run from |dyn_f+1..(13-dyn_f)*16+dyn_f|. We have our definition of large run +count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|. + +We can analyze our several dozen pixel files and determine an optimal value of +|dyn_f|, and use this value for all of the characters. Unfortunately, values +of |dyn_f| that pack small characters well tend to pack the large characters +poorly, and values that pack large characters well are not efficient for the +smaller characters. Thus, we choose the optimal |dyn_f| on a character basis, +picking the value that will pack each individual character in the smallest +number of nybbles. Legal values of |dyn_f| run from 0 (with no one-nybble run +counts) to 13 (with no two-nybble run counts). + +@ Our only remaining task in the coding of packed numbers is the large run +counts. We use a scheme suggested by D.~E.~Knuth +@^Knuth, Donald Ervin@> +that simply and elegantly represents arbitrarily large values. The +general scheme to represent an integer |i| is to write its hexadecimal +representation, with leading zeros removed. Then we count the number of +digits, and prepend one less than that many zeros before the hexadecimal +representation. Thus, the values from one to fifteen occupy one nybble; +the values sixteen through 255 occupy three, the values 256 through 4095 +require five, etc. + +For our purposes, however, we have already represented the numbers one +through |(13-dyn_f)*16+dyn_f|. In addition, the one-nybble values have +already been taken by our other commands, which means that only the values +from sixteen up are available to us for long run counts. Thus, we simply +normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and +adding 16, and then we represent the result according to the scheme above. + +@ The final algorithm for decoding the run counts based on the above scheme +looks like this, assuming that a procedure called \\{pk\_nyb} is available +to get the next nybble from the file, and assuming that the global +|repeat_count| indicates whether a row needs to be repeated. Note that this +routine is recursive, but since a repeat count can never directly follow +another repeat count, it can only be recursive to one level. + +@= +function pk_packed_num : integer ; +var i, @!j : integer ; +begin + i := get_nyb ; + if i = 0 then begin + repeat j := get_nyb ; incr(i) ; until j <> 0 ; + while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ; + pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ; + end else if i <= dyn_f then + pk_packed_num := i + else if i < 14 then + pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1 + else begin + if repeat_count <> 0 then abort('Second repeat count for this row!') ; +@.Second repeat count...@> + repeat_count := 1; {prevent recursion more than one level} + if i = 14 then repeat_count := pk_packed_num; + send_out(true, repeat_count) ; + pk_packed_num := pk_packed_num ; + end ; +end ; + +@ For low resolution fonts, or characters with `gray' areas, run encoding can +often make the character many times larger. Therefore, for those characters +that cannot be encoded efficiently with run counts, the \.{PK} format allows +bit-mapping of the characters. This is indicated by a |dyn_f| value of +14. The bits are packed tightly, by concatenating all of the horizontal raster +rows into one long string, and then packing this string eight bits to a byte. +The number of bytes required can be calculated by |(width*height+7) div 8|. +This format should only be used when packing the character by run counts takes +more bytes than this, although, of course, it is legal for any character. +Any extra bits in the last byte should be set to zero. + +@ At this point, we are ready to introduce the format for a character +descriptor. It consists of three parts: a flag byte, a character preamble, +and the raster data. The most significant four bits of the flag byte +yield the |dyn_f| value for that character. (Notice that only values of +0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character; +thus, the flag bytes do not conflict with the command bytes, whose upper nybble +is always 15.) The next bit (with weight 8) indicates whether the first run +count is a black count or a white count, with a one indicating a black count. +For bit-mapped characters, this bit should be set to a zero. The next bit +(with weight 4) indicates whether certain later parameters (referred to as size +parameters) are given in one-byte or two-byte quantities, with a one indicating +that they are in two-byte quantities. The last two bits are concatenated on to +the beginning of the packet-length parameter in the character preamble, +which will be explained below. + +However, if the last three bits of the flag byte are all set (normally +indicating that the size parameters are two-byte values and that a 3 should be +prepended to the length parameter), then a long format of the character +preamble should be used instead of one of the short forms. + +Therefore, there are three formats for the character preamble; the one that +is used depends on the least significant three bits of the flag byte. If the +least significant three bits are in the range zero through three, the short +format is used. If they are in the range four through six, the extended short +format is used. Otherwise, if the least significant bits are all set, then +the long form of the character preamble is used. The preamble formats are +explained below. + +\yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]| +|h[1]| |hoff[+1]| |voff[+1]|. +If this format of the character preamble is used, the above +parameters must all fit in the indicated number of bytes, signed or unsigned +as indicated. Almost all of the standard \TeX\ font characters fit; the few +exceptions are fonts such as \.{cminch}. + +\yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]| +|w[2]| |h[2]| |hoff[+2]| |voff[+2]|. Larger characters use this extended +format. + +\yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]| +|w[4]| |h[4]| |hoff[4]| |voff[4]|. This is the general format that +allows all of the +parameters of the \.{GF} file format, including vertical escapement. +\vskip\baselineskip +The |flag| parameter is the flag byte. The parameter |pl| (packet length) +contains the offset +of the byte following this character descriptor, with respect to the beginning +of the |tfm| width parameter. This is given so a \.{PK} reading program can, +once it has read the flag byte, packet length, and character code (|cc|), skip +over the character by simply reading this many more bytes. For the two short +forms of the character preamble, the last two bits of the flag byte should be +considered the two most-significant bits of the packet length. For the short +format, the true packet length might be calculated as |(flag mod 4)*256+pl|; +for the short extended format, it might be calculated as +|(flag mod 4)*65536+pl|. + +The |w| parameter is the width and the |h| parameter is the height in pixels +of the minimum bounding box. The |dx| and |dy| parameters are the horizontal +and vertical escapements, respectively. In the short formats, |dy| is assumed +to be zero and |dm| is |dx| but in pixels; +in the long format, |dx| and |dy| are both +in pixels multiplied by $2^{16}$. The |hoff| is the horizontal offset from the +upper left pixel to the reference pixel; the |voff| is the vertical offset. +They are both given in pixels, with right and down being positive. The +reference pixel is the pixel that occupies the unit square in \MF; the +\MF\ reference point is the lower left hand corner of this pixel. (See the +example below.) + +@ \TeX\ requires all characters that have the same character codes +modulo 256 to have also the same |tfm| widths and escapement values. The \.{PK} +format does not itself make this a requirement, but in order for the font to +work correctly with the \TeX\ software, this constraint should be observed. +(The standard version of \TeX\ cannot output character codes greater +than 255, but extended versions do exist.) + +Following the character preamble is the raster information for the +character, packed by run counts or by bits, as indicated by the flag byte. +If the character is packed by run counts and the required number of nybbles +is odd, then the last byte of the raster description should have a zero +for its least significant nybble. + +@ As an illustration of the \.{PK} format, the character \char4\ from the font +amr10 at 300 dots per inch will be encoded. This character was chosen +because it illustrates some +of the borderline cases. The raster for the character looks like this (the +row numbers are chosen for convenience, and are not \MF's row numbers.) + +\vskip\baselineskip +{\def\smbox{\vrule height 7pt width 7pt depth 0pt \hskip 3pt}% +\catcode`\*=\active \let*=\smbox +\centerline{\vbox{\baselineskip=10pt +\halign{\hfil#\quad&&\hfil#\hfil\cr +0& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +1& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +2& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +3& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +4& & &*&*& & & & & & & & & & & & & & & & &*&*\cr +5& & &*&*& & & & & & & & & & & & & & & & &*&*\cr +6& & &*&*& & & & & & & & & & & & & & & & &*&*\cr +7\cr +8\cr +9& & & & &*&*& & & & & & & & & & & & &*&*& & \cr +10& & & & &*&*& & & & & & & & & & & & &*&*& & \cr +11& & & & &*&*& & & & & & & & & & & & &*&*& & \cr +12& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr +13& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr +14& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr +15& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr +16& & & & &*&*& & & & & & & & & & & & &*&*& & \cr +17& & & & &*&*& & & & & & & & & & & & &*&*& & \cr +18& & & & &*&*& & & & & & & & & & & & &*&*& & \cr +19\cr +20\cr +21\cr +22& & &*&*& & & & & & & & & & & & & & & & &*&*\cr +23& & &*&*& & & & & & & & & & & & & & & & &*&*\cr +24& & &*&*& & & & & & & & & & & & & & & & &*&*\cr +25& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +26& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +27& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +28&+& &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr +&\hphantom{*}&\hphantom{*}\cr +}}}} +The width of the minimum bounding box for this character is 20; its height +is 29. The `+' represents the reference pixel; notice how it lies outside the +minimum bounding box. The |hoff| value is $-2$, and the |voff| is~28. + +The first task is to calculate the run counts and repeat counts. The repeat +counts are placed at the first transition (black to white or white to black) +in a row, and are enclosed in brackets. White counts are enclosed in +parentheses. It is relatively easy to generate the counts list: +\vskip\baselineskip +\centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]} +\centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82} +\vskip\baselineskip +Note that any duplicated rows that are not all white or all black are removed +before the run counts are calculated. The rows thus removed are rows 5, 6, +10, 11, 13, 14, 15, 17, 18, 23, and 24. + +@ The next step in the encoding of this character is to calculate the optimal +value of |dyn_f|. The details of how this calculation is done are not +important here; suffice it to say that there is a simple algorithm that can +determine the best value of |dyn_f| in one pass over the count list. For this +character, the optimal value turns out to be 8 (atypically low). Thus, all +count values less than or equal to 8 are packed in one nybble; those from +nine to $(13-8)*16+8$ or 88 are packed in two nybbles. The run encoded values +now become (in hex, separated according to the above list): +\vskip\baselineskip +\centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3} +\centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9} +\vskip\baselineskip\noindent +which comes to 36 nybbles, or 18 bytes. This is shorter than the 73 bytes +required for the bit map, so we use the run count packing. + +@ The short form of the character preamble is used because all of the +parameters fit in their respective lengths. The packet length is therefore +18 bytes for the raster, plus +eight bytes for the character preamble parameters following the character +code, or 26. The |tfm| width for this character is 640796, or {\tt 9C71C} in +hexadecimal. The horizontal escapement is 25 pixels. The flag byte is +88 hex, indicating the short preamble, the black first count, and the +|dyn_f| value of 8. The final total character packet, in hexadecimal, is: +\vskip\baselineskip +$$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr +Flag byte&88\cr +Packet length&1A\cr +Character code&04\cr +|tfm| width&09&C7&1C\cr +Horizontal escapement (pixels)&19\cr +Width of bit map&14\cr +Height of bit map&1D\cr +Horizontal offset (signed)&FE\cr +Vertical offset&1C\cr +Raster data&D9&E2&97\cr +&2B&1E&22\cr +&93&24&E3\cr +&97&4E&22\cr +&93&2C&5E\cr +&22&97&D9\cr}}$$ +********************************************************************/ + +#define PK_XXX1 240 +#define PK_XXX2 241 +#define PK_XXX3 242 +#define PK_XXX4 243 +#define PK_YYY 244 +#define PK_POST 245 +#define PK_NO_OP 246 +#define PK_PRE 247 + + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PkFontFile:: +PkFontFile() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::read +// Access: Public, Virtual +// Description: Attempts to read the font from the indicated file. +// Returns true if successful, false if there is an +// error. +//////////////////////////////////////////////////////////////////// +bool PkFontFile:: +read(const Filename &filename, bool extract_all, const string &extract_only) { + Filename input_filename = filename; + + input_filename.set_binary(); + ifstream pk_file; + if (!input_filename.open_read(pk_file)) { + return false; + } + + // First, read the whole thing into a memory buffer, so we can + // easily access bytes at random locations in the file. + unsigned char c = pk_file.get(); + while (pk_file && !pk_file.eof()) { + _pk.push_back(c); + c = pk_file.get(); + } + + _p = 0; + _high = true; + _post = false; + _post_warning = false; + + _extract_all = extract_all; + _extract_only = extract_only; + + return read_pk(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::fetch_nibble +// Access: Private +// Description: Returns the next 4-bit nibble from the pk stream. +//////////////////////////////////////////////////////////////////// +unsigned int PkFontFile:: +fetch_nibble() { + assert(_p < (int)_pk.size()); + if (_high) { + _high = false; + return _pk[_p] >> 4; + } else { + _high = true; + return _pk[_p++] & 0xf; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::fetch_packed_int +// Access: Private +// Description: Returns the next packed integer from the pk stream. +//////////////////////////////////////////////////////////////////// +unsigned int PkFontFile:: +fetch_packed_int() { + int i = fetch_nibble(); + if (i == 0) { + int j; + do { + j = fetch_nibble(); + i++; + } while (j == 0); + while (i > 0) { + j = (j << 4) | fetch_nibble(); + i--; + } + return j - 15 + (13 - _dyn_f)*16 + _dyn_f; + + } else if (i <= _dyn_f) { + return i; + + } else if (i < 14) { + return (i - _dyn_f - 1)*16 + fetch_nibble() + _dyn_f + 1; + + } else { + _repeat_count = 1; + if (i == 14) { + _repeat_count = fetch_packed_int(); + } + // nout << "[" << _repeat_count << "]"; + return fetch_packed_int(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::fetch_byte +// Access: Private +// Description: Returns the next 8-bit unsigned byte from the pk +// stream. +//////////////////////////////////////////////////////////////////// +unsigned int PkFontFile:: +fetch_byte() { + assert(_high); + assert(_p < (int)_pk.size()); + return _pk[_p++]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::fetch_int +// Access: Private +// Description: Returns the next n-byte unsigned int from +// the pk stream. +//////////////////////////////////////////////////////////////////// +unsigned int PkFontFile:: +fetch_int(int n) { + assert(_high); + + unsigned int result = 0; + for (int i = 0; i < n; i++) { + assert(_p < (int)_pk.size()); + result = (result << 8) | _pk[_p]; + _p++; + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::fetch_signed_int +// Access: Private +// Description: Returns the next n-byte signed int from +// the pk stream. +//////////////////////////////////////////////////////////////////// +int PkFontFile:: +fetch_signed_int(int n) { + assert(_high); + + assert(_p < (int)_pk.size()); + int result = (signed char)_pk[_p]; + _p++; + for (int i = 1; i < n; i++) { + assert(_p < (int)_pk.size()); + result = (result << 8) | _pk[_p]; + _p++; + } + + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::do_character +// Access: Private +// Description: Reads a single character from the pk file and +// processes it. Returns true if successful, false if +// something bad happened. +//////////////////////////////////////////////////////////////////// +bool PkFontFile:: +do_character(int flag_byte) { + // int start_p = _p - 1; + _dyn_f = (flag_byte >> 4); + bool first_black = ((flag_byte & 0x8) != 0); + int bsize = (flag_byte & 0x4) ? 2 : 1; + int prepend_length = (flag_byte & 0x3); + + bool use_long_form = ((flag_byte & 0x7) == 0x7); + + unsigned int pl, cc, itfm, w, h; + int hoff, voff; + unsigned int idx = 0; + unsigned int idy = 0; + int next_p; + + if (use_long_form) { + pl = fetch_int(); + cc = fetch_int(); + next_p = _p + pl; + itfm = fetch_int(); + idx = fetch_int(); + idy = fetch_int(); + w = fetch_int(); + h = fetch_int(); + hoff = fetch_signed_int(); + voff = fetch_signed_int(); + } else { + pl = fetch_int(bsize) | (prepend_length << bsize*8); + cc = fetch_byte(); + next_p = _p + pl; + itfm = fetch_int(3); + idx = fetch_int(bsize) << 16; + w = fetch_int(bsize); + h = fetch_int(bsize); + hoff = fetch_signed_int(bsize); + voff = fetch_signed_int(bsize); + } + + // double tfm = (double)itfm / (double)(1 << 24); + double dx = (double)idx / (double)(1 << 16); + double dy = (double)idy / (double)(1 << 16); + // double di_width = tfm * _ppu * _hppp / _vppp; + + if (_extract_all || + ((cc >= 33 && cc <= 127) && + (_extract_only.empty() || _extract_only.find((char)cc) != string::npos))) { + nout << " " << cc; + + CharBitmap *bm = new CharBitmap(cc, w, h, hoff, voff, dx, dy); + + if (_dyn_f == 14) { + // A bitmapped character: this character has the actual w x h + // bits stored directly in the pk file. This kind of character + // is quite rare, and the code is therefore untested. + if (h > 0 && w > 0) { + nout + << "\nA rare bitmapped character encountered! You are now running\n" + << "untested code. If this works, change this line in the program\n" + << "to indicate that the code is actually tested!\n\n"; + } + + unsigned int bit = 0; + unsigned int byte = 0; + for (unsigned int y = 0; y < h; y++) { + for (unsigned int x = 0; x < w; x++) { + if (bit == 0) { + bit = 0x80; + byte = fetch_byte(); + } + bm->_block[y][x] = ((byte & bit)!=0); + bit >>= 1; + } + } + + } else { + // A normal, rle character. This character has sequences of + // black and white runs stored in the pk file. Most characters + // will be stored this way. + bool black = first_black; + _repeat_count = 0; + + int count = fetch_packed_int(); + while (bm->paint(black, count, _repeat_count)) { + /* + if (black) { + nout << count; + } else { + nout << "(" << count << ")"; + } + */ + black = !black; + count = fetch_packed_int(); + } + // nout << "\n"; + } + + _chars.push_back(bm); + + /* + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + nout << (bm->_block[y][x] ? ' ' : '*'); + } + nout << "\n"; + } + */ + + if (!_high) { + _p++; + _high = true; + } + + if (_p != next_p) { + nout << "Expected p == " << next_p << " got " << _p << "\n"; + } + + } else { + nout << " (" << cc << ")"; + } + + _p = next_p; + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::do_xxx +// Access: Private +// Description: The xxx1 .. xxx4 series of commands specify an +// embedded comment or some such silliness in the pk +// file that must be skipped. +//////////////////////////////////////////////////////////////////// +void PkFontFile:: +do_xxx(int num_bytes) { + _p += fetch_int(num_bytes); +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::do_yyy +// Access: Private +// Description: The yyy command is an encoded number which might have +// meaning to a preceding xxx block, but means nothing +// to us. +//////////////////////////////////////////////////////////////////// +void PkFontFile:: +do_yyy() { + _p += 4; +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::do_post +// Access: Private +// Description: The beginning of the postamble. +//////////////////////////////////////////////////////////////////// +void PkFontFile:: +do_post() { + _post = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::do_pre +// Access: Private +// Description: The preamble. +//////////////////////////////////////////////////////////////////// +void PkFontFile:: +do_pre() { + int id = fetch_byte(); + if (id != 89) { + nout << "Warning: PK file had an unexpected ID, " << id << "\n"; + } + + int comment_len = fetch_byte(); + + assert(_p + comment_len <= (int)_pk.size()); + nout.write(&_pk[_p], comment_len); + nout << "\n"; + _p += comment_len; + + int ds = fetch_int(); + fetch_int(); // cs + int hppp = fetch_int(); + int vppp = fetch_int(); + + _ds = (double)ds / (double)(1 << 20); + _hppp = (double)hppp / (double)(1 << 16); + _vppp = (double)vppp / (double)(1 << 16); + + nout << "Font size is " << get_ds() << " points, rasterized at " + << get_dpi() << " DPI.\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PkFontFile::read_pk +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool PkFontFile:: +read_pk() { + if (_p >= (int)_pk.size()) { + nout << "PK file is empty.\n"; + return false; + } + unsigned int cmd = fetch_byte(); + if (cmd != PK_PRE) { + nout << "Not a PK file.\n"; + return false; + } + do_pre(); + + nout << "Characters:"; + + while (_p < (int)_pk.size()) { + unsigned int cmd = fetch_byte(); + if (_post && !_post_warning && cmd != PK_NO_OP) { + _post_warning = true; + nout << "\nWarning: postamble was not the last command.\n"; + } + if (cmd < 240) { + if (!do_character(cmd)) { + return true; + } + } else { + switch (cmd) { + case PK_XXX1: + do_xxx(1); + break; + + case PK_XXX2: + do_xxx(2); + break; + + case PK_XXX3: + do_xxx(3); + break; + + case PK_XXX4: + do_xxx(4); + break; + + case PK_YYY: + do_yyy(); + break; + + case PK_POST: + do_post(); + break; + + case PK_NO_OP: + break; + + default: + nout << "\nUnexpected command " << cmd << " encountered in PK file\n"; + return false; + } + } + } + nout << "\n"; + + if (!_post) { + nout << "Warning: did not encounter postamble.\n"; + } + + return true; +} diff --git a/pandatool/src/egg-mkfont/pkFontFile.h b/pandatool/src/egg-mkfont/pkFontFile.h new file mode 100644 index 0000000000..2be1f45abd --- /dev/null +++ b/pandatool/src/egg-mkfont/pkFontFile.h @@ -0,0 +1,51 @@ +// Filename: pkFontFile.h +// Created by: drose (18Feb01) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PKFONTFILE_H +#define PKFONTFILE_H + +#include + +#include "fontFile.h" + +//////////////////////////////////////////////////////////////////// +// Class : PkFontFile +// Description : A specialization on FontFile for reading TeX-style +// .pk fonts. +//////////////////////////////////////////////////////////////////// +class PkFontFile : public FontFile { +public: + PkFontFile(); + + virtual bool read(const Filename &filename, + bool extract_all, const string &extract_only); + +private: + unsigned int fetch_nibble(); + unsigned int fetch_packed_int(); + unsigned int fetch_byte(); + unsigned int fetch_int(int n = 4); + int fetch_signed_int(int n = 4); + bool do_character(int flag_byte); + void do_xxx(int num_bytes); + void do_yyy(); + void do_post(); + void do_pre(); + bool read_pk(); + + bool _post; + bool _post_warning; + int _p; + bool _high; + int _dyn_f; + int _repeat_count; + + bool _extract_all; + string _extract_only; + + vector _pk; +}; + +#endif diff --git a/pandatool/src/egg-mkfont/ttf2egg b/pandatool/src/egg-mkfont/ttf2egg new file mode 100755 index 0000000000..57fc272f9c --- /dev/null +++ b/pandatool/src/egg-mkfont/ttf2egg @@ -0,0 +1,94 @@ +#!/bin/sh +# +# Converts from a TTF font file to an egg file and associated texture map +# that is suitable for using with the TextNode class in Panda to render +# text in the world. +# +# This script takes advantage of ttf2tfm and ttf2pk, two programs that +# can be found in the freetype project--see www.freetype.org. Look +# in the contrib subdirectory of freetype-1.3.1; it doesn't seem to exist +# yet in freetype-2.0. +# +# +# Usage: +# +# ttf2egg [opts] file.ttf file.egg -- [mkfont-opts] +# +# Options that control the behavior of this script must appear first. +# The extra options options following the first two parameters are passed +# unchanged to egg-mkfont. +# +# Local options: +# +# -d dpi +# Specify the resolution at which to rasterize the font before +# converting it. This affects the size of the generated texture +# image and, in turn, the clarity of the resulting font. The +# default is 720, which generally seems to be a good ratio. +# +# -T file.enc +# Specify the name and/or location of the encoding file to use +# to convert the glyphs from the TTF font. A couple of suitable +# files are provided with ttf2tfm; most of the time, you will +# want to use T1-WGL4.enc. +# +# See egg-mkfont -h for a description of the additional options. +# +#ENDCOMMENT + +showhelp= +dpi=720 +enc=$PANDATOOL/shared/T1-WGL4.enc +while getopts "d:T:h" flag; do + case $flag in + d) dpi="$OPTARG";; + T) enc="$OPTARG";; + h) showhelp=y;; + \?) exit 1; + esac +done + +if [ -z "$1" -o -z "$2" -o "$showhelp" ]; then + sed '/#ENDCOMMENT/,$d' <$0 >&2 + exit 1 +fi + +shift `expr $OPTIND - 1` + +ttf=$1 +egg=$2 + +# Perform some sanity checks on input parameters. + +if [ `basename $ttf .ttf` = `basename $ttf` -a `basename $ttf .TTF` = `basename $ttf` ]; then + echo "$ttf should end in .ttf or .TTF" + exit 1 +fi + +if [ `basename $egg .egg` = `basename $egg` ]; then + echo "$egg should end in .egg" + exit 1 +fi + +fontname=`basename $egg .egg` +shift 2 + +if [ "x$1" = "x--" ]; then + shift +fi + +rm -f ttfonts1.map +echo ttf2tfm $ttf -q -u -T \"$enc\" $fontname.tfm +if ttf2tfm $ttf -q -u -T "$enc" $fontname.tfm > ttfonts1.map 2>/dev/null; then + rm -f ttfonts.map + tail -1 ttfonts1.map >ttfonts.map + echo ttf2pk -q $fontname $dpi + if ttf2pk -q $fontname $dpi >/dev/null 2>/dev/null; then + echo egg-mkfont -p 0 $* -o $egg $fontname.${dpi}pk + egg-mkfont -p 0 $* -o $egg $fontname.${dpi}pk + fi +fi +status=$? + +rm -f ttfonts1.map ttfonts.map $fontname.tfm $fontname.${dpi}pk +exit $status diff --git a/pandatool/src/progbase/programBase.cxx b/pandatool/src/progbase/programBase.cxx index 3b3c6bb18f..ac8bb911ea 100644 --- a/pandatool/src/progbase/programBase.cxx +++ b/pandatool/src/progbase/programBase.cxx @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -547,17 +548,9 @@ dispatch_count(const string &, const string &, void *var) { //////////////////////////////////////////////////////////////////// bool ProgramBase:: dispatch_int(const string &opt, const string &arg, void *var) { - if (arg.empty()) { - nout << "-" << opt << " requires an integer parameter.\n"; - return false; - } - int *ip = (int *)var; - const char *arg_str = arg.c_str(); - char *endptr; - (*ip) = strtol(arg_str, &endptr, 0); - if (*endptr != '\0') { + if (!string_to_int(arg, *ip)) { nout << "Invalid integer parameter for -" << opt << ": " << arg << "\n"; return false; @@ -575,40 +568,21 @@ dispatch_int(const string &opt, const string &arg, void *var) { //////////////////////////////////////////////////////////////////// bool ProgramBase:: dispatch_int_pair(const string &opt, const string &arg, void *var) { - if (arg.empty()) { - nout << "-" << opt - << " requires an pair of integers separated by a comma.\n"; - return false; - } - - size_t comma = arg.find(','); - if (comma == string::npos) { - nout << "-" << opt - << " requires an pair of integers separated by a comma.\n"; - return false; - } - - string first = arg.substr(0, comma); - string second = arg.substr(comma + 1); - int *ip = (int *)var; - char *endptr; - const char *first_str = first.c_str(); - ip[0] = strtol(first_str, &endptr, 0); + vector_string words; + tokenize(arg, words, ","); - if (*endptr != '\0') { - nout << "Invalid integer parameter for -" << opt << ": " - << first << "\n"; - return false; + bool okflag = false; + if (words.size() == 2) { + okflag = + string_to_int(words[0], ip[0]) && + string_to_int(words[1], ip[1]); } - const char *second_str = second.c_str(); - ip[1] = strtol(second_str, &endptr, 0); - - if (*endptr != '\0') { - nout << "Invalid integer parameter for -" << opt << ": " - << second << "\n"; + if (!okflag) { + nout << "-" << opt + << " requires a pair of integers separated by a comma.\n"; return false; } @@ -624,19 +598,144 @@ dispatch_int_pair(const string &opt, const string &arg, void *var) { //////////////////////////////////////////////////////////////////// bool ProgramBase:: dispatch_double(const string &opt, const string &arg, void *var) { - if (arg.empty()) { - nout << "-" << opt << " requires a floating-point parameter.\n"; + double *ip = (double *)var; + + if (!string_to_double(arg, *ip)) { + nout << "Invalid numeric parameter for -" << opt << ": " + << arg << "\n"; return false; } - double *ip = (double *)var; - const char *arg_str = arg.c_str(); - char *endptr; - (*ip) = strtod(arg_str, &endptr); + return true; +} - if (*endptr != '\0') { - nout << "Invalid floating-point parameter for -" << opt << ": " - << arg << "\n"; +//////////////////////////////////////////////////////////////////// +// Function: ProgramBase::dispatch_double_pair +// Access: Protected, Static +// Description: Standard dispatch function for an option that takes +// a pair of double parameters. The data pointer is to +// an array of two doubles. +//////////////////////////////////////////////////////////////////// +bool ProgramBase:: +dispatch_double_pair(const string &opt, const string &arg, void *var) { + double *ip = (double *)var; + + vector_string words; + tokenize(arg, words, ","); + + bool okflag = false; + if (words.size() == 2) { + okflag = + string_to_double(words[0], ip[0]) && + string_to_double(words[1], ip[1]); + } + + if (!okflag) { + nout << "-" << opt + << " requires a pair of numbers separated by a comma.\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ProgramBase::dispatch_double_triple +// Access: Protected, Static +// Description: Standard dispatch function for an option that takes +// a triple of double parameters. The data pointer is to +// an array of three doubles. +//////////////////////////////////////////////////////////////////// +bool ProgramBase:: +dispatch_double_triple(const string &opt, const string &arg, void *var) { + double *ip = (double *)var; + + vector_string words; + tokenize(arg, words, ","); + + bool okflag = false; + if (words.size() == 3) { + okflag = + string_to_double(words[0], ip[0]) && + string_to_double(words[1], ip[1]) && + string_to_double(words[2], ip[2]); + } + + if (!okflag) { + nout << "-" << opt + << " requires three numbers separated by commas.\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ProgramBase::dispatch_double_quad +// Access: Protected, Static +// Description: Standard dispatch function for an option that takes +// a quad of double parameters. The data pointer is to +// an array of four doubles. +//////////////////////////////////////////////////////////////////// +bool ProgramBase:: +dispatch_double_quad(const string &opt, const string &arg, void *var) { + double *ip = (double *)var; + + vector_string words; + tokenize(arg, words, ","); + + bool okflag = false; + if (words.size() == 4) { + okflag = + string_to_double(words[0], ip[0]) && + string_to_double(words[1], ip[1]) && + string_to_double(words[2], ip[2]) && + string_to_double(words[3], ip[3]); + } + + if (!okflag) { + nout << "-" << opt + << " requires four numbers separated by commas.\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ProgramBase::dispatch_color +// Access: Protected, Static +// Description: Standard dispatch function for an option that takes a +// color, either as r,g,b or as r,g,b,a. The data +// pointer is to an array of four doubles, e.g. a +// Colord. +//////////////////////////////////////////////////////////////////// +bool ProgramBase:: +dispatch_color(const string &opt, const string &arg, void *var) { + double *ip = (double *)var; + + vector_string words; + tokenize(arg, words, ","); + + bool okflag = false; + if (words.size() == 4) { + okflag = + string_to_double(words[0], ip[0]) && + string_to_double(words[1], ip[1]) && + string_to_double(words[2], ip[2]) && + string_to_double(words[3], ip[3]); + + } else if (words.size() == 3) { + okflag = + string_to_double(words[0], ip[0]) && + string_to_double(words[1], ip[1]) && + string_to_double(words[2], ip[2]); + ip[3] = 1.0; + } + + if (!okflag) { + nout << "-" << opt + << " requires three or four numbers separated by commas.\n"; return false; } diff --git a/pandatool/src/progbase/programBase.h b/pandatool/src/progbase/programBase.h index d9ba55ce41..e81cedf0a2 100644 --- a/pandatool/src/progbase/programBase.h +++ b/pandatool/src/progbase/programBase.h @@ -64,6 +64,10 @@ protected: static bool dispatch_int(const string &opt, const string &arg, void *var); static bool dispatch_int_pair(const string &opt, const string &arg, void *var); static bool dispatch_double(const string &opt, const string &arg, void *var); + static bool dispatch_double_pair(const string &opt, const string &arg, void *var); + static bool dispatch_double_triple(const string &opt, const string &arg, void *var); + static bool dispatch_double_quad(const string &opt, const string &arg, void *var); + static bool dispatch_color(const string &opt, const string &arg, void *var); static bool dispatch_string(const string &opt, const string &arg, void *var); static bool dispatch_filename(const string &opt, const string &arg, void *var); static bool dispatch_search_path(const string &opt, const string &arg, void *var);