Index: xc/extras/ttf2pt1/CHANGES.html =================================================================== RCS file: xc/extras/ttf2pt1/CHANGES.html diff -N xc/extras/ttf2pt1/CHANGES.html --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/CHANGES.html 13 Apr 2004 02:44:58 -0000 @@ -0,0 +1,805 @@ + + + +TTF2PT1 - CHANGES history + + + +

+TTF2PT1 - CHANGES history +

+ + + +

+3.4.4-SNAP-030526 +

+ + +New features: + + +Bug fixes: + + +

+3.4.3 -- December 2, 2002 +

+ + +New features: + + +Bug fixes: + + +

+3.4.2 -- August 30, 2002 +

+ + +New features: + + +Bug fixes: + + +

+3.4.1 -- June 13, 2002 +

+ + +New features: + + +Bug fixes: + + +

+3.4.0 -- November 24, 2001 +

+ + +New features: + + +Bug fixes: + + +

+3.3.5 -- September 12, 2001 +

+ + +Packaged by Sergey Babkin. +

+ +Bug fixes: +

+ +

+3.3.4 -- June 4, 2001 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bug fixes: + + +

+3.3.3 -- March 4, 2001 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bug fixes: + + +

+3.3.2 -- November 20, 2000 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bug fixes: + + +Other: + + +

+3.3.1 -- October 22, 2000 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bug fixes: + +

+ +

+3.3.0 -- September 22, 2000 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bug fixes: + + +

+3.22 -- May 23, 2000 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bug fixes: + + +

+3.21 -- March 1, 2000 +

+ + +Sergey Babkin: committed the changes by Petr Titera and +my bugfixes. +

+ +New features: +

+ +Bug fixes: + + + +

+3.2 -- January 15, 2000 +

+ + +Sergey Babkin: combined my changes with the changes by +Thomas Henlich. The result deserves a not-so-minor version +increase. +

+ +New features: +

+ +Bux fixes: + + + +

+3.13 -- October 18, 1999 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bux fixes: + + +

+3.12 -- October 2, 1999 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bux fixes: + + +

+3.11 -- May 24, 1999 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +

+3.1 -- March 28, 1999 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ + +

+3.0 -- March 6, 1999 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +

+3.0beta2 -- February 14, 1999 +

+ + +Packaged by Sergey Babkin. +

+ +New features: +

+ +Bux fixes: + + +

+3.0beta1 -- December 11, 1998 +

+ + +By Andrew Weeks. +

+ +New features: +

+ +Bux fixes: + + +

+3.0beta-afm -- December 5, 1998 +

+ + +By Thomas Henlich. +

+ +New features: +

+ +

+3.0beta -- November 15, 1998 +

+ + +By Sergey Babkin. +

+ +New features: +

+ +Bux fixes: + + +

+3.0alpha -- October 19, 1998 +

+ + +By Sergey Babkin. +

+ +New features: +

+ +Bux fixes: + + +

+June 22, 1998 (AKA 2.2) +

+ + +By Thomas Henlich. +

+ +Bux fixes: +

+ +

+February 13, 1998 +

+ + +By Mark Heath. +

+ +Bux fixes: +

+ +

+February 4, 1998 +

+ + +By Mark Heath. +

+ +Bux fixes: +

+ + +

+The older history seems to be lost. +

+ + +(S.B.: The story how we got the version numbers is rather funny. Initially +there were no version umbers, the releases were marked by dates. The version +from June 22 1998 untarred itself into a directory "ttf2pt1-22". When I +made my changes to it I assumed that this was the version number meaning +version 2.2. Since Mark asked me to send him a complete archive I supposed +that I have to bump the version number. And I bumped it to 3.0 because the +changes were rather extensive. Mark silently agreed and released the new +version as 3.0. And that's the end of the story about how we got this +Microsoft-like high version number.) + + + + Index: xc/extras/ttf2pt1/COPYRIGHT =================================================================== RCS file: xc/extras/ttf2pt1/COPYRIGHT diff -N xc/extras/ttf2pt1/COPYRIGHT --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/COPYRIGHT 13 Apr 2004 02:44:58 -0000 @@ -0,0 +1,87 @@ +The following copyright notice applies to all the files provided +in this distribution unless explicitly noted otherwise +(the most notable exception being t1asm.c). + + Copyright (c) 1997-2002 by the AUTHORS: + Andrew Weeks + Frank M. Siegert + Mark Heath + Thomas Henlich + Sergey Babkin , + Turgut Uyar + Rihardas Hepas + Szalay Tamas + Johan Vromans + Petr Titera + Lei Wang + Chen Xiangyang + Zvezdan Petkovic + Rigel + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the TTF2PT1 Project + and its contributors. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +For the approximate list of the AUTHORS' responsibilities see the +project history. + +Other contributions to the project are: + +Turgut Uyar + The Unicode translation table for the Turkish language. + +Rihardas Hepas + The Unicode translation table for the Baltic languages. + +Szalay Tamas + The Unicode translation table for the Central European languages. + +Johan Vromans + The RPM file. + +Petr Titera + The Unicode map format with names, the forced Unicode option. + +Frank M. Siegert + Port to Windows + +Lei Wang +Chen Xiangyang + Translation maps for Chinese fonts. + +Zvezdan Petkovic + The Unicode translation tables for the Cyrillic alphabet. + +Rigel + Generation of the dvips encoding files, modification to the Chinese maps. + +I. Lee Hetherington + The Type1 assembler (from the package 't1utils'), its full copyright + notice: + Copyright (c) 1992 by I. Lee Hetherington, all rights reserved. + Permission is hereby granted to use, modify, and distribute this program + for any purpose provided this copyright notice and the one below remain + intact. + Index: xc/extras/ttf2pt1/FONTS.hpux.html =================================================================== RCS file: xc/extras/ttf2pt1/FONTS.hpux.html diff -N xc/extras/ttf2pt1/FONTS.hpux.html --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/FONTS.hpux.html 13 Apr 2004 02:44:59 -0000 @@ -0,0 +1,197 @@ + + + +How to install new Type1 fonts on an HP-UX 10.20 machine + + + +Sergey A. Babkin +
+ +<babkin@bellatlantic.net> or <sab123@hotmail.com> +

+ + +

+How to install new Type1 fonts on an HP-UX 10.20 machine +

+ + +1. Add the font files to /usr/lib/X11/fonts/type1.st/typefaces. +

+ +2. Add the font descriptions to +/usr/lib/X11/fonts/type1.st/typefaces/fonts.scale. Run `mkfontdir' +in /usr/lib/X11/fonts/type1.st/typefaces. In the descriptions +you have to specify the font manufacturer as `misc', like: +

+ + +  -misc-courier-... + +

+ +3. Copy /usr/lib/X11/fonts/type1.st/typefaces/fonts.dir to +/usr/lib/X11/fonts/type1.st/licenses/STSYSTEM/DISPLAYS/fonts.dir. +Better yet, create a symbolic link. +

+ +4. For each font encoding you are going to use create a description +file in /usr/lib/X11/fonts/stadmin/type1/charsets. Of course, if you +are going to use the same fonts in several encodings, the best way +would be to create fair descriptions of charsets and really store +only one encoding in typefaces, all the others will be produced +automatically. That's not difficult at all. +But the simplest way is to just copy the file cp.iso8859-1 +to cp.<your-encoding-name>, like cp.koi8-r. +

+ +5. Restart you X server and/or font server. +

+ +

+What if you don't have the `root' privileges ? +

+ + +You still can run the font server and configure your X server +to get the fonts from it. +

+ +Further let's suppose that the name on which you are going +to run the font server is named `somehost'. Login to it +and configure the font server. +

+ +First, choose some unused port. Numbers around 9000 are a good +choice. Verify that this port is not used by somebody else +by entering +

+ +

+ netstat -naf inet |grep 9000 +
+ +and look what happens. If you get nothing, that's good, this +port is unused. If you get some lines of data, try abother port. +

+ +Go to you home directory $HOME and create some directory for +your font server, say, $HOME/fs. Copy the directory structure +of /usr/lib/X11/fonts/type1.st into $HOME/fs, so that in result +you get $HOME/fs/type1.st/<whatever was there>. Copy the directory +structure of /usr/lib/X11/fonts/stadmin/type1/charsets into $HOME/fs, +so that in result you get $HOME/fs/charsets/<whatever was there>. +Install the new fonts in these directorues as described above. +

+ +Then create the fontserver configuration file, say, $HOME/fs/xfs.cfg. +The sample contents (supposing that my $HOME is equal to /home/babkin) +is: +

+ + +


+ +# font server configuration file +
+# $XConsortium: config.cpp,v 1.7 91/08/22 11:39:59 rws Exp $ +
+ +
+rasterizers = /usr/lib/X11/fs/ufstrast.sl,/usr/lib/X11/fs/iforast.sl +
+ +
+clone-self = off +
+use-syslog = off +
+catalogue = /home/babkin/fs/type1.st +
+# in decipoints +
+default-point-size = 120 +
+default-resolutions = 100,100,75,75 +
+port=9000 +
+error-file=/home/babkin/fs/fs.err +
+
+ +

+ +Then create the script to start your font server, say, $HOME/fs/runme: +

+ + +


+ +TYPE1_CODEPAGE_DIR=$HOME/fs/charsets +
+export TYPE1_CODEPAGE_DIR +
+kill `ps -ef | grep $HOME/\[f\]s/xfs.cfg | awk '{print $2}'`; +
+nohup xfs -config $HOME/fs/xfs.cfg & +
+
+ +

+ +Don't forget to make $HOME/fs/runme executable. Then you can +execute it manually or from you .profile. +

+ +After you get your font server running, just execute the following +command (with proper host name and port number) in your X session +

+ +

+ xset fp+ tcp/somehost:9000 +
+ +to get the access to your private font server. You can add this +information to the configuration data of your X server or just +put it also into your .profile. In the latter case the best way +to do that would be like: +

+ + +


+ +... +
+$HOME/fs/runme +
+sleep 2 # give it some time to start +
+xset fp+ tcp/somehost:9000 +
+... +
+
+ +

+ + Index: xc/extras/ttf2pt1/FONTS.html =================================================================== RCS file: xc/extras/ttf2pt1/FONTS.html diff -N xc/extras/ttf2pt1/FONTS.html --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/FONTS.html 13 Apr 2004 02:44:59 -0000 @@ -0,0 +1,708 @@ + + + +The ttf2pt1 font installation guide + + + +Sergey A. Babkin +
+ +<babkin@bellatlantic.net> or <sab123@hotmail.com> +

+ + + + +

+THE FONT INSTALLATION GUIDE +
+for the TTF to Type1 converter and fonts generated by it +

+ + +There is historically a number of problems with the support of the 8-bit +character encodings. This installation guide pays a lot of attention +to the 8-bit issues, because these issues are responsible for the +most of troubles during the installation of fonts. But they are +not the only things covered in this guide, so it's worth reading +even if all you need is plain ASCII. For convenience of reading +I have marked the paragraphs dealing solely with 8-bit problems +with characters *8*. +

+ +To simplify this installation the distribution package of the +converter contains a number of scripts written in shell and +Perl. So, to run them you will need a shell interpreter (Bourne-shell, +POSIX-shell, Korn-shell are OK, ba-shell is probably also OK but not +tested yet). The Perl scripts were tested with Perl5 but probably +should work with Perl4 too. All the scripts are located in the +`scripts' subdirectory. +

+ +This guide considers the following issues of installation of the +fonts: +

+ + +

+

+ + +

+X11 +

+ + + + + +To simplify the conversion a set of scripts is provided with ttf2pt1. +They are collected in the `scripts' subdirectory. +

+ + +`Convert' is the master conversion script provided with ttf2pt1. +When installed into a public directory it's named `ttf2pt1_convert' +to avoid name collisions with the other programs. +

+ + +It's called as: +

+ + + + +

+ convert [config-file] +
+ + +If the configuration file is not specified as an argument then the file +`convert.cfg' in the current directory is used. This file contains +a set of configuration variables. The distribution contains a sample file +file `convert.cfg.sample'. Please copy it to `convert.cfg', +look inside it and change the configuration variables. The more stable +configuration variables, such as the path names of the scripts and +encoding files are located in `convert' itself, they are +automatically updated when installing ttf2pt1. +

+ +Put all the TTF fonts you want to convert into some directory (this +may be just the directory that already contains all the Windows +fonts on a mounted FAT filesystem). If you have fonts in different +source encoding then put the fonts in each of the encodings +into a separate directory. Up to 10 source directories are +supported. If you (in a rather unlikely case) have more source +directories then you can make two separate runs of the converter, +converting up to 10 directories at a time. +

+ +The variables in the configuration file are: +

+ + + +SRCDIRS - the list of directories (with absolute paths) with + TTF fonts. Each line contains at least 3 fields: the name of the directory, + the language of the fonts in it (if you have fonts for different + languages you have to put them into the separate directories) and the + encoding of the fonts. Again, if you have some of the TTF typefaces in + one encoding, and some in another (say, CP-1251 and KOI-8), you have + to put them into the separate source directories. Some lines may contain + 4 fields. Then the fourth field is the name of the external map to + convert the Unicode fonts into the desirable encoding. This map is + used instead of the built-in map for the specified language. +

+ +*8* +An interesting thing is that some languages have more than one +widely used character encodings. For example, the widely used +encodings for Russian are IBM CP-866 (MS-DOS and Unix), KOI-8 +(Unix and VAX, also the standard Internet encoding), IBM CP-1251 (MS Windows). +That's why I have provided the means to generate the converted fonts +in more than one encoding. See the file encodings/README for +details about the encoding tables. Actually, if you plan to use +these fonts with Netscape Navigator better use the aliases +cp-866 instead of ibm-866 and windows-1251 instead of ibm-1251 +because that's what Netscape wants. +

+ + +DSTDIR - directory for the resulting Type1 fonts. Be careful! + This directory gets completely wiped out before conversion, + so don't use any already existing directory for this purpose. +

+ + +DSTENC{language} - the list of encodings in which the destination + fonts will be generated for each language. Each font of that + language will be generated in each of the specified + encodings. If you don't want any translation, just specify both + SRCENC and DSTENC as iso8859-1 (or if you want any other encoding + specified in the fonts.dir, copy the description of 8859-1 with + new name and use this new name for SRCENC and DSTENC). +

+ + +FOUNDRY - the foundry name to be used in the fonts.dir file. I have + set it to `fromttf' to avoid name conflicts with any existing font for + sure. But this foundry name is not registered in X11 standards and + if you want to get the full standard compliance or have a font server + that enforces such a compliance, use `misc'. +

+ + +The next few parameters control the general behavior of the converter. +They default values are set to something reasonable. +

+ + + +CORRECTWIDTH - if the value is set to YES then use the + converter option -w, otherwise don't use it. See the description of + this option in the README file. +

+ + +REMOVET1A - if the value is set to YES then after + conversion remove the un-encoded .t1a font files and the + intermediate .xpfa font metric files. +

+ + +INSTALLFONTMAP - a Ghostscript parameter, if the value is set to + YES then install the entries for the new fonts + right into the main Fontmap file. Otherwise just leave + the file Fontmap.ttf in the Ghostscript configuration + directory. +

+ + +HINTSUBST - if the value is set to YES use the option + -H, otherwise don't use it. This option enables the + hint substitution technique. If you have not installed the X11 patch + described above, use this option with great caution. See further + description of this option in the README file. +

+ + +ENFORCEISO - if the value is set to YES then + disguise the resulting fonts as the fonts in ISOLatin1 encoding. Historically + this was neccessary due to the way the installer scripts created the + X11 font configuration files. It is not neccessary any more for this + purpose. But if you plan to use these fonts with some other application + that expects ISOLatin1 encoding then better enable this option. +

+ + +ALLGLYPHS - if the value is set to YES then + include all the glyphs from the source fonts into the resulting fonts, even + if these glyphs are inaccessible. If it's set to NO then + include only the glyphs which have codes assigned to them. The glyphs + without codes can not be used directly. But some clever programs, + such as the Type 1 library from XFree86 3.9 and higher can change + the encoding on the fly and use another set of glyphs. If you have not + installed the X11 patch described above, use this option with great + caution. See further description of the option option -a in the + README file. +

+ + +GENUID - if the value is set to YES then use + the option -uA of the converter to generate UniqueIDs for + the converted fonts. The standard X11 Type 1 library does not use + this ID, so it may only be neccessary for the other applications. + The script is clever enough to generate different UniqueID for the + same font converted to multiple encodings. Also after conversion it + checks all the fonts generacted during the session for duplicated + UniqueID and shows those. Still, this does not quarantee that these + UniqueIDs won't overlap with some other fonts. The UniqueIDs are + generated as hash values from the font names, so it's guaranteed + that if the `convert' script runs multiple times it will + generate the same UniqueIDs during each run. See further description + of this option in the README file. +

+ + +GENUID - if the value is set to YES then create + the .pfb files, otherwise the .pfa files. The .pfb + files are more compact but contain binary data, so you may experience some + troubles when transferring them through the network. +

+ + +The following parameters are used to locate the other scripts and +configuration files. By default the scripts do a bit of guessing for them: +they search in the ttf2pt1 installation directory if ttf2pt1 +was installed or otherwise suppose that you are running `convert' with +`scripts' subdirectory being the current directory. +

+ + + +ENCDIR - directory containing the descriptions of encodings +
+ +MAPDIR - directory containing the external map files +

+ + +Besides that a few parameters are built into the `convert' script itself. +You probably won't need to change them: +

+ + + +T1ASM, TTF2PT1, TRANS, T1FDIR, FORCEISO - paths to the other script +

+ + +Also there are a few parameters controlling the installation of +fonts for Ghostscript. Please look at their description in the +Ghostscript section of documentation or in the ttf2pt1_x2gs(1) +manual page before running `convert'. If these parameters are +set, `convert' will call the `x2gs' script automatically +to install the newly converted fonts in Ghostscript. +

+ +After creating the configuration file run the `convert' script. Look at +the result and the log file in DSTDIR. +

+ +Add the directory with newly converted fonts to the configuration +of X server or font server. For most of the systems this step is +very straightforward. For HP-UX it's rather tricky and poorly +documented, so the file FONTS.hpux gives a short description. +

+ +If you don't have the privileges of the root user, you still can +configure your private font server. Just use some non-standard +port number (see FONTS.hpux for an example, exept that you won't +need all the HP-related stuff on any other system). +

+ + +

+Known Problems +

+ + + + +
    +
  • One catch is that the X11 Type 1 font library has a rather low limit + on the font size. Because of this the fonts with more complicated + outlines and the enabled hint substitution may not fit into + this limit. The same applies to the fonts with very complicated + outlines or with very many glyphs (especially the fonts with + over 256 glyphs). So you will need to excercise caution with + these options if you plan using these fonts with X11. Some vendors + such as HP provide the Type 1 implementation licensed from Adobe + which should have no such problem. +

    + + But there is a solution even for the generic X11. A patch located + in the subdirectory `app/X11' fixes this problem as well + as some other minor problems. Its description is provided in + app/X11/README. +

    + + To fix the X11 font library, you have to get the X11 sources. I + can recommend the ftp sites of the XFree86 project ftp://ftp.xfree86.org + or of the Open Group ftp://ftp.x.org. This patch was made on the sources + of XFree86 so you may have better success with applying it to the + XFree86 distribution. After you have got the sources, make sure + that you can compile them. Then apply the patch as described. + Make sure that it was applied properly. Compile the sources again + (actually, you need only the fonts library, the fonts server, and + possibly the X server). It would be prudent now to save your old + font library, font server and, possibly, X server. Then install + the new recently compiled versions of these files. Of course, + if you know someone who already has compiled these files for the + same OS as yours, you can just copy the binary fles from him. +

    + + Alas, building the X11 system from the source code is not the + easiest thing in the world and if you have no experience it + can be quite difficult. In this case just avoid the aforementioned + features or check each converted font to make sure that it + works properly. +

    + +

  • The Type1 font library from the standard X11 distribution + does not work on HP-UX (at least, up to 10.01). The font server + supplied with HP-UX up to 10.01 is also broken. Starting from + HP-UX 10.20 (I don't know about 10.10) they supply a proprietary font + library and the converted fonts work fine with it, provided that + they are configured properly (see the file FONTS.hpux). +

    + +

  • The fonts.scale files created by the older versions of the + ttf2pt1 installation program (up to release 3.1) have conflicted + with the language definitions of the Xfsft font server and + parts of it included into XFree86. To overcome this incompatibility + the never versions creats the fonts.scale file describing all the + fonts as belonging to the adobe-fontspecific encoding and + the fonts.alias file with the proper names. The drawback of + this solution is that xlsfonts gives the list of twice more + fonts. But as a side effect the option ENFORCEISO in + `convert.cfg' is not required for X11 any more. +

    + +

  • The conversion script has no support for Eastern multi-plane fonts. + Contribution of such a support would be welcome. +

    +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Ghostscript +

+ + + + + + +The fonts generated with ttf2pt1 work fine with Ghostscript by +themselves. The script `x2gs' (or `ttf2pt1_x2gs' when installed +into a public directory, to avoid name conflicts with other +programs) links the font files from the X11 direcotry into the Ghostscript +directory and automatically creates the description file (Fontmap) +in Ghostscript format. + + +It's called as: +

+ + + + +

+ x2gs [config-file] +
+ + +If the configuration file is not specified as an argument then the file +`convert.cfg' in the current directory is used, just like the +`convert' script does. Indeed, this configuration file is used for +both scripts. +

+ +The Ghostscript-related parameters in the configuration file are: +

+ +DSTDIR - the X11 font directory used by `x2gs' as the + source of the fonts. This parameter is common with the X11 + configuration. +

+ +GSDIR - the base directory of Ghostsript. If this + parameter is set to an empty string then `convert' won't + call `x2gs'. So if you want to get only the X11 fonts + installed then set this parameter to an empty string. This + directory may vary on various system, so please check your + system and set this value accordingly before running the script. +

+ +GSFONTDIR - the font directory of Ghostscript. In the standard + Ghostscript installation it's a subdirectory of GSDIR + but some systems may use completely different directories. +

+ +GSCONFDIR - the configuration subdirectory of Ghostscript + that contains the Fontmap file. +

+ +INSTALLFONTMAP - if the value is set to YES then + install the entries for the new fonts right into the main + Fontmap file. Otherwise just leave the file Fontmap.ttf + in the Ghostscript configuration directory. +

+ + +After preparing the configuration file run the script. It symbolicaly links +all the font files and creates the description file Fontmap.ttf in +GSCONDFIR. After that there are two choices. +

+ +If the option INSTALLFONTMAP was set to YES then +the font descriptions are also automatically installed into the +master Fontmap file. The script is clever enough to +detect if it was run multiple times with the same directories +and if so it replaces the old Fontmap entries with +the new ones instead of just accumulating all of them. You +may also run it multiple times for multiple X11 directories +and all the results will be properly collected in the Fontmap. +But it's your responsibility to watch that the names of the +font files don't overlap. If the X11 font directory gets +renamed then you have to remove its font entries from the +Fontmap and only after that re-run `x2gs' +for the new directory. +

+ +On the other hand if the option INSTALLFONTMAP was set to +NO then go to the GSCONFDIR directory and insert the +contents of Fontmap.ttf into the Fontmap file +manually. This step may be left manual to make the installation +a little bit more safe. +

+ +After that you may also want to redefine some of the aliases in +Fontmap to refer to the newly installed fonts. +But the redefinition of the aliases may be dangerous if the width of +characters in the new font will be different from the old font. +Alas, there is no visible solution of this problem yet. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+MS Windows +

+ + +Ttf2pt1 can be built on Windows either with native compiler or in +POSIX emulation mode. +

+ +Native MS Windows compilers require a different way to build the converter +instead of the Makefile (their make programs commonly are quite weird +and limited in capabilities). An example of batch file winbuild.bat +is provided for MS Visual C/C++. Probably it can be easily adapted for other +32-bit Windows and DOS compilers. The important part is to define the +preprocessor symbol WINDOWS during compilation. +

+ +Cygnus make almost supports full Makefiles but not quite. Seems +like its POSIX support is also of the same quality "almost but not quite". +So another command file cygbuild.sh is provided for Cygnus GNU C, also +with the preprocessor symbol WINDOWS defined. It is intended to be run from +the Cygnus BASH shell. To run the programs produced by the Cygnus compiler +the Cygnus library file CYGWIN1.DLL should be copied first into +C:\WINDOWS. +

+ +To run the accompanying scripts Perl for Windows will be required as well as +other tools from the Cygnus set. +

+ +The Windows support was not particularly tested, so in case of problems with +building or running the converter please let us know. +

+ +The pre-built code (possibly of an older version) of ttf2pt1 for MS Windows is +available from the GnuWin32 project from + +http://gnuwin32.sourceforge.net/packages/ttf2pt1.htm +

+ + +

+Netscape Navigator/Communicator +

+ + +Basically, the biggest problem with Netscape Navigator is that +it has built-in fixed PostScript font names and built-in fixed +glyph tables for them. Oh, no, that's two! Let's start over: +basically the two biggest problems of Netscape Navigator are +that (one)it has built-in fixed PostScript font names and (two) +built-in fixed glyph tables for them and (three) it always +assumes that the fonts have ISOLatin1 encoding. OK, let's +start over again: basically the three biggest problems of Netscape +Navigator are that (one) it has built-in fixed PostScript font names, +(two) built-in fixed glyph tables for them and (three) it always +assumes that the fonts have ISOLatin1 encoding and (four) it +does not remember the scaled font size between the sessions. +You did not expect such a Spanish Inquisition, did you ? (*) +

+ +Luckily, we have solutions for all of these problems. They are +located in the subdirectory `app/netscape' and described +in app/netscape/README. +

+ + +  -------
+  *) See Monty Python's Flying Circus, episode 15 +

+ +*8* +

+Netscape and cyrillic fonts
+ +(courtesy of Zvezdan Petkovic) +

+ +If you use TrueType fonts in your X, as I do, and you always get +KOI8-R encoded pages, then your Netscape does not recognise windows-1251 +encoding. Microsoft TrueType fonts simply declare all encodings they +can support including KOI8-R. For some reason, KOI8-R always wins over +ISO-8859-5 in Netscape under X. If you are reading other cyrillic +languages besides Russian, you might want to either erase KOI8-R entries +from the fonts.dir and fonts.scale files, or alternatively fix Netscape. +I put this line in my .Xdefaults. +

+ +

+ Netscape*documentFonts.charset*koi8-r: iso-8859-5 +
+

+ +Notice that you can still read Russian sites without trouble because +Netscape translates KOI8-R to ISO-8859-5 on the fly. I read both Russian +and Serbian sites with no trouble. +

+ +Note: If anybody knows the way to tell Netscape under Unix how to +recognise {windows,ibm,cp}-1251 encoded fonts, I'd like to hear about that. +

+ + +

+Linux RPM package +

+ + +The spec file for the creation of a Linux RPM package is located in +app/RPM. It has been contributed by Johan Vromans. When +make all is ran in the main directory it among the other +things creates the version of itself adapted to Linux in app/RPM, +you may want to copy that version back to the main directory. +

+ +Warning: Please note that the install section is incomplete, and +the installed scripts won't work until the paths inside them +are corrected. +

+ + +

+FrameMaker +

+ + +The fonts and AFM files generated by the version 3.2 and higher +should work with Framemaker without problems. The AFM files +generated by the previous versions of the converter require a +line added to them: +

+ +  EncodingScheme FontSpecific +

+ +And the underscores in the font names of the font and AFM files +generated by the older versions may need to be changed to dashes. +

+ +NOTE by Jason Baietto: Ignore the directions in the Frame on-line docs +that say to put a "serverdict begin 0 exitserver" line in the pfa files. +Doing this caused both my printer and ghostscript to choke on the resulting +output from FrameMaker, so I would not advise doing this (though your +mileage may vary). +

+ + +

+StarOffice +

+ + +StarOffice 5.1x has been reported to crash if the .afm file contains +spaces in the values of such statements as Version, Weight etc. +These spaces are permitted by the Adobe spec, so this is a problem of +StarOffice. The easiest way to fix these .afm files for StarOffice +is to remove spaces in these strings or remove these strings (in case if +they are optional) at all. This can be done automatically with a sed +script. It seems that StarOffice 5.2 has this problem fixed, so we decided to +spend no efforts on providing workarounds for 5.1 with ttf2pt1. +

+ + + Index: xc/extras/ttf2pt1/Makefile =================================================================== RCS file: xc/extras/ttf2pt1/Makefile diff -N xc/extras/ttf2pt1/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/Makefile 13 Apr 2004 02:44:59 -0000 @@ -0,0 +1,279 @@ + +# This file should be configured before running `make'. +# Uncomment or change the values that are relevant for your OS. + +# The preferred C compiler (by default use the OS-specific default value). +# For BSD/OS, FreeBSD, Linux (all flavors), NetBSD, OpenBSD the default +# compiler is GNU C. +# (Note please the politically correct ordering by alphabet ! :-) +# +# Use GNU C even if it's not the default compiler +# +#CC=gcc +# +# Use the standard ANSI C compiler on HP-UX even if it's not default +# +#CC=c89 + +# +# The system-dependent flags for the C compiler +# +# Default + +CFLAGS_SYS= -O + +# For GNU C +# +#CFLAGS_SYS= -O2 +# +# For GNU C with long options support library (Linux etc.) +# +#CFLAGS_SYS= -O2 -D_GNU_SOURCE +# +# For GNU C on HP-UX/PA-RISC 1.1 +# +#CFLAGS_SYS= -O2 -Wa,-w +# +# For the standard ANSI C on HP-UX +# +#CFLAGS_SYS= +O2 -D_HPUX_SOURCE + +# +# The system-dependent libraries +# +# Defalut (for the BSD-style OSes) + +LIBS_SYS= -lm + +# For SystemV (such as SCO, UnixWare, Solaris, but _NOT_ Linux or HP-UX) +# +#LIBS_SYS= -lm -lsocket + +# +# The flags for C compiler for the FreeType-2 library (disabled by default). +# This WON'T BUILD with FT2-beta8, use the FreeType release 2.0.4 +# http://download.sourceforge.net/freetype/freetype-2.0.4.tar.gz + +CFLAGS_FT= + +# To enable use of the FreeType-2 library +# (if the include and lib directory do not match your installation, +# modify them), also uncomment LIBS_FT +# +#CFLAGS_FT = -DUSE_FREETYPE -I/usr/local/include/freetype2 -I/usr/local/include + +# +# The FreeType-2 library flags (disabled by default) + +LIBS_FT= + +# To enable use of the FreeType-2 library +# (if the include and lib directory do not match your installation, +# modify them), also uncomment CFLAGS_FT +# +#LIBS_FT= -L/usr/local/lib -lfreetype + +# +# The flags for C compiler for the Autotrace library (disabled by default). +# USE OF THIS FEATURE IS STRONGLY DISCOURAGED, THE BUILT-IN TRACING +# (AKA VECTORIZATION) PROVIDES MUCH BETTER RESULTS. +# The tested version is 0.29a (and the fonts produced with it are +# absolutely not usable). +# http://download.sourceforge.net/autotrace/autotrace-0.29.tar.gz + +CFLAGS_AT= + +# To enable use of the Autotrace library +# (if the include and lib directory do not match your installation, +# modify them), also uncomment LIBS_AT +# +#CFLAGS_AT = -DUSE_AUTOTRACE -I/usr/local/include + +# +# The Autotrace library flags (disabled by default) + +LIBS_AT= + +# To enable use of the Autotrace library +# (if the include and lib directory do not match your installation, +# modify them), also uncomment CFLAGS_AT +# +#LIBS_AT= -L/usr/local/lib -lautotrace + +# +# Preference of front-ends if multiple parsers match a file +# (by default the build-in front-end takes preference over FreeType) + +CFLAGS_PREF= + +# To prefer FreeType (if enabled): +# +#CFLAGS_PREF= -DPREFER_FREETYPE + +# Uncomment the second line to not compile t1asm into ttf2pt1 +CFLAGS_EXTT1ASM= +#CFLAGS_EXTT1ASM= -DEXTERNAL_T1ASM + +CFLAGS= $(CFLAGS_SYS) $(CFLAGS_FT) $(CFLAGS_AT) $(CFLAGS_PREF) +LIBS= $(LIBS_SYS) $(LIBS_FT) $(LIBS_AT) + +# Installation-related stuff +# +# The base dir for installation and subdirs in it +INSTDIR = /usr/local +# for binaries +BINDIR = $(INSTDIR)/bin +# for binaries of little general interest +LIBXDIR = $(INSTDIR)/libexec/ttf2pt1 +# for scripts, maps/encodings etc. +SHAREDIR = $(INSTDIR)/share/ttf2pt1 +MANDIR = $(INSTDIR)/man + +# owner and group of installed files +OWNER = root +GROUP = bin + +# After you have configured the Makefile, comment out the following +# definition: +warning: docs + @echo >&2 + @echo " You have to configure the Makefile before running make!" >&2 + @echo "(or if you are lazy and hope that it will work as is run \`make all')">&2 + @echo >&2 + +DOCS=CHANGES README FONTS FONTS.hpux encodings/README other/README \ + app/X11/README app/netscape/README app/TeX/README + +SUBDIRS = app encodings maps scripts other +TXTFILES = README* FONTS* CHANGES* COPYRIGHT + +MANS1=ttf2pt1.1 ttf2pt1_convert.1 ttf2pt1_x2gs.1 +MANS=$(MANS1) $(MANS5) + +all: t1asm ttf2pt1 docs mans rpm + +docs: $(DOCS) + +mans: $(MANS) + +clean: + rm -f t1asm ttf2pt1 *.o app/RPM/Makefile app/RPM/*.spec *.core core.* core + ( cd other && make clean; ) + ( cd app/netscape && make clean; ) + +veryclean: clean + rm -f $(DOCS) $(MANS) + +rpm: app/RPM/Makefile app/RPM/ttf2pt1.spec + +ttf2pt1.1: README.html + scripts/html2man . . app/RPM/Makefile + +app/RPM/ttf2pt1.spec: app/RPM/ttf2pt1.spec.src version.h + sed 's/^Version:.*/Version: '`grep TTF2PT1_VERSION version.h| cut -d\" -f2`'/' $@ + +t1asm: t1asm.c + $(CC) $(CFLAGS) -o t1asm -DSTANDALONE t1asm.c $(LIBS) + +ttf2pt1.o: ttf2pt1.c ttf.h pt1.h global.h version.h + $(CC) $(CFLAGS) -c ttf2pt1.c + +pt1.o: pt1.c ttf.h pt1.h global.h + $(CC) $(CFLAGS) -c pt1.c + +ttf.o: ttf.c ttf.h pt1.h global.h + $(CC) $(CFLAGS) -c ttf.c + +ft.o: ft.c pt1.h global.h + $(CC) $(CFLAGS) -c ft.c + +bdf.o: bdf.c pt1.h global.h + $(CC) $(CFLAGS) -c bdf.c + +bitmap.o: bitmap.c pt1.h global.h + $(CC) $(CFLAGS) -c bitmap.c + +runt1asm.o: runt1asm.c global.h + $(CC) $(CFLAGS) $(CFLAGS_EXTT1ASM) -c runt1asm.c + +ttf2pt1: ttf2pt1.o pt1.o runt1asm.o ttf.o ft.o bdf.o bitmap.o + $(CC) $(CFLAGS) -o ttf2pt1 ttf2pt1.o pt1.o runt1asm.o ttf.o ft.o bdf.o bitmap.o $(LIBS) + +CHANGES: CHANGES.html + scripts/unhtml CHANGES + +README: README.html + scripts/unhtml README + +encodings/README: encodings/README.html + scripts/unhtml encodings/README + +other/README: other/README.html + scripts/unhtml other/README + +app/X11/README: app/X11/README.html + scripts/unhtml app/X11/README + +app/netscape/README: app/netscape/README.html + scripts/unhtml app/netscape/README + +app/TeX/README: app/TeX/README.html + scripts/unhtml app/TeX/README + +FONTS: FONTS.html + scripts/unhtml FONTS + +FONTS.hpux: FONTS.hpux.html + scripts/unhtml FONTS.hpux + +install: all + scripts/inst_dir $(BINDIR) $(OWNER) $(GROUP) 0755 + scripts/inst_dir $(LIBXDIR) $(OWNER) $(GROUP) 0755 + scripts/inst_dir $(SHAREDIR) $(OWNER) $(GROUP) 0755 + scripts/inst_dir $(MANDIR)/man1 $(OWNER) $(GROUP) 0755 + scripts/inst_dir $(MANDIR)/man5 $(OWNER) $(GROUP) 0755 + cp -R $(TXTFILES) $(SUBDIRS) $(SHAREDIR) + chown -R $(OWNER) $(SHAREDIR) + chgrp -R $(GROUP) $(SHAREDIR) + chmod -R go-w $(SHAREDIR) + scripts/inst_file ttf2pt1 $(BINDIR)/ttf2pt1 $(OWNER) $(GROUP) 0755 + [ -f $(BINDIR)/t1asm ] || scripts/inst_file t1asm $(LIBXDIR)/t1asm $(OWNER) $(GROUP) 0755 + sed 's|^TTF2PT1_BINDIR=$$|TTF2PT1_BINDIR=$(BINDIR)|;\ + s|^TTF2PT1_LIBXDIR=$$|TTF2PT1_LIBXDIR=$(LIBXDIR)|;\ + s|^TTF2PT1_SHAREDIR=$$|TTF2PT1_SHAREDIR=$(SHAREDIR)|;' cvt.tmp + scripts/inst_file cvt.tmp $(BINDIR)/ttf2pt1_convert $(OWNER) $(GROUP) 0755 + scripts/inst_file cvt.tmp $(SHAREDIR)/scripts/convert $(OWNER) $(GROUP) 0755 + rm cvt.tmp + scripts/inst_file scripts/x2gs $(BINDIR)/ttf2pt1_x2gs $(OWNER) $(GROUP) 0755 + for i in $(MANS1); do { \ + sed 's|TTF2PT1_BINDIR|$(BINDIR)|;\ + s|TTF2PT1_LIBXDIR|$(LIBXDIR)|;\ + s|TTF2PT1_SHAREDIR|$(SHAREDIR)|;' <$$i >$(MANDIR)/man1/$$i \ + && chown $(OWNER) $(MANDIR)/man1/$$i \ + && chgrp $(GROUP) $(MANDIR)/man1/$$i \ + && chmod 0644 $(MANDIR)/man1/$$i \ + || exit 1; \ + } done + +uninstall: + rm -f $(BINDIR)/ttf2pt1 $(BINDIR)/ttf2pt1_convert $(BINDIR)/ttf2pt1_x2gs + rm -rf $(LIBXDIR) + rm -rf $(SHAREDIR) + for i in $(MANS1); do { \ + rm -f $(MANDIR)/man1/$$i $(MANDIR)/man1/$$i.gz; \ + } done + + +# targets for automatic generation of releases and snapshots + +snapshot: + scripts/mkrel snapshot + +release: + scripts/mkrel release Index: xc/extras/ttf2pt1/README.FIRST =================================================================== RCS file: xc/extras/ttf2pt1/README.FIRST diff -N xc/extras/ttf2pt1/README.FIRST --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/README.FIRST 13 Apr 2004 02:44:59 -0000 @@ -0,0 +1,4 @@ +To get the plain-text README and installation guides run: + + make docs + Index: xc/extras/ttf2pt1/README.html =================================================================== RCS file: xc/extras/ttf2pt1/README.html diff -N xc/extras/ttf2pt1/README.html --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/README.html 13 Apr 2004 02:45:00 -0000 @@ -0,0 +1,1182 @@ + + + +TTF2PT1 - A True Type to PostScript Type 1 Converter + + + + +

+ +TTF2PT1 - A True Type to PostScript Type 1 Font Converter + +

+ + + +[ +
+ + Based on ttf2pfa by Andrew Weeks, and help from Frank Siegert. +
+ Modification by Mark Heath. +
+ Further modification by Sergey Babkin. +
+ The Type1 assembler by I. Lee Hetherington with modifications by + Kai-Uwe Herbing. + +
+] +
+

+ +Ever wanted to install a particular font on your XServer but only could find +the font you are after in True Type Format? +

+ +Ever asked comp.fonts for a True Type to Type 1 converter and got a List +of Commercial software that doesn't run on your Operating System? +

+ +Well, this program should be the answer. This program is written in C (so it +should be portable) and therefore should run on any OS. The only limitation +is that the program requires some method of converting Big endian integers into +local host integers so the network functions ntohs and ntohl are used. These +can be replaced by macros if your platform doesn't have them. +Of course the target platform requires a C compiler and command line ability. +

+ + +Ttf2pt1 is a font converter from the True Type format (and some other formats +supported by the FreeType library as well) to the Adobe Type1 format. +

+ +The versions 3.0 and later got rather extensive post-processing algorithm that +brings the converted fonts to the requirements of the Type1 standard, tries to +correct the rounding errors introduced during conversions and some simple +kinds of bugs that are typical for the public domain TTF fonts. It +also generates the hints that enable much better rendering of fonts in +small sizes that are typical for the computer displays. But everything +has its price, and some of the optimizations may not work well for certain +fonts. That's why the options were added to the converter, to control +the performed optimizations. +

+ + +The converter is simple to run, just: +

+ + +

+ ttf2pt1 [-options] ttffont.ttf [Fontname] +
+or +
+ ttf2pt1 [-options] ttffont.ttf - +
+ +

+ + +The first variant creates the file Fontname.pfa (or Fontname.pfb if the +option '-b' was used) with the converted font and Fontname.afm with the +font metrics, the second one prints the font or another file (if the option +'-G' was used) on the standard output from where it can be immediately +piped through some filter. If no Fontname is specified for the first +variant, the name is generated from ttffont by replacing the .ttf +filename suffix. +

+ +Most of the time no options are neccessary (with a possible exception +of '-e'). But if there are some troubles with the resulting font, they +may be used to control the conversion. +The options are: +

+ + + +-a - Include all the glyphs from the source file into the converted + file. If this option is not specified then only the glyphs that have + been assigned some encoding are included, because the rest of glyphs + would be inaccessible anyway and would only consume the disk space. + But some applications are clever enough to change the encoding on + the fly and thus use the other glyphs, in this case they could + benefit from using this option. But there is a catch: the X11 library + has rather low limit for the font size. Including more glyphs increases + the file size and thus increases the chance of hitting this limit. + See app/X11/README for the description of a + patch to X11 which fixes this problem. +

+ + +-b - Encode the resulting font to produce a ready .pfb file. +

+ + +-d suboptions - Debugging options. The suboptions are: +

+ +

+ a - Print out the absolute coordinates of dots in outlines. Such + a font can not be used by any program (that's why this option is + incompatible with '-e') but it has proven to be a valuable debuging + information. +

+ + r - Do not reverse the direction of outlines. The TTF fonts have + the standard direction of outlines opposite to the Type1 fonts. So + they should be reversed during proper conversion. This option + may be used for debugging or to handle a TTF font with wrong + direction of outlines (possibly, converted in a broken way from + a Type1 font). The first signs of the wrong direction are the + letters like "P" or "B" without the unpainted "holes" inside. +

+

+ + +-e - Assemble the resulting font to produce a ready .pfa file. + + [ S.B.: Personally I don't think that this option is particularly useful. + The same result may be achieved by piping the unassembled data + through t1asm, the Type 1 assembler. And, anyways, it's good to + have the t1utils package handy. But Mark and many users think that + this functionality is good and it took not much time to add this option. ] + +

+ + +-F - Force the Unicode encoding: any type of MS encoding specified + in the font is ignored and the font is treated like it has Unicode + encoding. WARNING: this option is intended for buggy fonts + which actually are in Unicode but are marked as something else. The + effect on the other fonts is unpredictable. +

+ + +-G suboptions - File generation options. The suboptions may be lowercase + or uppercase, the lowercase ones disable the generation of particular + files, the corresponding uppercase suboptions enable the generation of the + same kind of files. If the result of ttf2pt1 is requested to be printed on + the standard output, the last enabling suboption of -G determines + which file will be written to the standard output and the rest of files + will be discarded. For example, -G A will request the AFM file. + The suboptions to disable/enable the generation of the files are: +

+ +

+ f/F - The font file. Depending on the other options this file + will have one of the suffixes .t1a, .pfa or .pfb. If the conversion result + is requested on the standard output ('-' is used as the output file name) + then the font file will also be written there by default, if not overwritten + by another suboption of -G. + Default: enabled +

+ + a/A - The Adobe font metrics file (.afm). + Default: enabled +

+ + e/E - The dvips encoding file (.enc). + Default: disabled +

+ +

+ + +-l language[+argument] - Extract the fonts for the specified language from a + multi-language Unicode font. If this option is not used the converter + tries to guess the language by the values of the shell variable LANG. + If it is not able to guess the language by LANG it tries all the + languages in the order they are listed. +

+ + After the plus sign an optional argument for the language extractor + may be specified. The format of the argument is absolutely up to + the particular language converter. The primary purpose of the + argument is to support selection of planes for the multi-plane + Eastern encodings but it can also be used in any other way. The + language extractor may decide to add the plane name in some form + to the name of the resulting font. None of the currently supported + languages make any use of the argument yet. +

+ + As of now the following languages are supported: +
+   latin1 - for all the languages using the Latin-1 encoding +
+   latin2 - for the Central European languages +
+   latin4 - for the Baltic languages +
+   latin5 - for the Turkish language +
+   cyrillic - for the languages with Cyrillic alphabet +
+   russian - historic synonym for cyrillic +
+   bulgarian - historic synonym for cyrillic +
+   adobestd - for the AdobeStandard encoding used by TeX +
+   plane+argument - to select one plane from a multi-byte encoding +

+ + The argument of the "plane" language may be in one of three forms: +

+   plane+pid=<pid>,eid=<eid> +
+   plane+pid=<pid>,eid=<eid>,<plane_number> +
+   plane+<plane_number> +

+ + Pid (TTF platform id) and eid (TTF encoding id) select a particular + TTF encoding table in the original font. They are specified as decimal + numbers. If this particular encoding table is not present in the font + file then the conversion fails. The native ("ttf") front-end parser supports + only pid=3 (Windows platform), the FreeType-based ("ft") front-end supports + any platform. If pid/eid is not specified then the TTF encoding table is + determined as usual: Unicode encoding if it's first or an 8-bit encoding + if not (and for an 8-bit encoding the plane number is silently ignored). + To prevent the converter from falling back to an 8-bit encoding, specify + the Unicode pid/eid value explicitly. +

+ + Plane_number is a hexadecimal (if starts with "0x") or decimal number. + It gives the values of upper bytes for which 256 characters will be + selected. If not specified, defaults to 0. It is also used as a font + name suffix (the leading "0x" is not included into the suffix). +

+ + + NOTE: + + It seems that many Eastern fonts use features of the TTF format that are + not supported by the ttf2pt1's built-in front-end parser. Because of + this for now we recommend using the FreeType-based parser (option + '-p ft') with the "plane" language. +

+ + + + + NOTE: + You may notice that the language names are not uniform: some are the + names of particular languages and some are names of encodings. This + is because of the different approaches. The original idea was to + implement a conversion from Unicode to the appropriate Windows + encoding for a given language. And then use the translation tables + to generate the fonts in whatever final encodings are needed. This + would allow to pile together the Unicode fonts and the non-Unicode + Windows fonts for that language and let the program to sort them out + automatically. And then generate fonts in all the possible encodings + for that language. An example of this approach is the Russian language + support. But if there is no multiplicity of encodings used for some + languages and if the non-Unicode fonts are not considered important + by the users, another way would be simpler to implement: just provide + only one table for extraction of the target encoding from Unicode + and don't bother with the translation tables. The latin* "languages" + are examples of this approach. If somebody feels that he needs the + Type1 fonts both in Latin-* and Windows encodings he or she is absolutely + welcome to submit the code to implement it. +

+ + WARNING: + Some of the glyphs included into the AdobeStandard encoding are not + included into the Unicode standard. The most typical examples of such + glyphs are ligatures like 'fi', 'fl' etc. Because of this the font + designers may place them at various places. The converter tries to + do its best, if the glyphs have honest Adobe names and/or are + placed at the same codes as in the Microsoft fonts they will be + picked up. Otherwise a possible solution is to use the option '-L' + with an external map. +

+ + +-L file[+[pid=<pid>,eid=<eid>,][plane]] - Extract the fonts for the specified + language from a multi-language font using the map from this file. This is + rather like the option '-l' but the encoding map is not + compiled into the program, it's taken from that file, so it's + easy to edit. Examples of such files are provided in + maps/adobe-standard-encoding.map, CP1250.map. (NOTE: + the 'standard encoding' map does not include all the glyphs of the + AdobeStandard encoding, it's provided only as an example.) The + description of the supported map formats is in the file + maps/unicode-sample.map. +

+ + Likewise to '-l', an argument may be specified after the map file + name. But in this case the argument has fixed meaning: it selects the + original TTF encoding table (the syntax is the same as in '-l plane') + and/or a plane of the map file. The plane name also gets added after dash + to the font name. The plane is a concept used in the Eastern fonts with big + number of glyphs: one TTF font gets divided into multiple Type1 fonts, + each containing one plane of up to 256 glyphs. But with a little + creativity this concept may be used for other purposes of combining + multiple translation maps into one file. To extract multiple planes + from a TTF font ttf2pt1 must be run multiple times, each time with + a different plane name specified. +

+ + The default original TTF encoding table used for the option '-L' is + Unicode. The map files may include directives to specify different original + TTF encodings. However if the pid/eid pair is specified with + it overrides any original encoding specified in the map file. +

+ + +-m type=value - Set maximal or minimal limits of resources. + These limits control the the font generation by limiting the resources + that the font is permitted to require from the PostScript interpreter. + The currently supported types of limits are: +

+ +

+ h - the maximal hint stack depth for the substituted hints. + The default value is 128, according to the limitation in X11. This seems to + be the lowest (and thus the safest) widespread value. To display the + hint stack depth required by each glyph in a .t1a file use the script + scripts/cntstems.pl. +

+

+ + +-O suboptions - Outline processing options. The suboptions + may be lowercase or uppercase, the lowercase ones disable the features, + the corresponding uppercase suboptions enable the same features. + The suboptions to disable/enable features are: +

+ +

+ b/B - Guessing of the ForceBold parameter. This parameter helps + the Type1 engine to rasterize the bold fonts properly at small sizes. + But the algorithm used to guess the proper value of this flag makes + that guess based solely on the font name. In rare cases that may cause + errors, in these cases you may want to disable this guessing. + Default: enabled +

+ + h/H - Autogeneration of hints. The really complex outlines + may confuse the algorithm, so theoretically it may be useful + sometimes to disable them. Although up to now it seems that + even bad hints are better than no hints at all. + Default: enabled +

+ + u/U - Hint substitution. Hint substitution is a technique + permitting generation of more detailed hints for the rasterizer. It allows + to use different sets of hints for different parts of a glyph and change + these sets as neccessary during rasterization (that's why "substituted"). + So it should improve the quality of the fonts rendered at small sizes. + But there are two catches: First, the X11 library has rather low limit for + the font size. More detailed hints increase the file size and thus increase + the chance of hitting this limit (that does not mean that you shall hit it + but you may if your fonts are particularly big). This is especially + probable for Unicode fonts converted with option '-a', so you may want to + use '-a' together with '-Ou'. See app/X11/README for the description of + a patch to X11 which fixes this problem. Second, some rasterizers (again, + X11 is the typical example) have a limitation for total number of hints + used when drawing a glyph (also known as the hint stack depth). If that + stack overflows the glyph is ignored. Starting from version 3.22 ttf2pt1 + uses algorithms to minimizing this depth, with the trade-off of slightly + bigger font files. The glyphs which still exceed the limit set by option + '-mh' have all the substituted hints removed and only base hints left. + The algorithms seem to have been refined far enough to make the fonts with + substituted hints look better than the fonts without them or at least the + same. Still if the original fonts are not well-designed the detailed + hinting may emphasize the defects of the design, such as non-even thickness + of lines. So provided that you are not afraid of the X11 bug the best idea + would be to generate a font with this feature and without it, then compare + the results using the program other/cmpf (see the description + in other/README) and decide which one looks better. + Default: enabled +

+ + o/O - Space optimization of the outlines' code. This kind of optimization + never hurts, and the only reason to disable this feature is for comparison + of the generated fonts with the fonts generated by the previous versions of + converter. Well, it _almost_ never hurts. As it turned out there exist + some brain-damaged printers which don't understand it. Actually this + feature does not change the outlines at all. The Type 1 font manual + provides a set of redundant operators that make font description shorter, + such as '10 hlineto' instead of '0 10 rlineto' to describe a horizontal + line. This feature enables use of these operators. + Default: enabled +

+ + s/S - Smoothing of outlines. If the font is broken in some + way (even the ones that are not easily noticeable), such smoothing + may break it further. So disabling this feature is the first thing to be + tried if some font looks odd. But with smoothing off the hint generation + algorithms may not work properly too. + Default: enabled +

+ + t/T - Auto-scaling to the 1000x1000 Type1 standard matrix. The + TTF fonts are described in terms of an arbitrary matrix up to + 4000x4000. The converted fonts must be scaled to conform to + the Type1 standard. But the scaling introduces additional rounding + errors, so it may be curious sometimes to look at the font in its + original scale. + Default: enabled +

+ + v/V - Do vectorization on the bitmap fonts. Functionally + "vectorization" is the same thing as "autotracing", a different word is + used purely to differentiate it from the Autotrace library. It tries to + produce nice smooth outlines from bitmaps. This feature is still a work + in progress though the results are already mostly decent. + Default: disabled +

+ + w/W - Glyphs' width corection. This option is designed to be + used on broken fonts which specify too narrow widths for the + letters. You can tell that a font can benefit from this option + if you see that the characters are smashed together without + any whitespace between them. This option causes the converter + to set the character widths to the actual width of this character + plus the width of a typical vertical stem. But on the other hand + the well-designed fonts may have characters that look better if + their widths are set slightly narrower. Such well-designed fonts + will benefit from disabling this feature. You may want to convert + a font with and without this feature, compare the results and + select the better one. This feature may be used only on proportional + fonts, it has no effect on the fixed-width fonts. + Default: disabled +

+ + z/Z - Use the Autotrace library on the bitmap fonts. The results + are horrible and the use of this option is not recommended. This option is + present for experimental purposes. It may change or be removed in the + future. The working tracing can be achieved with option -OV. + Default: disabled +

+

+ + +-p parser_name - Use the specified front-end parser to read the font file. + If this option is not used, ttf2pt1 selects the parser automatically based + on the suffix of the font file name, it uses the first parser in its + list that supports this font type. Now two parsers are supported: +

+ +   ttf - built-in parser for the ttf files (suffix .ttf) +
+   bdf - built-in parser for the BDF files (suffix .bdf) +
+   ft - parser based on the FreeType-2 library (suffixes .ttf, + .otf, .pfa, .pfb) +

+ + The parser ft is NOT linked in by default. See Makefile + for instructions how to enable it. We do no support this parser on + Windows: probably it will work but nobody tried and nobody knows how + to build it. +

+ + The conversion of the bitmap fonts (such as BDF) is simplistic yet, + producing jagged outlines. When converting such fonts, it might be + a good idea to turn off the hint substitution (using option -Ou) + because the hints produced will be huge but not adding much to the + quality of the fonts. +

+ + +-u number - Mark the font with this value as its + UniqueID. The UniqueID is used by the printers with the hard disks + to cache the rasterized characters and thus significantly + speed-up the printing. Some of those printers just can't + store the fonts without UniqueID on their disk.The problem + is that the ID is supposed to be unique, as it name says. And + there is no easy way to create a guaranteed unique ID. Adobe specifies + the range 4000000-4999999 for private IDs but still it's difficult + to guarantee the uniqueness within it. So if you don't really need the + UniqueID don't use it, it's optional. Luckily there are a few millions of + possible IDs, so the chances of collision are rather low. + If instead of the number a special value 'A' is given + then the converter generates the value of UniqueID automatically, + as a hash of the font name. (NOTE: in the version 3.22 the + algorithm for autogeneration of UniqueID was changed to fit the values + into the Adobe-spacified range. This means that if UniqueIDs were used + then the printer's cache may need to be flushed before replacing the + fonts converted by an old version with fonts converted by a newer version). + A simple way to find if any of the fonts in a given directory have + duplicated UniqueIDs is to use the command: +

+ +   cat *.pf[ab] | grep UniqueID | sort | uniq -c | grep -v ' 1 ' +

+ + Or if you use scripts/convert it will do that for you automatically + plus it will also give the exact list of files with duplicate UIDs. +

+ + +-v size - Re-scale the font to get the size of a typical uppercase + letter somewhere around the specified size. Actually, it re-scales + the whole font to get the size of one language-dependent letter to be + at least of the specified size. Now this letter is "A" in all the + supported languages. The size is specified in the points of the + Type 1 coordinate grids, the maximal value is 1000. This is an + experimental option and should be used with caution. It tries to + increase the visible font size for a given point size and thus make + the font more readable. But if overused it may cause the fonts to + look out of scale. As of now the interesting values of size for + this option seem to be located mostly between 600 and 850. This + re-scaling may be quite useful but needs more experience to + understand the balance of its effects. +

+ + +-W level - Select the verbosity level of the warnings. + Currently the levels from 0 to 4 are supported. Level 0 means no warnings + at all, level 4 means all the possible warnings. The default level is 3. + Other levels may be added in the future, so using the level number 99 is + recommended to get all the possible warnings. Going below level 2 is + not generally recommended because you may miss valuable information about + the problems with the fonts being converted. +

+ + +Obsolete option: +-A - Print the font metrics (.afm file) instead of the font on STDOUT. + Use -GA instead. +

+ + +Very obsolete option: +
+ The algorithm that implemented the forced fixed width had major + flaws, so it was disabled. The code is still in the program and + some day it will be refined and returned back. Meanwhile the + option name '-f' was reused for another option. The old version was: +
+-f - Don't try to force the fixed width of font. Normally the converter + considers the fonts in which the glyph width deviates by not more + than 5% as buggy fixed width fonts and forces them to have really + fixed width. If this is undesirable, it can be disabled by this option. +

+ + +The .pfa font format supposes that the description of the characters +is binary encoded and encrypted. This converter does not encode or +encrypt the data by default, you have to specify the option '-e' +or use the t1asm program to assemble (that means, encode and +encrypt) the font program. The t1asm program that is included with +the converter is actually a part of the t1utils package, rather old +version of which may be obtained from +

+ +

+ + http://ttf2pt1.sourceforge.net/t1utils.tar.gz + +
+

+ +Note that t1asm from the old version of that package won't work properly +with the files generated by ttf2pt1 version 3.20 and later. Please use +t1asm packaged with ttf2pt1 or from the new version t1utils +instead. For a newer version of t1utils please look at +

+ +

+ + http://www.lcdf.org/~eddietwo/type/ + +
+

+ + + +So, the following command lines: +

+ +

+ ttf2pt1 -e ttffont.ttf t1font +
+ ttf2pt1 ttffont.ttf - | t1asm >t1font.pfa +
+

+ +represent two ways to get a working font. The benefit of the second form +is that other filters may be applied to the font between the converter +and assembler. +

+ + +

+Installation and deinstallation of the converter +

+ + +The converter may be easily installed systemwide with + +
+ make install +
+ +and uninstalled with + +
+ make uninstall +
+ +By default the Makefile is configured to install in the hierarchy +of directory /usr/local. This destination directory as well as +the structure of the hierarchy may be changed by editing the Makefile. + +

+Installation of the fonts +

+ + +Running the converter manually becomes somewhat boring if it has to +be applied to a few hundreds of fonts and then you have to generate the +fonts.scale and/or Fontmap files. The FONTS file describes how to use +the supplied scripts to handle such cases easily. It also discusses +the installation of the fonts for a few widespread programs. +

+ +

+Other utilities +

+ + +A few other small interesting programs that allow a cloase look at +the fonts are located in the subdirectory 'other'. They +are described shortly in others/README. +

+ +

+Optional packages +

+ + +Some auxiliary files are not needed by everyone and are big enough that +moving them to a separate package speeds up the downloads of the main +package significantly. As of now we have one such optional package: +

+ +  ttf2pt1-chinese - contains the Chinese conversion maps +

+ +The general versioning policy for the optional packages is the following: +These packages may have no direct dependency on the ttf2pt1 version. +But they may be updated in future, as well as some versions of optional +packages may have dependencies on certain versions of ttf2pt1. +To avoid unneccessary extra releases on one hand and keep the updates in +sync with the ttf2pt1 itself on the other hand, a new version of an optional +package will be released only if there are any changes to it and it will be +given the same version number as ttf2pt1 released at the same time. So not +every release of ttf2pt1 would have a corresponding release of all optional +packages. For example, to get the correct version of optional packages for an +imaginary release 8.3.4 of ttf2pt1 you would need to look for optional +packages of the highest version not higher than (but possibly equal to) 8.3.4. +

+ +

+TO DO: +

+ + +
    +
  • Improve hinting. +
  • Improve the auto-tracing of bitmaps. +
  • Implement the family-level hints. +
  • Add generation of CID-fonts. +
  • Handle the composite glyphs with relative base points. +
  • Preserve the relative width of stems during scaling to 1000x1000 matrix. +
  • Add support for bitmap TTF fonts. +
  • Implement better support of Asian encodings. +
  • Implement automatic creation of ligatures. +
+ +

+TROUBLESHOOTING AND BUG REPORTS +

+ + + + +Have problems with conversion of some font ? The converter dumps core ? Or your +printer refuses to understand the converted fonts ? Or some characters are +missing ? Or some characters look strange ? +

+ +Send the bug reports to the ttf2pt1 development mailing list at +ttf2pt1-devel@lists.sourceforge.net. +

+ +Try to collect more information about the problem and include it into +the bug report. (Of course, even better if you would provide a ready +fix, but just a detailed bug report is also good). Provide detailed +information about your problem, this will speed up the response greatly. +Don't just write "this font looks strange after conversion" but describe +what's exactly wrong with it: for example, what characters look wrong +and what exactly is wrong about their look. Providing a link to the +original font file would be also a good idea. Try to do a little +troublehooting and report its result. This not only would help with +the fix but may also give you a temporary work-around for the bug. +

+ +First, enable full warnings with option '-W99', save them to +a file and read carefully. Sometimes the prolem is with a not implemented +feature which is reported in the warnings. Still, reporting about such +problems may be a good idea: some features were missed to cut corners, +in hope that no real font is using them. So a report about a font using +such a feature may motivate someone to implement it. Of course, you +may be the most motivated person: after all, you are the one wishing +to convert that font. ;-) Seriously, the philosophy "scrath your own itch" +seems to be the strongest moving force behind the Open Source software. +

+ +The next step is playing with the options. This serves a dual purpose: +on one hand, it helps to localize the bug, on the other hand you may be +able to get a working version of the font for the meantime while the +bug is being fixed. The typical options to try out are: first '-Ou', if +it does not help then '-Os', then '-Oh', then '-Oo'. +They are described in a bit more detail above. Try them one by one +and in combinations. See if with them the resulting fonts look better. +

+ +On some fonts ttf2pt1 just crashes. Commonly that happens because the +font being converted is highly defective (although sometimes the bug +is in ttf2pt1 itself). In any case it should not crash, so the reports +about such cases will help to handle these defects properly in future. +

+ +We try to respond to the bug reports in a timely fashion but alas, this +may not always be possible, especially if the problem is complex. +This is a volunteer project and its resources are limited. Because +of this we would appreciate bug reports as detailed as possible, +and we would appreciate the ready fixes and contributions even more. +

+ + + + + + + + + + + + + + + + + + +

+CONTACTS +

+ + + + + + + + + + + + +ttf2pt1-announce@lists.sourceforge.net +
+ The mailing list with announcements about ttf2pt1. It is a moderated mailing + with extremely low traffic. Everyone is encouraged to subscribe to keep in + touch with the current status of project. To subscribe use the Web interface + at http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-announce. + If you have only e-mail access to the Net then send a subscribe request to + the development mailing list ttf2pt1-devel@lists.sourceforge.net and somebody + will help you with subscription. +

+ + + +ttf2pt1-devel@lists.sourceforge.net +
+ +ttf2pt1-users@lists.sourceforge.net +
+ The ttf2pt1 mailing lists for development and users issues. They have not + that much traffic either. To subscribe use the Web interface at + http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-devel + and http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-users. + If you have only e-mail access to the Net then send a subscribe request to + the development mailing list ttf2pt1-devel@lists.sourceforge.net and somebody + will help you with subscription. +

+ + + +mheath@netspace.net.au +
+ Mark Heath +

+ + +A.Weeks@mcc.ac.uk +
+ Andrew Weeks +

+ + +babkin@users.sourceforge.net (preferred)
+ +sab123@hotmail.com +
+ +http://members.bellatlantic.net/~babkin +
+ Sergey Babkin +

+ +

+SEE ALSO +

+ + + + + +http://ttf2pt1.sourceforge.net +
+ The main page of the project. +

+ + +http://www.netspace.net.au/~mheath/ttf2pt1/ +
+ The old main page of the project. +

+ + + + +http://sourceforge.net/projects/gnuwin32 +
+ Precompiled binaries for Windows. +

+ + +http://www.lcdf.org/~eddietwo/type/ +
+ The home page of the Type 1 utilities package. +

+ + +http://www.rightbrain.com/pages/books.html +
+ The first book about PostScript on the Web, "Thinking in PostScript". +

+ + +http://fonts.apple.com/TTRefMan/index.html +
+ The True Type reference manual. +

+ + +http://partners.adobe.com/asn/developer/PDFS/TN/PLRM.pdf +
+ Adobe PostScript reference manual. +

+ + +http://partners.adobe.com/asn/developer/PDFS/TN/T1_SPEC.PDF +
+ Specification of the Type 1 font format. +

+ + +http://partners.adobe.com/asn/developer/PDFS/TN/5015.Type1_Supp.pdf +
+ The Type 1 font format supplement. +

+ + +http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf +
+ Specification of the Adobe font metrics file format. +

+ + +http://www.cs.wpi.edu/~matt/courses/cs563/talks/surface/bez_surf.html +
+ +http://www.cs.wpi.edu/~matt/courses/cs563/talks/curves.html +
+ Information about the Bezier curves. +

+ + +http://www.neuroinformatik.ruhr-uni-bochum.de/ini/PEOPLE/rmz/t1lib/t1lib.html +
+ A stand-alone library supporting the Type1 fonts. Is neccessary + to compile the programs other/cmpf and other/dmpf. +

+ + +http://www.freetype.org +
+ A library supporting the TTF fonts. Also many useful TTF programs + are included with it. +

+ + +http://heliotrope.homestead.com/files/printsoft.html +
+ Moses Gold's collection of links to printing software. +

+ + +http://linuxartist.org/fonts/ +
+ Collection of font-related links. +

+ +


+
+ + +Following is the Readme of ttf2pfa (true type to type 3 font converter) It +covers other issues regarding the use of this software. Please note that +although ttf2pfa is a public domain software, ttf2pt1 +is instead covered by an Open Source license. See the COPYRIGHT +file for details. +

+ +Please note also that ttf2pfa has not been maintained for a long time. +All of its functionality has been integrated into ttf2pt1 and all the +development moved to ttf2pt1, including Andrew Weeks, the author of +ttf2pfa. Ttf2pfa is provided for historical reasons only. Please use +ttf2pt1 instead. + +


+ + +

+True Type to Postscript Font converter +

+ + +My mind is still reeling from the discovery that I was able to write +this program. What it does is it reads a Microsoft TrueType font and +creates a Postscript font. '_A_ postscript font', that is, not necessarily +the same font, you understand, but a fair imitation. +

+ +Run it like this: +

+ +

+ ttf2pfa fontfile.ttf fontname +
+

+ +The first parameter is the truetype filename, the second is a stem for +the output file names. The program will create a fontname.pfa containing +the Postscript font and a fontname.afm containing the metrics. +

+ +The motivation behind this is that in Linux if you do not have a +Postscript printer, but only some other printer, you can only print +Postscript by using Ghostscript. But the fonts that come with +Ghostscript are very poor (they are converted from bitmaps and look +rather lumpy). This is rather frustrating as the PC running Linux +probably has MS-Windows as well and will therefore have truetype fonts, +but which are quite useless with Linux, X or Ghostscript. +

+ +The program has been tested on over a hundred different TrueType fonts +from various sources, and seems to work fairly well. The converted +characters look OK, and the program doesn't seem to crash any more. I'm +not sure about the AFM files though, as I have no means to test them. +

+ +The fonts generated will not work with X, as the font rasterizer that +comes with X only copes with Type 1 fonts. If I have the time I may +modify ttf2pfa to generate Type 1s. +

+ +

+Copyright issues +

+ + +I am putting this program into the public domain, so don't bother +sending me any money, I'd only have to declare it for income tax. +

+ +Copyright on fonts, however, is a difficult legal question. Any +copyright statements found in a font will be preserved in the output. +Whether you are entitled to translate them at all I don't know. +

+ +If you have a license to run a software package, like say MS-Windows, on +your PC, then you probably have a right to use any part of it, including +fonts, on that PC, even if not using that package for its intended +purpose. +

+ +I am not a lawyer, however, so this is not a legal opinion, and may be +garbage. +

+ +There shouldn't be a any problem with public domain fonts. +

+ +

+About the Program +

+ + +It was written in C on a IBM PC running Linux. +

+ +The TrueType format was originally developed by Apple for the MAC, which +has opposite endianness to the PC, so to ensure compatibility 16 and 32 +bit fields are the wrong way round from the PC's point of view. This is +the reason for all the 'ntohs' and 'ntohl' calls. Doing it this way +means the program will also work on big-endian machines like Suns. +

+ +I doubt whether it will work on a DOS-based PC though. +

+ +The program produces what technically are Type 3 rather than Type 1 +fonts. They are not compressed or encrypted and are plain text. This is +so I (and you) can see what's going on, and (if you're a Postscript guru +and really want to) can alter the outlines. +

+ +I only translate the outlines, not the 'instructions' that come with +them. This latter task is probably virtually impossible anyway. TrueType +outlines are B-splines rather than the Bezier curves that Postscript +uses. I believe that my conversion algorithm is reasonably correct, if +nothing else because the characters look right. +

+ +

+Problems that may occur +

+ + +Most seriously, very complex characters (with lots of outline segments) +can make Ghostscript releases 2.x.x fail with a 'limitcheck' error. It +is possible that this may happen with some older Postscript printers as +well. Such characters will be flagged by the program and there are +basically two things you can do. First is to edit the .pfa file to +simplify or remove the offending character. This is not really +recommended. The second is to use Ghostscript release 3, if you can get +it. This has much larger limits and does not seem to have any problems +with complex characters. +

+ +Then there are buggy fonts (yes, a font can have bugs). I try to deal +with these in as sane a manner as possible, but it's not always +possible. +

+ +

+Encodings +

+ + +A postscript font must have a 256 element array, called an encoding, +each element of which is a name, which is also the name of a procedure +contained within the font. The 'BuildChar' command takes a byte and uses +it to index the encoding array to find a character name, and then looks +that up in the font's procedure table find the commands to draw the +glyph. However, not all characters need be in the encoding array. Those +that are not cannot be drawn (at least not using 'show'), however it is +possible to 're-encode' the font to enable these characters. There are +several standard encodings: Adobe's original, ISO-Latin1 and Symbol +being the most commonly encountered. +

+ +TrueType fonts are organised differently. As well as the glyph +descriptions there are a number of tables. One of these is a mapping +from a character set into the glyph array, and another is a mapping from +the glyph array into a set of Postscript character names. The problems +are: +

+ 1) Microsoft uses Unicode, a 16-bit system, to encode the font. +
+ 2) that more than one glyph is given the same Postscript name. +

+ +I deal with (1) by assuming a Latin1 encoding. The MS-Windows and +Unicode character sets are both supersets of ISO-8859-1. This usually +means that most characters will be properly encoded, but you should be +warned that some software may assume that fonts have an Adobe encoding. +Symbol, or Dingbat, fonts are in fact less of a problem, as they have +private encodings starting at 0xF000. It is easy to just lose the top +byte. +

+ +Postscript fonts can be re-encoded, either manually, or by software. +Groff, for example, generates postscript that re-encodes fonts with the +Adobe encoding. The problem here is that not all characters in the Adobe +set are in the MS-Windows set. In particular there are no fi and fl +ligatures. This means that conversions of the versions of +Times-New-Roman and Arial that come with MS-Windows cannot be used +blindly as replacements for Adobe Times-Roman and Helvetica. You can get +expanded versions of MS fonts from Microsoft's web site which do contain +these ligatures (and a lot else besides). +

+ +I deal with (2) by creating new character names. This can be error-prone +because I do not know which of them is the correct glyph to give the +name to. Some (buggy) fonts have large numbers of blank glyphs, all with +the same name. +

+ +(almost every TrueType font has three glyphs called .notdef, one of them +is usually an empty square shape, one has no outline and has zero width, +and one has no outline and a positive width. This example is not really +a problem with well formed fonts since the .notdef characters are only +used for unprintable characters, which shouldn't occur in your documents +anyway). +

+ + Index: xc/extras/ttf2pt1/bdf.c =================================================================== RCS file: xc/extras/ttf2pt1/bdf.c diff -N xc/extras/ttf2pt1/bdf.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/bdf.c 13 Apr 2004 02:45:01 -0000 @@ -0,0 +1,660 @@ +/* + * The font parser for the BDF files + * + * Copyright (c) 2001 by the TTF2PT1 project + * Copyright (c) 2001 by Sergey Babkin + * + * see COPYRIGHT for the full copyright notice + */ + +#include +#include +#include +#include +#include "pt1.h" +#include "global.h" + +/* prototypes of call entries */ +static void openfont(char *fname, char *arg); +static void closefont( void); +static int getnglyphs ( void); +static int glnames( GLYPH *glyph_list); +static void readglyphs( GLYPH *glyph_list); +static int glenc( GLYPH *glyph_list, int *encoding, int *unimap); +static void fnmetrics( struct font_metrics *fm); +static void glpath( int glyphno, GLYPH *glyph_list); +static void kerning( GLYPH *glyph_list); + +/* globals */ + +/* front-end descriptor */ +struct frontsw bdf_sw = { + /*name*/ "bdf", + /*descr*/ "BDF bitmapped fonts", + /*suffix*/ { "bdf" }, + /*open*/ openfont, + /*close*/ closefont, + /*nglyphs*/ getnglyphs, + /*glnames*/ glnames, + /*glmetrics*/ readglyphs, + /*glenc*/ glenc, + /*fnmetrics*/ fnmetrics, + /*glpath*/ glpath, + /*kerning*/ kerning, +}; + +/* statics */ + +#define MAXLINE 10240 /* maximal line length in the input file */ + +static int lineno; /* line number */ + +#define GETLEN(s) s, (sizeof(s)-1) +#define LENCMP(str, txt) strncmp(str, txt, sizeof(txt)-1) + +static FILE *bdf_file; +static int nglyphs; +static struct font_metrics fmet; + +/* many BDF fonts are of small pixel size, so we better try + * to scale them by an integer to keep the dimensions in + * whole pixels. However if the size is too big and a non- + * integer scaling is needed, we use the standard ttf2pt1's + * scaling abilities. + */ +static int pixel_size; +static int scale; +static int scale_external; + +static char *slant; +static char xlfdname[201]; +static char *spacing; +static char *charset_reg; +static char *charset_enc; +static char *fnwidth; +static int is_unicode = 0; + +/* tempoary storage for returning data to ttf2pt1 later on request */ +static int maxenc = 0; +static int *fontenc; +static GENTRY **glpaths; + +static int got_glyphs = 0; +static GLYPH *glyphs; +static int curgl; + +static int readfile(FILE *f, int (*strfunc)(int len, char *str)); + +/* + * Read the file and parse each string with strfunc(), + * until strfunc() returns !=0 or the end of file happens. + * Returns -1 on EOF or strfunc() returning <0, else 0 + */ + +static int +readfile( + FILE *f, + int (*strfunc)(int len, char *str) +) +{ + static char str[MAXLINE]; /* input line, maybe should be dynamic ? */ + char *s; + int len, c, res; + + len=0; + while(( c=getc(f) )!=EOF) { + if(c=='\n') { + str[len]=0; + + res = strfunc(len, str); + lineno++; + if(res<0) + return -1; + else if(res!=0) + return 0; + + len=0; + } else if(len%d)\n", lineno, MAXLINE-1); + exit(1); + } + } + return -1; /* EOF */ +} + +/* + * Parse the header of the font file. + * Stop after the line CHARS is encountered. Ignore the unknown lines. + */ + +struct line { + char *name; /* property name with trailing space */ + int namelen; /* length of the name string */ + enum { + ALLOW_REPEAT = 0x01, /* this property may be repeated in multiple lines */ + IS_SEEN = 0x02, /* this property has been seen already */ + MUST_SEE = 0x04, /* this property must be seen */ + IS_LAST = 0x08 /* this is the last property to be read */ + } flags; + char *fmt; /* format string for the arguments, NULL means a string arg */ + int nvals; /* number of values to be read by sscanf */ + void *vp[4]; /* pointers to values to be read */ +}; + +static struct line header[] = { + { GETLEN("FONT "), 0, " %200s", 1, {&xlfdname} }, + { GETLEN("SIZE "), MUST_SEE, " %d", 1, {&pixel_size} }, + { GETLEN("FONTBOUNDINGBOX "), MUST_SEE, " %hd %hd %hd %hd", 4, + {&fmet.bbox[2], &fmet.bbox[3], &fmet.bbox[0], &fmet.bbox[1]} }, + { GETLEN("FAMILY_NAME "), MUST_SEE, NULL, 1, {&fmet.name_family} }, + { GETLEN("WEIGHT_NAME "), MUST_SEE, NULL, 1, {&fmet.name_style} }, + { GETLEN("COPYRIGHT "), 0, NULL, 1, {&fmet.name_copyright} }, + { GETLEN("SLANT "), MUST_SEE, NULL, 1, {&slant} }, + { GETLEN("SPACING "), 0, NULL, 1, {&spacing} }, + { GETLEN("SETWIDTH_NAME "), 0, NULL, 1, {&fnwidth} }, + { GETLEN("CHARSET_REGISTRY "), 0, NULL, 1, {&charset_reg} }, + { GETLEN("CHARSET_ENCODING "), 0, NULL, 1, {&charset_enc} }, + { GETLEN("FONT_ASCENT "), 0, " %hd", 1, {&fmet.ascender} }, + { GETLEN("FONT_DESCENT "), 0, " %hd", 1, {&fmet.descender} }, + + /* these 2 must go in this order for post-processing */ + { GETLEN("UNDERLINE_THICKNESS "), 0, " %hd", 1, {&fmet.underline_thickness} }, + { GETLEN("UNDERLINE_POSITION "), 0, " %hd", 1, {&fmet.underline_position} }, + + { GETLEN("CHARS "), MUST_SEE|IS_LAST, " %d", 1, {&nglyphs} }, + { NULL, 0, 0 } /* end mark: name==NULL */ +}; + +static int +handle_header( + int len, + char *str +) +{ + struct line *cl; + char *s, *p; + int c; + +#if 0 + fprintf(stderr, "line: %s\n", str); +#endif + for(cl = header; cl->name != 0; cl++) { + if(strncmp(str, cl->name, cl->namelen)) + continue; +#if 0 + fprintf(stderr, "match: %s\n", cl->name); +#endif + if(cl->flags & IS_SEEN) { + if(cl->flags & ALLOW_REPEAT) + continue; + + fprintf(stderr, "**** input line %d redefines the property %s\n", lineno, cl->name); + exit(1); + } + cl->flags |= IS_SEEN; + if(cl->fmt == 0) { + s = malloc(len - cl->namelen + 1); + if(s == 0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + *((char **)(cl->vp[0])) = s; + + /* skip until a quote */ + for(p = str+cl->namelen; (c = *p)!=0; p++) { + if(c == '"') { + p++; + break; + } + } + for(; (c = *p)!=0; p++) { + if(c == '"') { + c = *++p; + if(c == '"') + *s++ = c; + else + break; + } else + *s++ = c; + } + *s = 0; /* end of line */ + } else { + c = sscanf(str+cl->namelen, cl->fmt, cl->vp[0], cl->vp[1], cl->vp[2], cl->vp[3]); + if(c != cl->nvals) { + fprintf(stderr, "**** property %s at input line %d must have %d arguments\n", + cl->name, lineno, cl->nvals); + exit(1); + } + } + if(cl->flags & IS_LAST) + return 1; + else + return 0; + } + return 0; +} + +/* + * Parse the description of the glyphs + */ + +static int +handle_glyphs( + int len, + char *str +) +{ + static int inbmap=0; + static char *bmap; + static int xsz, ysz, xoff, yoff; + static int curln; + int i, c; + char *p, *plim, *psz; + + if(!LENCMP(str, "ENDFONT")) { + if(curgl < nglyphs) { + fprintf(stderr, "**** unexpected end of font file after %d glyphs\n", curgl); + exit(1); + } else + return 1; + } + if(curgl >= nglyphs) { + fprintf(stderr, "**** file contains more glyphs than advertised (%d)\n", nglyphs); + exit(1); + } + if(!LENCMP(str, "STARTCHAR")) { + /* sizeof will count \0 instead of ' ' */ + for(i=sizeof("STARTCHAR"); str[i] == ' '; i++) + {} + + glyphs[curgl].name = strdup(str + i); + if(glyphs[curgl].name == 0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } else if(!LENCMP(str, "ENCODING")) { + if(sscanf(str, "ENCODING %d", &fontenc[curgl])!=1) { + fprintf(stderr,"**** weird ENCODING statement at line %d\n", lineno); + exit(1); + } + if(fontenc[curgl] == -1) /* compatibility format */ + sscanf(str, "ENCODING -1 %d", &fontenc[curgl]); + if(fontenc[curgl] > maxenc) + maxenc = fontenc[curgl]; + } else if(!LENCMP(str, "DWIDTH")) { + if(sscanf(str, "DWIDTH %d %d", &xsz, &ysz)!=2) { + fprintf(stderr,"**** weird DWIDTH statement at line %d\n", lineno); + exit(1); + } + glyphs[curgl].width = xsz*scale; + } else if(!LENCMP(str, "BBX")) { + if(sscanf(str, "BBX %d %d %d %d", &xsz, &ysz, &xoff, &yoff)!=4) { + fprintf(stderr,"**** weird BBX statement at line %d\n", lineno); + exit(1); + } + bmap=malloc(xsz*ysz); + if(bmap==0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + glyphs[curgl].lsb = -xoff*scale; + glyphs[curgl].xMin = -xoff*scale; + glyphs[curgl].xMax = (xsz-xoff)*scale; + glyphs[curgl].yMin = -yoff*scale; + glyphs[curgl].yMax = (ysz-xoff)*scale; + } else if(!LENCMP(str, "BITMAP")) { + inbmap=1; + curln=ysz-1; /* the lowest line has index 0 */ + } else if(!LENCMP(str, "ENDCHAR")) { + inbmap=0; + if(bmap) { + glyphs[curgl].lastentry = 0; + glyphs[curgl].path = 0; + glyphs[curgl].entries = 0; + bmp_outline(&glyphs[curgl], scale, bmap, xsz, ysz, xoff, yoff); + free(bmap); + /* remember in a static table or it will be erased */ + glpaths[curgl] = glyphs[curgl].entries; + glyphs[curgl].entries = 0; + + if(glpaths[curgl]) + glyphs[curgl].ttf_pathlen = 1; + else + glyphs[curgl].ttf_pathlen = 0; + } + curgl++; + } else if(inbmap) { + if(curln<0) { + fprintf(stderr,"**** bitmap is longer than %d lines at line %d\n", ysz, lineno); + exit(1); + } + + i=0; + p=&bmap[curln*xsz]; psz=p+xsz; + while(ilsb = 0; + g->width = fmet.bbox[2]; + g->xMin = 0; + g->yMin = 0; + } + g = &glyphs[0]; + g->name = ".notdef"; + g->xMax = fmet.bbox[2]*4/5; + g->yMax = fmet.bbox[3]*4/5; + g->entries = g->path = g->lastentry = 0; + /* make it look as a black square */ + fg_rmoveto(g, 0.0, 0.0); + fg_rlineto(g, 0.0, (double)g->yMax); + fg_rlineto(g, (double)g->xMax, (double)g->yMax); + fg_rlineto(g, (double)g->xMax, 0.0); + fg_rlineto(g, 0.0, 0.0); + g_closepath(g); + glpaths[0] = g->entries; + g->entries = 0; + g->ttf_pathlen = 4; + + g = &glyphs[1]; + g->name = ".null"; + g->xMax = g->yMax = 0; + g->ttf_pathlen = 0; + + if(readfile(bdf_file, handle_glyphs) < 0) { + fprintf(stderr, "**** file does not contain the ENDFONT line\n"); + exit(1); + } + got_glyphs = 1; +} + +/* + * Open font and prepare to return information to the main driver. + * May print error and warning messages. + * Exit on error. + */ + +static void +openfont( + char *fname, + char *arg /* unused now */ +) +{ + struct line *cl; + int i, l; + + if ((bdf_file = fopen(fname, "r")) == NULL) { + fprintf(stderr, "**** Cannot open file '%s'\n", fname); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Processing file %s\n", fname); + } + + lineno = 1; + + for(cl = header; cl->name != 0; cl++) + cl->flags &= ~IS_SEEN; + if(readfile(bdf_file, handle_header) < 0) { + fprintf(stderr, "**** file does not contain the CHARS definition\n"); + exit(1); + } + for(cl = header; cl->name != 0; cl++) { + if( (cl->flags & MUST_SEE) && !(cl->flags & IS_SEEN) ) { + fprintf(stderr, "**** mandatory property %sis not found in the input line\n", + cl->name); /* cl->name has a space at the end */ + exit(1); + } + + /* set a few defaults */ + if( !(cl->flags & IS_SEEN) ) { + if(cl->vp[0] == &fmet.underline_thickness) { + fmet.underline_thickness = 1; + } else if(cl->vp[0] == &fmet.underline_position) { + fmet.underline_position = fmet.bbox[1] + fmet.underline_thickness + - (pixel_size - fmet.bbox[3]); + } else if(cl->vp[0] == &fmet.ascender) { + fmet.ascender = fmet.bbox[2] + fmet.bbox[0]; + } else if(cl->vp[0] == &fmet.descender) { + fmet.descender = fmet.bbox[0]; + } + } + } + + nglyphs += 2; /* add empty glyph and .notdef */ + + /* postprocessing to compensate for the differences in the metric formats */ + fmet.bbox[2] += fmet.bbox[0]; + fmet.bbox[3] += fmet.bbox[1]; + + scale = 1000/pixel_size; /* XXX ? */ + if(scale*pixel_size < 950) { + scale = 1; + scale_external = 1; + fmet.units_per_em = pixel_size; + } else { + scale_external = 0; + fmet.units_per_em = scale*pixel_size; + + fmet.underline_position *= scale; + fmet.underline_thickness *= scale; + fmet.ascender *= scale; + fmet.descender *= scale; + for(i=0; i<4; i++) + fmet.bbox[i] *= scale; + } + + fmet.italic_angle = 0.0; + if(spacing == 0 /* possibly an old font */ + || toupper(spacing[0]) != 'P') /* or anything non-proportional */ + fmet.is_fixed_pitch = 1; + else + fmet.is_fixed_pitch = 0; + + if(fmet.name_copyright==NULL) + fmet.name_copyright = ""; + + /* create the full name */ + l = strlen(fmet.name_family) + + (fmet.name_style? strlen(fmet.name_style) : 0) + + (fnwidth? strlen(fnwidth) : 0) + + strlen("Oblique") + 1; + + if(( fmet.name_full = malloc(l) )==NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + strcpy(fmet.name_full, fmet.name_family); + if(fnwidth && strcmp(fnwidth, "Normal")) { + strcat(fmet.name_full, fnwidth); + } + if(fmet.name_style && strcmp(fmet.name_style, "Medium")) { + strcat(fmet.name_full, fmet.name_style); + } + switch(toupper(slant[0])) { + case 'O': + strcat(fmet.name_full, "Oblique"); + break; + case 'I': + strcat(fmet.name_full, "Italic"); + break; + } + + fmet.name_ps = fmet.name_full; + fmet.name_version = "1.0"; + + if(charset_reg && charset_enc + && !strcmp(charset_reg, "iso10646") && !strcmp(charset_enc, "1")) + is_unicode = 1; + + if(( fontenc = calloc(nglyphs, sizeof *fontenc) )==NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + for(i=0; i=0 && e 255) + return 2; + else + return 0; +} + +/* + * Get the font metrics + */ +static void +fnmetrics( + struct font_metrics *fm +) +{ + *fm = fmet; +} + +/* + * Get the path of contrours for a glyph. + */ + +static void +glpath( + int glyphno, + GLYPH *glyf_list +) +{ + readglyphs(glyf_list); + glyf_list[glyphno].entries = glpaths[glyphno]; + glpaths[glyphno] = 0; +} + +/* + * Get the kerning data. + */ + +static void +kerning( + GLYPH *glyph_list +) +{ + return; /* no kerning in BDF */ +} Index: xc/extras/ttf2pt1/bitmap.c =================================================================== RCS file: xc/extras/ttf2pt1/bitmap.c diff -N xc/extras/ttf2pt1/bitmap.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/bitmap.c 13 Apr 2004 02:45:02 -0000 @@ -0,0 +1,2633 @@ +/* + * Handling of the bitmapped glyphs + * + * Copyright (c) 2001 by the TTF2PT1 project + * Copyright (c) 2001 by Sergey Babkin + * + * see COPYRIGHT for the full copyright notice + */ + +#include +#include +#include +#include "pt1.h" +#include "global.h" + +/* possible values of limits */ +#define L_NONE 0 /* nothing here */ +#define L_ON 1 /* black is on up/right */ +#define L_OFF 2 /* black is on down/left */ + +static int warnedhints = 0; + + +#ifdef USE_AUTOTRACE +#include + +/* + * Produce an autotraced outline from a bitmap. + * scale - factor to scale the sizes + * bmap - array of dots by lines, xsz * ysz + * xoff, yoff - offset of the bitmap's lower left corner + * from the logical position (0,0) + */ + +static void +autotraced_bmp_outline( + GLYPH *g, + int scale, + char *bmap, + int xsz, + int ysz, + int xoff, + int yoff +) +{ + at_bitmap_type atb; + at_splines_type *atsp; + at_fitting_opts_type *atoptsp; + at_spline_list_type *slp; + at_spline_type *sp; + int i, j, k; + double lastx, lasty; + double fscale; + char *xbmap; + + fscale = (double)scale; + + /* provide a white margin around the bitmap */ + xbmap = malloc((ysz+2)*(xsz+2)); + if(xbmap == 0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + memset(xbmap, 0, xsz+2); /* top margin */ + for(i=0, j=xsz+2; ilength; i++) { + slp = &atsp->data[i]; +#if 0 + fprintf(stderr, "%s: contour %d: %d entries clockwise=%d color=%02X%02X%02X\n", + g->name, i, slp->length, slp->clockwise, slp->color.r, slp->color.g, slp->color.b); +#endif + if(slp->length == 0) + continue; +#if 0 + if(slp->color.r + slp->color.g + slp->color.b == 0) + continue; +#endif + fg_rmoveto(g, fscale*(slp->data[0].v[0].x+xoff), fscale*(slp->data[0].v[0].y+yoff)); + for(j=0; jlength; j++) { +#if 0 + fprintf(stderr, " "); + for(k=0; k<4; k++) + fprintf(stderr, "(%g %g) ", + fscale*(slp->data[j].v[k].x+xoff), + fscale*(ysz-slp->data[j].v[k].y+yoff) + ); + fprintf(stderr, "\n"); +#endif + fg_rrcurveto(g, + fscale*(slp->data[j].v[1].x+xoff), fscale*(slp->data[j].v[1].y+yoff), + fscale*(slp->data[j].v[2].x+xoff), fscale*(slp->data[j].v[2].y+yoff), + fscale*(slp->data[j].v[3].x+xoff), fscale*(slp->data[j].v[3].y+yoff) ); + } + g_closepath(g); + } + + at_splines_free(atsp); + at_fitting_opts_free(atoptsp); + free(xbmap); +} + +#endif /*USE_AUTOTRACE*/ + +/* an extension of gentry for description of the fragments */ +typedef struct gex_frag GEX_FRAG; +struct gex_frag { + /* indexes to len, the exact values and order are important */ +#define GEXFI_NONE -1 +#define GEXFI_CONVEX 0 +#define GEXFI_CONCAVE 1 +#define GEXFI_LINE 2 /* a line with steps varying by +-1 pixel */ +#define GEXFI_EXACTLINE 3 /* a line with exactly the same steps */ +#define GEXFI_SERIF 4 /* small serifs at the ends of stemsi - must be last */ +#define GEXFI_COUNT 5 /* maximal index + 1 */ + unsigned short len[GEXFI_COUNT]; /* length of various fragment types starting here */ + unsigned short lenback[GEXFI_COUNT]; /* length back to the start of curve */ + + signed char ixstart; /* index of the frag type that starts here */ + signed char ixcont; /* index of the frag type that continues here */ + + short flags; +#define GEXFF_HLINE 0x0001 /* the exact line is longer in "horizontal" dimension */ +#define GEXFF_EXTR 0x0002 /* this gentry is an extremum in some direction */ +#define GEXFF_CIRC 0x0004 /* the joint at this gentry is for a circular curve */ +#define GEXFF_DRAWCURVE 0x0008 /* vect[] describes a curve to draw */ +#define GEXFF_DRAWLINE 0x0010 /* vect[] describes a line to draw */ +#define GEXFF_SPLIT 0x0020 /* is a result of splitting a line */ +#define GEXFF_SYMNEXT 0x0040 /* this subfrag is symmetric with next one */ +#define GEXFF_DONE 0x0080 /* this subfrag has been vectorized */ +#define GEXFF_LONG 0x0100 /* this gentry is longer than 1 pixel */ + + unsigned short aidx; /* index of gentry in the array representing the contour */ + + unsigned short vectlen; /* number of gentries represented by vect[] */ + + /* coordinates for vectored replacement of this fragment */ + /* (already scaled because it's needed for curve approximation) */ + double vect[4 /*ref.points*/][2 /*X,Y*/]; + + double bbox[2 /*X,Y*/]; /* absolute sizes of bounding box of a subfragment */ + + /* used when splitting the curved frags into subfrags */ + GENTRY *prevsub; /* to gentries describing neighboring subfrags */ + GENTRY *nextsub; + GENTRY *ordersub; /* single-linked list describing the order of calculation */ + + int sublen; /* length of this subfrag */ + /* the symmetry across the subfrags */ + int symaxis; /* the symmetry axis, with next subfrag */ + int symxlen; /* min length of adjacent symmetric frags */ + /* the symmetry within this subfrag (the axis is always diagonal) */ + GENTRY *symge; /* symge->i{x,y}3 is the symmetry point of symge==0 if none */ + +}; +#define X_FRAG(ge) ((GEX_FRAG *)((ge)->ext)) + +/* various interesting tables related to GEX_FRAG */ +static char *gxf_name[GEXFI_COUNT] = {"Convex", "Concave", "Line", "ExLine", "Serif"}; +static int gxf_cvk[2] = {-1, 1}; /* coefficients of concaveness */ + +/* + * Dump the contents of X_EXT()->len and ->lenback for a contour + */ +static void +gex_dump_contour( + GENTRY *ge, + int clen +) +{ + int i, j; + + for(j = 0; j < GEXFI_COUNT; j++) { + fprintf(stderr, "%-8s", gxf_name[j]); + for(i = 0; i < clen; i++, ge = ge->frwd) + fprintf(stderr, " %2d", X_FRAG(ge)->len[j]); + fprintf(stderr, " %p\n (back) ", ge); + for(i = 0; i < clen; i++, ge = ge->frwd) + fprintf(stderr, " %2d", X_FRAG(ge)->lenback[j]); + fprintf(stderr, "\n"); + } +} + +/* + * Calculate values of X_EXT()->lenback[] for all entries in + * a contour. The contour is identified by: + * ge - any gentry of the contour + * clen - contour length + */ + +static void +gex_calc_lenback( + GENTRY *ge, + int clen +) +{ + int i, j; + int end; + GEX_FRAG *f; + int len[GEXFI_COUNT]; /* length of the most recent fragment */ + int count[GEXFI_COUNT]; /* steps since beginning of the fragment */ + + for(j = 0; j < GEXFI_COUNT; j++) + len[j] = count[j] = 0; + + end = clen; + for(i = 0; i < end; i++, ge = ge->frwd) { + f = X_FRAG(ge); + for(j = 0; j < GEXFI_COUNT; j++) { + if(len[j] != count[j]) { + f->lenback[j] = count[j]++; + } else + f->lenback[j] = 0; + if(f->len[j] != 0) { + len[j] = f->len[j]; + count[j] = 1; /* start with the next gentry */ + /* if the fragment will wrap over the start, process to its end */ + if(i < clen && i + len[j] > end) + end = i + len[j]; + } + } + } + gex_dump_contour(ge, clen); +} + +/* Limit a curve to not exceed the given coordinates + * at its given side + */ + +static void +limcurve( + double curve[4][2 /*X,Y*/], + double lim[2 /*X,Y*/], + int where /* 0 - start, 3 - end */ +) +{ + int other = 3-where; /* the other end */ + int sgn[2 /*X,Y*/]; /* sign for comparison */ + double t, from, to, nt, t2, nt2, tt[4]; + double val[2 /*X,Y*/]; + int i; + + for(i=0; i<2; i++) + sgn[i] = fsign(curve[other][i] - curve[where][i]); + +#if 0 + fprintf(stderr, " limit (%g,%g)-(%g,%g) at %d by (%g,%g), sgn(%d,%d)\n", + curve[0][0], curve[0][1], curve[3][0], curve[3][1], + where, lim[0], lim[1], sgn[0], sgn[1]); +#endif + /* a common special case */ + if( sgn[0]*(curve[where][0] - lim[0]) >= 0. + && sgn[1]*(curve[where][1] - lim[1]) >= 0. ) + return; /* nothing to do */ + + if(other==0) { + from = 0.; + to = 1.; + } else { + from = 1.; + to = 0.; + } +#if 0 + fprintf(stderr, "t="); +#endif + while( fabs(from-to) > 0.00001 ) { + t = 0.5 * (from+to); + t2 = t*t; + nt = 1.-t; + nt2 = nt*nt; + tt[0] = nt2*nt; + tt[1] = 3*nt2*t; + tt[2] = 3*nt*t2; + tt[3] = t*t2; + for(i=0; i<2; i++) + val[i] = curve[0][i]*tt[0] + curve[1][i]*tt[1] + + curve[2][i]*tt[2] + curve[3][i]*tt[3]; +#if 0 + fprintf(stderr, "%g(%g,%g) ", t, val[0], val[1]); +#endif + if(fabs(val[0] - lim[0]) < 0.1 + || fabs(val[1] - lim[1]) < 0.1) + break; + + if(sgn[0] * (val[0] - lim[0]) < 0. + || sgn[1] * (val[1] - lim[1]) < 0.) + to = t; + else + from = t; + } + /* now t is the point of splitting */ +#define SPLIT(pt1, pt2) ( (pt1) + t*((pt2)-(pt1)) ) /* order is important! */ + for(i=0; i<2; i++) { + double v11, v12, v13, v21, v22, v31; /* intermediate points */ + + v11 = SPLIT(curve[0][i], curve[1][i]); + v12 = SPLIT(curve[1][i], curve[2][i]); + v13 = SPLIT(curve[2][i], curve[3][i]); + v21 = SPLIT(v11, v12); + v22 = SPLIT(v12, v13); + v31 = SPLIT(v21, v22); + if(other==0) { + curve[1][i] = v11; + curve[2][i] = v21; + curve[3][i] = fabs(v31 - lim[i]) < 0.1 ? lim[i] : v31; + } else { + curve[0][i] = fabs(v31 - lim[i]) < 0.1 ? lim[i] : v31; + curve[1][i] = v22; + curve[2][i] = v13; + } + } +#undef SPLIT +#if 0 + fprintf(stderr, "\n"); +#endif +} + +/* + * Vectorize a subfragment of a curve fragment. All the data has been already + * collected by this time + */ + +static void +dosubfrag( + GLYPH *g, + int fti, /* fragment type index */ + GENTRY *firstge, /* first gentry of fragment */ + GENTRY *ge, /* first gentry of subfragment */ + double fscale +) +{ + GENTRY *gel, *gei; /* last gentry of this subfrag */ + GEX_FRAG *f, *ff, *lf, *pf, *xf; + /* maximal amount of space that can be used at the beginning and the end */ + double fixfront[2], fixend[2]; /* fixed points - used to show direction */ + double mvfront[2], mvend[2]; /* movable points */ + double limfront[2], limend[2]; /* limit of movement for movabel points */ + double sympt; + int outfront, outend; /* the beginning/end is going outwards */ + int symfront, symend; /* a ready symmetric fragment is present at front/end */ + int drnd[2 /*X,Y*/]; /* size of the round part */ + int i, j, a1, a2, ndots; + double avg2, max2; /* squared distances */ + struct dot_dist *dots, *usedots; + + ff = X_FRAG(firstge); + f = X_FRAG(ge); + gel = f->nextsub; + lf = X_FRAG(gel); + if(f->prevsub != 0) + pf = X_FRAG(f->prevsub); + else + pf = 0; + + for(i=0; i<2; i++) + drnd[i] = gel->bkwd->ipoints[i][2] - ge->ipoints[i][2]; + + if(f->prevsub==0 && f->ixcont == GEXFI_NONE) { + /* nothing to join with : may use the whole length */ + for(i = 0; i < 2; i++) + limfront[i] = ge->bkwd->ipoints[i][2]; + } else { + /* limit to a half */ + for(i = 0; i < 2; i++) + limfront[i] = 0.5 * (ge->ipoints[i][2] + ge->bkwd->ipoints[i][2]); + } + if( (ge->ix3 == ge->bkwd->ix3) /* vert */ + ^ (isign(ge->bkwd->ix3 - ge->frwd->ix3)==isign(ge->bkwd->iy3 - ge->frwd->iy3)) + ^ (fti == GEXFI_CONCAVE) /* counter-clockwise */ ) { + /* the beginning is not a flat 90-degree end */ + outfront = 1; + for(i = 0; i < 2; i++) + fixfront[i] = ge->frwd->ipoints[i][2]; + } else { + outfront = 0; + for(i = 0; i < 2; i++) + fixfront[i] = ge->ipoints[i][2]; + } + + if(lf->nextsub==0 && lf->ixstart == GEXFI_NONE) { + /* nothing to join with : may use the whole length */ + for(i = 0; i < 2; i++) + limend[i] = gel->ipoints[i][2]; + } else { + /* limit to a half */ + for(i = 0; i < 2; i++) + limend[i] = 0.5 * (gel->ipoints[i][2] + gel->bkwd->ipoints[i][2]); + } + gei = gel->bkwd->bkwd; + if( (gel->ix3 == gel->bkwd->ix3) /* vert */ + ^ (isign(gel->ix3 - gei->ix3)==isign(gel->iy3 - gei->iy3)) + ^ (fti == GEXFI_CONVEX) /* clockwise */ ) { + /* the end is not a flat 90-degree end */ + outend = 1; + for(i = 0; i < 2; i++) + fixend[i] = gel->bkwd->bkwd->ipoints[i][2]; + } else { + outend = 0; + for(i = 0; i < 2; i++) + fixend[i] = gel->bkwd->ipoints[i][2]; + } + + for(i = 0; i < 2; i++) { + fixfront[i] *= fscale; + limfront[i] *= fscale; + fixend[i] *= fscale; + limend[i] *= fscale; + } + + fprintf(stderr, " %d out(%d[%d %d %d],%d[%d %d %d]) drnd(%d, %d)\n", + fti, + outfront, + (ge->ix3 == ge->bkwd->ix3), + (isign(ge->bkwd->ix3 - ge->frwd->ix3)==isign(ge->bkwd->iy3 - ge->frwd->iy3)), + (fti == GEXFI_CONCAVE), + outend, + (gel->ix3 == gel->bkwd->ix3), + (isign(gel->ix3 - gei->ix3)==isign(gel->iy3 - gei->iy3)), + (fti == GEXFI_CONVEX), + drnd[0], drnd[1]); + + /* prepare to calculate the distances */ + ndots = f->sublen - 1; + dots = malloc(sizeof(*dots) * ndots); + if(dots == 0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + for(i = 0, gei = ge; i < ndots; i++, gei = gei->frwd) { + for(a1 = 0; a1 < 2; a1++) + dots[i].p[a1] = fscale * gei->ipoints[a1][2]; + } + + /* see if we can mirror a ready symmetric curve */ + + /* check symmetry with the fragment before this */ + symfront = (pf != 0 && (pf->flags & GEXFF_SYMNEXT) && (pf->flags & GEXFF_DONE) + && ( outend && f->sublen <= pf->sublen + || ( pf->sublen == f->sublen + && (lf->sublen == 0 + || ( abs(limfront[0]-limend[0]) >= abs(pf->vect[0][0]-pf->vect[3][0]) + && abs(limfront[1]-limend[1]) >= abs(pf->vect[0][1]-pf->vect[3][1]) )) + ) + ) + ); + /* check symmetry with the fragment after this */ + symend = ( (f->flags & GEXFF_SYMNEXT) && (lf->flags & GEXFF_DONE) + && ( outfront && f->sublen <= lf->sublen + || ( lf->sublen == f->sublen + && (pf == 0 + || ( abs(limfront[0]-limend[0]) >= abs(lf->vect[0][0]-lf->vect[3][0]) + && abs(limfront[1]-limend[1]) >= abs(lf->vect[0][1]-lf->vect[3][1]) )) + ) + ) + ); + if(symfront || symend) { + /* mirror the symmetric neighbour subfrag */ + if(symfront) { + a1 = (ge->ix3 != ge->bkwd->ix3); /* the symmetry axis */ + a2 = !a1; /* the other axis (goes along the extremum gentry) */ + + /* the symmetry point on a2 */ + sympt = fscale * 0.5 * (ge->ipoints[a2][2] + ge->bkwd->ipoints[a2][2]); + xf = pf; /* the symmetric fragment */ + } else { + a1 = (gel->ix3 != gel->bkwd->ix3); /* the symmetry axis */ + a2 = !a1; /* the other axis (goes along the extremum gentry) */ + + /* the symmetry point on a2 */ + sympt = fscale * 0.5 * (gel->ipoints[a2][2] + gel->bkwd->ipoints[a2][2]); + xf = lf; /* the symmetric fragment */ + } + fprintf(stderr, " sym with %p f=%d(%p) e=%d(%p) a1=%c a2=%c sympt=%g\n", + xf, symfront, pf, symend, lf, + a1 ? 'Y': 'X', a2 ? 'Y': 'X', sympt + ); + for(i=0; i<4; i++) { + f->vect[3-i][a1] = xf->vect[i][a1]; + f->vect[3-i][a2] = sympt - (xf->vect[i][a2]-sympt); + } + if(symfront) { + if(outend || lf->sublen==0) + limcurve(f->vect, limend, 3); + } else { + if(outfront || pf == 0) + limcurve(f->vect, limfront, 0); + } + avg2 = fdotcurvdist2(f->vect, dots, ndots, &max2); + fprintf(stderr, " avg=%g max=%g fscale=%g\n", sqrt(avg2), sqrt(max2), fscale); + if(max2 <= fscale*fscale) { + f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE); + f->vectlen = f->sublen; + free(dots); + return; + } + } + + if( !outfront && !outend && f->symge != 0) { + /* a special case: try a circle segment as an approximation */ + double lenfront, lenend, len, maxlen; + + /* coefficient for a Bezier approximation of a circle */ +#define CIRCLE_FRAC 0.55 + + a1 = (ge->ix3 == ge->bkwd->ix3); /* get the axis along the front */ + a2 = !a1; /* axis along the end */ + + lenfront = fixfront[a1] - limfront[a1]; + lenend = fixend[a2] - limend[a2]; + if(fabs(lenfront) < fabs(lenend)) + len = fabs(lenfront); + else + len = fabs(lenend); + + /* make it go close to the round shape */ + switch(f->sublen) { + case 2: + maxlen = fscale; + break; + case 4: + case 6: + maxlen = fscale * 2.; + break; + default: + maxlen = fscale * abs(ge->frwd->frwd->ipoints[a1][2] + - ge->ipoints[a1][2]); + break; + } + if(len > maxlen) + len = maxlen; + + mvfront[a1] = fixfront[a1] - fsign(lenfront) * len; + mvfront[a2] = limfront[a2]; + mvend[a2] = fixend[a2] - fsign(lenend) * len; + mvend[a1] = limend[a1]; + + for(i=0; i<2; i++) { + f->vect[0][i] = mvfront[i]; + f->vect[3][i] = mvend[i]; + } + f->vect[1][a1] = mvfront[a1] + CIRCLE_FRAC*(mvend[a1]-mvfront[a1]); + f->vect[1][a2] = mvfront[a2]; + f->vect[2][a1] = mvend[a1]; + f->vect[2][a2] = mvend[a2] + CIRCLE_FRAC*(mvfront[a2]-mvend[a2]); + + avg2 = fdotcurvdist2(f->vect, dots, ndots, &max2); + fprintf(stderr, " avg=%g max=%g fscale=%g\n", sqrt(avg2), sqrt(max2), fscale); + if(max2 <= fscale*fscale) { + f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE); + f->vectlen = f->sublen; + free(dots); + return; + } +#undef CIRCLE_FRAC + } + for(i=0; i<2; i++) { + f->vect[0][i] = limfront[i]; + f->vect[1][i] = fixfront[i]; + f->vect[2][i] = fixend[i]; + f->vect[3][i] = limend[i]; + } + usedots = dots; + if(outfront) { + usedots++; ndots--; + } + if(outend) + ndots--; + if( fcrossrayscv(f->vect, NULL, NULL) == 0) { + fprintf(stderr, "**** Internal error: rays must cross but don't at %p-%p\n", + ge, gel); + fprintf(stderr, " (%g, %g) (%g, %g) (%g, %g) (%g, %g)\n", + limfront[0], limfront[1], + fixfront[0], fixfront[1], + fixend[0], fixend[1], + limend[0], limend[1] + ); + dumppaths(g, NULL, NULL); + exit(1); + } else { + if(ndots != 0) + fapproxcurve(f->vect, usedots, ndots); + f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE); + f->vectlen = f->sublen; + } + free(dots); +} + +/* + * Subtract a list of gentries (covered by a fragment of higher + * priority) from the set of fragments of a given + * type. + * + * An example is subtraction of the long exact line fragments + * from the curve fragments which get overridden. + */ + +static void +frag_subtract( + GLYPH *g, + GENTRY **age, /* array of gentries for this contour */ + int clen, /* length of the contour */ + GENTRY *ge, /* first gentry to be subtracted */ + int slen, /* number of gentries in the list to be subtracted */ + int d /* type of fragments from which to subtract, as in GEXFI_... */ +) +{ + GENTRY *pge; + GEX_FRAG *f, *pf; + int len, i, j; + + f = X_FRAG(ge); + len = slen; + + /* check if we overlap the end of some fragment */ + if(f->lenback[d]) { + /* chop off the end of conflicting fragment */ + len = f->lenback[d]; + pge = age[(f->aidx + clen - len)%clen]; + pf = X_FRAG(pge); + if(pf->len[d] == clen+1 && pf->flags & GEXFF_CIRC) { + /* the conflicting fragment is self-connected */ + + pf->len[d] = 0; + /* calculate the new value for lenback */ + len = clen+1 - slen; + for(pge = ge; len > 0; pge = pge->bkwd, len--) + X_FRAG(pge)->lenback[d] = len; + /* now pge points to the last entry of the line, + * which is also the new first entry of the curve + */ + X_FRAG(pge)->len[d] = clen+2 - slen; + /* clean lenback of gentries covered by the line */ + for(pge = ge->frwd, j = slen-1; j > 0; pge = pge->frwd, j--) + X_FRAG(pge)->lenback[d] = 0; + fprintf(stderr, " cut %s circular frag to %p-%p\n", + gxf_name[d], pge, ge); + gex_dump_contour(ge, clen); + } else { + /* when we chop off a piece of fragment, we leave the remaining + * piece(s) overlapping with the beginning and possibly the end + * of the line fragment under consideration + */ + fprintf(stderr, " cut %s frag at %p from len=%d to len=%d (end %p)\n", + gxf_name[d], pge, pf->len[d], len+1, ge); + j = pf->len[d] - len - 1; /* how many gentries are chopped off */ + pf->len[d] = len + 1; + i = slen - 1; + for(pge = ge->frwd; j > 0 && i > 0; j--, i--, pge = pge->frwd) + X_FRAG(pge)->lenback[d] = 0; + gex_dump_contour(ge, clen); + + if(j != 0) { + /* the conflicting fragment is split in two by this line + * fragment, fix up its tail + */ + + fprintf(stderr, " end of %s frag len=%d %p-", + gxf_name[d], j+1, pge->bkwd); + X_FRAG(pge->bkwd)->len[d] = j+1; /* the overlapping */ + for(i = 1; j > 0; j--, i++, pge = pge->frwd) + X_FRAG(pge)->lenback[d] = i; + fprintf(stderr, "%p\n", pge->bkwd); + gex_dump_contour(ge, clen); + } + } + } + /* check if we overlap the beginning of some fragments */ + i = slen-1; /* getntries remaining to consider */ + j = 0; /* gentries remaining in the overlapping fragment */ + for(pge = ge; i > 0; i--, pge = pge->frwd) { + if(j > 0) { + X_FRAG(pge)->lenback[d] = 0; + j--; + } + /* the beginning of one fragment may be the end of another + * fragment, in this case if j-- above results in 0, that will + * cause it to check the same gentry for the beginning + */ + if(j == 0) { + pf = X_FRAG(pge); + j = pf->len[d]; + if(j != 0) { + fprintf(stderr, " removed %s frag at %p len=%d\n", + gxf_name[d], pge, j); + gex_dump_contour(ge, clen); + pf->len[d] = 0; + j--; + } + } + } + /* pge points at the last gentry of the line fragment */ + if(j > 1) { /* we have the tail of a fragment left */ + fprintf(stderr, " end of %s frag len=%d %p-", + gxf_name[d], j, pge); + X_FRAG(pge)->len[d] = j; /* the overlapping */ + for(i = 0; j > 0; j--, i++, pge = pge->frwd) + X_FRAG(pge)->lenback[d] = i; + fprintf(stderr, "%p\n", pge->bkwd); + gex_dump_contour(ge, clen); + } else if(j == 1) { + X_FRAG(pge)->lenback[d] = 0; + } +} + +/* + * Produce an outline from a bitmap. + * scale - factor to scale the sizes + * bmap - array of dots by lines, xsz * ysz + * xoff, yoff - offset of the bitmap's lower left corner + * from the logical position (0,0) + */ + +void +bmp_outline( + GLYPH *g, + int scale, + char *bmap, + int xsz, + int ysz, + int xoff, + int yoff +) +{ + char *hlm, *vlm; /* arrays of the limits of outlines */ + char *amp; /* map of ambiguous points */ + int x, y; + char *ip, *op; + double fscale; + + if(xsz==0 || ysz==0) + return; + +#ifdef USE_AUTOTRACE + if(use_autotrace) { + autotraced_bmp_outline(g, scale, bmap, xsz, ysz, xoff, yoff); + return; + } +#endif /*USE_AUTOTRACE*/ + + fscale = (double)scale; + g->flags &= ~GF_FLOAT; /* build it as int first */ + + if(!warnedhints) { + warnedhints = 1; + if(hints && subhints) { + WARNING_2 fprintf(stderr, + "Use of hint substitution on bitmap fonts is not recommended\n"); + } + } + +#if 0 + printbmap(bmap, xsz, ysz, xoff, yoff); +#endif + + /* now find the outlines */ + hlm=calloc( xsz, ysz+1 ); /* horizontal limits */ + vlm=calloc( xsz+1, ysz ); /* vertical limits */ + amp=calloc( xsz, ysz ); /* ambiguous points */ + + if(hlm==0 || vlm==0 || amp==0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + + /* + * hlm and vlm represent a grid of horisontal and + * vertical lines. Each pixel is surrounded by the grid + * from all the sides. The values of [hv]lm mark the + * parts of grid where the pixel value switches from white + * to black and back. + */ + + /* find the horizontal limits */ + ip=bmap; op=hlm; + /* 1st row */ + for(x=0; x0; y--) + for(x=xsz-1; x>0; x--) { + if(bmap[y*xsz+x]) { + if( !bmap[y*xsz+x-1] && !bmap[y*xsz-xsz+x] && bmap[y*xsz-xsz+x-1] ) + amp[y*xsz+x]=1; + } else { + if( bmap[y*xsz+x-1] && bmap[y*xsz-xsz+x] && !bmap[y*xsz-xsz+x-1] ) + amp[y*xsz+x]=1; + } + } + +#if 0 + printlimits(hlm, vlm, amp, xsz, ysz); +#endif + + /* generate the vectored (stepping) outline */ + + while(1) { + int found = 0; + int outer; /* flag: this is an outer contour */ + int hor, newhor; /* flag: the current contour direction is horizontal */ + int dir; /* previous direction of the coordinate, 1 - L_ON, 0 - L_OFF */ + int startx, starty; /* start of a contour */ + int firstx, firsty; /* start of the current line */ + int newx, newy; /* new coordinates to try */ + char *lm, val; + int maxx, maxy, xor; + + for(y=ysz; !found && y>0; y--) + for(x=0; x L_NONE) + goto foundcontour; + break; /* have no contours left */ + + foundcontour: + ig_rmoveto(g, x+xoff, y+yoff); /* intermediate as int */ + + startx = firstx = x; + starty = firsty = y; + + if(hlm[y*xsz+x] == L_OFF) { + outer = 1; dir = 0; + hlm[y*xsz+x] = -hlm[y*xsz+x]; /* mark as seen */ + hor = 1; x++; + } else { + outer = 0; dir = 0; + hor = 0; y--; + vlm[y*(xsz+1)+x] = -vlm[y*(xsz+1)+x]; /* mark as seen */ + } + + while(x!=startx || y!=starty) { +#if 0 + printf("trace (%d, %d) outer=%d hor=%d dir=%d\n", x, y, outer, hor, dir); +#endif + + /* initialization common for try1 and try2 */ + if(hor) { + lm = vlm; maxx = xsz+1; maxy = ysz; newhor = 0; + } else { + lm = hlm; maxx = xsz; maxy = ysz+1; newhor = 1; + } + xor = (outer ^ hor ^ dir); + + try1: + /* first we try to change axis, to keep the + * contour as long as possible + */ + + newx = x; newy = y; + if(!hor && (!outer ^ dir)) + newx--; + if(hor && (!outer ^ dir)) + newy--; + + if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy) + goto try2; + + if(!xor) + val = L_ON; + else + val = L_OFF; +#if 0 + printf("try 1, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx], + (newhor ? 'h':'v'), newx, newy); +#endif + if( lm[newy*maxx + newx] == val ) + goto gotit; + + try2: + /* try to change the axis anyway */ + + newx = x; newy = y; + if(!hor && (outer ^ dir)) + newx--; + if(hor && (outer ^ dir)) + newy--; + + if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy) + goto try3; + + if(xor) + val = L_ON; + else + val = L_OFF; +#if 0 + printf("try 2, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx], + (newhor ? 'h':'v'), newx, newy); +#endif + if( lm[newy*maxx + newx] == val ) + goto gotit; + + try3: + /* try to continue in the old direction */ + + if(hor) { + lm = hlm; maxx = xsz; maxy = ysz+1; + } else { + lm = vlm; maxx = xsz+1; maxy = ysz; + } + newhor = hor; + newx = x; newy = y; + if(hor && dir) + newx--; + if(!hor && !dir) + newy--; + + if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy) + goto badtry; + + if(dir) + val = L_ON; + else + val = L_OFF; +#if 0 + printf("try 3, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx], + (newhor ? 'h':'v'), newx, newy); +#endif + if( lm[newy*maxx + newx] == val ) + goto gotit; + + badtry: + fprintf(stderr, "**** Internal error in the contour detection code at (%d, %d)\n", x, y); + fprintf(stderr, "glyph='%s' outer=%d hor=%d dir=%d\n", g->name, outer, hor, dir); + fflush(stdout); + exit(1); + + gotit: + if(hor != newhor) { /* changed direction, end the previous line */ + ig_rlineto(g, x+xoff, y+yoff); /* intermediate as int */ + firstx = x; firsty = y; + } + lm[newy*maxx + newx] = -lm[newy*maxx + newx]; + hor = newhor; + dir = (val == L_ON); + if(newhor) + x -= (dir<<1)-1; + else + y += (dir<<1)-1; + } +#if 0 + printf("trace (%d, %d) outer=%d hor=%d dir=%d\n", x, y, outer, hor, dir); +#endif + ig_rlineto(g, x+xoff, y+yoff); /* intermediate as int */ + g_closepath(g); + } + + + /* try to vectorize the curves and sloped lines in the bitmap */ + if(vectorize) { + GENTRY *ge, *pge, *cge, *loopge; + int i; + int skip; + + dumppaths(g, NULL, NULL); + + /* allocate the extensions */ + for(cge=g->entries; cge!=0; cge=cge->next) { + cge->ext = calloc(1, sizeof(GEX_FRAG) ); + if(cge->ext == 0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } + + for(cge=g->entries; cge!=0; cge=cge->next) { + if(cge->type != GE_MOVE) + continue; + + /* we've found the beginning of a contour */ + { + int d, vert, count, stepmore, delaystop; + int vdir, hdir, fullvdir, fullhdir, len; + int dx, dy, lastdx, lastdy; + int k1, k2, reversal, smooth, good; + int line[2 /*H,V*/], maxlen[2 /*H,V*/], minlen[2 /*H,V*/]; + GENTRY **age; /* array of gentries in a contour */ + int clen; /* contour length, size of ths array */ + int i, j; + GEX_FRAG *f; + + /* we know that all the contours start at the top-left corner, + * so at most it might be before/after the last element of + * the last/first fragment + */ + + ge = cge->next; + pge = ge->bkwd; + if(ge->ix3 == pge->ix3) { /* a vertical line */ + /* we want to start always from a horizontal line because + * then we always start from top and that is quaranteed to be a + * fragment boundary, so move the start point of the contour + */ + pge->prev->next = pge->next; + pge->next->prev = pge->prev; + cge->next = pge; + pge->prev = cge; + pge->next = ge; + ge->prev = pge; + ge = pge; pge = ge->bkwd; + cge->ix3 = pge->ix3; cge->iy3 = pge->iy3; + } + + /* fill the array of gentries */ + clen = 1; + for(ge = cge->next->frwd; ge != cge->next; ge = ge->frwd) + clen++; + age = (GENTRY **)malloc(sizeof(*age) * clen); + ge = cge->next; + count = 0; + do { + age[count] = ge; + X_FRAG(ge)->aidx = count++; + + /* and by the way find the extremums */ + for(i=0; i<2; i++) { + if( isign(ge->frwd->ipoints[i][2] - ge->ipoints[i][2]) + * isign(ge->bkwd->bkwd->ipoints[i][2] - ge->bkwd->ipoints[i][2]) == 1) { + X_FRAG(ge)->flags |= GEXFF_EXTR; + fprintf(stderr, " %s extremum at %p\n", (i?"vert":"hor"), ge); + } + if(abs(ge->ipoints[i][2] - ge->bkwd->ipoints[i][2]) > 1) + X_FRAG(ge)->flags |= GEXFF_LONG; + } + + ge = ge->frwd; + } while(ge != cge->next); + + /* Find the serif fragments, looking as either of: + * -+ | + * | | + * +-+ +-+ + * | | + * +--... +--... + * with the thickness of serifs being 1 pixel. We make no + * difference between serifs on vertical and horizontal stems. + */ + + ge = cge->next; + do { + GENTRY *nge; + int pdx, pdy, ndx, ndy; + + /* two gentries of length 1 mean a potential serif */ + pge = ge->bkwd; + nge = ge->frwd; + + dx = nge->ix3 - pge->ix3; + dy = nge->iy3 - pge->iy3; + + if(abs(dx) != 1 || abs(dy) != 1) /* 2 small ones */ + continue; + + if( + (!(X_FRAG(ge)->flags & GEXFF_EXTR) + || !(X_FRAG(ge->bkwd)->flags & GEXFF_EXTR)) + && (!(X_FRAG(ge->frwd)->flags & GEXFF_EXTR) + || !(X_FRAG(ge->frwd->frwd)->flags & GEXFF_EXTR)) + ) + continue; /* either side must be a couple of extremums */ + + pdx = pge->ix3 - pge->bkwd->ix3; + pdy = pge->iy3 - pge->bkwd->iy3; + ndx = nge->frwd->ix3 - nge->ix3; + ndy = nge->frwd->iy3 - nge->iy3; + + if(pdx*dx + pdy*dy > 0 && ndx*dx + ndy*dy > 0) + continue; /* definitely not a serif but a round corner */ + + if(abs(pdx) + abs(pdy) == 1 || abs(ndx) + abs(ndy) == 1) + continue; + + /* we've found a serif including this and next gentry */ + X_FRAG(ge)->len[GEXFI_SERIF] = 2; + + } while( (ge = ge->frwd) != cge->next ); + + + /* Find the convex and concave fragments, defined as: + * convex (clockwise): dy/dx <= dy0/dx0, + * or a reversal: dy/dx == - dy0/dx0 && abs(dxthis) == 1 && dy/dx > 0 + * concave (counter-clockwise): dy/dx >= dy0/dx0, + * or a reversal: dy/dx == - dy0/dx0 && abs(dxthis) == 1 && dy/dx < 0 + * + * Where dx and dy are measured between the end of this gentry + * and the start of the previous one (dx0 and dy0 are the same + * thing calculated for the previous gentry and its previous one), + * dxthis is between the end and begginning of this gentry. + * + * A reversal is a situation when the curve changes its direction + * along the x axis, so it passes through a momentary vertical + * direction. + */ + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + ge = cge->next; + pge = ge->bkwd; /* the beginning of the fragment */ + count = 1; + lastdx = pge->ix3 - pge->bkwd->bkwd->ix3; + lastdy = pge->iy3 - pge->bkwd->bkwd->iy3; + +#define CHKCURVCONN(ge, msg) do { \ + dx = (ge)->ix3 - (ge)->bkwd->bkwd->ix3; \ + dy = (ge)->iy3 - (ge)->bkwd->bkwd->iy3; \ + if(0 && msg) { \ + fprintf(stderr, " %p: dx=%d dy=%d dx0=%d dy0=%d ", \ + (ge), dx, dy, lastdx, lastdy); \ + } \ + k1 = X_FRAG(ge)->flags; \ + k2 = X_FRAG((ge)->bkwd)->flags; \ + if(0 && msg) { \ + fprintf(stderr, "fl=%c%c%c%c ", \ + (k1 & GEXFF_EXTR) ? 'X' : '-', \ + (k1 & GEXFF_LONG) ? 'L' : '-', \ + (k2 & GEXFF_EXTR) ? 'X' : '-', \ + (k2 & GEXFF_LONG) ? 'L' : '-' \ + ); \ + } \ + if( (k1 & GEXFF_EXTR) && (k2 & GEXFF_LONG) \ + || (k2 & GEXFF_EXTR) && (k1 & GEXFF_LONG) ) { \ + smooth = 0; \ + good = reversal = -1; /* for debugging */ \ + } else { \ + k1 = dy * lastdx; \ + k2 = lastdy * dx; \ + smooth = (abs(dx)==1 || abs(dy)==1); \ + good = (k1 - k2)*gxf_cvk[d] >= 0; \ + if(smooth && !good) { \ + reversal = (k1 == -k2 && abs((ge)->ix3 - (ge)->bkwd->ix3)==1 \ + && dy*dx*gxf_cvk[d] < 0); \ + } else \ + reversal = 0; \ + } \ + if(0 && msg) { \ + fprintf(stderr, "k1=%d k2=%d pge=%p count=%d %s good=%d rev=%d\n", \ + k1, k2, pge, count, gxf_name[d], good, reversal); \ + } \ + } while(0) + + do { + CHKCURVCONN(ge, 1); + + if(smooth && (good || reversal) ) + count++; + else { + /* can't continue */ +#if 0 + if(count >= 4) { /* worth remembering */ + fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); + } +#endif + X_FRAG(pge)->len[d] = count; + if(smooth) { + pge = ge->bkwd; + count = 2; + } else { + pge = ge; + count = 1; + } + } + lastdx = dx; lastdy = dy; + ge = ge->frwd; + } while(ge != cge->next); + + /* see if we can connect the last fragment to the first */ + CHKCURVCONN(ge, 1); + + if(smooth && (good || reversal) ) { + /* -1 to avoid ge->bkwd being counted twice */ + if( X_FRAG(ge->bkwd)->len[d] >= 2 ) + count += X_FRAG(ge->bkwd)->len[d] - 1; + else if(count == clen+1) { + /* we are joining a circular (closed) curve, check whether it + * can be joined at any point or whether it has a discontinuity + * at the point where we join it now + */ + lastdx = dx; lastdy = dy; + CHKCURVCONN(ge->frwd, 0); + + if(smooth && (good || reversal) ) { + /* yes, the curve is truly a circular one and can be + * joined at any point + */ + +#if 0 + fprintf(stderr, " found a circular joint point at %p\n", pge); +#endif + /* make sure that in a circular fragment we start from an extremum */ + while( ! (X_FRAG(pge)->flags & GEXFF_EXTR) ) + pge = pge->frwd; + X_FRAG(pge)->flags |= GEXFF_CIRC; + } + } +#if 0 + fprintf(stderr, " %s joined %p to %p count=%d bk_count=%d\n", gxf_name[d], pge, ge->bkwd, + count, X_FRAG(ge->bkwd)->len[d] ); +#endif + X_FRAG(ge->bkwd)->len[d] = 0; + } + X_FRAG(pge)->len[d] = count; +#if 0 + if(count >= 4) { /* worth remembering */ + fprintf(stderr, " %s last frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); + } +#endif +#undef CHKCURVCONN + + /* do postprocessing */ + ge = cge->next; + do { + f = X_FRAG(ge); + len = f->len[d]; +#if 0 + fprintf(stderr, " %p %s len=%d clen=%d\n", ge, gxf_name[d], len, clen); +#endif + if(len < 3) /* get rid of the fragments that are too short */ + f->len[d] = 0; + else if(len == 3) { + /* _ + * drop the |_| - shaped fragments, leave alone the _| - shaped + * (and even those only if not too short in pixels), + * those left alone are further filtered later + */ + k1 = (ge->ix3 == ge->bkwd->ix3); /* axis of the start */ + if(isign(ge->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) + != isign(ge->frwd->ipoints[k1][2] - ge->frwd->frwd->ipoints[k1][2]) + && abs(ge->frwd->frwd->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) > 2) { +#if 0 + fprintf(stderr, " %s frag %p count=%d good shape\n", + gxf_name[d], ge, count); +#endif + } else + f->len[d] = 0; + } else if(len == clen+1) + break; /* a closed fragment, nothing else interesting */ + else { /* only for open fragments */ + GENTRY *gem, *gex, *gei, *ges; + + ges = ge; /* the start entry */ + gem = age[(f->aidx + f->len[d])%clen]; /* entry past the end of the fragment */ + + gei = ge->frwd; + if( (ge->ix3 == ge->bkwd->ix3) /* vert */ + ^ (isign(ge->bkwd->ix3 - gei->ix3)==isign(ge->bkwd->iy3 - gei->iy3)) + ^ !(d == GEXFI_CONVEX) /* counter-clockwise */ ) { + +#if 0 + fprintf(stderr, " %p: %s potential spurious start\n", ge, gxf_name[d]); +#endif + /* the beginning may be a spurious entry */ + + gex = 0; /* the extremum closest to the beginning - to be found */ + for(gei = ge->frwd; gei != gem; gei = gei->frwd) { + if(X_FRAG(gei)->flags & GEXFF_EXTR) { + gex = gei; + break; + } + } + if(gex == 0) + gex = gem->bkwd; + + /* A special case: ignore the spurious ends on small curves that + * either enclose an 1-pixel-wide extremum or are 1-pixel deep. + * Any 5-or-less-pixel-long curve with extremum 2 steps away + * qualifies for that. + */ + + if(len <= 5 && gex == ge->frwd->frwd) { + good = 0; +#if 0 + fprintf(stderr, " E"); +#endif + } else { + good = 1; /* assume that ge is not spurious */ + + /* gei goes backwards, gex goes forwards from the extremum */ + gei = gex; + /* i is the symmetry axis, j is the other axis (X=0 Y=1) */ + i = (gex->ix3 != gex->bkwd->ix3); + j = !i; + for( ; gei!=ge && gex!=gem; gei=gei->bkwd, gex=gex->frwd) { + if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2] + || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2] + != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] + ) { + good = 0; /* no symmetry - must be spurious */ +#if 0 + fprintf(stderr, " M(%p,%p)(%d %d,%d)(%d %d,%d)", + gei, gex, + i, gei->bkwd->ipoints[i][2], gex->ipoints[i][2], + j, gei->bkwd->ipoints[j][2] - gei->ipoints[j][2], + gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] ); +#endif + break; + } + } + if(gex == gem) { /* oops, the other side is too short */ + good = 0; +#if 0 + fprintf(stderr, " X"); +#endif + } + if(good && gei == ge) { + if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]) + != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) { + good = 0; /* oops, goes into another direction */ +#if 0 + fprintf(stderr, " D"); +#endif + } + } + } + if(!good) { /* it is spurious, drop it */ +#if 0 + fprintf(stderr, " %p: %s spurious start\n", ge, gxf_name[d]); +#endif + f->len[d] = 0; + ges = ge->frwd; + len--; + X_FRAG(ges)->len[d] = len; + } + } + + gei = gem->bkwd->bkwd->bkwd; + if( (gem->ix3 != gem->bkwd->ix3) /* gem->bkwd is vert */ + ^ (isign(gem->bkwd->ix3 - gei->ix3)==isign(gem->bkwd->iy3 - gei->iy3)) + ^ (d == GEXFI_CONVEX) /* clockwise */ ) { + +#if 0 + fprintf(stderr, " %p: %s potential spurious end\n", gem->bkwd, gxf_name[d]); +#endif + /* the end may be a spurious entry */ + + gex = 0; /* the extremum closest to the end - to be found */ + for(gei = gem->bkwd->bkwd; gei != ges->bkwd; gei = gei->bkwd) { + if(X_FRAG(gei)->flags & GEXFF_EXTR) { + gex = gei; + break; + } + } + if(gex == 0) + gex = ges; + + good = 1; /* assume that gem->bkwd is not spurious */ + /* gei goes backwards, gex goes forwards from the extremum */ + gei = gex; + /* i is the symmetry axis, j is the other axis (X=0 Y=1) */ + i = (gex->ix3 != gex->bkwd->ix3); + j = !i; + for( ; gei!=ges->bkwd && gex!=gem->bkwd; gei=gei->bkwd, gex=gex->frwd) { + if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2] + || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2] + != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] + ) { + good = 0; /* no symmetry - must be spurious */ +#if 0 + fprintf(stderr, " M(%p,%p)(%d %d,%d)(%d %d,%d)", + gei, gex, + i, gei->bkwd->ipoints[i][2], gex->ipoints[i][2], + j, gei->bkwd->ipoints[j][2] - gei->ipoints[j][2], + gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] ); +#endif + break; + } + } + if(gei == ges->bkwd) { /* oops, the other side is too short */ + good = 0; +#if 0 + fprintf(stderr, " X"); +#endif + } + if(good && gex == gem->bkwd) { + if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]) + != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) { + good = 0; /* oops, goes into another direction */ +#if 0 + fprintf(stderr, " D"); +#endif + } + } + if(!good) { /* it is spurious, drop it */ +#if 0 + fprintf(stderr, " %p: %s spurious end\n", gem->bkwd, gxf_name[d]); +#endif + X_FRAG(ges)->len[d] = --len; + } + } + if(len < 4) { + X_FRAG(ges)->len[d] = 0; +#if 0 + fprintf(stderr, " %p: %s frag discarded, too small now\n", ge, gxf_name[d]); +#endif + } + if(ges != ge) { + if(ges == cge->next) + break; /* went around the loop */ + else { + ge = ges->frwd; /* don't look at this fragment twice */ + continue; + } + } + } + + ge = ge->frwd; + } while(ge != cge->next); + } + + /* Find the straight line fragments. + * Even though the lines are sloped, they are called + * "vertical" or "horizontal" according to their longer + * dimension. All the steps in the shother dimension must + * be 1 pixel long, all the steps in the longer dimension + * must be within the difference of 1 pixel. + */ + for(d = GEXFI_LINE; d<= GEXFI_EXACTLINE; d++) { + ge = cge->next; + pge = ge->bkwd; /* the beginning of the fragment */ + count = 1; + delaystop = 0; + do { + int h; + + stepmore = 0; + hdir = isign(ge->ix3 - ge->bkwd->ix3); + vdir = isign(ge->iy3 - ge->bkwd->iy3); + vert = (hdir == 0); + if(count==1) { + /* at this point pge==ge->bkwd */ + /* account for the previous gentry, which was !vert */ + if(!vert) { /* prev was vertical */ + maxlen[0] = minlen[0] = 0; + maxlen[1] = minlen[1] = abs(pge->iy3 - pge->bkwd->iy3); + line[0] = (maxlen[1] == 1); + line[1] = 1; + fullhdir = hdir; + fullvdir = isign(pge->iy3 - pge->bkwd->iy3); + } else { + maxlen[0] = minlen[0] = abs(pge->ix3 - pge->bkwd->ix3); + maxlen[1] = minlen[1] = 0; + line[0] = 1; + line[1] = (maxlen[0] == 1); + fullhdir = isign(pge->ix3 - pge->bkwd->ix3); + fullvdir = vdir; + } + } + h = line[0]; /* remember the prevalent direction */ +#if 0 + fprintf(stderr, " %p: v=%d(%d) h=%d(%d) vl(%d,%d,%d) hl=(%d,%d,%d) %s count=%d ", + ge, vdir, fullvdir, hdir, fullhdir, + line[1], minlen[1], maxlen[1], + line[0], minlen[0], maxlen[0], + gxf_name[d], count); +#endif + if(vert) { + if(vdir != fullvdir) + line[0] = line[1] = 0; + len = abs(ge->iy3 - ge->bkwd->iy3); + } else { + if(hdir != fullhdir) + line[0] = line[1] = 0; + len = abs(ge->ix3 - ge->bkwd->ix3); + } +#if 0 + fprintf(stderr, "len=%d\n", len); +#endif + if(len != 1) /* this is not a continuation in the short dimension */ + line[!vert] = 0; + + /* can it be a continuation in the long dimension ? */ + if( line[vert] ) { + if(maxlen[vert]==0) + maxlen[vert] = minlen[vert] = len; + else if(maxlen[vert]==minlen[vert]) { + if(d == GEXFI_EXACTLINE) { + if(len != maxlen[vert]) + line[vert] = 0; /* it can't */ + } else if(len < maxlen[vert]) { + if(len < minlen[vert]-1) + line[vert] = 0; /* it can't */ + else + minlen[vert] = len; + } else { + if(len > maxlen[vert]+1) + line[vert] = 0; /* it can't */ + else + maxlen[vert] = len; + } + } else if(len < minlen[vert] || len > maxlen[vert]) + line[vert] = 0; /* it can't */ + } + + if(line[0] == 0 && line[1] == 0) { +#if 0 + if(count >= 3) + fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); +#endif + X_FRAG(pge)->len[d] = count; + if(d == GEXFI_EXACTLINE && h) { + X_FRAG(pge)->flags |= GEXFF_HLINE; + } + if(count == 1) + pge = ge; + else { + stepmore = 1; /* may reconsider the 1st gentry */ + pge = ge = ge->bkwd; + count = 1; + } + } else + count++; + + ge = ge->frwd; + if(ge == cge->next && !stepmore) + delaystop = 1; /* consider the first gentry again */ + } while(stepmore || ge != cge->next ^ delaystop); + /* see if there is an unfinished line left */ + if(count != 1) { +#if 0 + if(count >= 3) + fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); +#endif + X_FRAG(ge->bkwd->bkwd)->len[d] = 0; + X_FRAG(pge)->len[d] = count; + } + } + + /* do postprocessing of the lines */ +#if 0 + fprintf(stderr, "Line postprocessing\n"); + gex_dump_contour(cge->next, clen); +#endif + + /* the non-exact line frags are related to exact line frags sort + * of like to individual gentries: two kinds of exact frags + * must be interleaved, with one kind having the size of 3 + * and the other kind having the size varying within +-2. + */ + + ge = cge->next; + do { + GEX_FRAG *pf, *lastf1, *lastf2; + int len1, len2, fraglen; + + f = X_FRAG(ge); + + fraglen = f->len[GEXFI_LINE]; + if(fraglen >= 4) { + + vert = 0; /* vert is a pseudo-directon */ + line[0] = line[1] = 1; + maxlen[0] = minlen[0] = maxlen[1] = minlen[1] = 0; + lastf2 = lastf1 = f; + len2 = len1 = 0; + for(pge = ge, i = 1; i < fraglen; i++, pge=pge->frwd) { + pf = X_FRAG(pge); + len = pf->len[GEXFI_EXACTLINE]; +#if 0 + fprintf(stderr, " pge=%p i=%d of %d ge=%p exLen=%d\n", pge, i, + f->len[GEXFI_LINE], ge, len); +#endif + len1++; len2++; + if(len==0) { + continue; + } + vert = !vert; /* alternate the pseudo-direction */ + if(len > 3) + line[!vert] = 0; + if(maxlen[vert] == 0) + maxlen[vert] = minlen[vert] = len; + else if(maxlen[vert]-2 >= len && minlen[vert]+2 <= len) { + if(len > maxlen[vert]) + maxlen[vert] = len; + else if(len < minlen[vert]) + minlen[vert] = len; + } else + line[vert] = 0; + if(line[0] == 0 && line[1] == 0) { +#if 0 + fprintf(stderr, " Line breaks at %p %c(%d, %d) %c(%d, %d) len=%d fl=%d l2=%d l1=%d\n", + pge, (!vert)?'*':' ', minlen[0], maxlen[0], + vert?'*':' ', minlen[1], maxlen[1], len, fraglen, len2, len1); +#endif + if(lastf2 != lastf1) { + lastf2->len[GEXFI_LINE] = len2-len1; + } + lastf1->len[GEXFI_LINE] = len1+1; + pf->len[GEXFI_LINE] = fraglen+1 - i; +#if 0 + gex_dump_contour(pge, clen); +#endif + + /* continue with the line */ + vert = 0; /* vert is a pseudo-directon */ + line[0] = line[1] = 1; + maxlen[0] = minlen[0] = maxlen[1] = minlen[1] = 0; + lastf2 = lastf1 = f; + len2 = len1 = 0; + } else { + lastf1 = pf; + len1 = 0; + } + } + } + + ge = ge->frwd; + } while(ge != cge->next); +#if 0 + fprintf(stderr, "Line postprocessing part 2\n"); + gex_dump_contour(cge->next, clen); +#endif + + ge = cge->next; + do { + f = X_FRAG(ge); + + if(f->len[GEXFI_LINE] >= 4) { + len = f->len[GEXFI_EXACTLINE]; + /* if a non-exact line covers precisely two exact lines, + * split it + */ + if(len > 0 && f->len[GEXFI_LINE] >= len+1) { + GEX_FRAG *pf; + pge = age[(f->aidx + len - 1)%clen]; /* last gentry of exact line */ + pf = X_FRAG(pge); + if(f->len[GEXFI_LINE] + 1 == len + pf->len[GEXFI_EXACTLINE]) { + f->len[GEXFI_LINE] = len; + f->flags |= GEXFF_SPLIT; + pf->len[GEXFI_LINE] = pf->len[GEXFI_EXACTLINE]; + pf->flags |= GEXFF_SPLIT; + } + } + } + + ge = ge->frwd; + } while(ge != cge->next); +#if 0 + fprintf(stderr, "Line postprocessing part 2a\n"); + gex_dump_contour(cge->next, clen); +#endif + ge = cge->next; + do { + f = X_FRAG(ge); + + /* too small lines are of no interest */ + if( (f->flags & GEXFF_SPLIT)==0 && f->len[GEXFI_LINE] < 4) + f->len[GEXFI_LINE] = 0; + + len = f->len[GEXFI_EXACTLINE]; + /* too small exact lines are of no interest */ + if(len < 3) /* exact lines may be shorter */ + f->len[GEXFI_EXACTLINE] = 0; + /* get rid of inexact additions to the end of the exact lines */ + else if(f->len[GEXFI_LINE] == len+1) + f->len[GEXFI_LINE] = len; + /* same at the beginning */ + else { + int diff = X_FRAG(ge->bkwd)->len[GEXFI_LINE] - len; + + if(diff == 1 || diff == 2) { + X_FRAG(ge->bkwd)->len[GEXFI_LINE] = 0; + f->len[GEXFI_LINE] = len; + } + } + + ge = ge->frwd; + } while(ge != cge->next); +#if 0 + fprintf(stderr, "Line postprocessing is completed\n"); + gex_dump_contour(cge->next, clen); +#endif + + gex_calc_lenback(cge->next, clen); /* prepare data */ + + /* resolve conflicts between lines and curves */ + + /* + * the short (3-gentry) curve frags must have one of the ends + * coinciding with another curve frag of the same type + */ + + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + ge = cge->next; + do { + f = X_FRAG(ge); + + if(f->len[d] == 3) { + pge = age[(f->aidx + 2)%clen]; /* last gentry of this frag */ + if(f->lenback[d] == 0 && X_FRAG(pge)->len[d] == 0) { + fprintf(stderr, " discarded small %s at %p-%p\n", gxf_name[d], ge, pge); + f->len[d] = 0; + X_FRAG(ge->frwd)->lenback[d] = 0; + X_FRAG(ge->frwd->frwd)->lenback[d] = 0; + } + } + ge = ge->frwd; + } while(ge != cge->next); + } + + /* the serifs take priority over everything else */ + ge = cge->next; + do { + f = X_FRAG(ge); + + len = f->len[GEXFI_SERIF]; + if(len == 0) + continue; + + if(len != 2) { /* this is used in the code below */ + fprintf(stderr, "Internal error at %s line %d: serif frags len is %d\n", + __FILE__, __LINE__, len); + exit(1); + } + + for(d = 0; d < GEXFI_SERIF; d++) { + /* serifs may not have common ends with the other fragments, + * this is expressed as extending them by 1 gentry on each side + */ + frag_subtract(g, age, clen, ge->bkwd, len+2, d); + } + } while( (ge = ge->frwd) != cge->next); + + /* + * longer exact lines take priority over curves; shorter lines + * and inexact lines are resolved with convex/concave conflicts + */ + ge = cge->next; + do { + f = X_FRAG(ge); + + len = f->len[GEXFI_EXACTLINE]; + + if(len < 6) { /* line is short */ + ge = ge->frwd; + continue; + } + + fprintf(stderr, " line at %p len=%d\n", ge, f->len[GEXFI_EXACTLINE]); + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + frag_subtract(g, age, clen, ge, len, d); + } + + ge = ge->frwd; + } while(ge != cge->next); + + /* + * The exact lines take priority over curves that coincide + * with them or extend by only one gentry on either side + * (but not both sides). By this time it applies only to the + * small exact lines. + * + * An interesting general case is when a curve matches more + * than one exact line going diamond-like. + */ + + ge = cge->next; + do { + int done, len2; + int sharpness; + GEX_FRAG *pf; + + f = X_FRAG(ge); + + /* "sharpness" shows how a group of exact line frags is connected: if the gentries + * of some of them overlap, the curve matching requirement is loosened: it may + * extend up to 1 gentry beyond each end of the group of exact line frags + * (sharpness=2); otherwise it may extend to only one end (sharpness=1) + */ + sharpness = 1; + + len = f->len[GEXFI_EXACTLINE]; + if(len >= 4) { + while(len < clen) { + done = 0; + pf = X_FRAG(ge->bkwd); + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + if(f->len[d] == len || f->len[d] == len+1) { + + fprintf(stderr, " removed %s frag at %p len=%d linelen=%d\n", + gxf_name[d], ge, f->len[d], len); + pge = ge->frwd; + for(i = f->len[d]; i > 1; i--, pge = pge->frwd) + X_FRAG(pge)->lenback[d] = 0; + f->len[d] = 0; + gex_dump_contour(ge, clen); + done = 1; + } else if(pf->len[d] == len+1 || pf->len[d] == len+sharpness) { + fprintf(stderr, " removed %s frag at %p len=%d next linelen=%d\n", + gxf_name[d], ge->bkwd, pf->len[d], len); + pge = ge; + for(i = pf->len[d]; i > 1; i--, pge = pge->frwd) + X_FRAG(pge)->lenback[d] = 0; + pf->len[d] = 0; + gex_dump_contour(ge, clen); + done = 1; + } + } + if(done) + break; + + /* is there any chance to match a sequence of exect lines ? */ + if(f->len[GEXFI_CONVEX] < len && f->len[GEXFI_CONCAVE] < len + && pf->len[GEXFI_CONVEX] < len && pf->len[GEXFI_CONCAVE] < len) + break; + + done = 1; + /* check whether the line is connected to another exact line at an extremum */ + pge = age[(f->aidx + len - 1)%clen]; /* last gentry of exact line */ + len2 = X_FRAG(pge)->len[GEXFI_EXACTLINE]; + if(len2 > 0) { + if( len2 >= 4 && (X_FRAG(pge)->flags & GEXFF_EXTR) ) { + len += len2 - 1; + sharpness = 2; + done = 0; + } + } else { + /* see if the extremum is between two exact lines */ + pge = pge->frwd; + if(X_FRAG(pge)->flags & GEXFF_EXTR) { + pge = pge->frwd; + len2 = X_FRAG(pge)->len[GEXFI_EXACTLINE]; + if(len2 >= 4) { + len += len2 + 1; + done = 0; + } + } + } + if(done) + break; + } + } + + ge = ge->frwd; + } while(ge != cge->next); + + /* + * The lines may cover only whole curves (or otherwise empty space), + * so cut them where they overlap parts of the curves. If 2 or less + * gentries are left in the line, remove the line. + * If a line and a curve fully coincide, remove the line. Otherwise + * remove the curves that are completely covered by the lines. + */ + + ge = cge->next; + do { + f = X_FRAG(ge); + + reconsider_line: + len = f->len[GEXFI_LINE]; + + if(len == 0) { + ge = ge->frwd; + continue; + } + + if(f->len[GEXFI_CONVEX] >= len + || f->len[GEXFI_CONCAVE] >= len) { + line_completely_covered: + fprintf(stderr, " removed covered Line frag at %p len=%d\n", + ge, len); + f->len[GEXFI_LINE] = 0; + for(pge = ge->frwd; len > 1; len--, pge = pge->frwd) + X_FRAG(pge)->lenback[GEXFI_LINE] = 0; + gex_dump_contour(ge, clen); + ge = ge->frwd; + continue; + } + + k1 = 0; /* how much to cut at the front */ + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + if(f->lenback[d]) { + pge = age[(f->aidx + clen - f->lenback[d])%clen]; + i = X_FRAG(pge)->len[d] - f->lenback[d] - 1; + if(i > k1) + k1 = i; + } + } + + k2 = 0; /* how much to cut at the end */ + pge = age[(f->aidx + len)%clen]; /* gentry after the end */ + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + i = X_FRAG(pge)->lenback[d] - 1; + if(i > k2) + k2 = i; + } + + if(k1+k2 > 0 && k1+k2 >= len-3) { + fprintf(stderr, " k1=%d k2=%d\n", k1, k2); + goto line_completely_covered; + } + + + if(k2 != 0) { /* cut the end */ + len -= k2; + f->len[GEXFI_LINE] = len; + /* pge still points after the end */ + for(i = k2, pge = pge->bkwd; i > 0; i--, pge = pge->bkwd) + X_FRAG(pge)->lenback[GEXFI_LINE] = 0; + } + if(k1 != 0) { /* cut the beginning */ + len -= k1; + f->len[GEXFI_LINE] = 0; + for(i = 1, pge = ge->frwd; i < k1; i++, pge = pge->frwd) + X_FRAG(pge)->lenback[GEXFI_LINE] = 0; + X_FRAG(pge)->len[GEXFI_LINE] = len; + for(i = 0; i < len; i++, pge = pge->frwd) + X_FRAG(pge)->lenback[GEXFI_LINE] = i; + } + if(k1 != 0 || k2 != 0) { + fprintf(stderr, " cut Line frag at %p by (%d,%d) to len=%d\n", + ge, k1, k2, len); + gex_dump_contour(ge, clen); + + goto reconsider_line; /* the line may have to be cut again */ + } + pge = age[(f->aidx + k1)%clen]; /* new beginning */ + good = 1; /* flag: no need do do a debugging dump */ + for(i=1; ifrwd) + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + if(X_FRAG(pge)->len[d]) { + fprintf(stderr, " removed %s frag at %p len=%d covered by line\n", + gxf_name[d], pge, X_FRAG(pge)->len[d], len); + good = 0; + } + X_FRAG(pge)->len[d] = 0; + } + pge = age[(f->aidx + k1 + 1)%clen]; /* next after new beginning */ + for(i=1; ifrwd) + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) + X_FRAG(pge)->lenback[d] = 0; + if(!good) + gex_dump_contour(ge, clen); + + ge = ge->frwd; + } while(ge != cge->next); + + /* Resolve conflicts between curves */ + for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { + dx = (GEXFI_CONVEX + GEXFI_CONCAVE) - d; /* the other type */ + ge = cge->next; + do { + GENTRY *sge; + + f = X_FRAG(ge); + len = f->len[d]; + if(len < 2) { + ge = ge->frwd; + continue; + } + sge = ge; /* the start of fragment */ + + i = f->len[dx]; + if(i != 0) { /* two curved frags starting here */ + /* should be i!=len because otherwise they would be + * covered by an exact line + */ + if(i > len) { + curve_completely_covered: + /* remove the convex frag */ + fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n", + gxf_name[d], ge, len, gxf_name[dx]); + f->len[d] = 0; + for(pge = ge->frwd, j = 1; j < len; j++, pge = pge->frwd) + X_FRAG(pge)->lenback[d] = 0; + gex_dump_contour(ge, clen); + + ge = ge->frwd; /* the frag is gone, nothing more to do */ + continue; + } else { + /* remove the concave frag */ + fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n", + gxf_name[dx], ge, i, gxf_name[d]); + f->len[dx] = 0; + for(pge = ge->frwd, j = 1; j < i; j++, pge = pge->frwd) + X_FRAG(pge)->lenback[dx] = 0; + gex_dump_contour(ge, clen); + } + } + + + k1 = X_FRAG(ge->frwd)->lenback[dx]; + if(k1 != 0) { /* conflict at the front */ + GENTRY *gels, *gele, *gei; + + pge = age[(f->aidx + clen - (k1-1))%clen]; /* first gentry of concave frag */ + k2 = X_FRAG(pge)->len[dx]; /* its length */ + + i = k2 - (k1-1); /* amount of overlap */ + if(i > len) + i = len; + /* i >= 2 by definition */ + if(i >= k2-1) { /* covers the other frag - maybe with 1 gentry showing */ + fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n", + gxf_name[dx], pge, k2, gxf_name[d]); + X_FRAG(pge)->len[dx] = 0; + for(pge = pge->frwd, j = 1; j < k2; j++, pge = pge->frwd) + X_FRAG(pge)->lenback[dx] = 0; + if(i >= len-1) { /* covers our frag too - maybe with 1 gentry showing */ + /* our frag will be removed as well, prepare a line to replace it */ + gels = ge; + gele = age[(f->aidx + i - 1)%clen]; + fprintf(stderr, " new Line frag at %p-%p len=%d\n", gels, gele, i); + X_FRAG(gels)->len[GEXFI_LINE] = i; + for(gei = gels->frwd, j = 1; j < i; gei = gei->frwd, j++) + X_FRAG(gei)->lenback[GEXFI_LINE] = j; + } else { + gex_dump_contour(ge, clen); + ge = ge->frwd; + continue; + } + } + if(i >= len-1) /* covers our frag - maybe with 1 gentry showing */ + goto curve_completely_covered; + + /* XXX need to do something better for the case when a curve frag + * is actually nothing but an artifact of two other curves of + * the opposite type touching each other, like on the back of "3" + */ + + /* change the overlapping part to a line */ + gels = ge; + gele = age[(f->aidx + i - 1)%clen]; + /* give preference to local extremums */ + if(X_FRAG(gels)->flags & GEXFF_EXTR) { + gels = gels->frwd; + i--; + } + if(X_FRAG(gele)->flags & GEXFF_EXTR) { + gele = gele->bkwd; + i--; + } + if(gels->bkwd == gele) { + /* Oops the line became negative. Probably should + * never happen but I can't think of any formal reasoning + * leading to that, so check just in case. Restore + * the previous state. + */ + gels = gele; gele = gels->frwd; i = 2; + } + + j = X_FRAG(gels)->lenback[dx] + 1; /* new length */ + if(j != k2) { + X_FRAG(pge)->len[dx] = j; + fprintf(stderr, " cut %s frag at %p len=%d to %p len=%d end overlap with %s\n", + gxf_name[dx], pge, k2, gels, j, gxf_name[d]); + for(gei = gels->frwd; j < k2; gei = gei->frwd, j++) + X_FRAG(gei)->lenback[dx] = 0; + } + + if(gele != ge) { + sge = gele; + f->len[d] = 0; + fprintf(stderr, " cut %s frag at %p len=%d ", gxf_name[d], ge, len); + len--; + for(gei = ge->frwd; gei != gele; gei = gei->frwd, len--) + X_FRAG(gei)->lenback[d] = 0; + X_FRAG(gele)->len[d] = len; + X_FRAG(gele)->lenback[d] = 0; + fprintf(stderr, "to %p len=%d start overlap with %s\n", + sge, len, gxf_name[dx]); + for(gei = gei->frwd, j = 1; j < len; gei = gei->frwd, j++) + X_FRAG(gei)->lenback[d] = j; + + } + if(i > 1) { + fprintf(stderr, " new Line frag at %p-%p len=%d\n", gels, gele, i); + X_FRAG(gels)->len[GEXFI_LINE] = i; + for(gei = gels->frwd, j = 1; j < i; gei = gei->frwd, j++) + X_FRAG(gei)->lenback[GEXFI_LINE] = j; + } + gex_dump_contour(ge, clen); + } + + ge = ge->frwd; + } while(ge != cge->next); + } + + /* + * Assert that there are no conflicts any more and + * for each gentry find the fragment types that start + * and continue here. + */ + ge = cge->next; + do { + f = X_FRAG(ge); + dx = GEXFI_NONE; /* type that starts here */ + dy = GEXFI_NONE; /* type that goes through here */ + /* GEXFI_EXACTLINE and GEXFI_SERIF are auxiliary and don't + * generate any actual lines/curves in the result + */ + for(d = GEXFI_CONVEX; d<= GEXFI_LINE; d++) { + if(f->len[d]) { + if(dx >= 0) { + fprintf(stderr, "**** Internal error in vectorization\n"); + fprintf(stderr, "CONFLICT in %s at %p between %s and %s\n", + g->name, ge, gxf_name[dx], gxf_name[d]); + dumppaths(g, cge->next, cge->next->bkwd); + gex_dump_contour(ge, clen); + exit(1); + } + dx = d; + } + if(f->lenback[d]) { + if(dy >= 0) { + fprintf(stderr, "**** Internal error in vectorization\n"); + fprintf(stderr, "CONFLICT in %s at %p between %s and %s\n", + g->name, ge, gxf_name[dy], gxf_name[d]); + dumppaths(g, cge->next, cge->next->bkwd); + gex_dump_contour(ge, clen); + exit(1); + } + dy = d; + } + } + f->ixstart = dx; + f->ixcont = dy; + ge = ge->frwd; + } while(ge != cge->next); + + /* + * make sure that the contour does not start in the + * middle of a fragment + */ + ge = cge->next; /* old start of the contour */ + f = X_FRAG(ge); + if(f->ixstart == GEXFI_NONE && f->ixcont != GEXFI_NONE) { + /* oops, it's mid-fragment, move the start */ + GENTRY *xge; + + xge = ge->bkwd->next; /* entry following the contour */ + + /* find the first gentry of this frag */ + pge = age[(f->aidx + clen - f->lenback[f->ixcont])%clen]; + + ge->prev = ge->bkwd; + ge->bkwd->next = ge; + + cge->next = pge; + pge->prev = cge; + + pge->bkwd->next = xge; + if(xge) + xge->prev = pge->bkwd; + + cge->ix3 = pge->bkwd->ix3; cge->iy3 = pge->bkwd->iy3; + } + + /* vectorize each fragment separately + * make 2 passes: first handle the straight lines, then + * the curves to allow the curver to be connected smoothly + * to the straights + */ + ge = cge->next; + do { /* pass 1 */ + f = X_FRAG(ge); + switch(f->ixstart) { + case GEXFI_LINE: + len = f->len[GEXFI_LINE]; + pge = age[(f->aidx + len - 1)%clen]; /* last gentry */ + + if(ge->iy3 == ge->bkwd->iy3) { /* frag starts and ends horizontally */ + k1 = 1/*Y*/ ; /* across the direction of start */ + k2 = 0/*X*/ ; /* along the direction of start */ + } else { /* frag starts and ends vertically */ + k1 = 0/*X*/ ; /* across the direction of start */ + k2 = 1/*Y*/ ; /* along the direction of start */ + } + + if(len % 2) { + /* odd number of entries in the frag */ + double halfstep, halfend; + + f->vect[0][k1] = fscale * ge->ipoints[k1][2]; + f->vect[3][k1] = fscale * pge->ipoints[k1][2]; + + halfstep = (pge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) + * 0.5 / ((len+1)/2); + if(f->ixcont != GEXFI_NONE) { + halfend = (ge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) * 0.5; + if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ + halfstep = halfend; + } + if(X_FRAG(pge)->ixstart != GEXFI_NONE) { + halfend = (pge->ipoints[k2][2] - pge->bkwd->ipoints[k2][2]) * 0.5; + if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ + halfstep = halfend; + } + f->vect[0][k2] = fscale * (ge->bkwd->ipoints[k2][2] + halfstep); + f->vect[3][k2] = fscale * (pge->ipoints[k2][2] - halfstep); + } else { + /* even number of entries */ + double halfstep, halfend; + + f->vect[0][k1] = fscale * ge->ipoints[k1][2]; + halfstep = (pge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) + * 0.5 / (len/2); + if(f->ixcont != GEXFI_NONE) { + halfend = (ge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) * 0.5; + if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ + halfstep = halfend; + } + f->vect[0][k2] = fscale * (ge->bkwd->ipoints[k2][2] + halfstep); + + halfstep = (pge->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) + * 0.5 / (len/2); + if(X_FRAG(pge)->ixstart != GEXFI_NONE) { + halfend = (pge->ipoints[k1][2] - pge->bkwd->ipoints[k1][2]) * 0.5; + if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ + halfstep = halfend; + } + f->vect[3][k1] = fscale * (pge->ipoints[k1][2] - halfstep); + f->vect[3][k2] = fscale * pge->ipoints[k2][2]; + } + f->vectlen = len; + f->flags |= GEXFF_DRAWLINE; + break; + } + } while((ge = ge->frwd) != cge->next); + + ge = cge->next; + do { /* pass 2 */ + /* data for curves */ + GENTRY *firstge, *lastge, *gef, *gel, *gei, *gex; + GENTRY *ordhd; /* head of the order list */ + GENTRY **ordlast; + int nsub; /* number of subfrags */ + GEX_FRAG *ff, *lf, *xf; + + f = X_FRAG(ge); + switch(f->ixstart) { + case GEXFI_CONVEX: + case GEXFI_CONCAVE: + len = f->len[f->ixstart]; + firstge = ge; + lastge = age[(f->aidx + len - 1)%clen]; /* last gentry */ + + nsub = 0; + gex = firstge; + xf = X_FRAG(gex); + xf->prevsub = 0; + xf->sublen = 1; + xf->flags &= ~GEXFF_DONE; + for(gei = firstge->frwd; gei != lastge; gei = gei->frwd) { + xf->sublen++; + if(X_FRAG(gei)->flags & GEXFF_EXTR) { + xf->nextsub = gei; + for(i=0; i<2; i++) + xf->bbox[i] = abs(gei->ipoints[i][2] - gex->bkwd->ipoints[i][2]); + nsub++; + xf = X_FRAG(gei); + xf->prevsub = gex; + xf->sublen = 1; + xf->flags &= ~GEXFF_DONE; + gex = gei; + } + } + xf->sublen++; + xf->nextsub = gei; + for(i=0; i<2; i++) + xf->bbox[i] = abs(gei->ipoints[i][2] - gex->bkwd->ipoints[i][2]); + nsub++; + ff = xf; /* remember the beginning of the last subfrag */ + xf = X_FRAG(gei); + xf->prevsub = gex; + if(firstge != lastge) { + xf->nextsub = 0; + xf->sublen = 0; + + /* correct the bounding box of the last and first subfrags for + * intersections with other fragments + */ + if(xf->ixstart != GEXFI_NONE) { + /* ff points to the beginning of the last subfrag */ + for(i=0; i<2; i++) + ff->bbox[i] -= 0.5 * abs(lastge->ipoints[i][2] - lastge->bkwd->ipoints[i][2]); + } + ff = X_FRAG(firstge); + if(ff->ixcont != GEXFI_NONE) { + for(i=0; i<2; i++) + ff->bbox[i] -= 0.5 * abs(firstge->ipoints[i][2] - firstge->bkwd->ipoints[i][2]); + } + } + + fprintf(stderr, " %s frag %p%s nsub=%d\n", gxf_name[f->ixstart], + ge, (f->flags&GEXFF_CIRC)?" circular":"", nsub); + + /* find the symmetry between the subfragments */ + for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { + ff = X_FRAG(gef); + gex = ff->nextsub; + xf = X_FRAG(gex); + gel = xf->nextsub; + if(gel == 0) { + ff->flags &= ~GEXFF_SYMNEXT; + break; /* not a circular frag */ + } + good = 1; /* assume that we have symmetry */ + /* gei goes backwards, gex goes forwards from the extremum */ + gei = gex; + /* i is the symmetry axis, j is the other axis (X=0 Y=1) */ + ff->symaxis = i = (gex->ix3 != gex->bkwd->ix3); + j = !i; + for( ; gei!=gef && gex!=gel; gei=gei->bkwd, gex=gex->frwd) { + if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2] + || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2] + != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] + ) { + good = 0; /* no symmetry */ + break; + } + } + if(good) { + if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]) + != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) { + good = 0; /* oops, goes into another direction */ + } + } + if(good) + ff->flags |= GEXFF_SYMNEXT; + else + ff->flags &= ~GEXFF_SYMNEXT; + } + + for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { + ff = X_FRAG(gef); + if((ff->flags & GEXFF_SYMNEXT)==0) { + ff->symxlen = 0; + continue; + } + gex = ff->prevsub; + if(gex == 0 || (X_FRAG(gex)->flags & GEXFF_SYMNEXT)==0) { + ff->symxlen = 0; + continue; + } + ff->symxlen = X_FRAG(gex)->sublen; + xf = X_FRAG(ff->nextsub); + if(xf->sublen < ff->symxlen) + ff->symxlen = xf->sublen; + } + + /* find the symmetry inside the subfragments */ + for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { + ff = X_FRAG(gef); + + if(ff->sublen % 2) { + /* we must have an even number of gentries for diagonal symmetry */ + ff->symge = 0; + continue; + } + + /* gei goes forwards from the front */ + gei = gef->frwd; + /* gex goes backwards from the back */ + gex = ff->nextsub->bkwd; + + /* i is the direction of gei, j is the direction of gex */ + i = (gei->iy3 != gei->bkwd->iy3); + j = !i; + for( ; gei->bkwd != gex; gei=gei->frwd, gex=gex->bkwd) { + if( abs(gei->bkwd->ipoints[i][2] - gei->ipoints[i][2]) + != abs(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) + break; /* no symmetry */ + i = j; + j = !j; + } + if(gei->bkwd == gex) + ff->symge = gex; + else + ff->symge = 0; /* no symmetry */ + } + + /* find the order of calculation: + * prefer to start from long fragments that have the longest + * neighbours symmetric with them, with all being equal prefer + * the fragments that have smaller physical size + */ + ordhd = 0; + for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { + ff = X_FRAG(gef); + + for(ordlast = &ordhd; *ordlast != 0; ordlast = &xf->ordersub) { + xf = X_FRAG(*ordlast); + if(ff->sublen > xf->sublen) + break; + if(ff->sublen < xf->sublen) + continue; + if(ff->symxlen > xf->symxlen) + break; + if(ff->symxlen < xf->symxlen) + continue; + if(ff->bbox[0] < xf->bbox[0] || ff->bbox[1] < xf->bbox[1]) + break; + } + + ff->ordersub = *ordlast; + *ordlast = gef; + } + + /* vectorize the subfragments */ + for(gef = ordhd; gef != 0; gef = ff->ordersub) { + + /* debugging stuff */ + ff = X_FRAG(gef); + fprintf(stderr, " %p-%p bbox[%g,%g] sym=%p %s len=%d xlen=%d\n", + gef, ff->nextsub, ff->bbox[0], ff->bbox[1], ff->symge, + (ff->flags & GEXFF_SYMNEXT) ? "symnext" : "", + ff->sublen, ff->symxlen); + + dosubfrag(g, f->ixstart, firstge, gef, fscale); + } + + break; + } + } while((ge = ge->frwd) != cge->next); + + free(age); + + } + + } + + /* all the fragments are found, extract the vectorization */ + pge = g->entries; + g->entries = g->lastentry = 0; + g->flags |= GF_FLOAT; + loopge = 0; + skip = 0; + + for(ge = pge; ge != 0; ge = ge->next) { + GEX_FRAG *f, *pf; + + switch(ge->type) { + case GE_LINE: + f = X_FRAG(ge); + if(skip == 0) { + if(f->flags & (GEXFF_DRAWLINE|GEXFF_DRAWCURVE)) { + /* draw a line to the start point */ + fg_rlineto(g, f->vect[0][0], f->vect[0][1]); + /* draw the fragment */ + if(f->flags & GEXFF_DRAWCURVE) + fg_rrcurveto(g, + f->vect[1][0], f->vect[1][1], + f->vect[2][0], f->vect[2][1], + f->vect[3][0], f->vect[3][1]); + else + fg_rlineto(g, f->vect[3][0], f->vect[3][1]); + skip = f->vectlen - 2; + } else { + fg_rlineto(g, fscale * ge->ix3, fscale * ge->iy3); + } + } else + skip--; + break; + case GE_MOVE: + fg_rmoveto(g, -1e6, -1e6); /* will be fixed by GE_PATH */ + skip = 0; + /* remember the reference to update it later */ + loopge = g->lastentry; + break; + case GE_PATH: + /* update the first MOVE of this contour */ + if(loopge) { + loopge->fx3 = g->lastentry->fx3; + loopge->fy3 = g->lastentry->fy3; + loopge = 0; + } + g_closepath(g); + break; + } + } + for(ge = pge; ge != 0; ge = cge) { + cge = ge->next; + free(ge->ext); + free(ge); + } + dumppaths(g, NULL, NULL); + + /* end of vectorization logic */ + } else { + /* convert the data to float */ + GENTRY *ge; + double x, y; + + for(ge = g->entries; ge != 0; ge = ge->next) { + ge->flags |= GEF_FLOAT; + if(ge->type != GE_MOVE && ge->type != GE_LINE) + continue; + + x = fscale * ge->ix3; + y = fscale * ge->iy3; + + ge->fx3 = x; + ge->fy3 = y; + } + g->flags |= GF_FLOAT; + } + + free(hlm); free(vlm); free(amp); +} + +#if 0 +/* print out the bitmap */ +printbmap(bmap, xsz, ysz, xoff, yoff) + char *bmap; + int xsz, ysz, xoff, yoff; +{ + int x, y; + + for(y=ysz-1; y>=0; y--) { + putchar( (y%10==0) ? y/10+'0' : ' ' ); + putchar( y%10+'0' ); + for(x=0; x=0; y--) { + for(x=0; x> 8))) +#define ntohl(x) \ + ((ULONG)((((ULONG)(x) & 0x000000ffU) << 24) | \ + (((ULONG)(x) & 0x0000ff00U) << 8) | \ + (((ULONG)(x) & 0x00ff0000U) >> 8) | \ + (((ULONG)(x) & 0xff000000U) >> 24))) +#endif Index: xc/extras/ttf2pt1/cygbuild.sh =================================================================== RCS file: xc/extras/ttf2pt1/cygbuild.sh diff -N xc/extras/ttf2pt1/cygbuild.sh --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/cygbuild.sh 13 Apr 2004 02:45:02 -0000 @@ -0,0 +1,8 @@ +: +# this file should be run from Cygnus BASH +# file to build ttf2pt1 with Cygnus GCC on Windows +# don't forget to copy CYGWIN1.DLL into C:\WINDOWS + +gcc -o ttf2pt1 -DWINDOWS ttf2pt1.c pt1.c t1asm.c ttf.c -lm +gcc -o t1asm -DWINDOWS -DSTANDALONE t1asm.c + Index: xc/extras/ttf2pt1/ft.c =================================================================== RCS file: xc/extras/ttf2pt1/ft.c diff -N xc/extras/ttf2pt1/ft.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ft.c 13 Apr 2004 02:45:03 -0000 @@ -0,0 +1,807 @@ +/* + * The font parser using the FreeType library version 2. + * + * see COPYRIGHT + * + */ + +#ifdef USE_FREETYPE + +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_BBOX_H +#include FT_GLYPH_H + +#include FT_CONFIG_CONFIG_H +#include FT_CONFIG_OPTIONS_H +#include FT_ERRORS_H +#include FT_SYSTEM_H +#include FT_IMAGE_H +#include FT_TYPES_H +#include FT_OUTLINE_H +#include FT_MODULE_H +#include FT_RENDER_H +#include FT_TYPE1_TABLES_H +#include FT_TRUETYPE_IDS_H +#include FT_TRUETYPE_TAGS_H +#include FT_MULTIPLE_MASTERS_H +#include FT_SFNT_NAMES_H + +#ifdef XP_PSTEXT +#include "os.h" +#include "Xproto.h" +#include "font.h" +#include "fontstruct.h" +#include "fntfilst.h" +#include "fontutil.h" +#include "fontenc.h" +#include "ft.h" +#include "ftfuncs.h" +#endif /* XP_PSTEXT */ + +#include "pt1.h" +#include "global.h" + +/* prototypes of call entries */ +static void openfont(char *fname, char *arg); +static void closefont( void); +static int getnglyphs ( void); +static int glnames( GLYPH *glyph_list); +static void glmetrics( GLYPH *glyph_list); +static int glenc( GLYPH *glyph_list, int *encoding, int *unimap); +static void fnmetrics( struct font_metrics *fm); +static void glpath( int glyphno, GLYPH *glyph_list); +static void kerning( GLYPH *glyph_list); + +/* globals */ + +/* front-end descriptor */ +struct frontsw freetype_sw = { + /*name*/ "ft", + /*descr*/ "based on the FreeType2 library", + /*suffix*/ { "ttf", "ttc", "otf", "otc", "pfa", "pfb" }, + /*open*/ openfont, + /*close*/ closefont, + /*nglyphs*/ getnglyphs, + /*glnames*/ glnames, + /*glmetrics*/ glmetrics, + /*glenc*/ glenc, + /*fnmetrics*/ fnmetrics, + /*glpath*/ glpath, + /*kerning*/ kerning, +}; + +/* statics */ + +static char * dupcnstring( unsigned char *s, int len); + +#ifndef XP_PSTEXT +static FT_Library library; +#endif /* !XP_PSTEXT */ +static FT_Face face; + +static int enc_type, enc_found; + +/* SFNT functions do not seem to be included by default in FT2beta8 */ +#define ENABLE_SFNT + +/* + * Open font and prepare to return information to the main driver. + * May print error and warning messages. + * Exit on error. + */ + +static void +openfont( + char *fname, + char *arg /* unused now */ +) +{ + FT_Error error; + +#ifdef XP_PSTEXT + extern FT_Face xp_pstext_ft_face; + extern FT_Library ftypeLibrary; /* defined in xc/lib/font/FreeType/ftfuncs.c */ + + face = xp_pstext_ft_face; +#else + if( FT_Init_FreeType( &library ) ) { + fprintf(stderr, "** FreeType initialization failed\n"); + exit(1); + } + + if( error = FT_New_Face( library, fname, 0, &face ) ) { + if ( error == FT_Err_Unknown_File_Format ) + fprintf(stderr, "**** %s has format unknown to FreeType\n", fname); + else + fprintf(stderr, "**** Cannot access %s ****\n", fname); + exit(1); + } +#endif /* XP_PSTEXT */ + + if(FT_HAS_FIXED_SIZES(face)) { + WARNING_1 fprintf(stderr, "Font contains bitmaps\n"); + } + if(FT_HAS_MULTIPLE_MASTERS(face)) { + WARNING_1 fprintf(stderr, "Font contains multiple masters, using default\n"); + } + + if(ISDBG(FT)) fprintf(stderr," %d units per EM\n", face->units_per_EM); + + enc_found = 0; +} + +/* + * Close font. + * Exit on error. + */ + +static void +closefont( + void +) +{ +#ifdef XP_PSTEXT + /* NOP */ +#else + if( FT_Done_Face(face) ) { + WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n"); + } + if( FT_Done_FreeType(library) ) { + WARNING_1 fprintf(stderr, "Errors when stopping FreeType, ignored\n"); + } +#endif /* XP_PSTEXT */ +} + +/* + * Get the number of glyphs in font. + */ + +static int +getnglyphs ( + void +) +{ + if(ISDBG(FT)) fprintf(stderr, "%d glyphs in font\n", face->num_glyphs); + return (int)face->num_glyphs; +} + +/* + * Get the names of the glyphs. + * Returns 0 if the names were assigned, non-zero if the font + * provides no glyph names. + */ + +static int +glnames( + GLYPH *glyph_list +) +{ +#define MAX_NAMELEN 1024 + +#ifdef XP_PSTEXT + char buf[1024]; + long i; + FT_Error error; + +#ifdef XP_ONLY_BLOCKS + extern unsigned long xp_font_block_offset; + extern FTFontPtr xp_xtf; + int bc; /* block counter */ + + + /* FixMe: This code should use PsOut_Get_FreeType_Glyph_Name() instead of + * duplicating the code + */ + for( bc = xp_font_block_offset ; bc < (xp_font_block_offset+256) ; bc++ ) { + /* Remap X11 font index to FreeType font index */ + i = FTRemap(face, &xp_xtf->mapping, bc); + + if( i >= face->num_glyphs ) + continue; +#else + for(i=0; i < face->num_glyphs; i++) { +#endif /* XP_ONLY_BLOCKS */ + if( FT_Has_PS_Glyph_Names(face) ) { + error = FT_Get_Glyph_Name(face, i, buf, MAX_NAMELEN); + } + else + { + error = -1; + } + + if( error ) { + /* Check for unicode mapping + * See Adobe document "Unicode and Glyph Names" + * (http://partners.adobe.com/asn/tech/type/unicodegn.jsp) + */ + if( (xp_xtf->mapping.mapping->type == FONT_ENCODING_UNICODE) && + (i < 0xFFFE) ) + { + sprintf(buf, "uni%04lx", i); + } + else + { + sprintf(buf, "ch%02lx", i); + } + } + glyph_list[i].name = strdup(buf); + if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, buf); + if (glyph_list[i].name == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } + + return 0; +#else + char bf[1024]; + long i; + + if( ! FT_HAS_GLYPH_NAMES(face) ) { + WARNING_1 fprintf(stderr, "Font has no glyph names\n"); + return 1; + } + + for(i=0; i < face->num_glyphs; i++) { + if( FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0 ) { + sprintf(bf, "_g_%d", i); + WARNING_2 fprintf(stderr, + "Glyph No. %d has no postscript name, becomes %s\n", i, bf); + } + glyph_list[i].name = strdup(bf); + if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, bf); + if (glyph_list[i].name == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } + + return 0; +#endif /* XP_PSTEXT */ +} + +/* + * Get the metrics of the glyphs. + */ + +static void +glmetrics( + GLYPH *glyph_list +) +{ + GLYPH *g; + int i; + FT_Glyph_Metrics *met; + FT_BBox bbox; + FT_Glyph gly; + +#ifdef XP_ONLY_BLOCKS + extern unsigned long xp_font_block_offset; + extern FTFontPtr xp_xtf; + int bc; /* block counter */ + + for( bc = xp_font_block_offset ; bc < (xp_font_block_offset+256) ; bc++ ) { + /* Remap X11 font index to FreeType font index */ + i = FTRemap(face, &xp_xtf->mapping, bc); + + if( i >= face->num_glyphs ) + continue; + +#else + for(i=0; i < face->num_glyphs; i++) { +#endif /* XP_ONLY_BLOCKS */ + g = &(glyph_list[i]); + + if( FT_Load_Glyph(face, i, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE) ) { + fprintf(stderr, "Can't load glyph %s, skipped\n", g->name); + continue; + } + + met = &face->glyph->metrics; + + if(FT_HAS_HORIZONTAL(face)) { + g->width = met->horiAdvance; + g->lsb = met->horiBearingX; + } else { + WARNING_2 fprintf(stderr, "Glyph %s has no horizontal metrics, guessed them\n", g->name); + g->width = met->width; + g->lsb = 0; + } + + if( FT_Get_Glyph(face->glyph, &gly) ) { + fprintf(stderr, "Can't access glyph %s bbox, skipped\n", g->name); + continue; + } + + FT_Glyph_Get_CBox(gly, ft_glyph_bbox_unscaled, &bbox); + g->xMin = bbox.xMin; + g->yMin = bbox.yMin; + g->xMax = bbox.xMax; + g->yMax = bbox.yMax; + + g->ttf_pathlen = face->glyph->outline.n_points; + } +} + +/* + * Get the original encoding of the font. + * Returns 1 for if the original encoding is Unicode, 2 if the + * original encoding is other 16-bit, 0 if 8-bit. + */ + +static int +glenc( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ +#ifdef XP_PSTEXT + int i, + e; + unsigned code; + extern FTFontPtr xp_xtf; + extern unsigned long xp_font_block_offset; + + enc_found = 1; + enc_type = 0; + + for(i=0; imapping, xp_font_block_offset+i); + + if(code == 0) + continue; /* .notdef */ + + encoding[i] = code; + } + + return enc_type; +#else + int i, e; + unsigned code; + + if(ISDBG(FT)) + for(e=0; e < face->num_charmaps; e++) { + fprintf(stderr, "found encoding pid=%d eid=%d\n", + face->charmaps[e]->platform_id, + face->charmaps[e]->encoding_id); + } + + if(enc_found) + goto populate_map; + + enc_type = 0; + + /* first check for an explicit PID/EID */ + + if(force_pid != -1) { + for(e=0; e < face->num_charmaps; e++) { + if(face->charmaps[e]->platform_id == force_pid + && face->charmaps[e]->encoding_id == force_eid) { + WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n", + force_pid, force_eid); + if( FT_Set_Charmap(face, face->charmaps[e]) ) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + exit(1); + } + enc_type = 1; + goto populate_map; + } + } + fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n", + force_pid, force_eid); + exit(1); + } + + /* next check for a direct Adobe mapping */ + + if(!forcemap) { + for(e=0; e < face->num_charmaps; e++) { + if(face->charmaps[e]->encoding == ft_encoding_adobe_custom) { + WARNING_1 fputs("Found Adobe Custom Encoding\n", stderr); + if( FT_Set_Charmap(face, face->charmaps[e]) ) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + exit(1); + } + goto populate_map; + } + } + } + + for(e=0; e < face->num_charmaps; e++) { + if(face->charmaps[e]->platform_id == 3) { + switch(face->charmaps[e]->encoding_id) { + case 0: + WARNING_1 fputs("Found Symbol Encoding\n", stderr); + break; + case 1: + WARNING_1 fputs("Found Unicode Encoding\n", stderr); + enc_type = 1; + break; + default: + WARNING_1 { + fprintf(stderr, + "****MS Encoding ID %d not supported****\n", + face->charmaps[e]->encoding_id); + fputs("Treating it like Symbol encoding\n", stderr); + } + break; + } + break; + } + } + if(e >= face->num_charmaps) { + WARNING_1 fputs("No Microsoft encoding, using first encoding available\n", stderr); + e = 0; + } + + if( FT_Set_Charmap(face, face->charmaps[e]) ) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + exit(1); + } + +populate_map: + enc_found = 1; + for(i=0; i=' ' && c!=127) + *out++ = c; + else if(!warned) { + warned=1; + WARNING_1 fprintf(stderr, "Some font name strings are in Unicode, may not show properly\n"); + } + } + *out = 0; + return res; +} + +/* + * Get the font metrics + */ +static void +fnmetrics( + struct font_metrics *fm +) +{ + char *str; + static char *fieldstocheck[3]; +#ifdef ENABLE_SFNT + FT_SfntName sn; +#endif /* ENABLE_SFNT */ + int i; + + fm->italic_angle = 0.0; /* FreeType hides the angle */ + fm->underline_position = face->underline_position; + fm->underline_thickness = face->underline_thickness; + fm->is_fixed_pitch = FT_IS_FIXED_WIDTH(face); + + fm->ascender = face->ascender; + fm->descender = face->descender; + + fm->units_per_em = face->units_per_EM; + + fm->bbox[0] = face->bbox.xMin; + fm->bbox[1] = face->bbox.yMin; + fm->bbox[2] = face->bbox.xMax; + fm->bbox[3] = face->bbox.yMax; + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_COPYRIGHT, &sn) ) +#endif /* ENABLE_SFNT */ + fm->name_copyright = ""; +#ifdef ENABLE_SFNT + else + fm->name_copyright = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + + fm->name_family = face->family_name; + + fm->name_style = face->style_name; + if(fm->name_style == NULL) + fm->name_style = ""; + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_FULL_NAME, &sn) ) +#endif /* ENABLE_SFNT */ + { + int len; + + len = strlen(fm->name_family) + strlen(fm->name_style) + 2; + if(( fm->name_full = malloc(len) )==NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + strcpy(fm->name_full, fm->name_family); + if(strlen(fm->name_style) != 0) { + strcat(fm->name_full, " "); + strcat(fm->name_full, fm->name_style); + } + } +#ifdef ENABLE_SFNT + else + fm->name_full = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_VERSION_STRING, &sn) ) +#endif /* ENABLE_SFNT */ + fm->name_version = "1.0"; +#ifdef ENABLE_SFNT + else + fm->name_version = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + +#ifdef XP_PSTEXT + { + extern const char *xp_psfontname; + + fm->name_ps = strdup(xp_psfontname); + + /* Handle the rare case if a family name was not provided by the TTF + * font (like Solaris TTF fonts in /usr/openwin/lib/locale/ko.UTF-8/X11/fonts/TrueType, + * /usr/openwin/lib/locale/ko/X11/fonts/TrueType) - in this case we + * have to generate a family name somehow... */ + if(fm->name_family == NULL) + fm->name_family = fm->name_ps; + } +#else + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_PS_NAME , &sn) ) { +#endif /* ENABLE_SFNT */ + if(( fm->name_ps = strdup(fm->name_full) )==NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } +#ifdef ENABLE_SFNT + } else + fm->name_ps = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + +#endif /* XP_PSTEXT */ + + for(i=0; fm->name_ps[i]!=0; i++) + if(fm->name_ps[i] == ' ') + fm->name_ps[i] = '_'; /* no spaces in the Postscript name *m + + /* guess the boldness from the font names */ + fm->force_bold=0; + + fieldstocheck[0] = fm->name_style; + fieldstocheck[1] = fm->name_full; + fieldstocheck[2] = fm->name_ps; + + for(i=0; !fm->force_bold && iforce_bold=1; + break; + } + } + } +} + +/* + * Functions to decompose the outlines + */ + +static GLYPH *curg; +static double lastx, lasty; + +static int +outl_moveto( + FT_Vector *to, + void *unused +) +{ + double tox, toy; + + tox = fscale((double)to->x); toy = fscale((double)to->y); + + /* FreeType does not do explicit closepath() */ + if(curg->lastentry) { + g_closepath(curg); + } + fg_rmoveto(curg, tox, toy); + lastx = tox; lasty = toy; + + return 0; +} + +static int +outl_lineto( + FT_Vector *to, + void *unused +) +{ + double tox, toy; + + tox = fscale((double)to->x); toy = fscale((double)to->y); + + fg_rlineto(curg, tox, toy); + lastx = tox; lasty = toy; + + return 0; +} + +static int +outl_conicto( + FT_Vector *control1, + FT_Vector *to, + void *unused +) +{ + double c1x, c1y, tox, toy; + + c1x = fscale((double)control1->x); c1y = fscale((double)control1->y); + tox = fscale((double)to->x); toy = fscale((double)to->y); + + fg_rrcurveto(curg, + (lastx + 2.0 * c1x) / 3.0, (lasty + 2.0 * c1y) / 3.0, + (2.0 * c1x + tox) / 3.0, (2.0 * c1y + toy) / 3.0, + tox, toy ); + lastx = tox; lasty = toy; + + return 0; +} + +static int +outl_cubicto( + FT_Vector *control1, + FT_Vector *control2, + FT_Vector *to, + void *unused +) +{ + double c1x, c1y, c2x, c2y, tox, toy; + + c1x = fscale((double)control1->x); c1y = fscale((double)control1->y); + c2x = fscale((double)control2->x); c2y = fscale((double)control2->y); + tox = fscale((double)to->x); toy = fscale((double)to->y); + + fg_rrcurveto(curg, c1x, c1y, c2x, c2y, tox, toy); + lastx = tox; lasty = toy; + + return 0; +} + +static FT_Outline_Funcs ft_outl_funcs = { + outl_moveto, + outl_lineto, + outl_conicto, + outl_cubicto, + 0, + 0 +}; + +/* + * Get the path of contrours for a glyph. + */ + +static void +glpath( + int glyphno, + GLYPH *glyf_list +) +{ + FT_Outline *ol; + + curg = &glyf_list[glyphno]; + + if( FT_Load_Glyph(face, glyphno, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE|FT_LOAD_NO_HINTING) + || face->glyph->format != ft_glyph_format_outline ) { + fprintf(stderr, "Can't load glyph %s, skipped\n", curg->name); + return; + } + + ol = &face->glyph->outline; + lastx = 0.0; lasty = 0.0; + + if( FT_Outline_Decompose(ol, &ft_outl_funcs, NULL) ) { + fprintf(stderr, "Can't decompose outline of glyph %s, skipped\n", curg->name); + return; + } + + /* FreeType does not do explicit closepath() */ + if(curg->lastentry) { + g_closepath(curg); + } + + if(ol->flags & ft_outline_reverse_fill) { + assertpath(curg->entries, __FILE__, __LINE__, curg->name); + reversepaths(curg); + } +} + +/* + * Get the kerning data. + */ + +static void +kerning( + GLYPH *glyph_list +) +{ + int i, j, n; + int nglyphs = face->num_glyphs; + FT_Vector k; + GLYPH *gl; + + if( nglyphs == 0 || !FT_HAS_KERNING(face) ) { + WARNING_1 fputs("No Kerning data\n", stderr); + return; + } + + for(i=0; i= 1) +#define WARNING_2 if(warnlevel >= 2) +#define WARNING_3 if(warnlevel >= 3) +#define WARNING_4 if(warnlevel >= 4) + +/* + * Bitmap control macros + */ + +#define BITMAP_BYTES(size) (((size)+7)>>3) +#define DEF_BITMAP(name, size) unsigned char name[BITMAP_BYTES(size)] +#define SET_BITMAP(name, bit) ( name[(bit)>>3] |= (1<<((bit)&7)) ) +#define CLR_BITMAP(name, bit) ( name[(bit)>>3] &= ~(1<<((bit)&7)) ) +#define IS_BITMAP(name, bit) ( name[(bit)>>3] & (1<<((bit)&7)) ) + +/* debugging */ + +/* debug flags */ +#define DEBUG_UNICODE 0x00000001 /* unicode to 8-bit code conversion */ +#define DEBUG_MAINSTEMS 0x00000002 /* glyph-wide main stem generation */ +#define DEBUG_SUBSTEMS 0x00000004 /* substituted stem generation */ +#define DEBUG_STEMS (DEBUG_MAINSTEMS|DEBUG_SUBSTEMS) +#define DEBUG_REVERSAL 0x00000008 /* reversal of the paths */ +#define DEBUG_FIXCVDIR 0x00000010 /* fixcvdir() */ +#define DEBUG_STEMOVERLAP 0x00000020 /* stemoverlap() */ +#define DEBUG_BLUESTEMS 0x00000040 /* markbluestems() */ +#define DEBUG_STRAIGHTEN 0x00000080 /* markbluestems() */ +#define DEBUG_EXTMAP 0x00000100 /* parsing of external map */ +#define DEBUG_TOINT 0x00000200 /* conversion of path to integer */ +#define DEBUG_BUILDG 0x00000400 /* building of glyph path */ +#define DEBUG_QUAD 0x00000800 /* splitting curves by quadrants */ +#define DEBUG_SQEQ 0x00001000 /* square equation solver */ +#define DEBUG_COMPOSITE 0x00002000 /* handling of composite glyphs */ +#define DEBUG_FCONCISE 0x00004000 /* normalization of curves */ +#define DEBUG_FT 0x00008000 /* FreeType front-end */ +#define DEBUG_BITMAP 0x00010000 /* conversion from bitmap */ +#define DEBUG_DISABLED 0x80000000 /* special flag: temporary disable debugging */ + +#if 0 +/* at what we want to look now */ +#ifndef DEBUG +# define DEBUG (DEBUG_BITMAP) +#endif + +/* uncomment the next line if debugging data is wanted for one glyph only */ +#define DBG_GLYPH "C118" /* */ +#endif + +#if 1 +# define ISDBG(name) (0) +# define ENABLEDBG(condition) (0) +# define DISABLEDBG(condition) (0) +#else + extern int debug; /* collection of the flags */ +/* this ISDBG will only work on ANSI C, not K&R */ +# define ISDBG(name) ( (debug & DEBUG_DISABLED) ? 0 : (debug & (DEBUG_##name)) ) +# define ENABLEDBG(condition) ( (condition) ? (debug&=~DEBUG_DISABLED) : 0 ) +# define DISABLEDBG(condition) ( (condition) ? (debug|=DEBUG_DISABLED) : 0 ) +#endif + +#ifdef DBG_GLYPH +# define DBG_TO_GLYPH(g) DISABLEDBG( strcmp( (g)->name, DBG_GLYPH ) ) +# define DBG_FROM_GLYPH(g) ENABLEDBG(1) +#else +# define DBG_TO_GLYPH(g) (0) +# define DBG_FROM_GLYPH(g) (0) +#endif + +/* prototypes */ +int iscale( int val); +double fscale( double val); +int unicode_rev_lookup( int unival); +void bmp_outline( GLYPH *g, int scale, char *bmap, + int xsz, int ysz, int xoff, int yoff); +int isign( int x); +int fsign( double x); + +/* global metrics for a font */ + +struct font_metrics { + /* post */ + double italic_angle; + short underline_position; + short underline_thickness; + short is_fixed_pitch; + + /* hhea */ + short ascender; + short descender; + + /* head */ + unsigned short units_per_em; + short bbox[4]; + + /* name */ + char *name_copyright; + char *name_family; + char *name_style; + char *name_full; + char *name_version; + char *name_ps; + + /* other */ + int force_bold; +}; + +/* size of the encoding table - glyphs beyond 255 are actually unnumbered */ + +#define ENCTABSZ 1024 + +/* switch table structure for front-ends */ + +#define MAXSUFFIX 10 + +struct frontsw { + char *name; /* name of the front end */ + char *descr; /* description of the front end */ + char *suffix[MAXSUFFIX]; /* possible file name suffixes */ + + void (*open)(char *fname, char *arg); /* open font file */ + void (*close)(void); /* close font file */ + int (*nglyphs)(void); /* get the number of glyphs */ + int (*glnames)(GLYPH *glyphs); /* get the names of glyphs */ + void (*glmetrics)(GLYPH *glyphs); /* get the metrics of glyphs */ + int (*glenc)(GLYPH *glyphs, int *enc, int *unimap); /* get the encoding */ + void (*fnmetrics)(struct font_metrics *fm); /* get the font metrics */ + void (*glpath)(int glyphno, GLYPH *glyphs); /* get the glyph path */ + void (*kerning)(GLYPH *glyph_list); /* extract the kerning data */ +}; Index: xc/extras/ttf2pt1/pt1.c =================================================================== RCS file: xc/extras/ttf2pt1/pt1.c diff -N xc/extras/ttf2pt1/pt1.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/pt1.c 13 Apr 2004 02:45:06 -0000 @@ -0,0 +1,7372 @@ +/* + * see COPYRIGHT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WINDOWS +# include +# include +#else +# include "windows.h" +#endif + +#include "ttf.h" +#include "pt1.h" +#include "global.h" + +/* big and small values for comparisons */ +#define FBIGVAL (1e20) +#define FEPS (100000./FBIGVAL) + +/* names of the axes */ +#define X 0 +#define Y 1 + +/* the GENTRY extension structure used in fforceconcise() */ + +struct gex_con { + double d[2 /*X, Y*/]; /* sizes of curve */ + double sin2; /* squared sinus of the angle to the next gentry */ + double len2; /* squared distance between the endpoints */ + +/* number of reference dots taken from each curve */ +#define NREFDOTS 3 + + double dots[NREFDOTS][2]; /* reference dots */ + + int flags; /* flags for gentry and tits joint to the next gentry */ +/* a vertical or horizontal line may be in 2 quadrants at once */ +#define GEXF_QUL 0x00000001 /* in up-left quadrant */ +#define GEXF_QUR 0x00000002 /* in up-right quadrant */ +#define GEXF_QDR 0x00000004 /* in down-right quadrant */ +#define GEXF_QDL 0x00000008 /* in down-left quadrant */ +#define GEXF_QMASK 0x0000000F /* quadrant mask */ + +/* if a line is nearly vertical or horizontal, we remember that idealized quartant too */ +#define GEXF_QTO_IDEAL(f) (((f)&0xF)<<4) +#define GEXF_QFROM_IDEAL(f) (((f)&0xF0)>>4) +#define GEXF_IDQ_L 0x00000090 /* left */ +#define GEXF_IDQ_R 0x00000060 /* right */ +#define GEXF_IDQ_U 0x00000030 /* up */ +#define GEXF_IDQ_D 0x000000C0 /* down */ + +/* possibly can be joined with conditions: + * (in order of increasing preference, the numeric order is important) + */ +#define GEXF_JLINE 0x00000100 /* into one line */ +#define GEXF_JIGN 0x00000200 /* if one entry's tangent angle is ignored */ +#define GEXF_JID 0x00000400 /* if one entry is idealized to hor/vert */ +#define GEXF_JFLAT 0x00000800 /* if one entry is flattened */ +#define GEXF_JGOOD 0x00001000 /* perfectly, no additional maodifications */ + +#define GEXF_JMASK 0x00001F00 /* the mask of all above */ +#define GEXF_JCVMASK 0x00001E00 /* the mask of all above except JLINE */ + +/* which entry needs to be modified for conditional joining */ +#define GEXF_JIGN1 0x00002000 +#define GEXF_JIGN2 0x00004000 +#define GEXF_JIGNDIR(dir) (GEXF_JIGN1<<(dir)) +#define GEXF_JID1 0x00008000 +#define GEXF_JID2 0x00010000 +#define GEXF_JIDDIR(dir) (GEXF_JID1<<(dir)) +#define GEXF_JFLAT1 0x00020000 +#define GEXF_JFLAT2 0x00040000 +#define GEXF_JFLATDIR(dir) (GEXF_JFLAT1<<(dir)) + +#define GEXF_VERT 0x00100000 /* is nearly vertical */ +#define GEXF_HOR 0x00200000 /* is nearly horizontal */ +#define GEXF_FLAT 0x00400000 /* is nearly flat */ + +#define GEXF_VDOTS 0x01000000 /* the dots are valid */ + + signed char isd[2 /*X,Y*/]; /* signs of the sizes */ +}; +typedef struct gex_con GEX_CON; + +/* convenience macros */ +#define X_CON(ge) ((GEX_CON *)((ge)->ext)) +#define X_CON_D(ge) (X_CON(ge)->d) +#define X_CON_DX(ge) (X_CON(ge)->d[0]) +#define X_CON_DY(ge) (X_CON(ge)->d[1]) +#define X_CON_ISD(ge) (X_CON(ge)->isd) +#define X_CON_ISDX(ge) (X_CON(ge)->isd[0]) +#define X_CON_ISDY(ge) (X_CON(ge)->isd[1]) +#define X_CON_SIN2(ge) (X_CON(ge)->sin2) +#define X_CON_LEN2(ge) (X_CON(ge)->len2) +#define X_CON_F(ge) (X_CON(ge)->flags) + +/* performance statistics about guessing the concise curves */ +static int ggoodcv=0, ggoodcvdots=0, gbadcv=0, gbadcvdots=0; + +int stdhw, stdvw; /* dominant stems widths */ +int stemsnaph[12], stemsnapv[12]; /* most typical stem width */ + +int bluevalues[14]; +int nblues; +int otherblues[10]; +int notherb; +int bbox[4]; /* the FontBBox array */ +double italic_angle; + +GLYPH *glyph_list; +int encoding[ENCTABSZ]; /* inverse of glyph[].char_no */ +int kerning_pairs = 0; + +/* prototypes */ +static void fixcvdir( GENTRY * ge, int dir); +static void fixcvends( GENTRY * ge); +static int fgetcvdir( GENTRY * ge); +static int igetcvdir( GENTRY * ge); +static int fiszigzag( GENTRY *ge); +static int iiszigzag( GENTRY *ge); +static GENTRY * freethisge( GENTRY *ge); +static void addgeafter( GENTRY *oge, GENTRY *nge ); +static GENTRY * newgentry( int flags); +static void debugstems( char *name, STEM * hstems, int nhs, STEM * vstems, int nvs); +static int addbluestems( STEM *s, int n); +static void sortstems( STEM * s, int n); +static int stemoverlap( STEM * s1, STEM * s2); +static int steminblue( STEM *s); +static void markbluestems( STEM *s, int nold); +static int joinmainstems( STEM * s, int nold, int useblues); +static void joinsubstems( STEM * s, short *pairs, int nold, int useblues); +static void fixendpath( GENTRY *ge); +static void fdelsmall( GLYPH *g, double minlen); +static void alloc_gex_con( GENTRY *ge); +static double fjointsin2( GENTRY *ge1, GENTRY *ge2); +static double fcvarea( GENTRY *ge); +static double fcvval( GENTRY *ge, int axis, double t); +static void fsampledots( GENTRY *ge, double dots[][2], int ndots); +static void fnormalizege( GENTRY *ge); +static void fanalyzege( GENTRY *ge); +static void fanalyzejoint( GENTRY *ge); +static void fconcisecontour( GLYPH *g, GENTRY *ge); +static double fclosegap( GENTRY *from, GENTRY *to, int axis, + double gap, double *ret); + +int +isign( + int x +) +{ + if (x > 0) + return 1; + else if (x < 0) + return -1; + else + return 0; +} + +int +fsign( + double x +) +{ + if (x > 0.0) + return 1; + else if (x < 0.0) + return -1; + else + return 0; +} + +static GENTRY * +newgentry( + int flags +) +{ + GENTRY *ge; + + ge = calloc(1, sizeof(GENTRY)); + + if (ge == 0) { + fprintf(stderr, "***** Memory allocation error *****\n"); + exit(255); + } + ge->stemid = -1; + ge->flags = flags; + /* the rest is set to 0 by calloc() */ + return ge; +} + +/* + * Routines to print out Postscript functions with optimization + */ + +void +rmoveto( + int dx, + int dy +) +{ + if (optimize && dx == 0) + fprintf(pfa_file, "%d vmoveto\n", dy); + else if (optimize && dy == 0) + fprintf(pfa_file, "%d hmoveto\n", dx); + else + fprintf(pfa_file, "%d %d rmoveto\n", dx, dy); +} + +void +rlineto( + int dx, + int dy +) +{ + if (optimize && dx == 0 && dy == 0) /* for special pathologic + * case */ + return; + else if (optimize && dx == 0) + fprintf(pfa_file, "%d vlineto\n", dy); + else if (optimize && dy == 0) + fprintf(pfa_file, "%d hlineto\n", dx); + else + fprintf(pfa_file, "%d %d rlineto\n", dx, dy); +} + +void +rrcurveto( + int dx1, + int dy1, + int dx2, + int dy2, + int dx3, + int dy3 +) +{ + /* first two ifs are for crazy cases that occur surprisingly often */ + if (optimize && dx1 == 0 && dx2 == 0 && dx3 == 0) + rlineto(0, dy1 + dy2 + dy3); + else if (optimize && dy1 == 0 && dy2 == 0 && dy3 == 0) + rlineto(dx1 + dx2 + dx3, 0); + else if (optimize && dy1 == 0 && dx3 == 0) + fprintf(pfa_file, "%d %d %d %d hvcurveto\n", + dx1, dx2, dy2, dy3); + else if (optimize && dx1 == 0 && dy3 == 0) + fprintf(pfa_file, "%d %d %d %d vhcurveto\n", + dy1, dx2, dy2, dx3); + else + fprintf(pfa_file, "%d %d %d %d %d %d rrcurveto\n", + dx1, dy1, dx2, dy2, dx3, dy3); +} + +void +closepath(void) +{ + fprintf(pfa_file, "closepath\n"); +} + +/* + * Many of the path processing routines exist (or will exist) in + * both floating-point and integer version. Fimally most of the + * processing will go in floating point and the integer processing + * will become legacy. + * The names of floating routines start with f, names of integer + * routines start with i, and those old routines existing in one + * version only have no such prefix at all. + */ + +/* +** Routine that checks integrity of the path, for debugging +*/ + +void +assertpath( + GENTRY * from, + char *file, + int line, + char *name +) +{ + GENTRY *first, *pe, *ge; + int isfloat; + + if(from==0) + return; + isfloat = (from->flags & GEF_FLOAT); + pe = from->prev; + for (ge = from; ge != 0; pe = ge, ge = ge->next) { + if( (ge->flags & GEF_FLOAT) ^ isfloat ) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "float flag changes from %s to %s at 0x%p (type %c, prev type %c)\n", + (isfloat ? "TRUE" : "FALSE"), (isfloat ? "FALSE" : "TRUE"), ge, ge->type, pe->type); + abort(); + } + if (pe != ge->prev) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "unidirectional chain 0x%x -next-> 0x%x -prev-> 0x%x \n", + pe, ge, ge->prev); + abort(); + } + + switch(ge->type) { + case GE_MOVE: + break; + case GE_PATH: + if (ge->prev == 0) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "empty path at 0x%x \n", ge); + abort(); + } + break; + case GE_LINE: + case GE_CURVE: + if(ge->frwd->bkwd != ge) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "unidirectional chain 0x%x -frwd-> 0x%x -bkwd-> 0x%x \n", + ge, ge->frwd, ge->frwd->bkwd); + abort(); + } + if(ge->prev->type == GE_MOVE) { + first = ge; + if(ge->bkwd->next->type != GE_PATH) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "broken first backlink 0x%x -bkwd-> 0x%x -next-> 0x%x \n", + ge, ge->bkwd, ge->bkwd->next); + abort(); + } + } + if(ge->next->type == GE_PATH) { + if(ge->frwd != first) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "broken loop 0x%x -...-> 0x%x -frwd-> 0x%x \n", + first, ge, ge->frwd); + abort(); + } + } + break; + } + + } +} + +void +assertisfloat( + GLYPH *g, + char *msg +) +{ + if( !(g->flags & GF_FLOAT) ) { + fprintf(stderr, "**! Glyph %s is not float: %s\n", g->name, msg); + abort(); + } + if(g->lastentry) { + if( !(g->lastentry->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! Glyphs %s last entry is int: %s\n", g->name, msg); + abort(); + } + } +} + +void +assertisint( + GLYPH *g, + char *msg +) +{ + if( (g->flags & GF_FLOAT) ) { + fprintf(stderr, "**! Glyph %s is not int: %s\n", g->name, msg); + abort(); + } + if(g->lastentry) { + if( (g->lastentry->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! Glyphs %s last entry is float: %s\n", g->name, msg); + abort(); + } + } +} + + +/* + * Routines to save the generated data about glyph + */ + +void +fg_rmoveto( + GLYPH * g, + double x, + double y) +{ + GENTRY *oge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: f rmoveto(%g, %g)\n", g->name, x, y); + + assertisfloat(g, "adding float MOVE"); + + if ((oge = g->lastentry) != 0) { + if (oge->type == GE_MOVE) { /* just eat up the first move */ + oge->fx3 = x; + oge->fy3 = y; + } else if (oge->type == GE_LINE || oge->type == GE_CURVE) { + fprintf(stderr, "Glyph %s: MOVE in middle of path\n", g->name); + } else { + GENTRY *nge; + + nge = newgentry(GEF_FLOAT); + nge->type = GE_MOVE; + nge->fx3 = x; + nge->fy3 = y; + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } + } else { + GENTRY *nge; + + nge = newgentry(GEF_FLOAT); + nge->type = GE_MOVE; + nge->fx3 = x; + nge->fy3 = y; + nge->bkwd = (GENTRY*)&g->entries; + g->entries = g->lastentry = nge; + } + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +void +ig_rmoveto( + GLYPH * g, + int x, + int y) +{ + GENTRY *oge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: i rmoveto(%d, %d)\n", g->name, x, y); + + assertisint(g, "adding int MOVE"); + + if ((oge = g->lastentry) != 0) { + if (oge->type == GE_MOVE) { /* just eat up the first move */ + oge->ix3 = x; + oge->iy3 = y; + } else if (oge->type == GE_LINE || oge->type == GE_CURVE) { + fprintf(stderr, "Glyph %s: MOVE in middle of path, ignored\n", g->name); + } else { + GENTRY *nge; + + nge = newgentry(0); + nge->type = GE_MOVE; + nge->ix3 = x; + nge->iy3 = y; + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } + } else { + GENTRY *nge; + + nge = newgentry(0); + nge->type = GE_MOVE; + nge->ix3 = x; + nge->iy3 = y; + nge->bkwd = (GENTRY*)&g->entries; + g->entries = g->lastentry = nge; + } + +} + +void +fg_rlineto( + GLYPH * g, + double x, + double y) +{ + GENTRY *oge, *nge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: f rlineto(%g, %g)\n", g->name, x, y); + + assertisfloat(g, "adding float LINE"); + + nge = newgentry(GEF_FLOAT); + nge->type = GE_LINE; + nge->fx3 = x; + nge->fy3 = y; + + if ((oge = g->lastentry) != 0) { + if (x == oge->fx3 && y == oge->fy3) { /* empty line */ + /* ignore it or we will get in troubles later */ + free(nge); + return; + } + if (g->path == 0) { + g->path = nge; + nge->bkwd = nge->frwd = nge; + } else { + oge->frwd = nge; + nge->bkwd = oge; + g->path->bkwd = nge; + nge->frwd = g->path; + } + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } else { + WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name); + free(nge); + } + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +void +ig_rlineto( + GLYPH * g, + int x, + int y) +{ + GENTRY *oge, *nge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: i rlineto(%d, %d)\n", g->name, x, y); + + assertisint(g, "adding int LINE"); + + nge = newgentry(0); + nge->type = GE_LINE; + nge->ix3 = x; + nge->iy3 = y; + + if ((oge = g->lastentry) != 0) { + if (x == oge->ix3 && y == oge->iy3) { /* empty line */ + /* ignore it or we will get in troubles later */ + free(nge); + return; + } + if (g->path == 0) { + g->path = nge; + nge->bkwd = nge->frwd = nge; + } else { + oge->frwd = nge; + nge->bkwd = oge; + g->path->bkwd = nge; + nge->frwd = g->path; + } + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } else { + WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name); + free(nge); + } + +} + +void +fg_rrcurveto( + GLYPH * g, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3) +{ + GENTRY *oge, *nge; + + oge = g->lastentry; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: f rrcurveto(%g, %g, %g, %g, %g, %g)\n" + ,g->name, x1, y1, x2, y2, x3, y3); + + assertisfloat(g, "adding float CURVE"); + + if (oge && oge->fx3 == x1 && x1 == x2 && x2 == x3) /* check if it's + * actually a line */ + fg_rlineto(g, x1, y3); + else if (oge && oge->fy3 == y1 && y1 == y2 && y2 == y3) + fg_rlineto(g, x3, y1); + else { + nge = newgentry(GEF_FLOAT); + nge->type = GE_CURVE; + nge->fx1 = x1; + nge->fy1 = y1; + nge->fx2 = x2; + nge->fy2 = y2; + nge->fx3 = x3; + nge->fy3 = y3; + + if (oge != 0) { + if (x3 == oge->fx3 && y3 == oge->fy3) { + free(nge); /* consider this curve empty */ + /* ignore it or we will get in troubles later */ + return; + } + if (g->path == 0) { + g->path = nge; + nge->bkwd = nge->frwd = nge; + } else { + oge->frwd = nge; + nge->bkwd = oge; + g->path->bkwd = nge; + nge->frwd = g->path; + } + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } else { + WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name); + free(nge); + } + } + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +void +ig_rrcurveto( + GLYPH * g, + int x1, + int y1, + int x2, + int y2, + int x3, + int y3) +{ + GENTRY *oge, *nge; + + oge = g->lastentry; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: i rrcurveto(%d, %d, %d, %d, %d, %d)\n" + ,g->name, x1, y1, x2, y2, x3, y3); + + assertisint(g, "adding int CURVE"); + + if (oge && oge->ix3 == x1 && x1 == x2 && x2 == x3) /* check if it's + * actually a line */ + ig_rlineto(g, x1, y3); + else if (oge && oge->iy3 == y1 && y1 == y2 && y2 == y3) + ig_rlineto(g, x3, y1); + else { + nge = newgentry(0); + nge->type = GE_CURVE; + nge->ix1 = x1; + nge->iy1 = y1; + nge->ix2 = x2; + nge->iy2 = y2; + nge->ix3 = x3; + nge->iy3 = y3; + + if (oge != 0) { + if (x3 == oge->ix3 && y3 == oge->iy3) { + free(nge); /* consider this curve empty */ + /* ignore it or we will get in troubles later */ + return; + } + if (g->path == 0) { + g->path = nge; + nge->bkwd = nge->frwd = nge; + } else { + oge->frwd = nge; + nge->bkwd = oge; + g->path->bkwd = nge; + nge->frwd = g->path; + } + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } else { + WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name); + free(nge); + } + } +} + +void +g_closepath( + GLYPH * g +) +{ + GENTRY *oge, *nge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: closepath\n", g->name); + + oge = g->lastentry; + + if (g->path == 0) { + WARNING_1 fprintf(stderr, "Warning: **** closepath on empty path in glyph \"%s\" ****\n", + g->name); + if (oge == 0) { + WARNING_1 fprintf(stderr, "No previois entry\n"); + } else { + WARNING_1 fprintf(stderr, "Previous entry type: %c\n", oge->type); + if (oge->type == GE_MOVE) { + g->lastentry = oge->prev; + if (oge->prev == 0) + g->entries = 0; + else + g->lastentry->next = 0; + free(oge); + } + } + return; + } + + nge = newgentry(oge->flags & GEF_FLOAT); /* keep the same type */ + nge->type = GE_PATH; + + g->path = 0; + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +/* + * * SB * Routines to smooth and fix the glyphs + */ + +/* +** we don't want to see the curves with coinciding middle and +** outer points +*/ + +static void +fixcvends( + GENTRY * ge +) +{ + int dx, dy; + int x0, y0, x1, y1, x2, y2, x3, y3; + + if (ge->type != GE_CURVE) + return; + + if(ge->flags & GEF_FLOAT) { + fprintf(stderr, "**! fixcvends(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + x0 = ge->prev->ix3; + y0 = ge->prev->iy3; + x1 = ge->ix1; + y1 = ge->iy1; + x2 = ge->ix2; + y2 = ge->iy2; + x3 = ge->ix3; + y3 = ge->iy3; + + + /* look at the start of the curve */ + if (x1 == x0 && y1 == y0) { + dx = x2 - x1; + dy = y2 - y1; + + if (dx == 0 && dy == 0 + || x2 == x3 && y2 == y3) { + /* Oops, we actually have a straight line */ + /* + * if it's small, we hope that it will get optimized + * later + */ + if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) { + ge->ix1 = x3; + ge->iy1 = y3; + ge->ix2 = x0; + ge->iy2 = y0; + } else {/* just make it a line */ + ge->type = GE_LINE; + } + } else { + if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very + * small */ + ge->ix1 = x2; + ge->iy1 = y2; + } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */ + ge->ix1 += dx / 2; + ge->iy1 += dy / 2; + } else { + ge->ix1 += dx / 4; + ge->iy1 += dy / 4; + } + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) { + if (abs(x3 - x0) * abs(ge->iy1 - y0) > abs(y3 - y0) * abs(ge->ix1 - x0)) + ge->ix1 += isign(dx); + } else { + if (abs(x3 - x0) * abs(ge->iy1 - y0) < abs(y3 - y0) * abs(ge->ix1 - x0)) + ge->iy1 += isign(dy); + } + + ge->ix2 += (x3 - x2) / 8; + ge->iy2 += (y3 - y2) / 8; + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(y3 - y2) < abs(y3 - y0) * abs(x3 - x2)) { + if (abs(x3 - x0) * abs(y3 - ge->iy2) > abs(y3 - y0) * abs(x3 - ge->ix2)) + ge->iy1 -= isign(y3 - y2); + } else { + if (abs(x3 - x0) * abs(y3 - ge->iy2) < abs(y3 - y0) * abs(x3 - ge->ix2)) + ge->ix1 -= isign(x3 - x2); + } + + } + } else if (x2 == x3 && y2 == y3) { + dx = x1 - x2; + dy = y1 - y2; + + if (dx == 0 && dy == 0) { + /* Oops, we actually have a straight line */ + /* + * if it's small, we hope that it will get optimized + * later + */ + if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) { + ge->ix1 = x3; + ge->iy1 = y3; + ge->ix2 = x0; + ge->iy2 = y0; + } else {/* just make it a line */ + ge->type = GE_LINE; + } + } else { + if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very + * small */ + ge->ix2 = x1; + ge->iy2 = y1; + } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */ + ge->ix2 += dx / 2; + ge->iy2 += dy / 2; + } else { + ge->ix2 += dx / 4; + ge->iy2 += dy / 4; + } + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) { + if (abs(x3 - x0) * abs(ge->iy2 - y3) > abs(y3 - y0) * abs(ge->ix2 - x3)) + ge->ix2 += isign(dx); + } else { + if (abs(x3 - x0) * abs(ge->iy2 - y3) < abs(y3 - y0) * abs(ge->ix2 - x3)) + ge->iy2 += isign(dy); + } + + ge->ix1 += (x0 - x1) / 8; + ge->iy1 += (y0 - y1) / 8; + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(y0 - y1) < abs(y3 - y0) * abs(x0 - x1)) { + if (abs(x3 - x0) * abs(y0 - ge->iy1) > abs(y3 - y0) * abs(x0 - ge->ix1)) + ge->iy1 -= isign(y0 - y1); + } else { + if (abs(x3 - x0) * abs(y0 - ge->iy1) < abs(y3 - y0) * abs(x0 - ge->ix1)) + ge->ix1 -= isign(x0 - x1); + } + + } + } +} + +/* +** After transformations we want to make sure that the resulting +** curve is going in the same quadrant as the original one, +** because rounding errors introduced during transformations +** may make the result completeley wrong. +** +** `dir' argument describes the direction of the original curve, +** it is the superposition of two values for the front and +** rear ends of curve: +** +** >EQUAL - goes over the line connecting the ends +** =EQUAL - coincides with the line connecting the ends +** flags & GEF_FLOAT) { + fprintf(stderr, "**! fixcvdir(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + fdir = (dir & CVDIR_FRONT) - CVDIR_FEQUAL; + if ((dir & CVDIR_REAR) == CVDIR_RSAME) + rdir = fdir; /* we need only isign, exact value doesn't matter */ + else + rdir = (dir & CVDIR_REAR) - CVDIR_REQUAL; + + fixcvends(ge); + + c = isign(ge->ix3 - ge->prev->ix3); /* note the direction of + * curve */ + d = isign(ge->iy3 - ge->prev->iy3); + + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + + changed = 1; + while (changed) { + if (ISDBG(FIXCVDIR)) { + /* for debugging */ + fprintf(stderr, "fixcvdir %d %d (%d %d %d %d %d %d) %f %f %f\n", + fdir, rdir, + ge->ix1 - ge->prev->ix3, + ge->iy1 - ge->prev->iy3, + ge->ix2 - ge->ix1, + ge->iy2 - ge->iy1, + ge->ix3 - ge->ix2, + ge->iy3 - ge->iy2, + kk1, kk, kk2); + } + changed = 0; + + if (fdir > 0) { + if (kk1 > kk) { /* the front end has problems */ + if (c * (ge->ix1 - ge->prev->ix3) > 0) { + ge->ix1 -= c; + changed = 1; + } if (d * (ge->iy2 - ge->iy1) > 0) { + ge->iy1 += d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } else if (fdir < 0) { + if (kk1 < kk) { /* the front end has problems */ + if (c * (ge->ix2 - ge->ix1) > 0) { + ge->ix1 += c; + changed = 1; + } if (d * (ge->iy1 - ge->prev->iy3) > 0) { + ge->iy1 -= d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } + if (rdir > 0) { + if (kk2 < kk) { /* the rear end has problems */ + if (c * (ge->ix2 - ge->ix1) > 0) { + ge->ix2 -= c; + changed = 1; + } if (d * (ge->iy3 - ge->iy2) > 0) { + ge->iy2 += d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } else if (rdir < 0) { + if (kk2 > kk) { /* the rear end has problems */ + if (c * (ge->ix3 - ge->ix2) > 0) { + ge->ix2 += c; + changed = 1; + } if (d * (ge->iy2 - ge->iy1) > 0) { + ge->iy2 -= d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } + } + fixcvends(ge); +} + +/* Get the directions of ends of curve for further usage */ + +/* expects that the previous element is also float */ + +static int +fgetcvdir( + GENTRY * ge +) +{ + double a, b; + double k, k1, k2; + int dir = 0; + + if( !(ge->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! fgetcvdir(0x%x) on int entry, ABORT\n", ge); + abort(); /* dump core */ + } + + a = fabs(ge->fy3 - ge->prev->fy3); + b = fabs(ge->fx3 - ge->prev->fx3); + k = a < FEPS ? (b < FEPS ? 1. : 100000.) : ( b / a); + + a = fabs(ge->fy1 - ge->prev->fy3); + b = fabs(ge->fx1 - ge->prev->fx3); + if(a < FEPS) { + if(b < FEPS) { + a = fabs(ge->fy2 - ge->prev->fy3); + b = fabs(ge->fx2 - ge->prev->fx3); + k1 = a < FEPS ? (b < FEPS ? k : 100000.) : ( b / a); + } else + k1 = FBIGVAL; + } else + k1 = b / a; + + a = fabs(ge->fy3 - ge->fy2); + b = fabs(ge->fx3 - ge->fx2); + if(a < FEPS) { + if(b < FEPS) { + a = fabs(ge->fy3 - ge->fy1); + b = fabs(ge->fx3 - ge->fx1); + k2 = a < FEPS ? (b < FEPS ? k : 100000.) : ( b / a); + } else + k2 = FBIGVAL; + } else + k2 = b / a; + + if(fabs(k1-k) < 0.0001) + dir |= CVDIR_FEQUAL; + else if (k1 < k) + dir |= CVDIR_FUP; + else + dir |= CVDIR_FDOWN; + + if(fabs(k2-k) < 0.0001) + dir |= CVDIR_REQUAL; + else if (k2 > k) + dir |= CVDIR_RUP; + else + dir |= CVDIR_RDOWN; + + return dir; +} + + +/* expects that the previous element is also int */ + +static int +igetcvdir( + GENTRY * ge +) +{ + int a, b; + double k, k1, k2; + int dir = 0; + + if(ge->flags & GEF_FLOAT) { + fprintf(stderr, "**! igetcvdir(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + k = (a == 0) ? (b == 0 ? 1. : 100000.) : fabs((double) b / (double) a); + + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + if(a == 0) { + if(b == 0) { + a = ge->iy2 - ge->prev->iy3; + b = ge->ix2 - ge->prev->ix3; + k1 = (a == 0) ? (b == 0 ? k : 100000.) : fabs((double) b / (double) a); + } else + k1 = FBIGVAL; + } else + k1 = fabs((double) b / (double) a); + + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + if(a == 0) { + if(b == 0) { + a = ge->iy3 - ge->iy1; + b = ge->ix3 - ge->ix1; + k2 = (a == 0) ? (b == 0 ? k : 100000.) : fabs((double) b / (double) a); + } else + k2 = FBIGVAL; + } else + k2 = fabs((double) b / (double) a); + + if(fabs(k1-k) < 0.0001) + dir |= CVDIR_FEQUAL; + else if (k1 < k) + dir |= CVDIR_FUP; + else + dir |= CVDIR_FDOWN; + + if(fabs(k2-k) < 0.0001) + dir |= CVDIR_REQUAL; + else if (k2 > k) + dir |= CVDIR_RUP; + else + dir |= CVDIR_RDOWN; + + return dir; +} + +#if 0 +/* a function just to test the work of fixcvdir() */ +static void +testfixcvdir( + GLYPH * g +) +{ + GENTRY *ge; + int dir; + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_CURVE) { + dir = igetcvdir(ge); + fixcvdir(ge, dir); + } + } +} +#endif + +static int +iround( + double val +) +{ + return (int) (val > 0 ? val + 0.5 : val - 0.5); +} + +/* for debugging - dump the glyph + * mark with a star the entries from start to end inclusive + * (start == NULL means don't mark any, end == NULL means to the last) + */ + +void +dumppaths( + GLYPH *g, + GENTRY *start, + GENTRY *end +) +{ + GENTRY *ge; + int i; + char mark=' '; + + fprintf(stderr, "Glyph %s:\n", g->name); + + /* now do the conversion */ + for(ge = g->entries; ge != 0; ge = ge->next) { + if(ge == start) + mark = '*'; + fprintf(stderr, " %c %8x", mark, ge); + switch(ge->type) { + case GE_MOVE: + case GE_LINE: + if(ge->flags & GEF_FLOAT) + fprintf(stderr," %c float (%g, %g)\n", ge->type, ge->fx3, ge->fy3); + else + fprintf(stderr," %c int (%d, %d)\n", ge->type, ge->ix3, ge->iy3); + break; + case GE_CURVE: + if(ge->flags & GEF_FLOAT) { + fprintf(stderr," C float "); + for(i=0; i<3; i++) + fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]); + fprintf(stderr,"\n"); + } else { + fprintf(stderr," C int "); + for(i=0; i<3; i++) + fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); + fprintf(stderr,"\n"); + } + break; + default: + fprintf(stderr, " %c\n", ge->type); + break; + } + if(ge == end) + mark = ' '; + } +} + +/* + * Routine that converts all entries in the path from float to int + */ + +void +pathtoint( + GLYPH *g +) +{ + GENTRY *ge; + int x[3], y[3]; + int i; + + + if(ISDBG(TOINT)) + fprintf(stderr, "TOINT: glyph %s\n", g->name); + assertisfloat(g, "converting path to int\n"); + + fdelsmall(g, 1.0); /* get rid of sub-pixel contours */ + assertpath(g->entries, __FILE__, __LINE__, g->name); + + /* 1st pass, collect the directions of the curves: have + * to do that in advance, while everyting is float + */ + for(ge = g->entries; ge != 0; ge = ge->next) { + if( !(ge->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! glyphs %s has int entry, found in conversion to int\n", + g->name); + exit(1); + } + if(ge->type == GE_CURVE) { + ge->dir = fgetcvdir(ge); + } + } + + /* now do the conversion */ + for(ge = g->entries; ge != 0; ge = ge->next) { + switch(ge->type) { + case GE_MOVE: + case GE_LINE: + if(ISDBG(TOINT)) + fprintf(stderr," %c float x=%g y=%g\n", ge->type, ge->fx3, ge->fy3); + x[0] = iround(ge->fx3); + y[0] = iround(ge->fy3); + for(i=0; i<3; i++) { /* put some valid values everywhere, for convenience */ + ge->ixn[i] = x[0]; + ge->iyn[i] = y[0]; + } + if(ISDBG(TOINT)) + fprintf(stderr," int x=%d y=%d\n", ge->ix3, ge->iy3); + break; + case GE_CURVE: + if(ISDBG(TOINT)) + fprintf(stderr," %c float ", ge->type); + + for(i=0; i<3; i++) { + if(ISDBG(TOINT)) + fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]); + x[i] = iround(ge->fxn[i]); + y[i] = iround(ge->fyn[i]); + } + + if(ISDBG(TOINT)) + fprintf(stderr,"\n int "); + + for(i=0; i<3; i++) { + ge->ixn[i] = x[i]; + ge->iyn[i] = y[i]; + if(ISDBG(TOINT)) + fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); + } + ge->flags &= ~GEF_FLOAT; /* for fixcvdir */ + fixcvdir(ge, ge->dir); + + if(ISDBG(TOINT)) { + fprintf(stderr,"\n fixed "); + for(i=0; i<3; i++) + fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); + fprintf(stderr,"\n"); + } + + break; + } + ge->flags &= ~GEF_FLOAT; + } + g->flags &= ~GF_FLOAT; +} + + +/* check whether we can fix up the curve to change its size by (dx,dy) */ +/* 0 means NO, 1 means YES */ + +/* for float: if scaling would be under 10% */ + +int +fcheckcv( + GENTRY * ge, + double dx, + double dy +) +{ + if( !(ge->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! fcheckcv(0x%x) on int entry, ABORT\n", ge); + abort(); /* dump core */ + } + + if (ge->type != GE_CURVE) + return 0; + + if( fabs(ge->fx3 - ge->prev->fx3) < fabs(dx) * 10 ) + return 0; + + if( fabs(ge->fy3 - ge->prev->fy3) < fabs(dy) * 10 ) + return 0; + + return 1; +} + +/* for int: if won't create new zigzags at the ends */ + +int +icheckcv( + GENTRY * ge, + int dx, + int dy +) +{ + int xdep, ydep; + + if(ge->flags & GEF_FLOAT) { + fprintf(stderr, "**! icheckcv(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + if (ge->type != GE_CURVE) + return 0; + + xdep = ge->ix3 - ge->prev->ix3; + ydep = ge->iy3 - ge->prev->iy3; + + if (ge->type == GE_CURVE + && (xdep * (xdep + dx)) > 0 + && (ydep * (ydep + dy)) > 0) { + return 1; + } else + return 0; +} + +/* float connect the ends of open contours */ + +void +fclosepaths( + GLYPH * g +) +{ + GENTRY *ge, *fge, *xge, *nge; + int i; + + assertisfloat(g, "fclosepaths float\n"); + + for (xge = g->entries; xge != 0; xge = xge->next) { + if( xge->type != GE_PATH ) + continue; + + ge = xge->prev; + if(ge == 0 || ge->type != GE_LINE && ge->type!= GE_CURVE) { + fprintf(stderr, "**! Glyph %s got empty path\n", + g->name); + exit(1); + } + + fge = ge->frwd; + if (fge->prev == 0 || fge->prev->type != GE_MOVE) { + fprintf(stderr, "**! Glyph %s got strange beginning of path\n", + g->name); + exit(1); + } + fge = fge->prev; + if (fge->fx3 != ge->fx3 || fge->fy3 != ge->fy3) { + /* we have to fix this open path */ + + WARNING_4 fprintf(stderr, "Glyph %s got path open by dx=%g dy=%g\n", + g->name, fge->fx3 - ge->fx3, fge->fy3 - ge->fy3); + + + /* add a new line */ + nge = newgentry(GEF_FLOAT); + (*nge) = (*ge); + nge->fx3 = fge->fx3; + nge->fy3 = fge->fy3; + nge->type = GE_LINE; + + addgeafter(ge, nge); + + if (fabs(ge->fx3 - fge->fx3) <= 2 && fabs(ge->fy3 - fge->fy3) <= 2) { + /* + * small change, try to get rid of the new entry + */ + + double df[2]; + + for(i=0; i<2; i++) { + df[i] = ge->fpoints[i][2] - fge->fpoints[i][2]; + df[i] = fclosegap(nge, nge, i, df[i], NULL); + } + + if(df[0] == 0. && df[1] == 0.) { + /* closed gap successfully, remove the added entry */ + freethisge(nge); + } + } + } + } +} + +void +smoothjoints( + GLYPH * g +) +{ + GENTRY *ge, *ne; + int dx1, dy1, dx2, dy2, k; + int dir; + + return; /* this stuff seems to create problems */ + + assertisint(g, "smoothjoints int"); + + if (g->entries == 0) /* nothing to do */ + return; + + for (ge = g->entries->next; ge != 0; ge = ge->next) { + ne = ge->frwd; + + /* + * although there should be no one-line path * and any path + * must end with CLOSEPATH, * nobody can say for sure + */ + + if (ge == ne || ne == 0) + continue; + + /* now handle various joints */ + + if (ge->type == GE_LINE && ne->type == GE_LINE) { + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + dx2 = ne->ix3 - ge->ix3; + dy2 = ne->iy3 - ge->iy3; + + /* check whether they have the same direction */ + /* and the same slope */ + /* then we can join them into one line */ + + if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 && dx1 * dy2 == dy1 * dx2) { + /* extend the previous line */ + ge->ix3 = ne->ix3; + ge->iy3 = ne->iy3; + + /* and get rid of the next line */ + freethisge(ne); + } + } else if (ge->type == GE_LINE && ne->type == GE_CURVE) { + fixcvends(ne); + + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + dx2 = ne->ix1 - ge->ix3; + dy2 = ne->iy1 - ge->iy3; + + /* if the line is nearly horizontal and we can fix it */ + if (dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0 + && icheckcv(ne, 0, -dy1) + && abs(dy1) <= 4) { + dir = igetcvdir(ne); + ge->iy3 -= dy1; + ne->iy1 -= dy1; + fixcvdir(ne, dir); + if (ge->next != ne) + ne->prev->iy3 -= dy1; + dy1 = 0; + } else if (dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0 + && icheckcv(ne, -dx1, 0) + && abs(dx1) <= 4) { + /* the same but vertical */ + dir = igetcvdir(ne); + ge->ix3 -= dx1; + ne->ix1 -= dx1; + fixcvdir(ne, dir); + if (ge->next != ne) + ne->prev->ix3 -= dx1; + dx1 = 0; + } + /* + * if line is horizontal and curve begins nearly + * horizontally + */ + if (dy1 == 0 && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0) { + dir = igetcvdir(ne); + ne->iy1 -= dy2; + fixcvdir(ne, dir); + dy2 = 0; + } else if (dx1 == 0 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0) { + /* the same but vertical */ + dir = igetcvdir(ne); + ne->ix1 -= dx2; + fixcvdir(ne, dir); + dx2 = 0; + } + } else if (ge->type == GE_CURVE && ne->type == GE_LINE) { + fixcvends(ge); + + dx1 = ge->ix3 - ge->ix2; + dy1 = ge->iy3 - ge->iy2; + dx2 = ne->ix3 - ge->ix3; + dy2 = ne->iy3 - ge->iy3; + + /* if the line is nearly horizontal and we can fix it */ + if (dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0 + && icheckcv(ge, 0, dy2) + && abs(dy2) <= 4) { + dir = igetcvdir(ge); + ge->iy3 += dy2; + ge->iy2 += dy2; + fixcvdir(ge, dir); + if (ge->next != ne) + ne->prev->iy3 += dy2; + dy2 = 0; + } else if (dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0 + && icheckcv(ge, dx2, 0) + && abs(dx2) <= 4) { + /* the same but vertical */ + dir = igetcvdir(ge); + ge->ix3 += dx2; + ge->ix2 += dx2; + fixcvdir(ge, dir); + if (ge->next != ne) + ne->prev->ix3 += dx2; + dx2 = 0; + } + /* + * if line is horizontal and curve ends nearly + * horizontally + */ + if (dy2 == 0 && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0) { + dir = igetcvdir(ge); + ge->iy2 += dy1; + fixcvdir(ge, dir); + dy1 = 0; + } else if (dx2 == 0 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0) { + /* the same but vertical */ + dir = igetcvdir(ge); + ge->ix2 += dx1; + fixcvdir(ge, dir); + dx1 = 0; + } + } else if (ge->type == GE_CURVE && ne->type == GE_CURVE) { + fixcvends(ge); + fixcvends(ne); + + dx1 = ge->ix3 - ge->ix2; + dy1 = ge->iy3 - ge->iy2; + dx2 = ne->ix1 - ge->ix3; + dy2 = ne->iy1 - ge->iy3; + + /* + * check if we have a rather smooth joint at extremal + * point + */ + /* left or right extremal point */ + if (abs(dx1) <= 4 && abs(dx2) <= 4 + && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0 + && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0 + && (ge->iy3 < ge->prev->iy3 && ne->iy3 < ge->iy3 + || ge->iy3 > ge->prev->iy3 && ne->iy3 > ge->iy3) + && (ge->ix3 - ge->prev->ix3) * (ne->ix3 - ge->ix3) < 0 + ) { + dir = igetcvdir(ge); + ge->ix2 += dx1; + dx1 = 0; + fixcvdir(ge, dir); + dir = igetcvdir(ne); + ne->ix1 -= dx2; + dx2 = 0; + fixcvdir(ne, dir); + } + /* top or down extremal point */ + else if (abs(dy1) <= 4 && abs(dy2) <= 4 + && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0 + && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0 + && (ge->ix3 < ge->prev->ix3 && ne->ix3 < ge->ix3 + || ge->ix3 > ge->prev->ix3 && ne->ix3 > ge->ix3) + && (ge->iy3 - ge->prev->iy3) * (ne->iy3 - ge->iy3) < 0 + ) { + dir = igetcvdir(ge); + ge->iy2 += dy1; + dy1 = 0; + fixcvdir(ge, dir); + dir = igetcvdir(ne); + ne->iy1 -= dy2; + dy2 = 0; + fixcvdir(ne, dir); + } + /* or may be we just have a smooth junction */ + else if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 + && 10 * abs(k = abs(dx1 * dy2) - abs(dy1 * dx2)) < (abs(dx1 * dy2) + abs(dy1 * dx2))) { + int tries[6][4]; + int results[6]; + int i, b; + + /* build array of changes we are going to try */ + /* uninitalized entries are 0 */ + if (k > 0) { + static int t1[6][4] = { + {0, 0, 0, 0}, + {-1, 0, 1, 0}, + {-1, 0, 0, 1}, + {0, -1, 1, 0}, + {0, -1, 0, 1}, + {-1, -1, 1, 1}}; + memcpy(tries, t1, sizeof tries); + } else { + static int t1[6][4] = { + {0, 0, 0, 0}, + {1, 0, -1, 0}, + {1, 0, 0, -1}, + {0, 1, -1, 0}, + {0, 1, 0, -1}, + {1, 1, -1, -1}}; + memcpy(tries, t1, sizeof tries); + } + + /* now try the changes */ + results[0] = abs(k); + for (i = 1; i < 6; i++) { + results[i] = abs((abs(dx1) + tries[i][0]) * (abs(dy2) + tries[i][1]) - + (abs(dy1) + tries[i][2]) * (abs(dx2) + tries[i][3])); + } + + /* and find the best try */ + k = abs(k); + b = 0; + for (i = 1; i < 6; i++) + if (results[i] < k) { + k = results[i]; + b = i; + } + /* and finally apply it */ + if (dx1 < 0) + tries[b][0] = -tries[b][0]; + if (dy2 < 0) + tries[b][1] = -tries[b][1]; + if (dy1 < 0) + tries[b][2] = -tries[b][2]; + if (dx2 < 0) + tries[b][3] = -tries[b][3]; + + dir = igetcvdir(ge); + ge->ix2 -= tries[b][0]; + ge->iy2 -= tries[b][2]; + fixcvdir(ge, dir); + dir = igetcvdir(ne); + ne->ix1 += tries[b][3]; + ne->iy1 += tries[b][1]; + fixcvdir(ne, dir); + } + } + } +} + +/* debugging: print out stems of a glyph */ +static void +debugstems( + char *name, + STEM * hstems, + int nhs, + STEM * vstems, + int nvs +) +{ + int i; + + fprintf(pfa_file, "%% %s\n", name); + fprintf(pfa_file, "%% %d horizontal stems:\n", nhs); + for (i = 0; i < nhs; i++) + fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c%c%c\n", i, hstems[i].value, + hstems[i].from, hstems[i].to, + ((hstems[i].flags & ST_UP) ? 'U' : 'D'), + ((hstems[i].flags & ST_END) ? 'E' : '-'), + ((hstems[i].flags & ST_FLAT) ? 'F' : '-'), + ((hstems[i].flags & ST_ZONE) ? 'Z' : ' '), + ((hstems[i].flags & ST_TOPZONE) ? 'T' : ' ')); + fprintf(pfa_file, "%% %d vertical stems:\n", nvs); + for (i = 0; i < nvs; i++) + fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c\n", i, vstems[i].value, + vstems[i].from, vstems[i].to, + ((vstems[i].flags & ST_UP) ? 'U' : 'D'), + ((vstems[i].flags & ST_END) ? 'E' : '-'), + ((vstems[i].flags & ST_FLAT) ? 'F' : '-')); +} + +/* add pseudo-stems for the limits of the Blue zones to the stem array */ +static int +addbluestems( + STEM *s, + int n +) +{ + int i; + + for(i=0; i + (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT) + ) + continue; + } else { + if( + (s[i].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT) + < + (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT) + ) + continue; + } + } + } + x = s[j]; + s[j] = s[i]; + s[i] = x; + } +} + +/* check whether two stem borders overlap */ + +static int +stemoverlap( + STEM * s1, + STEM * s2 +) +{ + int result; + + if (s1->from <= s2->from && s1->to >= s2->from + || s2->from <= s1->from && s2->to >= s1->from) + result = 1; + else + result = 0; + + if (ISDBG(STEMOVERLAP)) + fprintf(pfa_file, "%% overlap %d(%d..%d)x%d(%d..%d)=%d\n", + s1->value, s1->from, s1->to, s2->value, s2->from, s2->to, result); + return result; +} + +/* + * check if the stem [border] is in an appropriate blue zone + * (currently not used) + */ + +static int +steminblue( + STEM *s +) +{ + int i, val; + + val=s->value; + if(s->flags & ST_UP) { + /* painted size up, look at lower zones */ + if(nblues>=2 && val>=bluevalues[0] && val<=bluevalues[1] ) + return 1; + for(i=0; i=otherblues[i] && val<=otherblues[i+1] ) + return 1; + } + } else { + /* painted side down, look at upper zones */ + for(i=2; i=bluevalues[i] && val<=bluevalues[i+1] ) + return 1; + } + } + + return 0; +} + +/* mark the outermost stem [borders] in the blue zones */ + +static void +markbluestems( + STEM *s, + int nold +) +{ + int i, j, a, b, c; + /* + * traverse the list of Blue Values, mark the lowest upper + * stem in each bottom zone and the topmost lower stem in + * each top zone with ST_BLUE + */ + + /* top zones */ + for(i=2; i=0; j--) { + if( s[j].flags & (ST_ZONE|ST_UP|ST_END) ) + continue; + c=s[j].value; + if(c=0 && s[j].value==c + && (s[j].flags & (ST_UP|ST_ZONE))==0 ; j--) + s[j].flags |= ST_BLUE; + break; + } + } + } + /* baseline */ + if(nblues>=2) { + a=bluevalues[0]; b=bluevalues[1]; + for(j=0; jb) /* too high */ + break; + if(c>=a) { /* found the lowest stem border */ + /* mark all the stems with the same value */ + if(ISDBG(BLUESTEMS)) + fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value); + /* include ST_END values */ + while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 ) + j--; + s[j].flags |= ST_BLUE; + for(j++; jb) /* too high */ + break; + if(c>=a) { /* found the lowest stem border */ + /* mark all the stems with the same value */ + if(ISDBG(BLUESTEMS)) + fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value); + /* include ST_END values */ + while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 ) + j--; + s[j].flags |= ST_BLUE; + for(j++; j=b) { /* have no free space */ + for(j=nold; j>=b; j--) /* make free space */ + s[j]=s[j-1]; + b++; + nold++; + } + s[nnew]=s[a]; + s[nnew].flags &= ~(ST_UP|ST_BLUE); + nnew++; + i=b-1; + } else { + s[nnew++]=s[c]; + i=c; /* skip up to this point */ + } + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d U BLUE\n", + s[nnew-2].value, s[nnew-1].value); + } else { + if (nstack >= MAX_STACK) { + WARNING_1 fprintf(stderr, "Warning: **** converter's stem stack overflow ****\n"); + nstack = 0; + } + stack[nstack++] = s[i]; + } + } else if(s[i].flags & ST_BLUE) { + /* again, we just HAVE to use this value */ + if (readystem) + nnew += 2; + readystem=0; + + /* remember the list of Blue zone stems with the same value */ + for(a=i, i++; i= 0; i--) { + if( (stack[i].flags & ST_UP)==0 ) { + if( (stack[i].flags & (ST_ZONE|ST_TOPZONE))==ST_ZONE ) + break; + else + continue; + } + for(j=a; j=0; j-=2) { + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% ?stem %d...%d -- %d\n", + s[j].value, s[j+1].value, stack[c].value); + if(s[j+1].value < stack[c].value) /* no conflict */ + break; + if(s[j].flags & ST_BLUE) { + /* oops, we don't want to spoil other blue zones */ + stack[c].value=s[j+1].value+1; + break; + } + if( (s[j].flags|s[j+1].flags) & ST_END ) { + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% -stem %d...%d p=1\n", + s[j].value, s[j+1].value); + continue; /* pri==1, silently discard it */ + } + /* we want to discard no nore than 2 stems of pri>=2 */ + if( ++readystem > 2 ) { + /* change our stem to not conflict */ + stack[c].value=s[j+1].value+1; + break; + } else { + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% -stem %d...%d p>=2\n", + s[j].value, s[j+1].value); + continue; + } + } + nnew=j+2; + /* add this stem */ + if(nnew>=b-1) { /* have no free space */ + for(j=nold; j>=b-1; j--) /* make free space */ + s[j]=s[j-1]; + b++; + nold++; + } + s[nnew++]=stack[c]; + s[nnew++]=s[b-1]; + /* clean up the stack */ + nstack=sbottom=0; + readystem=0; + /* set the next position to search */ + i=b-1; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d D BLUE\n", + s[nnew-2].value, s[nnew-1].value); + } else if (nstack > 0) { + + /* + * check whether our stem overlaps with anything in + * stack + */ + for (j = nstack - 1; j >= sbottom; j--) { + if (s[i].value <= stack[j].value) + break; + if (stack[j].flags & ST_ZONE) + continue; + + if ((s[i].flags & ST_END) + || (stack[j].flags & ST_END)) + pri = 1; + else if ((s[i].flags & ST_FLAT) + || (stack[j].flags & ST_FLAT)) + pri = 3; + else + pri = 2; + + if (pri < readystem && s[nnew + 1].value >= stack[j].value + || !stemoverlap(&stack[j], &s[i])) + continue; + + if (readystem > 1 && s[nnew + 1].value < stack[j].value) { + nnew += 2; + readystem = 0; + nlps = 0; + } + /* + * width of the previous stem (if it's + * present) + */ + w1 = s[nnew + 1].value - s[nnew].value; + + /* width of this stem */ + w2 = s[i].value - stack[j].value; + + if (readystem == 0) { + /* nothing yet, just add a new stem */ + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + readystem = pri; + if (pri == 1) + nlps = 1; + else if (pri == 2) + sbottom = j; + else { + sbottom = j + 1; + while (sbottom < nstack + && stack[sbottom].value <= stack[j].value) + sbottom++; + } + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } else if (pri == 1) { + if (stack[j].value > s[nnew + 1].value) { + /* + * doesn't overlap with the + * previous one + */ + nnew += 2; + nlps++; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } else if (w2 < w1) { + /* is narrower */ + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d %d->%d\n", + stack[j].value, s[i].value, pri, nlps, w1, w2); + } + } else if (pri == 2) { + if (readystem == 2) { + /* choose the narrower stem */ + if (w1 > w2) { + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + sbottom = j; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + /* else readystem==1 */ + } else if (stack[j].value > s[nnew + 1].value) { + /* + * value doesn't overlap with + * the previous one + */ + nnew += 2; + nlps = 0; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + sbottom = j; + readystem = pri; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } else if (nlps == 1 + || stack[j].value > s[nnew - 1].value) { + /* + * we can replace the top + * stem + */ + nlps = 0; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + readystem = pri; + sbottom = j; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + } else if (readystem == 3) { /* that means also + * pri==3 */ + /* choose the narrower stem */ + if (w1 > w2) { + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + sbottom = j + 1; + while (sbottom < nstack + && stack[sbottom].value <= stack[j].value) + sbottom++; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + } else if (pri == 3) { + /* + * we can replace as many stems as + * neccessary + */ + nnew += 2; + while (nnew > 0 && s[nnew - 1].value >= stack[j].value) { + nnew -= 2; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% -stem %d..%d\n", + s[nnew].value, s[nnew + 1].value); + } + nlps = 0; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + readystem = pri; + sbottom = j + 1; + while (sbottom < nstack + && stack[sbottom].value <= stack[j].value) + sbottom++; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + } + } + } + if (readystem) + nnew += 2; + + /* change the 1-pixel-wide stems to 20-pixel-wide stems if possible + * the constant 20 is recommended in the Type1 manual + */ + if(useblues) { + for(i=0; ii+2 && s[i+2].value0 && s[i-1].value>s[i].value-22) + s[i].value=s[i-1].value+2; /* compensate for fuzziness */ + else + s[i].value-=20; + } + } + } + /* make sure that no stem it stretched between + * a top zone and a bottom zone + */ + if(useblues) { + for(i=0; i=s[i].value && c<=s[i+1].value && c=2) { + c=bluevalues[1]; + if( c>=s[i].value && c<=s[i+1].value && c>b ) + b=c; + } + for(j=1; j=s[i].value && c<=s[i+1].value && c>b ) + b=c; + } + if( a!=10000 && b!= -10000 ) { /* it is stretched */ + /* split the stem into 2 ghost stems */ + for(j=nnew+1; j>i+1; j--) /* make free space */ + s[j]=s[j-2]; + nnew+=2; + + if(s[i].value+22 >= a) + s[i+1].value=a-2; /* leave space for fuzziness */ + else + s[i+1].value=s[i].value+20; + + if(s[i+3].value-22 <= b) + s[i+2].value=b+2; /* leave space for fuzziness */ + else + s[i+2].value=s[i+3].value-20; + + i+=2; + } + } + } + /* look for triple stems */ + for (i = 0; i < nnew; i += 2) { + if (nnew - i >= 6) { + a = s[i].value + s[i + 1].value; + b = s[i + 2].value + s[i + 3].value; + c = s[i + 4].value + s[i + 5].value; + + w1 = s[i + 1].value - s[i].value; + w2 = s[i + 3].value - s[i + 2].value; + w3 = s[i + 5].value - s[i + 4].value; + + fw = w3 - w1; /* fuzz in width */ + fd = ((c - b) - (b - a)); /* fuzz in distance + * (doubled) */ + + /* we are able to handle some fuzz */ + /* + * it doesn't hurt if the declared stem is a bit + * narrower than actual unless it's an edge in + * a blue zone + */ + if (abs(abs(fd) - abs(fw)) * 5 < w2 + && abs(fw) * 20 < (w1 + w3)) { /* width dirrerence <10% */ + + if(useblues) { /* check that we don't disturb any blue stems */ + j=c; k=a; + if (fw > 0) { + if (fd > 0) { + if( s[i+5].flags & ST_BLUE ) + continue; + j -= fw; + } else { + if( s[i+4].flags & ST_BLUE ) + continue; + j += fw; + } + } else if(fw < 0) { + if (fd > 0) { + if( s[i+1].flags & ST_BLUE ) + continue; + k -= fw; + } else { + if( s[i].flags & ST_BLUE ) + continue; + k += fw; + } + } + pri = ((j - b) - (b - k)); + + if (pri > 0) { + if( s[i+2].flags & ST_BLUE ) + continue; + } else if(pri < 0) { + if( s[i+3].flags & ST_BLUE ) + continue; + } + } + + /* + * first fix up the width of 1st and 3rd + * stems + */ + if (fw > 0) { + if (fd > 0) { + s[i + 5].value -= fw; + c -= fw; + } else { + s[i + 4].value += fw; + c += fw; + } + } else { + if (fd > 0) { + s[i + 1].value -= fw; + a -= fw; + } else { + s[i].value += fw; + a += fw; + } + } + fd = ((c - b) - (b - a)); + + if (fd > 0) { + s[i + 2].value += abs(fd) / 2; + } else { + s[i + 3].value -= abs(fd) / 2; + } + + s[i].flags |= ST_3; + i += 4; + } + } + } + + return (nnew & ~1); /* number of lines must be always even */ +} + +/* + * these macros and function allow to set the base stem, + * check that it's not empty and subtract another stem + * from the base stem (possibly dividing it into multiple parts) + */ + +/* pairs for pieces of the base stem */ +static short xbstem[MAX_STEMS*2]; +/* index of the last point */ +static int xblast= -1; + +#define setbasestem(from, to) \ + (xbstem[0]=from, xbstem[1]=to, xblast=1) +#define isbaseempty() (xblast<=0) + +/* returns 1 if was overlapping, 0 otherwise */ +static int +subfrombase( + int from, + int to +) +{ + int a, b; + int i, j; + + if(isbaseempty()) + return 0; + + /* handle the simple case simply */ + if(from > xbstem[xblast] || to < xbstem[0]) + return 0; + + /* the binary search may be more efficient */ + /* but for now the linear search is OK */ + for(b=1; from > xbstem[b]; b+=2) {} /* result: from <= xbstem[b] */ + for(a=xblast-1; to < xbstem[a]; a-=2) {} /* result: to >= xbstem[a] */ + + /* now the interesting examples are: + * (it was hard for me to understand, so I looked at the examples) + * 1 + * a|-----| |-----|b |-----| |-----| + * f|-----|t + * 2 + * a|-----|b |-----| |-----| |-----| + * f|--|t + * 3 + * a|-----|b |-----| |-----| |-----| + * f|-----|t + * 4 + * |-----|b a|-----| |-----| |-----| + * f|------------|t + * 5 + * |-----| |-----|b |-----| a|-----| + * f|-----------------------------|t + * 6 + * |-----|b |-----| |-----| a|-----| + * f|--------------------------------------------------|t + * 7 + * |-----|b |-----| a|-----| |-----| + * f|--------------------------|t + */ + + if(a < b-1) /* hits a gap - example 1 */ + return 0; + + /* now the subtraction itself */ + + if(a==b-1 && from > xbstem[a] && to < xbstem[b]) { + /* overlaps with only one subrange and splits it - example 2 */ + j=xblast; i=(xblast+=2); + while(j>=b) + xbstem[i--]=xbstem[j--]; + xbstem[b]=from-1; + xbstem[b+1]=to+1; + return 1; + /* becomes + * 2a + * a|b || |-----| |-----| |-----| + * f|--|t + */ + } + + if(xbstem[b-1] < from) { + /* cuts the back of this subrange - examples 3, 4, 7 */ + xbstem[b] = from-1; + b+=2; + /* becomes + * 3a + * a|----| |-----|b |-----| |-----| + * f|-----|t + * 4a + * |---| a|-----|b |-----| |-----| + * f|------------|t + * 7a + * |---| |-----|b a|-----| |-----| + * f|--------------------------|t + */ + } + + if(xbstem[a+1] > to) { + /* cuts the front of this subrange - examples 4a, 5, 7a */ + xbstem[a] = to+1; + a-=2; + /* becomes + * 4b + * a|---| |---|b |-----| |-----| + * f|------------|t + * 5b + * |-----| |-----|b a|-----| || + * f|-----------------------------|t + * 7b + * |---| a|-----|b || |-----| + * f|--------------------------|t + */ + } + + if(a < b-1) /* now after modification it hits a gap - examples 3a, 4b */ + return 1; /* because we have removed something */ + + /* now remove the subranges completely covered by the new stem */ + /* examples 5b, 6, 7b */ + i=b-1; j=a+2; + /* positioned as: + * 5b i j + * |-----| |-----|b a|-----| || + * f|-----------------------------|t + * 6 i xblast j + * |-----|b |-----| |-----| a|-----| + * f|--------------------------------------------------|t + * 7b i j + * |---| a|-----|b || |-----| + * f|--------------------------|t + */ + while(j <= xblast) + xbstem[i++]=xbstem[j++]; + xblast=i-1; + return 1; +} + +/* for debugging */ +static void +printbasestem(void) +{ + int i; + + printf("( "); + for(i=0; i lastpri + && ( lastpri==1 || s[j].value-v<20 || (s[x].value-v)*2 >= s[j].value-v ) ) { + lastpri=pri; + x=j; + } + } + } else { + for(j=i-1; j>=0; j--) { + if(mx[i][j]==0) + continue; + + if( (f | s[j].flags) & ST_END ) + pri=1; + else if( (f | s[j].flags) & ST_FLAT ) + pri=3; + else + pri=2; + + if(lastpri==0 + || pri > lastpri + && ( lastpri==1 || v-s[j].value<20 || (v-s[x].value)*2 >= v-s[j].value ) ) { + lastpri=pri; + x=j; + } + } + } + if(x== -1 && mx[i][i]) + pairs[i]=i; /* a special case */ + else + pairs[i]=x; + } + + if(ISDBG(SUBSTEMS)) { + for(i=0; i0) + fprintf(pfa_file, "%% %d...%d (%d x %d)\n", s[i].value, s[j].value, i, j); + } + } +} + +/* + * Make all the stems originating at the same value get the + * same width. Without this the rasterizer may move the dots + * randomly up or down by one pixel, and that looks bad. + * The prioritisation is the same as in findstemat(). + */ +static void +uniformstems( + STEM * s, + short *pairs, + int ns +) +{ + int i, j, from, to, val, dir; + int pri, prevpri[2], wd, prevwd[2], prevbest[2]; + + for(from=0; from prevpri[dir] || wd= 0) { + if(ISDBG(SUBSTEMS)) { + fprintf(stderr, "at %d (%s %d) pair %d->%d(%d)\n", i, + (dir ? "UP":"DOWN"), s[i].value, pairs[i], prevbest[dir], + s[prevbest[dir]].value); + } + pairs[i] = prevbest[dir]; + } + } + } +} + +/* + * Find the best stem in the array at the specified (value, origin), + * related to the entry ge. + * Returns its index in the array sp, -1 means "none". + * prevbest is the result for the other end of the line, we must + * find something better than it or leave it as it is. + */ +static int +findstemat( + int value, + int origin, + GENTRY *ge, + STEM *sp, + short *pairs, + int ns, + int prevbest /* -1 means "none" */ +) +{ + int i, min, max; + int v, si; + int pri, prevpri; /* priority, 0 = has ST_END, 1 = no ST_END */ + int wd, prevwd; /* stem width */ + + si= -1; /* nothing yet */ + + /* stems are ordered by value, binary search */ + min=0; max=ns; /* min <= i < max */ + while( min < max ) { + i=(min+max)/2; + v=sp[i].value; + if(vvalue) + max=i; + else { + si=i; /* temporary value */ + break; + } + } + + if( si < 0 ) /* found nothing this time */ + return prevbest; + + /* find the priority of the prevbest */ + /* we expect that prevbest has a pair */ + if(prevbest>=0) { + i=pairs[prevbest]; + prevpri=1; + if( (sp[prevbest].flags | sp[i].flags) & ST_END ) + prevpri=0; + prevwd=abs(sp[i].value-value); + } + + /* stems are not ordered by origin, so now do the linear search */ + + while( si>0 && sp[si-1].value==value ) /* find the first one */ + si--; + + for(; siprevpri + || pri==prevpri && prevwd==0 || wd!=0 && wdprev->ix3; + y=ge->prev->iy3; + + if(*nextvsi == -2) + si[SI_VP]=findstemat(x, y, ge, vs, vpairs, nvs, -1); + else { + si[SI_VP]= *nextvsi; *nextvsi= -2; + } + if(*nexthsi == -2) + si[SI_HP]=findstemat(y, x, ge, hs, hpairs, nhs, -1); + else { + si[SI_HP]= *nexthsi; *nexthsi= -2; + } + + /* + * For the horizontal lines we make sure that both + * ends of the line have the same horizontal stem, + * and the same thing for vertical lines and stems. + * In both cases we enforce the stem for the next entry. + * Otherwise unpleasant effects may arise. + */ + + if(ge->type==GE_LINE) { + if(ge->ix3==x) { /* vertical line */ + *nextvsi=si[SI_VP]=findstemat(x, ge->iy3, ge->frwd, vs, vpairs, nvs, si[SI_VP]); + } else if(ge->iy3==y) { /* horizontal line */ + *nexthsi=si[SI_HP]=findstemat(y, ge->ix3, ge->frwd, hs, hpairs, nhs, si[SI_HP]); + } + } + + if(si[SI_VP]+si[SI_HP] == -2) /* no stems, leave it alone */ + return 0; + + /* build the array of relevant bounds */ + nr=0; + for(i=0; i< sizeof(si) / sizeof(si[0]); i++) { + STEM *sp; + short *pairs; + int step; + int f; + int nzones, firstzone, binzone, einzone; + int btype, etype; + + if(si[i] < 0) + continue; + + if(i r[nr].high) { + j=r[nr].low; r[nr].low=r[nr].high; r[nr].high=j; + step= -1; + } else { + step=1; + } + + /* handle the interaction with Blue Zones */ + + if(i>=SI_HP) { /* only for horizontal stems */ + if(si[i]==pairs[si[i]]) { + /* special case, the outermost stem in the + * Blue Zone without a pair, simulate it to 20-pixel + */ + if(sp[ si[i] ].flags & ST_UP) { + r[nr].high+=20; + for(j=si[i]+1; j sp[j].value-2) + r[nr].high=sp[j].value-2; + break; + } + } else { + r[nr].low-=20; + for(j=si[i]-1; j>=0; j--) + if( (sp[j].flags & (ST_ZONE|ST_TOPZONE)) + == (ST_ZONE) ) { + if(r[nr].low < sp[j].value+2) + r[nr].low=sp[j].value+2; + break; + } + } + } + + /* check that the stem borders don't end up in + * different Blue Zones */ + f=sp[ si[i] ].flags; + nzones=0; einzone=binzone=0; + for(j=si[i]; j!=pairs[ si[i] ]; j+=step) { + if( (sp[j].flags & ST_ZONE)==0 ) + continue; + /* if see a zone border going in the same direction */ + if( ((f ^ sp[j].flags) & ST_UP)==0 ) { + if( ++nzones == 1 ) { + firstzone=sp[j].value; /* remember the first one */ + etype=sp[j].flags & ST_TOPZONE; + } + einzone=1; + + } else { /* the opposite direction */ + if(nzones==0) { /* beginning is in a blue zone */ + binzone=1; + btype=sp[j].flags & ST_TOPZONE; + } + einzone=0; + } + } + + /* beginning and end are in Blue Zones of different types */ + if( binzone && einzone && (btype ^ etype)!=0 ) { + if( sp[si[i]].flags & ST_UP ) { + if(firstzone > r[nr].low+22) + r[nr].high=r[nr].low+20; + else + r[nr].high=firstzone-2; + } else { + if(firstzone < r[nr].high-22) + r[nr].low=r[nr].high-20; + else + r[nr].low=firstzone+2; + } + } + } + + if(ISDBG(SUBSTEMS)) + fprintf(pfa_file, "%% at(%d,%d)[%d,%d] %d..%d %c (%d x %d)\n", x, y, i, nr, + r[nr].low, r[nr].high, r[nr].isvert ? 'v' : 'h', + si[i], pairs[si[i]]); + + nr++; + } + + /* now try to find a group */ + conflict=0; /* no conflicts found yet */ + for(j=0; j= lb ) { + if( r[j].low == lb && r[j].high == hb ) /* coincides */ + r[j].already=1; + else + conflict=1; + } + + if(conflict) + break; + } + + if(conflict) { /* nope, check all the groups */ + for(j=0; j= lb ) { + if( r[j].low == lb && r[j].high == hb ) /* coincides */ + r[j].already=1; + else + conflict=1; + } + + if(conflict) + i=egp[grp]-1; /* fast forward to the next group */ + } + } + + /* do we have any empty group ? */ + if(conflict && grp < NSTEMGRP-1) { + grp++; conflict=0; + for(j=0; j 0) { + for(i=egp[NSTEMGRP-1]-1; i>=egp[grp]; i--) + s[i+rexpand]=s[i]; + for(i=0; istemid = gssentry_lastgrp = grp; + return 0; +} + +/* + * Create the groups of substituted stems from the list. + * Each group will be represented by a subroutine in the Subs + * array. + */ + +static void +groupsubstems( + GLYPH *g, + STEM *hs, /* horizontal stems, sorted by value */ + short *hpairs, + int nhs, + STEM *vs, /* vertical stems, sorted by value */ + short *vpairs, + int nvs +) +{ + GENTRY *ge; + int i, j; + + /* temporary storage */ + STEMBOUNDS s[MAX_STEMS*2]; + /* indexes in there, pointing past the end each stem group */ + short egp[NSTEMGRP]; + + int nextvsi, nexthsi; /* -2 means "check by yourself" */ + + for(i=0; ientries; ge != 0; ge = ge->next) { + if(ge->type!=GE_LINE && ge->type!=GE_CURVE) { + nextvsi=nexthsi= -2; /* next path is independent */ + continue; + } + + if( gssentry(ge, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) { + WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n", + g->name, NSTEMGRP); + /* it's better to have no substituted hints at all than have only part */ + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid= -1; + g->nsg=0; /* just to be safe, already is 0 by initialization */ + return; + } + + /* + * handle the last vert/horiz line of the path specially, + * correct the hint for the first entry of the path + */ + if(ge->frwd != ge->next && (nextvsi != -2 || nexthsi != -2) ) { + if( gssentry(ge->frwd, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) { + WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n", + g->name, NSTEMGRP); + /* it's better to have no substituted hints at all than have only part */ + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid= -1; + g->nsg=0; /* just to be safe, already is 0 by initialization */ + return; + } + } + + } + + /* find the index of the first empty group - same as the number of groups */ + if(egp[0]>0) { + for(i=1; insg=i; + } else + g->nsg=0; + + if(ISDBG(SUBSTEMS)) { + fprintf(pfa_file, "%% %d substem groups (%d %d %d)\n", g->nsg, + g->nsg>1 ? egp[g->nsg-2] : -1, + g->nsg>0 ? egp[g->nsg-1] : -1, + g->nsgnsg] : -1 ); + j=0; + for(i=0; insg; i++) { + fprintf(pfa_file, "%% grp %3d: ", i); + for(; jnsg==1) { /* it would be the same as the main stems */ + /* so erase it */ + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid= -1; + g->nsg=0; + } + + if(g->nsg>0) { + if( (g->nsbs=malloc(g->nsg * sizeof (egp[0]))) == 0 ) { + fprintf(stderr, "**** not enough memory for substituted hints ****\n"); + exit(255); + } + memmove(g->nsbs, egp, g->nsg * sizeof(short)); + if( (g->sbstems=malloc(egp[g->nsg-1] * sizeof (s[0]))) == 0 ) { + fprintf(stderr, "**** not enough memory for substituted hints ****\n"); + exit(255); + } + memmove(g->sbstems, s, egp[g->nsg-1] * sizeof(s[0])); + } +} + +void +buildstems( + GLYPH * g +) +{ + STEM hs[MAX_STEMS], vs[MAX_STEMS]; /* temporary working + * storage */ + short hs_pairs[MAX_STEMS], vs_pairs[MAX_STEMS]; /* best pairs for these stems */ + STEM *sp; + GENTRY *ge, *nge, *pge; + int nx, ny; + int ovalue; + int totals, grp, lastgrp; + + assertisint(g, "buildstems int"); + + g->nhs = g->nvs = 0; + memset(hs, 0, sizeof hs); + memset(vs, 0, sizeof vs); + + /* first search the whole character for possible stem points */ + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_CURVE) { + + /* + * SURPRISE! + * We consider the stems bound by the + * H/V ends of the curves as flat ones. + * + * But we don't include the point on the + * other end into the range. + */ + + /* first check the beginning of curve */ + /* if it is horizontal, add a hstem */ + if (ge->iy1 == ge->prev->iy3) { + hs[g->nhs].value = ge->iy1; + + if (ge->ix1 < ge->prev->ix3) + hs[g->nhs].flags = ST_FLAT | ST_UP; + else + hs[g->nhs].flags = ST_FLAT; + + hs[g->nhs].origin = ge->prev->ix3; + hs[g->nhs].ge = ge; + + if (ge->ix1 < ge->prev->ix3) { + hs[g->nhs].from = ge->ix1+1; + hs[g->nhs].to = ge->prev->ix3; + if(hs[g->nhs].from > hs[g->nhs].to) + hs[g->nhs].from--; + } else { + hs[g->nhs].from = ge->prev->ix3; + hs[g->nhs].to = ge->ix1-1; + if(hs[g->nhs].from > hs[g->nhs].to) + hs[g->nhs].to++; + } + if (ge->ix1 != ge->prev->ix3) + g->nhs++; + } + /* if it is vertical, add a vstem */ + else if (ge->ix1 == ge->prev->ix3) { + vs[g->nvs].value = ge->ix1; + + if (ge->iy1 > ge->prev->iy3) + vs[g->nvs].flags = ST_FLAT | ST_UP; + else + vs[g->nvs].flags = ST_FLAT; + + vs[g->nvs].origin = ge->prev->iy3; + vs[g->nvs].ge = ge; + + if (ge->iy1 < ge->prev->iy3) { + vs[g->nvs].from = ge->iy1+1; + vs[g->nvs].to = ge->prev->iy3; + if(vs[g->nvs].from > vs[g->nvs].to) + vs[g->nvs].from--; + } else { + vs[g->nvs].from = ge->prev->iy3; + vs[g->nvs].to = ge->iy1-1; + if(vs[g->nvs].from > vs[g->nvs].to) + vs[g->nvs].to++; + } + + if (ge->iy1 != ge->prev->iy3) + g->nvs++; + } + /* then check the end of curve */ + /* if it is horizontal, add a hstem */ + if (ge->iy3 == ge->iy2) { + hs[g->nhs].value = ge->iy3; + + if (ge->ix3 < ge->ix2) + hs[g->nhs].flags = ST_FLAT | ST_UP; + else + hs[g->nhs].flags = ST_FLAT; + + hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if (ge->ix3 < ge->ix2) { + hs[g->nhs].from = ge->ix3; + hs[g->nhs].to = ge->ix2-1; + if( hs[g->nhs].from > hs[g->nhs].to ) + hs[g->nhs].to++; + } else { + hs[g->nhs].from = ge->ix2+1; + hs[g->nhs].to = ge->ix3; + if( hs[g->nhs].from > hs[g->nhs].to ) + hs[g->nhs].from--; + } + + if (ge->ix3 != ge->ix2) + g->nhs++; + } + /* if it is vertical, add a vstem */ + else if (ge->ix3 == ge->ix2) { + vs[g->nvs].value = ge->ix3; + + if (ge->iy3 > ge->iy2) + vs[g->nvs].flags = ST_FLAT | ST_UP; + else + vs[g->nvs].flags = ST_FLAT; + + vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if (ge->iy3 < ge->iy2) { + vs[g->nvs].from = ge->iy3; + vs[g->nvs].to = ge->iy2-1; + if( vs[g->nvs].from > vs[g->nvs].to ) + vs[g->nvs].to++; + } else { + vs[g->nvs].from = ge->iy2+1; + vs[g->nvs].to = ge->iy3; + if( vs[g->nvs].from > vs[g->nvs].to ) + vs[g->nvs].from--; + } + + if (ge->iy3 != ge->iy2) + g->nvs++; + } else { + + /* + * check the end of curve for a not smooth + * local extremum + */ + nge = ge->frwd; + + if (nge == 0) + continue; + else if (nge->type == GE_LINE) { + nx = nge->ix3; + ny = nge->iy3; + } else if (nge->type == GE_CURVE) { + nx = nge->ix1; + ny = nge->iy1; + } else + continue; + + /* check for vertical extremums */ + if (ge->iy3 > ge->iy2 && ge->iy3 > ny + || ge->iy3 < ge->iy2 && ge->iy3 < ny) { + hs[g->nhs].value = ge->iy3; + hs[g->nhs].from + = hs[g->nhs].to + = hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if (ge->ix3 < ge->ix2 + || nx < ge->ix3) + hs[g->nhs].flags = ST_UP; + else + hs[g->nhs].flags = 0; + + if (ge->ix3 != ge->ix2 || nx != ge->ix3) + g->nhs++; + } + /* + * the same point may be both horizontal and + * vertical extremum + */ + /* check for horizontal extremums */ + if (ge->ix3 > ge->ix2 && ge->ix3 > nx + || ge->ix3 < ge->ix2 && ge->ix3 < nx) { + vs[g->nvs].value = ge->ix3; + vs[g->nvs].from + = vs[g->nvs].to + = vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if (ge->iy3 > ge->iy2 + || ny > ge->iy3) + vs[g->nvs].flags = ST_UP; + else + vs[g->nvs].flags = 0; + + if (ge->iy3 != ge->iy2 || ny != ge->iy3) + g->nvs++; + } + } + + } else if (ge->type == GE_LINE) { + nge = ge->frwd; + + /* if it is horizontal, add a hstem */ + /* and the ends as vstems if they brace the line */ + if (ge->iy3 == ge->prev->iy3 + && ge->ix3 != ge->prev->ix3) { + hs[g->nhs].value = ge->iy3; + if (ge->ix3 < ge->prev->ix3) { + hs[g->nhs].flags = ST_FLAT | ST_UP; + hs[g->nhs].from = ge->ix3; + hs[g->nhs].to = ge->prev->ix3; + } else { + hs[g->nhs].flags = ST_FLAT; + hs[g->nhs].from = ge->prev->ix3; + hs[g->nhs].to = ge->ix3; + } + hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + pge = ge->bkwd; + + /* add beginning as vstem */ + vs[g->nvs].value = pge->ix3; + vs[g->nvs].origin + = vs[g->nvs].from + = vs[g->nvs].to = pge->iy3; + vs[g->nvs].ge = ge; + + if(pge->type==GE_CURVE) + ovalue=pge->iy2; + else + ovalue=pge->prev->iy3; + + if (pge->iy3 > ovalue) + vs[g->nvs].flags = ST_UP | ST_END; + else if (pge->iy3 < ovalue) + vs[g->nvs].flags = ST_END; + else + vs[g->nvs].flags = 0; + + if( vs[g->nvs].flags != 0 ) + g->nvs++; + + /* add end as vstem */ + vs[g->nvs].value = ge->ix3; + vs[g->nvs].origin + = vs[g->nvs].from + = vs[g->nvs].to = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if(nge->type==GE_CURVE) + ovalue=nge->iy1; + else + ovalue=nge->iy3; + + if (ovalue > ge->iy3) + vs[g->nvs].flags = ST_UP | ST_END; + else if (ovalue < ge->iy3) + vs[g->nvs].flags = ST_END; + else + vs[g->nvs].flags = 0; + + if( vs[g->nvs].flags != 0 ) + g->nvs++; + + g->nhs++; + } + /* if it is vertical, add a vstem */ + /* and the ends as hstems if they brace the line */ + else if (ge->ix3 == ge->prev->ix3 + && ge->iy3 != ge->prev->iy3) { + vs[g->nvs].value = ge->ix3; + if (ge->iy3 > ge->prev->iy3) { + vs[g->nvs].flags = ST_FLAT | ST_UP; + vs[g->nvs].from = ge->prev->iy3; + vs[g->nvs].to = ge->iy3; + } else { + vs[g->nvs].flags = ST_FLAT; + vs[g->nvs].from = ge->iy3; + vs[g->nvs].to = ge->prev->iy3; + } + vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + pge = ge->bkwd; + + /* add beginning as hstem */ + hs[g->nhs].value = pge->iy3; + hs[g->nhs].origin + = hs[g->nhs].from + = hs[g->nhs].to = pge->ix3; + hs[g->nhs].ge = ge; + + if(pge->type==GE_CURVE) + ovalue=pge->ix2; + else + ovalue=pge->prev->ix3; + + if (pge->ix3 < ovalue) + hs[g->nhs].flags = ST_UP | ST_END; + else if (pge->ix3 > ovalue) + hs[g->nhs].flags = ST_END; + else + hs[g->nhs].flags = 0; + + if( hs[g->nhs].flags != 0 ) + g->nhs++; + + /* add end as hstem */ + hs[g->nhs].value = ge->iy3; + hs[g->nhs].origin + = hs[g->nhs].from + = hs[g->nhs].to = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if(nge->type==GE_CURVE) + ovalue=nge->ix1; + else + ovalue=nge->ix3; + + if (ovalue < ge->ix3) + hs[g->nhs].flags = ST_UP | ST_END; + else if (ovalue > ge->ix3) + hs[g->nhs].flags = ST_END; + else + hs[g->nhs].flags = 0; + + if( hs[g->nhs].flags != 0 ) + g->nhs++; + + g->nvs++; + } + /* + * check the end of line for a not smooth local + * extremum + */ + nge = ge->frwd; + + if (nge == 0) + continue; + else if (nge->type == GE_LINE) { + nx = nge->ix3; + ny = nge->iy3; + } else if (nge->type == GE_CURVE) { + nx = nge->ix1; + ny = nge->iy1; + } else + continue; + + /* check for vertical extremums */ + if (ge->iy3 > ge->prev->iy3 && ge->iy3 > ny + || ge->iy3 < ge->prev->iy3 && ge->iy3 < ny) { + hs[g->nhs].value = ge->iy3; + hs[g->nhs].from + = hs[g->nhs].to + = hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if (ge->ix3 < ge->prev->ix3 + || nx < ge->ix3) + hs[g->nhs].flags = ST_UP; + else + hs[g->nhs].flags = 0; + + if (ge->ix3 != ge->prev->ix3 || nx != ge->ix3) + g->nhs++; + } + /* + * the same point may be both horizontal and vertical + * extremum + */ + /* check for horizontal extremums */ + if (ge->ix3 > ge->prev->ix3 && ge->ix3 > nx + || ge->ix3 < ge->prev->ix3 && ge->ix3 < nx) { + vs[g->nvs].value = ge->ix3; + vs[g->nvs].from + = vs[g->nvs].to + = vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if (ge->iy3 > ge->prev->iy3 + || ny > ge->iy3) + vs[g->nvs].flags = ST_UP; + else + vs[g->nvs].flags = 0; + + if (ge->iy3 != ge->prev->iy3 || ny != ge->iy3) + g->nvs++; + } + } + } + + g->nhs=addbluestems(hs, g->nhs); + sortstems(hs, g->nhs); + sortstems(vs, g->nvs); + + if (ISDBG(STEMS)) + debugstems(g->name, hs, g->nhs, vs, g->nvs); + + /* find the stems interacting with the Blue Zones */ + markbluestems(hs, g->nhs); + + if(subhints) { + if (ISDBG(SUBSTEMS)) + fprintf(pfa_file, "%% %s: joining subst horizontal stems\n", g->name); + joinsubstems(hs, hs_pairs, g->nhs, 1); + uniformstems(hs, hs_pairs, g->nhs); + + if (ISDBG(SUBSTEMS)) + fprintf(pfa_file, "%% %s: joining subst vertical stems\n", g->name); + joinsubstems(vs, vs_pairs, g->nvs, 0); + + groupsubstems(g, hs, hs_pairs, g->nhs, vs, vs_pairs, g->nvs); + } + + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% %s: joining main horizontal stems\n", g->name); + g->nhs = joinmainstems(hs, g->nhs, 1); + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% %s: joining main vertical stems\n", g->name); + g->nvs = joinmainstems(vs, g->nvs, 0); + + if (ISDBG(MAINSTEMS)) + debugstems(g->name, hs, g->nhs, vs, g->nvs); + + if(g->nhs > 0) { + if ((sp = malloc(sizeof(STEM) * g->nhs)) == 0) { + fprintf(stderr, "**** not enough memory for hints ****\n"); + exit(255); + } + g->hstems = sp; + memcpy(sp, hs, sizeof(STEM) * g->nhs); + } else + g->hstems = 0; + + if(g->nvs > 0) { + if ((sp = malloc(sizeof(STEM) * g->nvs)) == 0) { + fprintf(stderr, "**** not enough memory for hints ****\n"); + exit(255); + } + g->vstems = sp; + memcpy(sp, vs, sizeof(STEM) * g->nvs); + } else + g->vstems = 0; + + /* now check that the stems won't overflow the interpreter's stem stack: + * some interpreters (like X11) push the stems on each change into + * stack and pop them only after the whole glyphs is completed. + */ + + totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */ + lastgrp = -1; + + for (ge = g->entries; ge != 0; ge = ge->next) { + grp=ge->stemid; + if(grp >= 0 && grp != lastgrp) { + if(grp==0) + totals += g->nsbs[0]; + else + totals += g->nsbs[grp] - g->nsbs[grp-1]; + + lastgrp = grp; + } + } + + /* be on the safe side, check for >= , not > */ + if(totals >= max_stemdepth) { /* oops, too deep */ + WARNING_2 { + fprintf(stderr, "Warning: glyph %s needs hint stack depth %d\n", g->name, totals); + fprintf(stderr, " (limit %d): removed the substituted hints from it\n", max_stemdepth); + } + if(g->nsg > 0) { + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid = -1; + free(g->sbstems); g->sbstems = 0; + free(g->nsbs); g->nsbs = 0; + g->nsg = 0; + } + } + + /* now check if there are too many main stems */ + totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */ + if(totals >= max_stemdepth) { + /* even worse, too much of non-substituted stems */ + WARNING_2 { + fprintf(stderr, "Warning: glyph %s has %d main hints\n", g->name, totals); + fprintf(stderr, " (limit %d): removed the hints from it\n", max_stemdepth); + } + if(g->vstems) { + free(g->vstems); g->vstems = 0; g->nvs = 0; + } + if(g->hstems) { + free(g->hstems); g->hstems = 0; g->nhs = 0; + } + } +} + +/* convert weird curves that are close to lines into lines. +*/ + +void +fstraighten( + GLYPH * g +) +{ + GENTRY *ge, *pge, *nge, *ige; + double df; + int dir; + double iln, oln; + int svdir, i, o; + + for (ige = g->entries; ige != 0; ige = ige->next) { + if (ige->type != GE_CURVE) + continue; + + ge = ige; + pge = ge->bkwd; + nge = ge->frwd; + + df = 0.; + + /* look for vertical then horizontal */ + for(i=0; i<2; i++) { + o = !i; /* other axis */ + + iln = fabs(ge->fpoints[i][2] - pge->fpoints[i][2]); + oln = fabs(ge->fpoints[o][2] - pge->fpoints[o][2]); + /* + * if current curve is almost a vertical line, and it + * doesn't begin or end horizontally (and the prev/next + * line doesn't join smoothly ?) + */ + if( oln < 1. + || ge->fpoints[o][2] == ge->fpoints[o][1] + || ge->fpoints[o][0] == pge->fpoints[o][2] + || iln > 2. + || iln > 1. && iln/oln > 0.1 ) + continue; + + + if(ISDBG(STRAIGHTEN)) + fprintf(stderr,"** straighten almost %s\n", (i? "horizontal":"vertical")); + + df = ge->fpoints[i][2] - pge->fpoints[i][2]; + dir = fsign(ge->fpoints[o][2] - pge->fpoints[o][2]); + ge->type = GE_LINE; + + /* + * suck in all the sequence of such almost lines + * going in the same direction but not deviating + * too far from vertical + */ + iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]); + oln = nge->fpoints[o][2] - ge->fpoints[o][2]; + + while (fabs(df) <= 5 && nge->type == GE_CURVE + && dir == fsign(oln) /* that also gives oln != 0 */ + && iln <= 2. + && ( iln <= 1. || iln/fabs(oln) <= 0.1 ) ) { + ge->fx3 = nge->fx3; + ge->fy3 = nge->fy3; + + if(ISDBG(STRAIGHTEN)) + fprintf(stderr,"** straighten collapsing %s\n", (i? "horizontal":"vertical")); + freethisge(nge); + fixendpath(ge); + pge = ge->bkwd; + nge = ge->frwd; + + df = ge->fpoints[i][2] - pge->fpoints[i][2]; + + iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]); + oln = nge->fpoints[o][2] - ge->fpoints[o][2]; + } + + /* now check what do we have as previous/next line */ + + if(ge != pge) { + if( pge->type == GE_LINE && pge->fpoints[i][2] == pge->prev->fpoints[i][2] + && fabs(pge->fpoints[o][2] != pge->prev->fpoints[o][2]) ) { + if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with previous 0x%x 0x%x\n", pge, ge); + /* join the previous line with current */ + pge->fx3 = ge->fx3; + pge->fy3 = ge->fy3; + + ige = freethisge(ge)->prev; /* keep the iterator valid */ + ge = pge; + fixendpath(ge); + pge = ge->bkwd; + } + } + + if(ge != nge) { + if (nge->type == GE_LINE && nge->fpoints[i][2] == ge->fpoints[i][2] + && fabs(nge->fpoints[o][2] != ge->fpoints[o][2]) ) { + if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with next 0x%x 0x%x\n", ge, nge); + /* join the next line with current */ + ge->fx3 = nge->fx3; + ge->fy3 = nge->fy3; + + freethisge(nge); + fixendpath(ge); + pge = ge->bkwd; + nge = ge->frwd; + + } + } + + if(ge != pge) { + /* try to align the lines if neccessary */ + if(df != 0.) + fclosegap(ge, ge, i, df, NULL); + } else { + /* contour consists of only one line, get rid of it */ + ige = freethisge(ge); /* keep the iterator valid */ + if(ige == 0) /* this was the last contour */ + return; + ige = ige->prev; + } + + break; /* don't bother looking at the other axis */ + } + } +} + +/* solve a square equation, + * returns the number of solutions found, the solutions + * are stored in res which should point to array of two doubles. + * min and max limit the area for solutions + */ + +static int +fsqequation( + double a, + double b, + double c, + double *res, + double min, + double max +) +{ + double D; + int n; + + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq(%g,%g,%g) [%g;%g]\n", a, b, c, min, max); + + if(fabs(a) < 0.000001) { /* if a linear equation */ + n=0; + if(fabs(b) < 0.000001) /* not an equation at all */ + return 0; + res[0] = -c/b; + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: linear t=%g\n", res[0]); + if(res[0] >= min && res[0] <= max) + n++; + return n; + } + + D = b*b - 4.0*a*c; + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: D=%g\n", D); + if(D<0) + return 0; + + D = sqrt(D); + + n=0; + res[0] = (-b+D) / (2*a); + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t1=%g\n", res[0]); + if(res[0] >= min && res[0] <= max) + n++; + + res[n] = (-b-D) / (2*a); + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t2=%g\n", res[n]); + if(res[n] >= min && res[n] <= max) + n++; + + /* return 2nd solution only if it's different enough */ + if(n==2 && fabs(res[0]-res[1])<0.000001) + n=1; + + return n; +} + +/* check that the curves don't cross quadrant boundary */ +/* (float) */ + +/* + Here we make sure that the curve does not continue past + horizontal or vertical extremums. The horizontal points are + explained, vertical points are by analogy. + + The horizontal points are where the derivative + dy/dx is equal to 0. But the Bezier curves are defined by + parametric formulas + x=fx(t) + y=fy(t) + so finding this derivative is complicated. + Also even if we find some point (x,y) splitting at this point + is far not obvious. Fortunately we can use dy/dt = 0 instead, + this gets to a rather simple square equation and splitting + at a known value of t is simple. + + The formulas are: + + y = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 + y = (-A+3*B-3*C+D)*t^3 + (3*A-6*B+3*C)*t^2 + (-3*A+3*B)*t + A + dy/dt = 3*(-A+3*B-3*C+D)*t^2 + 2*(3*A-6*B+3*C)*t + (-3*A+3*B) + */ + +void +ffixquadrants( + GLYPH *g +) +{ + GENTRY *ge, *nge; + int i, j, np, oldnp; + double sp[5]; /* split points, last one empty */ + char dir[5]; /* for debugging, direction by which split happened */ + double a, b, *pts; /* points of a curve */ + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + doagain: + np = 0; /* no split points yet */ + if(ISDBG(QUAD)) { + fprintf(stderr, "%s: trying 0x%x (%g %g) (%g %g) (%g %g) (%g %g)\n ", g->name, + ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2, + ge->fx3, ge->fy3); + } + for(i=0; i<2; i++) { /* first for x then for y */ + /* find the cooridnates of control points */ + a = ge->prev->fpoints[i][2]; + pts = &ge->fpoints[i][0]; + + oldnp = np; + np += fsqequation( + 3.0*(-a + 3.0*pts[0] - 3.0*pts[1] + pts[2]), + 6.0*(a - 2.0*pts[0] + pts[1]), + 3.0*(-a + pts[0]), + &sp[np], + 0.0, 1.0); /* XXX range is [0;1] */ + + if(np == oldnp) + continue; + + if(ISDBG(QUAD)) + fprintf(stderr, "%s: 0x%x: %d pts(%c): ", + g->name, ge, np-oldnp, i? 'y':'x'); + + /* remove points that are too close to the ends + * because hor/vert ends are permitted, also + * if the split point is VERY close to the ends + * but not exactly then just flatten it and check again. + */ + for(j = oldnp; jfpoints[i][0] != ge->prev->fpoints[i][2]) { + ge->fpoints[i][0] = ge->prev->fpoints[i][2]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened at front\n"); + goto doagain; + } + if( ge->fpoints[i][1] != ge->fpoints[i][0] + && fsign(ge->fpoints[i][2] - ge->fpoints[i][1]) + != fsign(ge->fpoints[i][1] - ge->fpoints[i][0]) ) { + ge->fpoints[i][1] = ge->fpoints[i][0]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at front\n"); + goto doagain; + } + sp[j] = sp[j+1]; np--; j--; + if(ISDBG(QUAD)) fprintf(stderr, "(front flat) "); + } else if(sp[j] > 0.97) { /* rear end of curve */ + if(ge->fpoints[i][1] != ge->fpoints[i][2]) { + ge->fpoints[i][1] = ge->fpoints[i][2]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened at rear\n"); + goto doagain; + } + if( ge->fpoints[i][0] != ge->fpoints[i][1] + && fsign(ge->prev->fpoints[i][2] - ge->fpoints[i][0]) + != fsign(ge->fpoints[i][0] - ge->fpoints[i][1]) ) { + ge->fpoints[i][0] = ge->fpoints[i][1]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at rear\n"); + goto doagain; + } + sp[j] = sp[j+1]; np--; j--; + if(ISDBG(QUAD)) fprintf(stderr, "(rear flat) "); + } + } + if(ISDBG(QUAD)) fprintf(stderr, "\n"); + } + + if(np==0) /* no split points, leave it alone */ + continue; + + if(ISDBG(QUAD)) { + fprintf(stderr, "%s: splitting 0x%x (%g %g) (%g %g) (%g %g) (%g %g) at %d points\n ", g->name, + ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2, + ge->fx3, ge->fy3, np); + for(i=0; i sp[j]) { + a = sp[i]; sp[i] = sp[j]; sp[j] = a; + } + + /* now finally do the split on each point */ + for(j=0; jfpoints[i][0]; /* get the middle points */ + b = ge->fpoints[i][1]; + + /* calculate new internal points */ + c = SPLIT(a, b); + + ge->fpoints[i][0] = SPLIT(ge->prev->fpoints[i][2], a); + ge->fpoints[i][1] = SPLIT(ge->fpoints[i][0], c); + + nge->fpoints[i][1] = SPLIT(b, nge->fpoints[i][2]); + nge->fpoints[i][0] = SPLIT(c, nge->fpoints[i][1]); + + ge->fpoints[i][2] = SPLIT(ge->fpoints[i][1], + + nge->fpoints[i][0]); + } +#undef SPLIT + + addgeafter(ge, nge); + + /* go to the next part, adjust remaining points */ + ge = nge; + for(i=j+1; itype != GE_CURVE) + return 0; + + a = ge->iy2 - ge->iy1; + b = ge->ix2 - ge->ix1; + if(a == 0) { + if(b == 0) { + return 0; + } else + k = FBIGVAL; + } else + k = fabs((double) b / (double) a); + + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + if(a == 0) { + if(b == 0) { + return 0; + } else + k1 = FBIGVAL; + } else + k1 = fabs((double) b / (double) a); + + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + if(a == 0) { + if(b == 0) { + return 0; + } else + k2 = FBIGVAL; + } else + k2 = fabs((double) b / (double) a); + + /* if the curve is not a zigzag */ + if (k1+0.0001 >= k && k2 <= k+0.0001 || k1 <= k+0.0001 && k2+0.0001 >= k) + return 0; + else + return 1; +} + +/* check if a curve is a zigzag - floating */ + +static int +fiszigzag( + GENTRY *ge +) +{ + double k, k1, k2; + double a, b; + + if (ge->type != GE_CURVE) + return 0; + + a = fabs(ge->fy2 - ge->fy1); + b = fabs(ge->fx2 - ge->fx1); + if(a < FEPS) { + if(b < FEPS) { + return 0; + } else + k = FBIGVAL; + } else + k = b / a; + + a = fabs(ge->fy1 - ge->prev->fy3); + b = fabs(ge->fx1 - ge->prev->fx3); + if(a < FEPS) { + if(b < FEPS) { + return 0; + } else + k1 = FBIGVAL; + } else + k1 = b / a; + + a = fabs(ge->fy3 - ge->fy2); + b = fabs(ge->fx3 - ge->fx2); + if(a < FEPS) { + if(b < FEPS) { + return 0; + } else + k2 = FBIGVAL; + } else + k2 = b / a; + + /* if the curve is not a zigzag */ + if (k1+0.0001 >= k && k2 <= k+0.0001 || k1 <= k+0.0001 && k2+0.0001 >= k) + return 0; + else + return 1; +} + +/* split the zigzag-like curves into two parts */ + +void +fsplitzigzags( + GLYPH * g +) +{ + GENTRY *ge, *nge; + double a, b, c, d; + + assertisfloat(g, "splitting zigzags"); + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + /* if the curve is not a zigzag */ + if ( !fiszigzag(ge) ) { + continue; + } + + if(ISDBG(FCONCISE)) { + double maxsc1, maxsc2; + fprintf(stderr, "split a zigzag "); + fnormalizege(ge); + if( fcrossraysge(ge, ge, &maxsc1, &maxsc2, NULL) ) { + fprintf(stderr, "sc1=%g sc2=%g\n", maxsc1, maxsc2); + } else { + fprintf(stderr, "(rays don't cross)\n"); + } + } + /* split the curve by t=0.5 */ + nge = newgentry(GEF_FLOAT); + (*nge) = (*ge); + nge->type = GE_CURVE; + + a = ge->prev->fx3; + b = ge->fx1; + c = ge->fx2; + d = ge->fx3; + nge->fx3 = d; + nge->fx2 = (c + d) / 2.; + nge->fx1 = (b + 2. * c + d) / 4.; + ge->fx3 = (a + b * 3. + c * 3. + d) / 8.; + ge->fx2 = (a + 2. * b + c) / 4.; + ge->fx1 = (a + b) / 2.; + + a = ge->prev->fy3; + b = ge->fy1; + c = ge->fy2; + d = ge->fy3; + nge->fy3 = d; + nge->fy2 = (c + d) / 2.; + nge->fy1 = (b + 2. * c + d) / 4.; + ge->fy3 = (a + b * 3. + c * 3. + d) / 8.; + ge->fy2 = (a + 2. * b + c) / 4.; + ge->fy1 = (a + b) / 2.; + + addgeafter(ge, nge); + + if(ISDBG(FCONCISE)) { + dumppaths(g, ge, nge); + } + } +} + +/* free this GENTRY, returns what was ge->next + * (ge must be of type GE_LINE or GE_CURVE) + * works on both float and int entries + */ + +static GENTRY * +freethisge( + GENTRY *ge +) +{ + GENTRY *xge; + + if (ge->bkwd != ge->prev) { + /* at beginning of the contour */ + + xge = ge->bkwd; + if(xge == ge) { /* was the only line in contour */ + /* remove the contour completely */ + /* prev is GE_MOVE, next is GE_PATH, remove them all */ + + /* may be the first contour, then ->bkwd points to ge->entries */ + if(ge->prev->prev == 0) + *(GENTRY **)(ge->prev->bkwd) = ge->next->next; + else + ge->prev->prev->next = ge->next->next; + + if(ge->next->next) { + ge->next->next->prev = ge->prev->prev; + ge->next->next->bkwd = ge->prev->bkwd; + } + + xge = ge->next->next; + free(ge->prev); free(ge->next); free(ge); + return xge; + } + + /* move the start point of the contour */ + if(ge->flags & GEF_FLOAT) { + ge->prev->fx3 = xge->fx3; + ge->prev->fy3 = xge->fy3; + } else { + ge->prev->ix3 = xge->ix3; + ge->prev->iy3 = xge->iy3; + } + } else if(ge->frwd != ge->next) { + /* at end of the contour */ + + xge = ge->frwd->prev; + /* move the start point of the contour */ + if(ge->flags & GEF_FLOAT) { + xge->fx3 = ge->bkwd->fx3; + xge->fy3 = ge->bkwd->fy3; + } else { + xge->ix3 = ge->bkwd->ix3; + xge->iy3 = ge->bkwd->iy3; + } + } + + ge->prev->next = ge->next; + ge->next->prev = ge->prev; + ge->bkwd->frwd = ge->frwd; + ge->frwd->bkwd = ge->bkwd; + + xge = ge->next; + free(ge); + return xge; +} + +/* inserts a new gentry (LINE or CURVE) after another (MOVE + * or LINE or CURVE) + * corrects the first GE_MOVE if neccessary + */ + +static void +addgeafter( + GENTRY *oge, /* after this */ + GENTRY *nge /* insert this */ +) +{ + if(oge->type == GE_MOVE) { + /* insert before next */ + if(oge->next->type == GE_PATH) { + /* first and only GENTRY in path */ + nge->frwd = nge->bkwd = nge; + } else { + nge->frwd = oge->next; + nge->bkwd = oge->next->bkwd; + oge->next->bkwd->frwd = nge; + oge->next->bkwd = nge; + } + } else { + nge->frwd = oge->frwd; + nge->bkwd = oge; + oge->frwd->bkwd = nge; + oge->frwd = nge; + } + + nge->next = oge->next; + nge->prev = oge; + oge->next->prev = nge; + oge->next = nge; + + if(nge->frwd->prev->type == GE_MOVE) { + /* fix up the GE_MOVE entry */ + if(nge->flags & GEF_FLOAT) { + nge->frwd->prev->fx3 = nge->fx3; + nge->frwd->prev->fy3 = nge->fy3; + } else { + nge->frwd->prev->ix3 = nge->ix3; + nge->frwd->prev->iy3 = nge->iy3; + } + } +} + +/* + * Check if this GENTRY happens to be at the end of path + * and fix the first MOVETO accordingly + * handles both int and float + */ + +static void +fixendpath( + GENTRY *ge +) +{ + GENTRY *mge; + + mge = ge->frwd->prev; + if(mge->type == GE_MOVE) { + if(ge->flags & GEF_FLOAT) { + mge->fx3 = ge->fx3; + mge->fy3 = ge->fy3; + } else { + mge->ix3 = ge->ix3; + mge->iy3 = ge->iy3; + } + } +} + +/* + * This function adjusts the rest of path (the part from...to is NOT changed) + * to cover the specified gap by the specified axis (0 - X, 1 - Y). + * Gap is counted in direction (end_of_to - beginning_of_from). + * Returns by how much the gap was not closed (0.0 if it was fully closed). + * Ret contains by how much the first and last points of [from...to] + * were moved to bring them in consistence to the rest of the path. + * If ret==NULL then this info is not returned. + */ + +static double +fclosegap( + GENTRY *from, + GENTRY *to, + int axis, + double gap, + double *ret +) +{ +#define TIMESLARGER 10. /* how many times larger must be a curve to not change too much */ + double rm[2]; + double oldpos[2]; + double times, limit, df, dx; + int j, k; + GENTRY *xge, *pge, *nge, *bge[2]; + + /* remember the old points to calculate ret */ + oldpos[0] = from->prev->fpoints[axis][2]; + oldpos[1] = to->fpoints[axis][2]; + + rm[0] = rm[1] = gap / 2. ; + + bge[0] = from; /* this is convenient for iterations */ + bge[1] = to; + + /* first try to modify large curves but if have none then settle for small */ + for(times = (TIMESLARGER-1); times > 0.1; times /= 2. ) { + + if(rm[0]+rm[1] == 0.) + break; + + /* iterate in both directions, backwards then forwards */ + for(j = 0; j<2; j++) { + + if(rm[j] == 0.) /* if this direction is exhausted */ + continue; + + limit = fabs(rm[j]) * (1.+times); + + for(xge = bge[j]->cntr[j]; xge != bge[!j]; xge = xge->cntr[j]) { + dx = xge->fpoints[axis][2] - xge->prev->fpoints[axis][2]; + df = fabs(dx) - limit; + if( df <= FEPS ) /* curve is too small to change */ + continue; + + if( df >= fabs(rm[j]) ) + df = rm[j]; + else + df *= fsign(rm[j]); /* we may cover this part of rm */ + + rm[j] -= df; + limit = fabs(rm[j]) * (1.+times); + + if(xge->type == GE_CURVE) { /* correct internal points */ + double scale = ((dx+df) / dx) - 1.; + double base; + + if(j) + base = xge->fpoints[axis][2]; + else + base = xge->prev->fpoints[axis][2]; + + for(k = 0; k<2; k++) + xge->fpoints[axis][k] += scale * + (xge->fpoints[axis][k] - base); + } + + /* move all the intermediate lines */ + if(j) { + df = -df; /* absolute direction */ + pge = bge[1]->bkwd; + nge = xge->bkwd; + } else { + xge->fpoints[axis][2] += df; + pge = bge[0]; + nge = xge->frwd; + } + while(nge != pge) { + if(nge->type == GE_CURVE) { + nge->fpoints[axis][0] +=df; + nge->fpoints[axis][1] +=df; + } + nge->fpoints[axis][2] += df; + if(nge->next != nge->frwd) { /* last entry of contour */ + nge->frwd->prev->fpoints[axis][2] += df; + } + nge = nge->cntr[!j]; + } + + if(rm[j] == 0.) + break; + } + } + } + + /* find the difference */ + oldpos[0] -= from->prev->fpoints[axis][2]; + oldpos[1] -= to->fpoints[axis][2]; + + if(ret) { + ret[0] = oldpos[0] - from->prev->fpoints[axis][2]; + ret[1] = oldpos[1] - to->fpoints[axis][2]; + } + +#if 0 + if( rm[0]+rm[1] != gap - oldpos[1] + oldpos[0]) { + fprintf(stderr, "** gap=%g rm[0]=%g rm[1]=%g o[0]=%g o[1]=%g rg=%g og=%g\n", + gap, rm[0], rm[1], oldpos[0], oldpos[1], rm[0]+rm[1], + gap - oldpos[1] + oldpos[0]); + } +#endif + + return rm[0]+rm[1]; +#undef TIMESLARGER +} + +/* remove the lines or curves smaller or equal to the size limit */ + +static void +fdelsmall( + GLYPH *g, + double minlen +) +{ + GENTRY *ge, *nge, *pge, *xge, *next; + int i, k; + double dx, dy, d2, d2m; + double minlen2; +#define TIMESLARGER 10. /* how much larger must be a curve to not change too much */ + + minlen2 = minlen*minlen; + + for (ge = g->entries; ge != 0; ge = next) { + next = ge->next; + + if (ge->type != GE_CURVE && ge->type != GE_LINE) + continue; + + d2m = 0; + for(i= (ge->type==GE_CURVE? 0: 2); i<3; i++) { + dx = ge->fxn[i] - ge->prev->fx3; + dy = ge->fyn[i] - ge->prev->fy3; + d2 = dx*dx + dy*dy; + if(d2m < d2) + d2m = d2; + } + + if( d2m > minlen2 ) { /* line is not too small */ + /* XXX add more normalization here */ + continue; + } + + /* if the line is too small */ + + /* check forwards if we have a whole sequence of them */ + nge = ge; + for(xge = ge->frwd; xge != ge; xge = xge->frwd) { + d2m = 0; + for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) { + dx = xge->fxn[i] - xge->prev->fx3; + dy = xge->fyn[i] - xge->prev->fy3; + d2 = dx*dx + dy*dy; + if(d2m < d2) + d2m = d2; + } + if( d2m > minlen2 ) /* line is not too small */ + break; + nge = xge; + if(next == nge) /* move the next step past this sequence */ + next = next->next; + } + + /* check backwards if we have a whole sequence of them */ + pge = ge; + for(xge = ge->bkwd; xge != ge; xge = xge->bkwd) { + d2m = 0; + for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) { + dx = xge->fxn[i] - xge->prev->fx3; + dy = xge->fyn[i] - xge->prev->fy3; + d2 = dx*dx + dy*dy; + if(d2m < d2) + d2m = d2; + } + if( d2m > minlen2 ) /* line is not too small */ + break; + pge = xge; + } + + /* now we have a sequence of small fragments in pge...nge (inclusive) */ + + if(ISDBG(FCONCISE)) { + fprintf(stderr, "glyph %s has very small fragments(%x..%x..%x)\n", + g->name, pge, ge, nge); + dumppaths(g, pge, nge); + } + + /* reduce whole sequence to one part and remember the middle point */ + if(pge != nge) { + while(1) { + xge = pge->frwd; + if(xge == nge) { + pge->fx1 = pge->fx2 = pge->fx3; + pge->fx3 = nge->fx3; + pge->fy1 = pge->fy2 = pge->fy3; + pge->fy3 = nge->fy3; + pge->type = GE_CURVE; + freethisge(nge); + break; + } + if(xge == nge->bkwd) { + pge->fx1 = pge->fx2 = (pge->fx3+xge->fx3)/2.; + pge->fx3 = nge->fx3; + pge->fy1 = pge->fy2 = (pge->fy3+xge->fy3)/2.; + pge->fy3 = nge->fy3; + pge->type = GE_CURVE; + freethisge(nge); + freethisge(xge); + break; + } + freethisge(pge); pge = xge; + xge = nge->bkwd; freethisge(nge); nge = xge; + } + } + ge = pge; + + /* check if the whole sequence is small */ + dx = ge->fx3 - ge->prev->fx3; + dy = ge->fy3 - ge->prev->fy3; + d2 = dx*dx + dy*dy; + + if( d2 > minlen2 ) { /* no, it is not */ + double b, d; + + WARNING_3 fprintf(stderr, "glyph %s had a sequence of fragments < %g points each, reduced to one curve\n", + g->name, minlen); + + /* check that we did not create a monstrosity spanning quadrants */ + if(fsign(ge->fx1 - ge->prev->fx1) * fsign(ge->fx3 - ge->fx1) < 0 + || fsign(ge->fy1 - ge->prev->fy1) * fsign(ge->fy3 - ge->fy1) < 0 ) { + /* yes, we did; are both parts of this thing big enough ? */ + dx = ge->fx1 - ge->prev->fx3; + dy = ge->fy1 - ge->prev->fy3; + d2 = dx*dx + dy*dy; + + dx = ge->fx3 - ge->fx1; + dy = ge->fy3 - ge->fy1; + d2m = dx*dx + dy*dy; + + if(d2 > minlen2 && d2m > minlen2) { /* make two straights */ + nge = newgentry(GEF_FLOAT); + *nge = *ge; + + for(i=0; i<2; i++) { + ge->fpoints[i][2] = ge->fpoints[i][0]; + b = nge->fpoints[i][0]; + d = nge->fpoints[i][2] - b; + nge->fpoints[i][0] = b + 0.1*d; + nge->fpoints[i][1] = b + 0.9*d; + } + } + for(i=0; i<2; i++) { /* make one straight or first of two straights */ + b = ge->prev->fpoints[i][2]; + d = ge->fpoints[i][2] - b; + ge->fpoints[i][0] = b + 0.1*d; + ge->fpoints[i][1] = b + 0.9*d; + } + } + continue; + } + + if(ge->frwd == ge) { /* points to itself, just remove the path completely */ + WARNING_3 fprintf(stderr, "glyph %s had a path made of fragments < %g points each, removed\n", + g->name, minlen); + + next = freethisge(ge); + continue; + } + + /* now close the gap by x and y */ + for(i=0; i<2; i++) { + double gap; + + gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; + if( fclosegap(ge, ge, i, gap, NULL) != 0.0 ) { + double scale, base; + + /* not good, as the last resort just scale the next line */ + gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; + + if(ISDBG(FCONCISE)) + fprintf(stderr, " last resort on %c: closing next by %g\n", + (i==0 ? 'x' : 'y'), gap); + + nge = ge->frwd; + base = nge->fpoints[i][2]; + dx = ge->fpoints[i][2] - base; + if(fabs(dx) < FEPS) + continue; + + scale = ((dx-gap) / dx); + + if(nge->type == GE_CURVE) + for(k = 0; k<2; k++) + nge->fpoints[i][k] = base + + scale * (nge->fpoints[i][k] - base); + + ge->fpoints[i][2] -= gap; + } + } + + /* OK, the gap is closed - remove this useless GENTRY */ + freethisge(ge); + } +#undef TIMESLARGER +} + +/* find the point where two rays continuing vectors cross + * returns 1 if they cross, 0 if they don't + * If they cross optionally (if the pointers are not NULL) returns + * the maximal scales for both vectors and also optionally the point + * where the rays cross (twice). + * Expects that the curves are normalized. + * + * For convenience there are 2 front-end functions taking + * arguments in different formats + */ + +struct ray { + double x1, y1, x2, y2; + int isvert; + double k, b; /* lines are represented as y = k*x + b */ + double *maxp; +}; +static struct ray ray[3]; + +/* the back-end doing the actual work + * the rays are defined in the static array ray[] + */ + +static int +fcrossraysxx( + double crossdot[2][2] +) +{ + double x, y, max; + int i; + + for(i=0; i<2; i++) { + if(ray[i].x1 == ray[i].x2) + ray[i].isvert = 1; + else { + ray[i].isvert = 0; + ray[i].k = (ray[i].y2 - ray[i].y1) / (ray[i].x2 - ray[i].x1); + ray[i].b = ray[i].y2 - ray[i].k * ray[i].x2; + } + } + + if(ray[0].isvert && ray[1].isvert) { + if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: both vertical\n"); + return 0; /* both vertical, don't cross */ + } + + if(ray[1].isvert) { + ray[2] = ray[0]; /* exchange them */ + ray[0] = ray[1]; + ray[1] = ray[2]; + } + + if(ray[0].isvert) { + x = ray[0].x1; + } else { + if( fabs(ray[0].k - ray[1].k) < FEPS) { + if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: parallel lines, k = %g, %g\n", + ray[0].k, ray[1].k); + return 0; /* parallel lines */ + } + x = (ray[1].b - ray[0].b) / (ray[0].k - ray[1].k) ; + } + y = ray[1].k * x + ray[1].b; + + for(i=0; i<2; i++) { + if(ray[i].isvert) + max = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1); + else + max = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1); + /* check if wrong sides of rays cross */ + if( max < 0 ) { + if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: %c scale=%g @(%g,%g) (%g,%g)<-(%g,%g)\n", + (i?'Y':'X'), max, x, y, ray[i].x2, ray[i].y2, ray[i].x1, ray[i].y1); + return 0; + } + if(ray[i].maxp) + *ray[i].maxp = max; + } + if(crossdot != 0) { + crossdot[0][0] = crossdot[1][0] = x; + crossdot[0][1] = crossdot[1][1] = y; + } + return 1; +} + +/* the front-end getting the arguments from 4 dots defining + * a curve in the same format as for fapproxcurve(): + * rays are defined as beginning and end of the curve, + * the crossdot is inserted as the two middle dots of the curve + */ + +int +fcrossrayscv( + double curve[4][2 /*X,Y*/], + double *max1, + double *max2 +) +{ + ray[0].x1 = curve[0][X]; + ray[0].y1 = curve[0][Y]; + ray[0].x2 = curve[1][X]; + ray[0].y2 = curve[1][Y]; + ray[0].maxp = max1; + + ray[1].x1 = curve[2][X]; + ray[1].y1 = curve[2][Y]; + ray[1].x2 = curve[3][X]; + ray[1].y2 = curve[3][Y]; + ray[1].maxp = max2; + + return fcrossraysxx(&curve[1]); +} + +/* the front-end getting the arguments from gentries: + * rays are defined as beginning of curve1 and end of curve 2 + */ + +int +fcrossraysge( + GENTRY *ge1, + GENTRY *ge2, + double *max1, + double *max2, + double crossdot[2][2] +) +{ + ray[0].x1 = ge1->prev->fx3; + ray[0].y1 = ge1->prev->fy3; + ray[0].x2 = ge1->fpoints[X][ge1->ftg]; + ray[0].y2 = ge1->fpoints[Y][ge1->ftg]; + ray[0].maxp = max1; + + ray[1].x1 = ge2->fx3; + ray[1].y1 = ge2->fy3; + if(ge2->rtg < 0) { + ray[1].x2 = ge2->prev->fx3; + ray[1].y2 = ge2->prev->fy3; + } else { + ray[1].x2 = ge2->fpoints[X][ge2->rtg]; + ray[1].y2 = ge2->fpoints[Y][ge2->rtg]; + } + ray[1].maxp = max2; + + return fcrossraysxx(crossdot); +} + +/* debugging printout functions */ + +#if defined(DEBUG_DOTSEG) || defined(DEBUG_DOTCURVE) || defined(DEBUG_APPROXCV) + +/* for debugging */ +static +printdot( + double dot[2] +) +{ + fprintf(stderr, "(%g,%g)", dot[0], dot[1]); +} + +static +printseg( + double seg[2][2] +) +{ + putc('[', stderr); + printdot(seg[0]); + putc(' ', stderr); + printdot(seg[1]); + putc(']', stderr); +} + +#endif /* DEBUG_* */ + +/* + * Find squared distance from a dot to a line segment + */ + +double +fdotsegdist2( + double seg[2][2 /*X,Y*/], + double dot[2 /*X,Y*/] +) +{ +#define x1 seg[0][X] +#define y1 seg[0][Y] +#define x2 seg[1][X] +#define y2 seg[1][Y] +#define xdot dot[X] +#define ydot dot[Y] + + double dx, dy; /* segment dimensions */ + double kline, bline; /* segment line formula is y=k*x+b */ + double kperp, bperp; /* perpendicular from the dot to the line */ + double xcross, ycross; /* where the perpendicular crosses the segment */ + +/* handle the situation where the nearest point of the segment is its end */ +#define HANDLE_LIMITS(less12, lesscr1, lesscr2) \ + if( less12 ) { \ + if( lesscr1 ) { \ + xcross = x1; \ + ycross = y1; \ + } else if( !(lesscr2) ) { \ + xcross = x2; \ + ycross = y2; \ + } \ + } else { \ + if( !(lesscr1) ) { \ + xcross = x1; \ + ycross = y1; \ + } else if( lesscr2 ) { \ + xcross = x2; \ + ycross = y2; \ + } \ + } \ + /* end of macro */ + + + dx = x2 - x1; + dy = y2 - y1; + + if(fabs(dx) < FEPS) { + /* special case - vertical line */ +#ifdef DEBUG_DOTSEG + printf("vertical line!\n"); +#endif + xcross = x1; + ycross = ydot; + HANDLE_LIMITS( y1 < y2, ycross < y1, ycross < y2); + } else if(fabs(dy) < FEPS) { + /* special case - horizontal line */ +#ifdef DEBUG_DOTSEG + printf("horizontal line!\n"); +#endif + xcross = xdot; + ycross = y1; + HANDLE_LIMITS( x1 < x2, xcross < x1, xcross < x2) + } else { + kline = dy/dx; + bline = y1 - x1*kline; + kperp = -1./kline; + bperp = ydot - xdot*kperp; + + xcross = (bline-bperp) / (kperp-kline); + ycross = kline*xcross + bline; + + HANDLE_LIMITS( x1 < x2, xcross < x1, xcross < x2) + } +#ifdef DEBUG_DOTSEG + printf("crossover at (%g,%g)\n", xcross, ycross); +#endif + + dx = xdot-xcross; + dy = ydot-ycross; + return dx*dx+dy*dy; +#undef x1 +#undef y1 +#undef x2 +#undef y2 +#undef xdot +#undef ydot +#undef HANDLE_LIMITS +} + +/* find the weighted quadratic average for the distance of a set + * of dots from the curve; also fills out the individual distances + * for each dot; if maxp!=NULL then returns the maximal squared + * distance in there + */ + +double +fdotcurvdist2( + double curve[4][2 /*X,Y*/ ], + struct dot_dist *dots, + int ndots, /* number of entries in dots */ + double *maxp +) +{ + /* a curve is approximated by this many straight segments */ +#define NAPSECT 16 + /* a curve is divided into this many sections with equal weight each */ +#define NWSECT 4 + /* table of coefficients for finding the dots on the curve */ + /* tt[0] is left unused */ + static double tt[NAPSECT][4]; + static int havett = 0; /* flag: tt is initialized */ + /* dots on the curve */ + double cvd[NAPSECT+1][2 /*X,Y*/]; + /* sums by sections */ + double sum[NWSECT]; + /* counts by sections */ + double count[NWSECT]; + int d, i, j; + int id1, id2; + double dist1, dist2, dist3, dx, dy, x, y; + double max = 0.; + + if(!havett) { + double t, nt, t2, nt2, step; + + havett++; + step = 1. / NAPSECT; + t = 0; + for(i=1; iid1+1; i--) { + dx = x - cvd[i][X]; + dy = y - cvd[i][Y]; + dist3 = dx*dx + dy*dy; +#ifdef DEBUG_DOTCURVE + printf(" dot %d ",i); printdot(cvd[i]); printf(" dist=%g\n", sqrt(dist3)); +#endif + if(dist3 < dist2) { + dist2 = dist3; + id2 = i; + } else + break; + } + + /* now find which of the local minimums is smaller */ + if(dist2 < dist1) { + id1 = id2; + } + } + + /* the nearest segment must include the nearest dot */ + if(id1==0) { + dots[d].seg = 0; + dots[d].dist2 = fdotsegdist2(&cvd[0], dots[d].p); + } else if(id1==NAPSECT) { + dots[d].seg = NAPSECT-1; + dots[d].dist2 = fdotsegdist2(&cvd[NAPSECT-1], dots[d].p); + } else { + dist1 = fdotsegdist2(&cvd[id1], dots[d].p); + dist2 = fdotsegdist2(&cvd[id1-1], dots[d].p); + if(dist2 < dist1) { + dots[d].seg = id1-1; + dots[d].dist2 = dist2; + } else { + dots[d].seg = id1; + dots[d].dist2 = dist1; + } + } + + i = dots[d].seg % NWSECT; + sum[i] += dots[d].dist2; + if(dots[d].dist2 > max) + max = dots[d].dist2; + count[i]++; +#ifdef DEBUG_DOTCURVE + printf(" best seg %d sect %d dist=%g\n", dots[d].seg, i, sqrt(dots[d].dist2)); +#endif + } + + /* calculate the weighted average */ + id1=0; + dist1=0.; + for(i=0; irtg < 0) { + d[1][X] = ge1->fx3 - ge1->prev->fx3; + d[1][Y] = ge1->fy3 - ge1->prev->fy3; + } else { + d[1][X] = ge1->fx3 - ge1->fpoints[X][ge1->rtg]; + d[1][Y] = ge1->fy3 - ge1->fpoints[Y][ge1->rtg]; + } + d[2][X] = ge2->fpoints[X][ge2->ftg] - ge2->prev->fx3; + d[2][Y] = ge2->fpoints[Y][ge2->ftg] - ge2->prev->fy3; + + len1 = sqrt( d[1][X]*d[1][X] + d[1][Y]*d[1][Y] ); + len2 = sqrt( d[2][X]*d[2][X] + d[2][Y]*d[2][Y] ); + /* scale the 2nd segment to the length of 1 + * and to make sure that the 1st segment is longer scale it to + * the length of 2 and extend to the same distance backwards + */ + scale1 = 2./len1; + scale2 = 1./len2; + + for(axis=0; axis <2; axis++) { + d[0][axis] = -( d[1][axis] *= scale1 ); + d[2][axis] *= scale2; + } + return fdotsegdist2(d, d[2]); +} + +#if 0 +/* find the area covered by the curve + * (limited by the projections to the X axis) + */ + +static double +fcvarea( + GENTRY *ge +) +{ + double Ly, My, Ny, Py, Qx, Rx, Sx; + double area; + + /* y = Ly*t^3 + My*t^2 + Ny*t + Py */ + Ly = -ge->prev->fy3 + 3*(ge->fy1 - ge->fy2) + ge->fy3; + My = 3*ge->prev->fy3 - 6*ge->fy1 + 3*ge->fy2; + Ny = 3*(-ge->prev->fy3 + ge->fy1); + Py = ge->prev->fy3; + + /* dx/dt = Qx*t^2 + Rx*t + Sx */ + Qx = 3*(-ge->prev->fx3 + 3*(ge->fx1 - ge->fx2) + ge->fx3); + Rx = 6*(ge->prev->fx3 - 2*ge->fx1 + ge->fx2); + Sx = 3*(-ge->prev->fx3 + ge->fx1); + + /* area is integral[from 0 to 1]( y(t) * dx(t)/dt *dt) */ + area = 1./6.*(Ly*Qx) + 1./5.*(Ly*Rx + My*Qx) + + 1./4.*(Ly*Sx + My*Rx + Ny*Qx) + 1./3.*(My*Sx + Ny*Rx + Py*Qx) + + 1./2.*(Ny*Sx + Py*Rx) + Py*Sx; + + return area; +} +#endif + +/* find the value of point on the curve at the given parameter t, + * along the given axis (0 - X, 1 - Y). + */ + +static double +fcvval( + GENTRY *ge, + int axis, + double t +) +{ + double t2, mt, mt2; + + /* val = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 */ + t2 = t*t; + mt = 1-t; + mt2 = mt*mt; + + return ge->prev->fpoints[axis][2]*mt2*mt + + 3*(ge->fpoints[axis][0]*mt2*t + ge->fpoints[axis][1]*mt*t2) + + ge->fpoints[axis][2]*t*t2; +} + +/* + * Find ndots equally spaced dots on a curve or line and fill + * their coordinates into the dots array + */ + +static void +fsampledots( + GENTRY *ge, + double dots[][2], /* the dots to fill */ + int ndots +) +{ + int i, axis; + double t, nf, dx, d[2]; + + nf = ndots+1; + if(ge->type == GE_CURVE) { + for(i=0; ifx3 - ge->prev->fx3; + d[1] = ge->fy3 - ge->prev->fy3; + for(i=0; iprev->fpoints[axis][2] + + t*d[axis]; + } + } +} + +/* + * Allocate a structure gex_con + */ + +static void +alloc_gex_con( + GENTRY *ge +) +{ + ge->ext = (void*)calloc(1, sizeof(GEX_CON)); + if(ge->ext == 0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } +} + +/* + * Normalize a gentry for fforceconcise() : find the points that + * can be used to calculate the tangents. + */ + +static void +fnormalizege( + GENTRY *ge +) +{ + int midsame, frontsame, rearsame; + + if(ge->type == GE_LINE) { + ge->ftg = 2; + ge->rtg = -1; + } else { /* assume it's a curve */ + midsame = (fabs(ge->fx1-ge->fx2)fy1-ge->fy2)fx1-ge->prev->fx3)fy1-ge->prev->fy3)fx3-ge->fx2)fy3-ge->fy2)ftg = 2; + ge->rtg = -1; + } else { + if(frontsame) { + ge->ftg = 1; + } else { + ge->ftg = 0; + } + if(rearsame) { + ge->rtg = 0; + } else { + ge->rtg = 1; + } + } + } +} + +/* various definition for the processing of outlines */ + +/* maximal average quadratic distance from the original curve + * (in dots) to consider the joined curve good + */ +#define CVEPS 1.5 +#define CVEPS2 (CVEPS*CVEPS) +/* squared sinus of the maximal angle that we consider a smooth joint */ +#define SMOOTHSIN2 0.25 /* 0.25==sin(30 degrees)^2 */ +/* squared line length that we consider small */ +#define SMALL_LINE2 (15.*15.) +/* how many times a curve must be bigger than a line to join, squared */ +#define TIMES_LINE2 (3.*3.) + +/* + * Normalize and analyse a gentry for fforceconcise() and fill out the gex_con + * structure + */ + +static void +fanalyzege( + GENTRY *ge +) +{ + int i, ix, iy; + double avsd2, dots[3][2 /*X,Y*/]; + GEX_CON *gex; + + gex = X_CON(ge); + memset(gex, 0, sizeof *gex); + + gex->len2 = 0; + for(i=0; i<2; i++) { + avsd2 = gex->d[i] = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; + gex->len2 += avsd2*avsd2; + } + gex->sin2 = fjointsin2(ge, ge->frwd); + if(ge->type == GE_CURVE) { + ge->dir = fgetcvdir(ge); + for(i=0; i<2; i++) { + dots[0][i] = ge->prev->fpoints[i][2]; + dots[1][i] = ge->fpoints[i][2]; + dots[2][i] = fcvval(ge, i, 0.5); + } + avsd2 = fdotsegdist2(dots, dots[2]); + if(avsd2 <= CVEPS2) { + gex->flags |= GEXF_FLAT; + } + } else { + ge->dir = CVDIR_FEQUAL|CVDIR_REQUAL; + gex->flags |= GEXF_FLAT; + } + if(gex->flags & GEXF_FLAT) { + if( fabs(gex->d[X]) > FEPS && fabs(gex->d[Y]) < 5. + && fabs(gex->d[Y] / gex->d[X]) < 0.2) + gex->flags |= GEXF_HOR; + else if( fabs(gex->d[Y]) > FEPS && fabs(gex->d[X]) < 5. + && fabs(gex->d[X] / gex->d[Y]) < 0.2) + gex->flags |= GEXF_VERT; + } + ix = gex->isd[X] = fsign(gex->d[X]); + iy = gex->isd[Y] = fsign(gex->d[Y]); + if(ix <= 0) { + if(iy <= 0) + gex->flags |= GEXF_QDL; + if(iy >= 0) + gex->flags |= GEXF_QUL; + if(gex->flags & GEXF_HOR) + gex->flags |= GEXF_IDQ_L; + } + if(ix >= 0) { + if(iy <= 0) + gex->flags |= GEXF_QDR; + if(iy >= 0) + gex->flags |= GEXF_QUR; + if(gex->flags & GEXF_HOR) + gex->flags |= GEXF_IDQ_R; + } + if(gex->flags & GEXF_VERT) { + if(iy <= 0) { + gex->flags |= GEXF_IDQ_U; + } else { /* supposedly there is no 0-sized entry */ + gex->flags |= GEXF_IDQ_D; + } + } +} + +/* + * Analyse a joint between this and following gentry for fforceconcise() + * and fill out the corresponding parts of the gex_con structure + * Bothe entries must be analyzed first. + */ + +static void +fanalyzejoint( + GENTRY *ge +) +{ + GENTRY *nge = ge->frwd; + GENTRY tge; + GEX_CON *gex, *ngex; + double avsd2, dots[3][2 /*X,Y*/]; + int i; + + gex = X_CON(ge); ngex = X_CON(nge); + + /* look if they can be joined honestly */ + + /* if any is flat, they should join smoothly */ + if( (gex->flags & GEXF_FLAT || ngex->flags & GEXF_FLAT) + && gex->sin2 > SMOOTHSIN2) + goto try_flatboth; + + if(ge->type == GE_LINE) { + if(nge->type == GE_LINE) { + if(gex->len2 > SMALL_LINE2 || ngex->len2 > SMALL_LINE2) + goto try_flatboth; + } else { + if(gex->len2*TIMES_LINE2 > ngex->len2) + goto try_flatboth; + } + } else if(nge->type == GE_LINE) { + if(ngex->len2*TIMES_LINE2 > gex->len2) + goto try_flatboth; + } + + /* if curve changes direction */ + if( gex->isd[X]*ngex->isd[X]<0 || gex->isd[Y]*ngex->isd[Y]<0) + goto try_idealone; + + /* if would create a zigzag */ + if( ((ge->dir&CVDIR_FRONT)-CVDIR_FEQUAL) * ((nge->dir&CVDIR_REAR)-CVDIR_REQUAL) < 0 ) + goto try_flatone; + + if( fcrossraysge(ge, nge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JGOOD; + +try_flatone: + /* look if they can be joined by flatting out one of the entries */ + + /* at this point we know that the general direction of the + * gentries is OK + */ + + if( gex->flags & GEXF_FLAT ) { + tge = *ge; + tge.fx1 = tge.fx3; + tge.fy1 = tge.fy3; + fnormalizege(&tge); + if( fcrossraysge(&tge, nge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JFLAT|GEXF_JFLAT1; + } + if( ngex->flags & GEXF_FLAT ) { + tge = *nge; + tge.fx2 = ge->fx3; + tge.fy2 = ge->fy3; + fnormalizege(&tge); + if( fcrossraysge(ge, &tge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JFLAT|GEXF_JFLAT2; + } + +try_idealone: + /* look if one of the entries can be brought to an idealized + * horizontal or vertical position and then joined + */ + if( gex->flags & GEXF_HOR && gex->isd[X]*ngex->isd[X]>=0 ) { + tge = *ge; + tge.fx1 = tge.fx3; + tge.fy1 = ge->prev->fy3; /* force horizontal */ + fnormalizege(&tge); + if( fcrossraysge(&tge, nge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JID|GEXF_JID1; + } else if( gex->flags & GEXF_VERT && gex->isd[Y]*ngex->isd[Y]>=0 ) { + tge = *ge; + tge.fx1 = ge->prev->fx3; /* force vertical */ + tge.fy1 = tge.fy3; + fnormalizege(&tge); + if( fcrossraysge(&tge, nge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JID|GEXF_JID1; + } + if( ngex->flags & GEXF_HOR && gex->isd[X]*ngex->isd[X]>=0 ) { + tge = *nge; + tge.fx2 = ge->fx3; + tge.fy2 = nge->fy3; /* force horizontal */ + fnormalizege(&tge); + if( fcrossraysge(ge, &tge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JID|GEXF_JID2; + } else if( ngex->flags & GEXF_VERT && gex->isd[Y]*ngex->isd[Y]>=0 ) { + tge = *nge; + tge.fx2 = nge->fx3; /* force vertical */ + tge.fy2 = ge->fy3; + fnormalizege(&tge); + if( fcrossraysge(ge, &tge, NULL, NULL, NULL) ) + gex->flags |= GEXF_JID|GEXF_JID2; + } + +try_flatboth: + /* look if we can change them to one line */ + if(gex->flags & GEXF_FLAT && ngex->flags & GEXF_FLAT) { + for(i=0; i<2; i++) { + dots[0][i] = ge->prev->fpoints[i][2]; + dots[1][i] = nge->fpoints[i][2]; + dots[2][i] = ge->fpoints[i][2]; + } + if( fdotsegdist2(dots, dots[2]) <= CVEPS2) + gex->flags |= GEXF_JLINE; + } +} + +/* + * Force conciseness of one contour in the glyph, + * the contour is indicated by one entry from it. + */ + +static void +fconcisecontour( + GLYPH *g, + GENTRY *startge +) +{ +/* initial maximal number of dots to be used as reference */ +#define MAXDOTS ((NREFDOTS+1)*12) + + GENTRY *ge, *pge, *nge, *ige; + GEX_CON *gex, *pgex, *ngex, *nngex; + GENTRY tpge, tnge; + int quad, qq, i, j, ndots, maxdots; + int found[2]; + int joinmask, pflag, nflag; + struct dot_dist *dots; + double avsd2, maxd2, eps2; + double apcv[4][2]; + + if(startge == 0) { + fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n", + __FILE__, __LINE__); + fprintf(stderr, "Strange contour in glyph %s\n", g->name); + dumppaths(g, NULL, NULL); + return; + } + + if(startge->type != GE_CURVE && startge->type != GE_LINE) + return; /* probably a degenerate contour */ + + if(ISDBG(FCONCISE)) + fprintf(stderr, "processing contour 0x%p of glyph %s\n", startge, g->name); + + maxdots = MAXDOTS; + dots = (struct dot_dist *)malloc(sizeof(*dots)*maxdots); + if(dots == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + + ge = startge; + joinmask = GEXF_JGOOD; + while(1) { + restart: + gex = X_CON(ge); + if((gex->flags & GEXF_JMASK) > ((joinmask<<1)-1)) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "found higher flag (%x>%x) at 0x%p\n", + gex->flags & GEXF_JMASK, ((joinmask<<1)-1), ge); + joinmask <<= 1; + startge = ge; /* have to redo the pass */ + continue; + } + if(( gex->flags & joinmask )==0) + goto next; + + /* if we happen to be in the middle of a string of + * joinable entries, find its beginning + */ + if( gex->flags & (GEXF_JCVMASK^GEXF_JID) ) + quad = gex->flags & X_CON_F(ge->frwd) & GEXF_QMASK; + else if( gex->flags & GEXF_JID2 ) + quad = gex->flags & GEXF_QFROM_IDEAL(X_CON_F(ge->frwd)) & GEXF_QMASK; + else /* must be GEXF_JID1 */ + quad = GEXF_QFROM_IDEAL(gex->flags) & X_CON_F(ge->frwd) & GEXF_QMASK; + + pge = ge; + pgex = X_CON(pge->bkwd); + + if(ISDBG(FCONCISE)) + fprintf(stderr, "ge %p prev -> 0x%p ", ge, pge); + + while(pgex->flags & GEXF_JCVMASK) { + if( !(pgex->flags & ((GEXF_JCVMASK^GEXF_JID)|GEXF_JID2)) ) + qq = GEXF_QFROM_IDEAL(pgex->flags); + else + qq = pgex->flags & GEXF_QMASK; + + if(ISDBG(FCONCISE)) + fprintf(stderr, "(%x?%x)", quad, qq); + + if( !(quad & qq) ) { + if( !(X_CON_F(pge) & (GEXF_JCVMASK^GEXF_JID)) + && pgex->flags & (GEXF_JCVMASK^GEXF_JID) ) { + /* the previos entry is definitely a better match */ + if(pge == ge) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "\nprev is a better match at %p\n", pge); + startge = ge; + goto next; + } else + pge = pge->frwd; + } + break; + } + + quad &= qq; + pge = pge->bkwd; + pgex = X_CON(pge->bkwd); + if(ISDBG(FCONCISE)) + fprintf(stderr, "0x%p ", pge); + } + + /* collect as many entries for joining as possible */ + nge = ge->frwd; + ngex = X_CON(nge); + nngex = X_CON(nge->frwd); + + if(ISDBG(FCONCISE)) + fprintf(stderr, ": 0x%x\nnext -> 0x%p ", pge, nge); + + while(ngex->flags & GEXF_JCVMASK) { + if( !(ngex->flags & ((GEXF_JCVMASK^GEXF_JID)|GEXF_JID1)) ) + qq = GEXF_QFROM_IDEAL(nngex->flags); + else + qq = nngex->flags & GEXF_QMASK; + + if(ISDBG(FCONCISE)) + fprintf(stderr, "(%x?%x)", quad, qq); + if( !(quad & qq) ) { + if( !(X_CON_F(nge->bkwd) & (GEXF_JCVMASK^GEXF_JID)) + && ngex->flags & (GEXF_JCVMASK^GEXF_JID) ) { + /* the next-next entry is definitely a better match */ + if(nge == ge->frwd) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "\nnext %x is a better match than %x at %p (jmask %x)\n", + ngex->flags & GEXF_JCVMASK, gex->flags & GEXF_JCVMASK, nge, joinmask); + goto next; + } else + nge = nge->bkwd; + } + break; + } + + quad &= qq; + nge = nge->frwd; + ngex = nngex; + nngex = X_CON(nge->frwd); + if(ISDBG(FCONCISE)) + fprintf(stderr, "0x%p ", nge); + } + + if(ISDBG(FCONCISE)) + fprintf(stderr, ": 0x%x\n", nge); + + /* XXX add splitting of last entries if neccessary */ + + /* make sure that all the reference dots are valid */ + for(ige = pge; ige != nge->frwd; ige = ige->frwd) { + nngex = X_CON(ige); + if( !(nngex->flags & GEXF_VDOTS) ) { + fsampledots(ige, nngex->dots, NREFDOTS); + nngex->flags |= GEXF_VDOTS; + } + } + + /* do the actual joining */ + while(1) { + pgex = X_CON(pge); + ngex = X_CON(nge->bkwd); + /* now the segments to be joined are pge...nge */ + + ndots = 0; + for(ige = pge; ige != nge->frwd; ige = ige->frwd) { + if(maxdots < ndots+(NREFDOTS+1)) { + maxdots += MAXDOTS; + dots = (struct dot_dist *)realloc((void *)dots, sizeof(*dots)*maxdots); + if(dots == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } + nngex = X_CON(ige); + for(i=0; idots[i][j]; + ndots++; + } + for(j=0; j<2; j++) + dots[ndots].p[j] = ige->fpoints[j][2]; + ndots++; + } + ndots--; /* the last point is not interesting */ + + tpge = *pge; + pflag = pgex->flags; + if(pflag & (GEXF_JGOOD|GEXF_JFLAT2|GEXF_JID2)) { + /* nothing */ + } else if(pflag & GEXF_JFLAT) { + tpge.fx1 = tpge.fx3; + tpge.fy1 = tpge.fy3; + } else if(pflag & GEXF_JID) { + if(pflag & GEXF_HOR) + tpge.fy1 = tpge.bkwd->fy3; + else + tpge.fx1 = tpge.bkwd->fx3; + } + + tnge = *nge; + nflag = ngex->flags; + if(nflag & (GEXF_JGOOD|GEXF_JFLAT1|GEXF_JID) + && !(nflag & GEXF_JID2)) { + /* nothing */ + } else if(nflag & GEXF_JFLAT) { + tnge.fx2 = tnge.bkwd->fx3; + tnge.fy2 = tnge.bkwd->fy3; + } else if(nflag & GEXF_JID) { + if(X_CON_F(nge) & GEXF_HOR) + tnge.fy2 = tnge.fy3; + else + tnge.fx2 = tnge.fx3; + } + + fnormalizege(&tpge); + fnormalizege(&tnge); + if( fcrossraysge(&tpge, &tnge, NULL, NULL, &apcv[1]) ) { + apcv[0][X] = tpge.bkwd->fx3; + apcv[0][Y] = tpge.bkwd->fy3; + /* apcv[1] and apcv[2] were filled by fcrossraysge() */ + apcv[3][X] = tnge.fx3; + apcv[3][Y] = tnge.fy3; + + /* calculate the precision depending on the smaller dimension of the curve */ + maxd2 = apcv[3][X]-apcv[0][X]; + maxd2 *= maxd2; + eps2 = apcv[3][Y]-apcv[0][Y]; + eps2 *= eps2; + if(maxd2 < eps2) + eps2 = maxd2; + eps2 *= (CVEPS2*4.) / (400.*400.); + if(eps2 < CVEPS2) + eps2 = CVEPS2; + else if(eps2 > CVEPS2*4.) + eps2 = CVEPS2*4.; + + fapproxcurve(apcv, dots, ndots); + + avsd2 = fdotcurvdist2(apcv, dots, ndots, &maxd2); + if(ISDBG(FCONCISE)) + fprintf(stderr, "avsd = %g, maxd = %g, ", sqrt(avsd2), sqrt(maxd2)); + if(avsd2 <= eps2 && maxd2 <= eps2*2.) { + /* we've guessed a curve that is close enough */ + ggoodcv++; ggoodcvdots += ndots; + + if(ISDBG(FCONCISE)) { + fprintf(stderr, "in %s joined %p-%p to ", g->name, pge, nge); + for(i=0; i<4; i++) { + fprintf(stderr, " (%g, %g)", apcv[i][X], apcv[i][Y]); + } + fprintf(stderr, " from\n"); + dumppaths(g, pge, nge); + } + for(i=0; i<3; i++) { + pge->fxn[i] = apcv[i+1][X]; + pge->fyn[i] = apcv[i+1][Y]; + } + pge->type = GE_CURVE; + ge = pge; + for(ige = pge->frwd; ; ige = pge->frwd) { + if(ige == pge) { + fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n", + __FILE__, __LINE__); + free(dots); + return; + } + if(startge == ige) + startge = pge; + free(ige->ext); + freethisge(ige); + if(ige == nge) + break; + } + fnormalizege(ge); + if(ISDBG(FCONCISE)) { + fprintf(stderr, "normalized "); + for(i=0; i<3; i++) { + fprintf(stderr, " (%g, %g)", ge->fpoints[X][i], ge->fpoints[Y][i]); + } + fprintf(stderr, "\n"); + } + fanalyzege(ge); + fanalyzejoint(ge); + fanalyzege(ge->bkwd); + fanalyzejoint(ge->bkwd); + + /* the results of this join will have to be reconsidered */ + startge = ge = ge->frwd; + goto restart; + } else { + gbadcv++; gbadcvdots += ndots; + } + } + + /* if we're down to 2 entries then the join has failed */ + if(pge->frwd == nge) { + pgex->flags &= ~joinmask; + if(ISDBG(FCONCISE)) + fprintf(stderr, "no match\n"); + goto next; + } + + /* reduce the number of entries by dropping one at some end, + * should never drop the original ge from the range + */ + + if(nge->bkwd == ge + || pge != ge && (pgex->flags & GEXF_JCVMASK) <= (ngex->flags & GEXF_JCVMASK) ) { + pge = pge->frwd; + } else { + nge = nge->bkwd; + } + if(ISDBG(FCONCISE)) + fprintf(stderr, "next try: %p to %p\n", pge, nge); + } + +next: + ge = ge->frwd; + if(ge == startge) { + joinmask = (joinmask >> 1) & GEXF_JCVMASK; + if(joinmask == 0) + break; + } + } + + /* join flat segments into lines */ + /* here ge==startge */ + while(1) { + gex = X_CON(ge); + if( !(gex->flags & GEXF_JLINE) ) + goto next2; + + ndots = 0; + dots[ndots].p[X] = ge->fx3; + dots[ndots].p[Y] = ge->fy3; + ndots++; + + pge = ge->bkwd; + nge = ge->frwd; + + if(ISDBG(FCONCISE)) + fprintf(stderr, "joining LINE from %p-%p\n", ge, nge); + + while(pge!=nge) { + pgex = X_CON(pge); + ngex = X_CON(nge); + if(ISDBG(FCONCISE)) + fprintf(stderr, "(p=%p/%x n=0x%x/%x) ", pge, pgex->flags & GEXF_JLINE, + nge, ngex->flags & GEXF_JLINE); + if( !((pgex->flags | ngex->flags) & GEXF_JLINE) ) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "(end p=%p n=%p) ", pge, nge); + break; + } + + if(maxdots < ndots+2) { + maxdots += MAXDOTS; + dots = (struct dot_dist *)realloc((void *)dots, sizeof(*dots)*maxdots); + if(dots == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } + if( pgex->flags & GEXF_JLINE ) { + for(i=0; i<2; i++) { + apcv[0][i] = pge->bkwd->fpoints[i][2]; + apcv[1][i] = nge->fpoints[i][2]; + dots[ndots].p[i] = pge->fpoints[i][2]; + } + ndots++; + for(i=0; i CVEPS2) + break; + } + if(iflags &= ~GEXF_JLINE; + } else { + pge = pge->bkwd; + if(pge == nge) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "intersected at prev %p ", pge); + break; /* oops, tried to self-intersect */ + } + } + } else if(ISDBG(FCONCISE)) + fprintf(stderr, "(p=%p) ", pge); + + if( ngex->flags & GEXF_JLINE ) { + for(i=0; i<2; i++) { + apcv[0][i] = pge->fpoints[i][2]; /* pge points before the 1st segment */ + apcv[1][i] = nge->frwd->fpoints[i][2]; + dots[ndots].p[i] = nge->fpoints[i][2]; + } + ndots++; + for(i=0; i CVEPS2) + break; + } + if(ifrwd); + ndots--; + ngex->flags &= ~GEXF_JLINE; + } else { + nge = nge->frwd; + } + } else if(ISDBG(FCONCISE)) + fprintf(stderr, "(n=%p) ", nge); + } + + pge = pge->frwd; /* now the limits are pge...nge inclusive */ + if(pge == nge) /* a deeply perversive contour */ + break; + + if(ISDBG(FCONCISE)) { + fprintf(stderr, "\nin %s joined LINE %p-%p from\n", g->name, pge, nge); + dumppaths(g, pge, nge); + } + pge->type = GE_LINE; + for(i=0; i<2; i++) { + pge->fpoints[i][2] = nge->fpoints[i][2]; + } + fnormalizege(pge); + X_CON_F(pge) &= ~GEXF_JLINE; + + ge = pge; + for(ige = pge->frwd; ; ige = pge->frwd) { + if(ige == pge) { + fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n", + __FILE__, __LINE__); + free(dots); + return; + } + if(startge == ige) + startge = pge; + free(ige->ext); + freethisge(ige); + if(ige == nge) + break; + } +next2: + ge = ge->frwd; + if(ge == startge) + break; + } + + free(dots); +} + +/* force conciseness: substitute 2 or more curves going in the +** same quadrant with one curve +** in floating point +*/ + +void +fforceconcise( + GLYPH * g +) +{ + + GENTRY *ge, *nge, *endge, *xge; + + assertisfloat(g, "enforcing conciseness"); + + fdelsmall(g, 0.05); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + if(ISDBG(FCONCISE)) + dumppaths(g, NULL, NULL); + + /* collect more information about each gentry and their joints */ + for (ge = g->entries; ge != 0; ge = ge->next) + if (ge->type == GE_CURVE || ge->type == GE_LINE) + fnormalizege(ge); + + for (ge = g->entries; ge != 0; ge = ge->next) + if (ge->type == GE_CURVE || ge->type == GE_LINE) { + alloc_gex_con(ge); + fanalyzege(ge); + } + + /* see what we can do about joining */ + for (ge = g->entries; ge != 0; ge = ge->next) + if (ge->type == GE_CURVE || ge->type == GE_LINE) + fanalyzejoint(ge); + + /* now do the joining */ + for (ge = g->entries; ge != 0; ge = ge->next) + if(ge->type == GE_MOVE) + fconcisecontour(g, ge->next); + + for (ge = g->entries; ge != 0; ge = ge->next) + if (ge->type == GE_CURVE || ge->type == GE_LINE) + free(ge->ext); +} + +void +print_glyph( + int glyphno +) +{ + GLYPH *g; + GENTRY *ge; + int x = 0, y = 0; + int i; + int grp, lastgrp= -1; + + if(ISDBG(FCONCISE) && glyphno == 0) { + fprintf(stderr, "Guessed curves: bad %d/%d good %d/%d\n", + gbadcv, gbadcvdots, ggoodcv, ggoodcvdots); + } + + g = &glyph_list[glyphno]; + + fprintf(pfa_file, "/%s { \n", g->name); + + /* consider widths >MAXLEGALWIDTH as bugs */ + if( g->scaledwidth <= MAXLEGALWIDTH ) { + fprintf(pfa_file, "0 %d hsbw\n", g->scaledwidth); + } else { + fprintf(pfa_file, "0 1000 hsbw\n"); + WARNING_2 fprintf(stderr, "glyph %s: width %d seems to be buggy, set to 1000\n", + g->name, g->scaledwidth); + } + +#if 0 + fprintf(pfa_file, "%% contours: "); + for (i = 0; i < g->ncontours; i++) + fprintf(pfa_file, "%s(%d,%d) ", (g->contours[i].direction == DIR_OUTER ? "out" : "in"), + g->contours[i].xofmin, g->contours[i].ymin); + fprintf(pfa_file, "\n"); + + if (g->rymin < 5000) + fprintf(pfa_file, "%d lower%s\n", g->rymin, (g->flatymin ? "flat" : "curve")); + if (g->rymax > -5000) + fprintf(pfa_file, "%d upper%s\n", g->rymax, (g->flatymax ? "flat" : "curve")); +#endif + + if (g->hstems) + for (i = 0; i < g->nhs; i += 2) { + if (g->hstems[i].flags & ST_3) { + fprintf(pfa_file, "%d %d %d %d %d %d hstem3\n", + g->hstems[i].value, + g->hstems[i + 1].value - g->hstems[i].value, + g->hstems[i + 2].value, + g->hstems[i + 3].value - g->hstems[i + 2].value, + g->hstems[i + 4].value, + g->hstems[i + 5].value - g->hstems[i + 4].value + ); + i += 4; + } else { + fprintf(pfa_file, "%d %d hstem\n", g->hstems[i].value, + g->hstems[i + 1].value - g->hstems[i].value); + } + } + + if (g->vstems) + for (i = 0; i < g->nvs; i += 2) { + if (g->vstems[i].flags & ST_3) { + fprintf(pfa_file, "%d %d %d %d %d %d vstem3\n", + g->vstems[i].value, + g->vstems[i + 1].value - g->vstems[i].value, + g->vstems[i + 2].value, + g->vstems[i + 3].value - g->vstems[i + 2].value, + g->vstems[i + 4].value, + g->vstems[i + 5].value - g->vstems[i + 4].value + ); + i += 4; + } else { + fprintf(pfa_file, "%d %d vstem\n", g->vstems[i].value, + g->vstems[i + 1].value - g->vstems[i].value); + } + } + + for (ge = g->entries; ge != 0; ge = ge->next) { + if(g->nsg>0) { + grp=ge->stemid; + if(grp >= 0 && grp != lastgrp) { + fprintf(pfa_file, "%d 4 callsubr\n", grp+g->firstsubr); + lastgrp=grp; + } + } + + switch (ge->type) { + case GE_MOVE: + if (absolute) + fprintf(pfa_file, "%d %d amoveto\n", ge->ix3, ge->iy3); + else + rmoveto(ge->ix3 - x, ge->iy3 - y); + if (0) + fprintf(stderr, "Glyph %s: print moveto(%d, %d)\n", + g->name, ge->ix3, ge->iy3); + x = ge->ix3; + y = ge->iy3; + break; + case GE_LINE: + if (absolute) + fprintf(pfa_file, "%d %d alineto\n", ge->ix3, ge->iy3); + else + rlineto(ge->ix3 - x, ge->iy3 - y); + x = ge->ix3; + y = ge->iy3; + break; + case GE_CURVE: + if (absolute) + fprintf(pfa_file, "%d %d %d %d %d %d arcurveto\n", + ge->ix1, ge->iy1, ge->ix2, ge->iy2, ge->ix3, ge->iy3); + else + rrcurveto(ge->ix1 - x, ge->iy1 - y, + ge->ix2 - ge->ix1, ge->iy2 - ge->iy1, + ge->ix3 - ge->ix2, ge->iy3 - ge->iy2); + x = ge->ix3; + y = ge->iy3; + break; + case GE_PATH: + closepath(); + break; + default: + WARNING_1 fprintf(stderr, "**** Glyph %s: unknown entry type '%c'\n", + g->name, ge->type); + break; + } + } + + fprintf(pfa_file, "endchar } ND\n"); +} + +/* print the subroutines for this glyph, returns the number of them */ +int +print_glyph_subs( + int glyphno, + int startid /* start numbering subroutines from this id */ +) +{ + GLYPH *g; + int i, grp; + + g = &glyph_list[glyphno]; + + if(!hints || !subhints || g->nsg<1) + return 0; + + g->firstsubr=startid; + +#if 0 + fprintf(pfa_file, "%% %s %d\n", g->name, g->nsg); +#endif + for(grp=0; grpnsg; grp++) { + fprintf(pfa_file, "dup %d {\n", startid++); + for(i= (grp==0)? 0 : g->nsbs[grp-1]; insbs[grp]; i++) + fprintf(pfa_file, "\t%d %d %cstem\n", g->sbstems[i].low, + g->sbstems[i].high-g->sbstems[i].low, + g->sbstems[i].isvert ? 'v' : 'h'); + fprintf(pfa_file, "\treturn\n\t} NP\n"); + } + + return g->nsg; +} + +void +print_glyph_metrics( + int code, + int glyphno +) +{ + GLYPH *g; + + g = &glyph_list[glyphno]; + + if(transform) + fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n", + code, g->scaledwidth, g->name, + iscale(g->xMin), iscale(g->yMin), iscale(g->xMax), iscale(g->yMax)); + else + fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n", + code, g->scaledwidth, g->name, + g->xMin, g->yMin, g->xMax, g->yMax); +} + +/* + SB: + An important note about the BlueValues. + + The Adobe documentation says that the maximal width of a Blue zone + is connected to the value of BlueScale, which is by default 0.039625. + The BlueScale value defines, at which point size the overshoot + suppression be disabled. + + The formula for it that is given in the manual is: + + BlueScale=point_size/240, for a 300dpi device + + that makes us wonder what is this 240 standing for. Incidentally + 240=72*1000/300, where 72 is the relation between inches and points, + 1000 is the size of the glyph matrix, and 300dpi is the resolution of + the output device. Knowing that we can recalculate the formula for + the font size in pixels rather than points: + + BlueScale=pixel_size/1000 + + That looks a lot simpler than the original formula, does not it ? + And the limitation about the maximal width of zone also looks + a lot simpler after the transformation: + + max_width < 1000/pixel_size + + that ensures that even at the maximal pixel size when the overshoot + suppression is disabled the zone width will be less than one pixel. + This is important, failure to comply to this limit will result in + really ugly fonts (been there, done that). But knowing the formula + for the pixel width, we see that in fact we can use the maximal width + of 24, not 23 as specified in the manual. + +*/ + +#define MAXBLUEWIDTH (24) + +/* + * Find the indexes of the most frequent values + * in the hystogram, sort them in ascending order, and save which one + * was the best one (if asked). + * Returns the number of values found (may be less than maximal because + * we ignore the zero values) + */ + +#define MAXHYST (2000) /* size of the hystogram */ +#define HYSTBASE 500 + +static int +besthyst( + int *hyst, /* the hystogram */ + int base, /* the base point of the hystogram */ + int *best, /* the array for indexes of best values */ + int nbest, /* its allocated size */ + int width, /* minimal difference between indexes */ + int *bestindp /* returned top point */ +) +{ + unsigned char hused[MAXHYST / 8 + 1]; + int i, max, j, w, last = 0; + int nf = 0; + + width--; + + memset(hused, 0 , sizeof hused); + + max = 1; + for (i = 0; i < nbest && max != 0; i++) { + best[i] = 0; + max = 0; + for (j = 1; j < MAXHYST - 1; j++) { + w = hyst[j]; + + if (w > max && (hused[j>>3] & (1 << (j & 0x07))) == 0) { + best[i] = j; + max = w; + } + } + if (max != 0) { + if (max < last/2) { + /* do not pick the too low values */ + break; + } + for (j = best[i] - width; j <= best[i] + width; j++) { + if (j >= 0 && j < MAXHYST) + hused[j >> 3] |= (1 << (j & 0x07)); + } + last = max; + best[i] -= base; + nf = i + 1; + } + } + + if (bestindp) + *bestindp = best[0]; + + /* sort the indexes in ascending order */ + for (i = 0; i < nf; i++) { + for (j = i + 1; j < nf; j++) + if (best[j] < best[i]) { + w = best[i]; + best[i] = best[j]; + best[j] = w; + } + } + + return nf; +} + +/* + * Find the next best Blue zone in the hystogram. + * Return the weight of the found zone. + */ + +static int +bestblue( + short *zhyst, /* the zones hystogram */ + short *physt, /* the points hystogram */ + short *ozhyst, /* the other zones hystogram */ + int *bluetab /* where to put the found zone */ +) +{ + int i, j, w, max, ind, first, last; + + /* find the highest point in the zones hystogram */ + /* if we have a plateau, take its center */ + /* if we have multiple peaks, take the first one */ + + max = -1; + first = last = -10; + for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { + w = zhyst[i]; + if (w > max) { + first = last = i; + max = w; + } else if (w == max) { + if (last == i - 1) + last = i; + } + } + ind = (first + last) / 2; + + if (max == 0) /* no zones left */ + return 0; + + /* now we reuse `first' and `last' as inclusive borders of the zone */ + first = ind; + last = ind + (MAXBLUEWIDTH - 1); + + /* our maximal width is far too big, so we try to make it narrower */ + w = max; + j = (w & 1); /* a pseudo-random bit */ + while (1) { + while (physt[first] == 0) + first++; + while (physt[last] == 0) + last--; + if (last - first < (MAXBLUEWIDTH * 2 / 3) || (max - w) * 10 > max) + break; + + if (physt[first] < physt[last] + || physt[first] == physt[last] && j) { + if (physt[first] * 20 > w) /* if weight is >5%, + * stop */ + break; + w -= physt[first]; + first++; + j = 0; + } else { + if (physt[last] * 20 > w) /* if weight is >5%, + * stop */ + break; + w -= physt[last]; + last--; + j = 1; + } + } + + /* save our zone */ + bluetab[0] = first - HYSTBASE; + bluetab[1] = last - HYSTBASE; + + /* invalidate all the zones overlapping with this one */ + /* the constant of 2 is determined by the default value of BlueFuzz */ + for (i = first - (MAXBLUEWIDTH - 1) - 2; i <= last + 2; i++) + if (i >= 0 && i < MAXHYST) { + zhyst[i] = 0; + ozhyst[i] = 0; + } + return w; +} + +/* + * Try to find the Blue Values, bounding box and italic angle + */ + +void +findblues(void) +{ + /* hystograms for upper and lower zones */ + short hystl[MAXHYST]; + short hystu[MAXHYST]; + short zuhyst[MAXHYST]; + short zlhyst[MAXHYST]; + int nchars; + int i, j, k, w, max; + GENTRY *ge; + GLYPH *g; + double ang; + + /* find the lowest and highest points of glyphs */ + /* and by the way build the values for FontBBox */ + /* and build the hystogram for the ItalicAngle */ + + /* re-use hystl for the hystogram of italic angle */ + + bbox[0] = bbox[1] = 5000; + bbox[2] = bbox[3] = -5000; + + for (i = 0; i < MAXHYST; i++) + hystl[i] = 0; + + nchars = 0; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if (g->flags & GF_USED) { + nchars++; + + g->rymin = 5000; + g->rymax = -5000; + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_LINE) { + + j = ge->iy3 - ge->prev->iy3; + k = ge->ix3 - ge->prev->ix3; + if (j > 0) + ang = atan2(-k, j) * 180.0 / M_PI; + else + ang = atan2(k, -j) * 180.0 / M_PI; + + k /= 100; + j /= 100; + if (ang > -45.0 && ang < 45.0) { + /* + * be careful to not overflow + * the counter + */ + hystl[HYSTBASE + (int) (ang * 10.0)] += (k * k + j * j) / 4; + } + if (ge->iy3 == ge->prev->iy3) { + if (ge->iy3 <= g->rymin) { + g->rymin = ge->iy3; + g->flatymin = 1; + } + if (ge->iy3 >= g->rymax) { + g->rymax = ge->iy3; + g->flatymax = 1; + } + } else { + if (ge->iy3 < g->rymin) { + g->rymin = ge->iy3; + g->flatymin = 0; + } + if (ge->iy3 > g->rymax) { + g->rymax = ge->iy3; + g->flatymax = 0; + } + } + } else if (ge->type == GE_CURVE) { + if (ge->iy3 < g->rymin) { + g->rymin = ge->iy3; + g->flatymin = 0; + } + if (ge->iy3 > g->rymax) { + g->rymax = ge->iy3; + g->flatymax = 0; + } + } + if (ge->type == GE_LINE || ge->type == GE_CURVE) { + if (ge->ix3 < bbox[0]) + bbox[0] = ge->ix3; + if (ge->ix3 > bbox[2]) + bbox[2] = ge->ix3; + if (ge->iy3 < bbox[1]) + bbox[1] = ge->iy3; + if (ge->iy3 > bbox[3]) + bbox[3] = ge->iy3; + } + } + } + } + + /* get the most popular angle */ + max = 0; + w = 0; + for (i = 0; i < MAXHYST; i++) { + if (hystl[i] > w) { + w = hystl[i]; + max = i; + } + } + ang = (double) (max - HYSTBASE) / 10.0; + WARNING_2 fprintf(stderr, "Guessed italic angle: %f\n", ang); + if (italic_angle == 0.0) + italic_angle = ang; + + /* build the hystogram of the lower points */ + for (i = 0; i < MAXHYST; i++) + hystl[i] = 0; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if ((g->flags & GF_USED) + && g->rymin + HYSTBASE >= 0 && g->rymin < MAXHYST - HYSTBASE) { + hystl[g->rymin + HYSTBASE]++; + } + } + + /* build the hystogram of the upper points */ + for (i = 0; i < MAXHYST; i++) + hystu[i] = 0; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if ((g->flags & GF_USED) + && g->rymax + HYSTBASE >= 0 && g->rymax < MAXHYST - HYSTBASE) { + hystu[g->rymax + HYSTBASE]++; + } + } + + /* build the hystogram of all the possible lower zones with max width */ + for (i = 0; i < MAXHYST; i++) + zlhyst[i] = 0; + + for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { + for (j = 0; j < MAXBLUEWIDTH; j++) + zlhyst[i] += hystl[i + j]; + } + + /* build the hystogram of all the possible upper zones with max width */ + for (i = 0; i < MAXHYST; i++) + zuhyst[i] = 0; + + for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { + for (j = 0; j < MAXBLUEWIDTH; j++) + zuhyst[i] += hystu[i + j]; + } + + /* find the baseline */ + w = bestblue(zlhyst, hystl, zuhyst, &bluevalues[0]); + if (0) + fprintf(stderr, "BaselineBlue zone %d%% %d...%d\n", w * 100 / nchars, + bluevalues[0], bluevalues[1]); + + if (w == 0) /* no baseline, something weird */ + return; + + /* find the upper zones */ + for (nblues = 2; nblues < 14; nblues += 2) { + w = bestblue(zuhyst, hystu, zlhyst, &bluevalues[nblues]); + + if (0) + fprintf(stderr, "Blue zone %d%% %d...%d\n", w * 100 / nchars, + bluevalues[nblues], bluevalues[nblues+1]); + + if (w * 20 < nchars) + break; /* don't save this zone */ + } + + /* find the lower zones */ + for (notherb = 0; notherb < 10; notherb += 2) { + w = bestblue(zlhyst, hystl, zuhyst, &otherblues[notherb]); + + if (0) + fprintf(stderr, "OtherBlue zone %d%% %d...%d\n", w * 100 / nchars, + otherblues[notherb], otherblues[notherb+1]); + + + if (w * 20 < nchars) + break; /* don't save this zone */ + } + +} + +/* + * Find the actual width of the glyph and modify the + * description to reflect it. Not guaranteed to do + * any good, may make character spacing too wide. + */ + +void +docorrectwidth(void) +{ + int i; + GENTRY *ge; + GLYPH *g; + int xmin, xmax; + int maxwidth, minsp; + + /* enforce this minimal spacing, + * we limit the amount of the enforced spacing to avoid + * spacing the bold wonts too widely + */ + minsp = (stdhw>60 || stdhw<10)? 60 : stdhw; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + g->oldwidth=g->scaledwidth; /* save the old width, will need for AFM */ + + if (correctwidth && g->flags & GF_USED) { + xmin = 5000; + xmax = -5000; + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_LINE && ge->type != GE_CURVE) + continue; + + if (ge->ix3 <= xmin) { + xmin = ge->ix3; + } + if (ge->ix3 >= xmax) { + xmax = ge->ix3; + } + } + + maxwidth=xmax+minsp; + if( g->scaledwidth < maxwidth ) { + g->scaledwidth = maxwidth; + WARNING_3 fprintf(stderr, "glyph %s: extended from %d to %d\n", + g->name, g->oldwidth, g->scaledwidth ); + } + } + } + +} + +/* + * Try to find the typical stem widths + */ + +void +stemstatistics(void) +{ +#define MINDIST 10 /* minimal distance between the widths */ + int hyst[MAXHYST+MINDIST*2]; + int best[12]; + int i, j, k, w; + int nchars; + int ns; + STEM *s; + GLYPH *g; + + /* start with typical stem width */ + + nchars=0; + + /* build the hystogram of horizontal stem widths */ + memset(hyst, 0, sizeof hyst); + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if (g->flags & GF_USED) { + nchars++; + s = g->hstems; + for (j = 0; j < g->nhs; j += 2) { + if ((s[j].flags | s[j + 1].flags) & ST_END) + continue; + w = s[j + 1].value - s[j].value+1; + if(w==20) /* split stems should not be counted */ + continue; + if (w > 0 && w < MAXHYST - 1) { + /* + * handle some fuzz present in + * converted fonts + */ + hyst[w+MINDIST] += MINDIST-1; + for(k=1; kflags & GF_USED) { + s = g->vstems; + for (j = 0; j < g->nvs; j += 2) { + if ((s[j].flags | s[j + 1].flags) & ST_END) + continue; + w = s[j + 1].value - s[j].value+1; + if (w > 0 && w < MAXHYST - 1) { + /* + * handle some fuzz present in + * converted fonts + */ + hyst[w+MINDIST] += MINDIST-1; + for(k=1; knext) { + if(ge->type == GE_LINE || ge->type == GE_CURVE) { + if (ISDBG(REVERSAL)) + fprintf(stderr, "reverse path 0x%x <- 0x%x, 0x%x\n", ge, ge->prev, ge->bkwd); + + /* cut out the path itself */ + pge = ge->prev; /* GE_MOVE */ + if (pge == 0) { + fprintf(stderr, "**! No MOVE before line !!! Fatal. ****\n"); + exit(1); + } + nge = ge->bkwd->next; /* GE_PATH */ + pge->next = nge; + nge->prev = pge; + ge->bkwd->next = 0; /* mark end of chain */ + + /* remember the starting point */ + if(ge->flags & GEF_FLOAT) { + flast[0] = pge->fx3; + flast[1] = pge->fy3; + } else { + ilast[0] = pge->ix3; + ilast[1] = pge->iy3; + } + + /* then reinsert them in backwards order */ + for(cur = ge; cur != 0; cur = next ) { + next = cur->next; /* or addgeafter() will screw it up */ + if(cur->flags & GEF_FLOAT) { + for(i=0; i<2; i++) { + /* reverse the direction of path element */ + f = cur->fpoints[i][0]; + cur->fpoints[i][0] = cur->fpoints[i][1]; + cur->fpoints[i][1] = f; + f = flast[i]; + flast[i] = cur->fpoints[i][2]; + cur->fpoints[i][2] = f; + } + } else { + for(i=0; i<2; i++) { + /* reverse the direction of path element */ + n = cur->ipoints[i][0]; + cur->ipoints[i][0] = cur->ipoints[i][1]; + cur->ipoints[i][1] = n; + n = ilast[i]; + ilast[i] = cur->ipoints[i][2]; + cur->ipoints[i][2] = n; + } + } + addgeafter(pge, cur); + } + + /* restore the starting point */ + if(ge->flags & GEF_FLOAT) { + pge->fx3 = flast[0]; + pge->fy3 = flast[1]; + } else { + pge->ix3 = ilast[0]; + pge->iy3 = ilast[1]; + } + + ge = nge; + } + + } +} + +void +reversepaths( + GLYPH * g +) +{ + reversepathsfromto(g->entries, NULL); +} + +/* add a kerning pair information, scales the value */ + +void +addkernpair( + unsigned id1, + unsigned id2, + int unscval +) +{ + static unsigned char *bits = 0; + static int lastid; + GLYPH *g = &glyph_list[id1]; + int i, n; + struct kern *p; + + if(unscval == 0 || id1 >= numglyphs || id2 >= numglyphs) + return; + + if( (glyph_list[id1].flags & GF_USED)==0 + || (glyph_list[id2].flags & GF_USED)==0 ) + return; + + if(bits == 0) { + bits = calloc( BITMAP_BYTES(numglyphs), 1); + if (bits == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + lastid = id1; + } + + if(lastid != id1) { + /* refill the bitmap cache */ + memset(bits, 0,BITMAP_BYTES(numglyphs)); + p = g->kern; + for(i=g->kerncount; i>0; i--) { + n = (p++)->id; + SET_BITMAP(bits, n); + } + lastid = id1; + } + + if(IS_BITMAP(bits, id2)) + return; /* duplicate */ + + if(g->kerncount <= g->kernalloc) { + g->kernalloc += 8; + p = realloc(g->kern, sizeof(struct kern) * g->kernalloc); + if(p == 0) { + fprintf (stderr, "** realloc failed, kerning data will be incomplete\n"); + } + g->kern = p; + } + + SET_BITMAP(bits, id2); + p = &g->kern[g->kerncount]; + p->id = id2; + p->val = iscale(unscval) - (g->scaledwidth - g->oldwidth); + g->kerncount++; + kerning_pairs++; +} + +/* print out the kerning information */ + +void +print_kerning( + FILE *afm_file +) +{ + int i, j, n; + GLYPH *g; + struct kern *p; + + if( kerning_pairs == 0 ) + return; + + fprintf(afm_file, "StartKernData\n"); + fprintf(afm_file, "StartKernPairs %hd\n", kerning_pairs); + + for(i=0; iflags & GF_USED) ==0) + continue; + p = g->kern; + for(j=g->kerncount; j>0; j--, p++) { + fprintf(afm_file, "KPX %s %s %d\n", g->name, + glyph_list[ p->id ].name, p->val ); + } + } + + fprintf(afm_file, "EndKernPairs\n"); + fprintf(afm_file, "EndKernData\n"); +} + + +#if 0 + +/* +** This function is commented out because the information +** collected by it is not used anywhere else yet. Now +** it only collects the directions of contours. And the +** direction of contours gets fixed already in draw_glyf(). +** +*********************************************** +** +** Here we expect that the paths are already closed. +** We also expect that the contours do not intersect +** and that curves doesn't cross any border of quadrant. +** +** Find which contours go inside which and what is +** their proper direction. Then fix the direction +** to make it right. +** +*/ + +#define MAXCONT 1000 + +void +fixcontours( + GLYPH * g +) +{ + CONTOUR cont[MAXCONT]; + short ymax[MAXCONT]; /* the highest point */ + short xofmax[MAXCONT]; /* X-coordinate of any point + * at ymax */ + short ymin[MAXCONT]; /* the lowest point */ + short xofmin[MAXCONT]; /* X-coordinate of any point + * at ymin */ + short count[MAXCONT]; /* count of lines */ + char dir[MAXCONT]; /* in which direction they must go */ + GENTRY *start[MAXCONT], *minptr[MAXCONT], *maxptr[MAXCONT]; + int ncont; + int i; + int dx1, dy1, dx2, dy2; + GENTRY *ge, *nge; + + /* find the contours and their most upper/lower points */ + ncont = 0; + ymax[0] = -5000; + ymin[0] = 5000; + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_LINE || ge->type == GE_CURVE) { + if (ge->iy3 > ymax[ncont]) { + ymax[ncont] = ge->iy3; + xofmax[ncont] = ge->ix3; + maxptr[ncont] = ge; + } + if (ge->iy3 < ymin[ncont]) { + ymin[ncont] = ge->iy3; + xofmin[ncont] = ge->ix3; + minptr[ncont] = ge; + } + } + if (ge->frwd != ge->next) { + start[ncont++] = ge->frwd; + ymax[ncont] = -5000; + ymin[ncont] = 5000; + } + } + + /* determine the directions of contours */ + for (i = 0; i < ncont; i++) { + ge = minptr[i]; + nge = ge->frwd; + + if (ge->type == GE_CURVE) { + dx1 = ge->ix3 - ge->ix2; + dy1 = ge->iy3 - ge->iy2; + + if (dx1 == 0 && dy1 == 0) { /* a pathological case */ + dx1 = ge->ix3 - ge->ix1; + dy1 = ge->iy3 - ge->iy1; + } + if (dx1 == 0 && dy1 == 0) { /* a more pathological + * case */ + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + } + } else { + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + } + if (nge->type == GE_CURVE) { + dx2 = ge->ix3 - nge->ix1; + dy2 = ge->iy3 - nge->iy1; + if (dx1 == 0 && dy1 == 0) { /* a pathological case */ + dx2 = ge->ix3 - nge->ix2; + dy2 = ge->iy3 - nge->iy2; + } + if (dx1 == 0 && dy1 == 0) { /* a more pathological + * case */ + dx2 = ge->ix3 - nge->ix3; + dy2 = ge->iy3 - nge->iy3; + } + } else { + dx2 = ge->ix3 - nge->ix3; + dy2 = ge->iy3 - nge->iy3; + } + + /* compare angles */ + cont[i].direction = DIR_INNER; + if (dy1 == 0) { + if (dx1 < 0) + cont[i].direction = DIR_OUTER; + } else if (dy2 == 0) { + if (dx2 > 0) + cont[i].direction = DIR_OUTER; + } else if (dx2 * dy1 < dx1 * dy2) + cont[i].direction = DIR_OUTER; + + cont[i].ymin = ymin[i]; + cont[i].xofmin = xofmin[i]; + } + + /* save the information that may be needed further */ + g->ncontours = ncont; + if (ncont > 0) { + g->contours = malloc(sizeof(CONTOUR) * ncont); + if (g->contours == 0) { + fprintf(stderr, "***** Memory allocation error *****\n"); + exit(255); + } + memcpy(g->contours, cont, sizeof(CONTOUR) * ncont); + } +} + +#endif + +/* + * + */ + Index: xc/extras/ttf2pt1/pt1.h =================================================================== RCS file: xc/extras/ttf2pt1/pt1.h diff -N xc/extras/ttf2pt1/pt1.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/pt1.h 13 Apr 2004 02:45:06 -0000 @@ -0,0 +1,257 @@ +/* + * see COPYRIGHT + */ + + +/* glyph entry, one drawing command */ +typedef struct gentry { + /* this list links all GENTRYs of a GLYPH sequentially */ + struct gentry *next; /* double linked list */ + struct gentry *prev; + + /* this list links all GENTRYs of one contour - + * of types GE_LINE and GE_CURVE only + * bkwd is also reused: in the very first entry (normally + * of type GE_MOVE) it points to g->entries + */ + struct gentry *cntr[2]; /* double-linked circular list */ +/* convenience handles */ +#define bkwd cntr[0] +#define frwd cntr[1] + + /* various extended structures used at some stage of transformation */ + void *ext; + + union { + struct { + int val[2][3]; /* integer values */ + } i; + struct { + double val[2][3]; /* floating values */ + } f; + } points; /* absolute values, NOT deltas */ +/* convenience handles */ +#define ipoints points.i.val +#define fpoints points.f.val +#define ixn ipoints[0] +#define iyn ipoints[1] +#define fxn fpoints[0] +#define fyn fpoints[1] +#define ix1 ixn[0] +#define ix2 ixn[1] +#define ix3 ixn[2] +#define iy1 iyn[0] +#define iy2 iyn[1] +#define iy3 iyn[2] +#define fx1 fxn[0] +#define fx2 fxn[1] +#define fx3 fxn[2] +#define fy1 fyn[0] +#define fy2 fyn[1] +#define fy3 fyn[2] + + char flags; +#define GEF_FLOAT 0x02 /* entry contains floating point data */ +#define GEF_LINE 0x04 /* entry looks like a line even if it's a curve */ + + unsigned char dir; /* used to temporarily store the values for + * the directions of the ends of curves */ +/* front end */ +#define CVDIR_FUP 0x02 /* goes over the line connecting the ends */ +#define CVDIR_FEQUAL 0x01 /* coincides with the line connecting the + * ends */ +#define CVDIR_FDOWN 0x00 /* goes under the line connecting the ends */ +#define CVDIR_FRONT 0x0F /* mask of all front directions */ +/* rear end */ +#define CVDIR_RSAME 0x30 /* is the same as for the front end */ +#define CVDIR_RUP 0x20 /* goes over the line connecting the ends */ +#define CVDIR_REQUAL 0x10 /* coincides with the line connecting the + * ends */ +#define CVDIR_RDOWN 0x00 /* goes under the line connecting the ends */ +#define CVDIR_REAR 0xF0 /* mask of all rear directions */ + + signed char stemid; /* connection to the substituted stem group */ + char type; +#define GE_HSBW 'B' +#define GE_MOVE 'M' +#define GE_LINE 'L' +#define GE_CURVE 'C' +#define GE_PATH 'P' + + /* indexes of the points to be used for calculation of the tangents */ + signed char ftg; /* front tangent */ + signed char rtg; /* rear tangent, -1 means "idx 2 of the previous entry" */ +} GENTRY; + +/* stem structure, describes one [hv]stem */ +/* acually, it describes one border of a stem */ +/* the whole stem is a pair of these structures */ + +typedef struct stem { + short value; /* value of X or Y coordinate */ + short origin; /* point of origin for curve stems */ + GENTRY *ge; /* entry that has (value, origin) as its first dot */ + /* also for all the stems the couple (value, origin) + * is used to determine whether a stem is relevant for a + * line, it's considered revelant if this tuple is + * equal to any of the ends of the line. + * ge is also used to resolve ambiguity if there is more than + * one line going through certain pointi, it is used to + * distinguish these lines. + */ + + short from, to; /* values of other coordinate between + * which this stem is valid */ + + short flags; + /* ordering of ST_END, ST_FLAT, ST_ZONE is IMPORTANT for sorting */ +#define ST_END 0x01 /* end of line, lowest priority */ +#define ST_FLAT 0x02 /* stem is defined by a flat line, not a + * curve */ +#define ST_ZONE 0x04 /* pseudo-stem, the limit of a blue zone */ +#define ST_UP 0x08 /* the black area is to up or right from + * value */ +#define ST_3 0x20 /* first stem of [hv]stem3 */ +#define ST_BLUE 0x40 /* stem is in blue zone */ +#define ST_TOPZONE 0x80 /* 1 - top zone, 0 - bottom zone */ +#define ST_VERT 0x100 /* vertical stem (used in substitutions) */ +} STEM; + +#define MAX_STEMS 2000 /* we can't have more stems than path + * elements (or hope so) */ +#define NSTEMGRP 50 /* maximal number of the substituted stem groups */ + +/* structure for economical representation of the + * substituted stems + */ + +typedef struct stembounds { + short low; /* low bound */ + short high; /* high bound */ + char isvert; /* 1 - vertical, 0 - horizontal */ + char already; /* temp. flag: is aleready included */ +} STEMBOUNDS; + +struct kern { + unsigned id; /* ID of the second glyph */ + int val; /* kerning value */ +}; + +typedef struct contour { + short ymin, xofmin; + short inside; /* inside which contour */ + char direction; +#define DIR_OUTER 1 +#define DIR_INNER 0 +} CONTOUR; + +typedef struct glyph { + int char_no;/* Encoding of glyph */ + int orig_code;/* code of glyph in the font's original encoding */ + char *name; /* Postscript name of glyph */ + int xMin, yMin, xMax, yMax; /* values from TTF dictionary */ + int lsb; /* left sidebearing */ + int ttf_pathlen; /* total length of TTF paths */ + short width; + short flags; +#define GF_USED 0x0001 /* whether is this glyph used in T1 font */ +#define GF_FLOAT 0x0002 /* thys glyph contains floating point entries */ + + GENTRY *entries;/* doube linked list of entries */ + GENTRY *lastentry; /* the last inserted entry */ + GENTRY *path; /* beggining of the last path */ + int oldwidth; /* actually also scaled */ + int scaledwidth; +#define MAXLEGALWIDTH 10000 + + struct kern *kern; /* kerning data */ + int kerncount; /* number of kerning pairs */ + int kernalloc; /* for how many pairs we have space */ + + STEM *hstems; /* global horiz. and vert. stems */ + STEM *vstems; + int nhs, nvs; /* numbers of stems */ + + STEMBOUNDS *sbstems; /* substituted stems for all the groups */ + short *nsbs; /* indexes of the group ends in the common array */ + int nsg; /* actual number of the stem groups */ + int firstsubr; /* first substistuted stems subroutine number */ + + CONTOUR *contours; /* it is not used now */ + int ncontours; + + int rymin, rymax; /* real values */ + /* do we have flat surfaces on top/bottom */ + char flatymin, flatymax; + +} GLYPH; + +/* description of a dot for calculation of its distance to a curve */ + +struct dot_dist { + double p[2 /*X,Y*/]; /* coordinates of a dot */ + double dist2; /* squared distance from the dot to the curve */ + short seg; /* the closest segment of the curve */ +}; + +extern int stdhw, stdvw; /* dominant stems widths */ +extern int stemsnaph[12], stemsnapv[12]; /* most typical stem width */ + +extern int bluevalues[14]; +extern int nblues; +extern int otherblues[10]; +extern int notherb; +extern int bbox[4]; /* the FontBBox array */ +extern double italic_angle; + +extern GLYPH *glyph_list; +extern int encoding[]; /* inverse of glyph[].char_no */ + +/* prototypes of functions */ +void rmoveto( int dx, int dy); +void rlineto( int dx, int dy); +void rrcurveto( int dx1, int dy1, int dx2, int dy2, int dx3, int dy3); +void assertpath( GENTRY * from, char *file, int line, char *name); + +void fg_rmoveto( GLYPH * g, double x, double y); +void ig_rmoveto( GLYPH * g, int x, int y); +void fg_rlineto( GLYPH * g, double x, double y); +void ig_rlineto( GLYPH * g, int x, int y); +void fg_rrcurveto( GLYPH * g, double x1, double y1, + double x2, double y2, double x3, double y3); +void ig_rrcurveto( GLYPH * g, int x1, int y1, + int x2, int y2, int x3, int y3); +void g_closepath( GLYPH * g); + +void pathtoint( GLYPH *g); +void ffixquadrants( GLYPH *g); +void flattencurves( GLYPH * g); +int checkcv( GENTRY * ge, int dx, int dy); +void iclosepaths( GLYPH * g); +void fclosepaths( GLYPH * g); +void smoothjoints( GLYPH * g); +void buildstems( GLYPH * g); +void fstraighten( GLYPH * g); +void istraighten( GLYPH * g, int zigonly); +void isplitzigzags( GLYPH * g); +void fsplitzigzags( GLYPH * g); +void fforceconcise( GLYPH * g); +void iforceconcise( GLYPH * g); +void reversepathsfromto( GENTRY * from, GENTRY * to); +void reversepaths( GLYPH * g); +void dumppaths( GLYPH * g, GENTRY *start, GENTRY *end); +void print_glyph( int glyphno); +int print_glyph_subs( int glyphno, int startid); +void print_glyph_metrics( int code, int glyphno); +void findblues(void); +void stemstatistics(void); +void docorrectwidth(void); +void addkernpair( unsigned id1, unsigned id2, int unscval); +void print_kerning( FILE *afm_file); + +int fcrossrayscv( double curve[4][2], double *max1, double *max2); +int fcrossraysge( GENTRY *ge1, GENTRY *ge2, double *max1, double *max2, + double crossdot[2][2]); +double fdotsegdist2( double seg[2][2], double dot[2]); +double fdotcurvdist2( double curve[4][2], struct dot_dist *dots, int ndots, double *maxp); +void fapproxcurve( double cv[4][2], struct dot_dist *dots, int ndots); Index: xc/extras/ttf2pt1/runt1asm.c =================================================================== RCS file: xc/extras/ttf2pt1/runt1asm.c diff -N xc/extras/ttf2pt1/runt1asm.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/runt1asm.c 13 Apr 2004 02:45:06 -0000 @@ -0,0 +1,61 @@ +/* + * Wrap-around code to either compile in t1asm or call it externally + * + * Copyright (C) 2000 by Sergey Babkin + * Copyright (C) 2000 by The TTF2PT1 Project + * + * See COPYRIGHT for full license + */ + +#ifdef EXTERNAL_T1ASM + +#include +#include + +FILE *ifp; +FILE *ofp; + +int +runt1asm( + int pfbflag +) +{ + char *cmd; + int id, od; + int error; + + /* first make a copy in case some of then is already stdin/stdout */ + if(( id = dup(fileno(ifp)) )<0) { + perror("** Re-opening input file for t1asm"); + exit(1); + } + if(( od = dup(fileno(ofp)) )<0) { + perror("** Re-opening output file for t1asm"); + exit(1); + } + fclose(ifp); fclose(ofp); + close(0); + if(( dup(id) )!=0) { + perror("** Re-directing input file for t1asm"); + exit(1); + } + close(1); + if(( dup(od) )!=1) { + perror("** Re-directing output file for t1asm"); + exit(1); + } + close(id); close(od); + + if(pfbflag) + error = execlp("t1asm", "t1asm", "-b", NULL); + else + error = execlp("t1asm", "t1asm", NULL); + + perror("** Calling t1asm"); + + exit(1); +} + +#else +# include "t1asm.c" +#endif Index: xc/extras/ttf2pt1/t1asm.c =================================================================== RCS file: xc/extras/ttf2pt1/t1asm.c diff -N xc/extras/ttf2pt1/t1asm.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/t1asm.c 13 Apr 2004 02:45:07 -0000 @@ -0,0 +1,606 @@ +/* t1asm + * + * This program `assembles' Adobe Type-1 font programs in pseudo-PostScript + * form into either PFB or PFA format. The human readable/editable input is + * charstring- and eexec-encrypted as specified in the `Adobe Type 1 Font + * Format' version 1.1 (the `black book'). There is a companion program, + * t1disasm, which `disassembles' PFB and PFA files into a pseudo-PostScript + * file. + * + * Copyright (c) 1992 by I. Lee Hetherington, all rights reserved. + * + * Permission is hereby granted to use, modify, and distribute this program + * for any purpose provided this copyright notice and the one below remain + * intact. + * + * I. Lee Hetherington (ilh@lcs.mit.edu) + * + * Revision 1.2 92/05/22 11:54:45 ilh + * Fixed bug where integers larger than 32000 could not be encoded in + * charstrings. Now integer range is correct for four-byte + * twos-complement integers: -(1<<31) <= i <= (1<<31)-1. Bug detected by + * Piet Tutelaers (rcpt@urc.tue.nl). + * + * Revision 1.1 92/05/22 11:48:46 ilh + * initial version + * + * Ported to Microsoft C/C++ Compiler and MS-DOS operating system by + * Kai-Uwe Herbing (herbing@netmbx.netmbx.de) on June 12, 1992. Code + * specific to the MS-DOS version is encapsulated with #ifdef _MSDOS + * ... #endif, where _MSDOS is an identifier, which is automatically + * defined, if you compile with the Microsoft C/C++ Compiler. + * + */ + +#ifndef lint +static char copyright[] = + "@(#) Copyright (c) 1992 by I. Lee Hetherington, all rights reserved."; +#ifdef _MSDOS +static char portnotice[] = + "@(#) Ported to MS-DOS by Kai-Uwe Herbing (herbing@netmbx.netmbx.de)."; +#endif +#endif + +/* Note: this is ANSI C. */ + +#ifdef _MSDOS + #include + #include + #include +#endif +#include +#include +#include +#include +#include + +#ifdef WINDOWS +# ifdef STANDALONE +# define WINDOWS_FUNCTIONS +# include "windows.h" +# endif +#endif + +/* int32 must be at least 32-bit and uint16 must be at least 16-bit */ +#ifndef AIXV3 +#if INT_MAX >= 0x7FFFFFFFUL +typedef int int32; +#else +typedef long int32; +#endif +#endif /* !AIXV3 */ +#if USHRT_MAX >= 0xFFFFUL +typedef unsigned short uint16; +#else +typedef unsigned int uint16; +#endif + +#define LINESIZE 256 + +#define MAXBLOCKLEN ((1L<<17)-6) +#define MINBLOCKLEN ((1L<<8)-6) + +#define MARKER 128 +#define ASCII 1 +#define BINARY 2 +#define DONE 3 + +typedef unsigned char byte; + +/* must be visible from outside */ +FILE *ifp; +FILE *ofp; + +/* flags */ +static int pfb = 0; +static int active = 0; +static int start_charstring = 0; +static int in_eexec = 0; + +static char line[LINESIZE]; + +/* lenIV and charstring start command */ +static int lenIV = 4; +static char cs_start[10]; + +/* for charstring buffering */ +static byte charstring_buf[65535]; +static byte *charstring_bp; + +/* for PFB block buffering */ +static byte blockbuf[MAXBLOCKLEN]; +static int32 blocklen = MAXBLOCKLEN; +static int32 blockpos = -1; +static int blocktyp = ASCII; + +/* decryption stuff */ +static uint16 er, cr; +static uint16 c1 = 52845, c2 = 22719; + +/* table of charstring commands */ +static struct command { + char *name; + int one, two; +} command_table[] = { + { "callothersubr", 12, 16 }, + { "callsubr", 10, -1 }, + { "closepath", 9, -1 }, + { "div", 12, 12 }, + { "dotsection", 12, 0 }, + { "endchar", 14, -1 }, + { "hlineto", 6, -1 }, + { "hmoveto", 22, -1 }, + { "hsbw", 13, -1 }, + { "hstem", 1, -1 }, + { "hstem3", 12, 2 }, + { "hvcurveto", 31, -1 }, + { "pop", 12, 17 }, + { "return", 11, -1 }, + { "rlineto", 5, -1 }, + { "rmoveto", 21, -1 }, + { "rrcurveto", 8, -1 }, + { "sbw", 12, 7 }, + { "seac", 12, 6 }, + { "setcurrentpoint", 12, 33 }, + { "vhcurveto", 30, -1 }, + { "vlineto", 7, -1 }, + { "vmoveto", 4, -1 }, + { "vstem", 3, -1 }, + { "vstem3", 12, 1 }, +}; /* alphabetical */ + +/* Two separate encryption functions because eexec and charstring encryption + must proceed in parallel. */ + +static byte eencrypt(byte plain) +{ + byte cipher; + + cipher = (byte) (plain ^ (er >> 8)); + er = (uint16) ((cipher + er) * c1 + c2); + return cipher; +} + +static byte cencrypt(byte plain) +{ + byte cipher; + + cipher = (byte) (plain ^ (cr >> 8)); + cr = (uint16) ((cipher + cr) * c1 + c2); + return cipher; +} + +/* This function flushes a buffered PFB block. */ + +static void output_block() +{ + int32 i; + + /* output four-byte block length */ + fputc((int) (blockpos & 0xff), ofp); + fputc((int) ((blockpos >> 8) & 0xff), ofp); + fputc((int) ((blockpos >> 16) & 0xff), ofp); + fputc((int) ((blockpos >> 24) & 0xff), ofp); + + /* output block data */ + for (i = 0; i < blockpos; i++) + fputc(blockbuf[i], ofp); + + /* mark block buffer empty and uninitialized */ + blockpos = -1; +} + +/* This function outputs a single byte. If output is in PFB format then output + is buffered through blockbuf[]. If output is in PFA format, then output + will be hexadecimal if in_eexec is set, ASCII otherwise. */ + +static void output_byte(byte b) +{ + static char *hexchar = "0123456789ABCDEF"; + static int hexcol = 0; + + if (pfb) { + /* PFB */ + if (blockpos < 0) { + fputc(MARKER, ofp); + fputc(blocktyp, ofp); + blockpos = 0; + } + blockbuf[blockpos++] = b; + if (blockpos == blocklen) + output_block(); + } else { + /* PFA */ + if (in_eexec) { + /* trim hexadecimal lines to 64 columns */ + if (hexcol >= 64) { + fputc('\n', ofp); + hexcol = 0; + } + fputc(hexchar[(b >> 4) & 0xf], ofp); + fputc(hexchar[b & 0xf], ofp); + hexcol += 2; + } else { + fputc(b, ofp); + } + } +} + +/* This function outputs a byte through possible eexec encryption. */ + +static void eexec_byte(byte b) +{ + if (in_eexec) + output_byte(eencrypt(b)); + else + output_byte(b); +} + +/* This function outputs a null-terminated string through possible eexec + encryption. */ + +static void eexec_string(char *string) +{ + while (*string) + eexec_byte((byte) *string++); +} + +/* This function gets ready for the eexec-encrypted data. If output is in + PFB format then flush current ASCII block and get ready for binary block. + We start encryption with four random (zero) bytes. */ + +static void eexec_start() +{ + eexec_string(line); + if (pfb) { + output_block(); + blocktyp = BINARY; + } + + in_eexec = 1; + er = 55665; + eexec_byte(0); + eexec_byte(0); + eexec_byte(0); + eexec_byte(0); +} + +/* This function wraps-up the eexec-encrypted data. + If output is in PFB format then this entails flushing binary block and + starting an ASCII block. */ + +static void eexec_end() +{ + int i, j; + + if (pfb) { + output_block(); + blocktyp = ASCII; + } else { + fputc('\n', ofp); + } + in_eexec = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 64; j++) + eexec_byte('0'); + eexec_byte('\n'); + } +#if 0 + eexec_string("cleartomark\n"); +#endif +} + +/* This function writes ASCII trailer. + If output is in PFB format then this entails flushing binary block and + starting an ASCII block. */ + +static void file_end() +{ + if (pfb) { + output_block(); + fputc(MARKER, ofp); + fputc(DONE, ofp); + } +} +/* This function returns an input line of characters. A line is terminated by + length (including terminating null) greater than LINESIZE, a newline \n, or + when active (looking for charstrings) by '{'. When terminated by a newline + the newline is put into line[]. When terminated by '{', the '{' is not put + into line[], and the flag start_charstring is set to 1. */ + +static void t1asm_getline() +{ + int c; + char *p = line; + int comment = 0; + + start_charstring = 0; + while (p < line + LINESIZE) { + c = fgetc(ifp); + if (c == EOF) + break; + if (c == '%') + comment = 1; + if (active && !comment && c == '{') { + start_charstring = 1; + break; + } + *p++ = (char) c; + if (c == '\n') + break; + } + *p = '\0'; +} + +/* This function is used by the binary search, bsearch(), for command names in + the command table. */ + +static int command_compare(const void *key, const void *item) +{ + return strcmp((char *) key, ((struct command *) item)->name); +} + +/* This function returns 1 if the string is an integer and 0 otherwise. */ + +static int is_integer(char *string) +{ + if (isdigit(string[0]) || string[0] == '-' || string[0] == '+') { + while (*++string && isdigit(*string)) + ; /* deliberately empty */ + if (!*string) + return 1; + } + return 0; +} + +/* This function initializes charstring encryption. Note that this is called + at the beginning of every charstring. */ + +static void charstring_start() +{ + int i; + + charstring_bp = charstring_buf; + cr = 4330; + for (i = 0; i < lenIV; i++) + *charstring_bp++ = cencrypt((byte) 0); +} + +/* This function encrypts and buffers a single byte of charstring data. */ + +static void charstring_byte(int v) +{ + byte b = (byte) (v & 0xff); + + if (charstring_bp - charstring_buf > sizeof(charstring_buf)) { + fprintf(stderr, "error: charstring_buf full (%d bytes)\n", + sizeof(charstring_buf)); + exit(1); + } + *charstring_bp++ = cencrypt(b); +} + +/* This function outputs buffered, encrypted charstring data through possible + eexec encryption. */ + +static void charstring_end() +{ + byte *bp; + + sprintf(line, "%d ", charstring_bp - charstring_buf); + eexec_string(line); + sprintf(line, "%s ", cs_start); + eexec_string(line); + for (bp = charstring_buf; bp < charstring_bp; bp++) + eexec_byte(*bp); +} + +/* This function generates the charstring representation of an integer. */ + +static void charstring_int(int num) +{ + int x; + + if (num >= -107 && num <= 107) { + charstring_byte(num + 139); + } else if (num >= 108 && num <= 1131) { + x = num - 108; + charstring_byte(x / 256 + 247); + charstring_byte(x % 256); + } else if (num >= -1131 && num <= -108) { + x = abs(num) - 108; + charstring_byte(x / 256 + 251); + charstring_byte(x % 256); + } else if (num >= (-2147483647-1) && num <= 2147483647) { + charstring_byte(255); + charstring_byte(num >> 24); + charstring_byte(num >> 16); + charstring_byte(num >> 8); + charstring_byte(num); + } else { + fprintf(stderr, + "error: cannot format the integer %d, too large\n", num); + exit(1); + } +} + +/* This function parses an entire charstring into integers and commands, + outputting bytes through the charstring buffer. */ + +static void parse_charstring() +{ + struct command *cp; + + charstring_start(); + while (fscanf(ifp, "%s", line) == 1) { + if (line[0] == '%') { + /* eat comment to end of line */ + while (fgetc(ifp) != '\n' && !feof(ifp)) + ; /* deliberately empty */ + continue; + } + if (line[0] == '}') + break; + if (is_integer(line)) { + charstring_int(atoi(line)); + } else { + cp = (struct command *) + bsearch((void *) line, (void *) command_table, + sizeof(command_table) / sizeof(struct command), + sizeof(struct command), + command_compare); + if (cp) { + charstring_byte(cp->one); + if (cp->two >= 0) + charstring_byte(cp->two); + } else { + fprintf(stderr, "error: cannot use `%s' in charstring\n",line); + exit(1); + } + } + } + charstring_end(); +} + +static void usage() +{ + fprintf(stderr, + "usage: t1asm [-b] [-l block-length] [input [output]]\n"); + fprintf(stderr, + "\n-b means output in PFB format, otherwise PFA format.\n"); + fprintf(stderr, + "The block length applies to the length of blocks in the\n"); + fprintf(stderr, + "PFB output file; the default is to use the largest possible.\n"); + exit(1); +} + +static void print_banner() +{ + static char rcs_revision[] = ""; /* removed RCS */ + static char revision[20]; + + if (sscanf(rcs_revision, "$Revision: %19s", revision) != 1) + revision[0] = '\0'; + fprintf(stderr, "This is t1asm %s.\n", revision); +} + +#ifdef STANDALONE +int main(int argc, char **argv) +{ + char *p, *q, *r; + int c; + + extern char *optarg; + extern int optind; + + ifp = stdin; + ofp = stdout; + + print_banner(); + + /* interpret command line arguments using getopt */ + while ((c = getopt(argc, argv, "bl:")) != -1) + switch (c) { + case 'b': + pfb = 1; + break; + case 'l': + blocklen = atoi(optarg); + if (blocklen < MINBLOCKLEN) { + blocklen = MINBLOCKLEN; + fprintf(stderr, + "warning: using minimum block length of %d\n", + blocklen); + } else if (blocklen > MAXBLOCKLEN) { + blocklen = MAXBLOCKLEN; + fprintf(stderr, + "warning: using maximum block length of %d\n", + blocklen); + } + break; + default: + usage(); + break; + } + if (argc - optind > 2) + usage(); + + /* possibly open input & output files */ + if (argc - optind >= 1) { + ifp = fopen(argv[optind], "r"); + if (!ifp) { + fprintf(stderr, "error: cannot open %s for reading\n", argv[1]); + exit(1); + } + } + if (argc - optind >= 2) { + ofp = fopen(argv[optind + 1], "w"); + if (!ofp) { + fprintf(stderr, "error: cannot open %s for writing\n", argv[2]); + exit(1); + } + } + +#else +int runt1asm(int pfbflag) +{ + char *p, *q, *r; + + pfb = pfbflag; +#endif + + #ifdef _MSDOS + /* If we are processing a PFB (binary) output */ + /* file, we must set its file mode to binary. */ + if (pfb) + _setmode(_fileno(ofp), _O_BINARY); + #endif + + /* Finally, we loop until no more input. Some special things to look for + are the `currentfile eexec' line, the beginning of the `/Subrs' + definition, the definition of `/lenIV', and the definition of the + charstring start command which has `...string currentfile...' in it. */ + + while (!feof(ifp) && !ferror(ifp)) { + t1asm_getline(); + if (strcmp(line, "currentfile eexec\n") == 0) { + eexec_start(); + continue; + } else if (strstr(line, "/Subrs") && isspace(line[6])) { + active = 1; + } else if ((p = strstr(line, "/lenIV"))) { + sscanf(p, "%*s %d", &lenIV); + } else if ((p = strstr(line, "string currentfile"))) { + /* locate the name of the charstring start command */ + *p = '\0'; /* damage line[] */ + q = strrchr(line, '/'); + if (q) { + r = cs_start; + ++q; + while (!isspace(*q) && *q != '{') + *r++ = *q++; + *r = '\0'; + } + *p = 's'; /* repair line[] */ + } + /* output line data */ + eexec_string(line); + if ((p = strstr(line, "currentfile closefile"))) { + eexec_end(); + } + if (start_charstring) { + if (!cs_start[0]) { + fprintf(stderr, "error: couldn't find charstring start command\n"); + exit(1); + } + parse_charstring(); + } + } + file_end(); + + fclose(ifp); + fclose(ofp); + + return 0; +} Index: xc/extras/ttf2pt1/ttf.c =================================================================== RCS file: xc/extras/ttf2pt1/ttf.c diff -N xc/extras/ttf2pt1/ttf.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ttf.c 13 Apr 2004 02:45:07 -0000 @@ -0,0 +1,1480 @@ +/* + * True Type Font to Adobe Type 1 font converter + * By Mark Heath + * Based on ttf2pfa by Andrew Weeks + * With help from Frank M. Siegert + * + * see COPYRIGHT + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WINDOWS +# include +# include +#else +# include "windows.h" +#endif + +#include "ttf.h" +#include "pt1.h" +#include "global.h" + +/* prototypes of call entries */ +static void openfont(char *fname, char *arg); +static void closefont( void); +static int getnglyphs ( void); +static int glnames( GLYPH *glyph_list); +static void glmetrics( GLYPH *glyph_list); +static int glenc( GLYPH *glyph_list, int *encoding, int *unimap); +static void fnmetrics( struct font_metrics *fm); +static void glpath( int glyphno, GLYPH *glyph_list); +static void kerning( GLYPH *glyph_list); + +/* globals */ + +/* front-end descriptor */ +struct frontsw ttf_sw = { + /*name*/ "ttf", + /*descr*/ "built-in TTF support", + /*suffix*/ { "ttf" }, + /*open*/ openfont, + /*close*/ closefont, + /*nglyphs*/ getnglyphs, + /*glnames*/ glnames, + /*glmetrics*/ glmetrics, + /*glenc*/ glenc, + /*fnmetrics*/ fnmetrics, + /*glpath*/ glpath, + /*kerning*/ kerning, +}; + +/* statics */ + +static FILE *ttf_file; +static int ttf_nglyphs, long_offsets; + +static TTF_DIRECTORY *directory; +static TTF_DIR_ENTRY *dir_entry; +static char *filebuffer; +static char *filebuffer_end; +static TTF_NAME *name_table = NULL; +static TTF_NAME_REC *name_record; +static TTF_HEAD *head_table = NULL; +static TTF_HHEA *hhea_table = NULL; +static TTF_KERN *kern_table = NULL; +static TTF_CMAP *cmap_table = NULL; +static LONGHORMETRIC *hmtx_table = NULL; +static TTF_GLYF *glyf_table; +static BYTE *glyf_start = NULL; +static TTF_MAXP *maxp_table = NULL; +static TTF_POST_HEAD *post_table = NULL; +static union { + USHORT *sp; + ULONG *lp; +} loca_table; +#define short_loca_table loca_table.sp +#define long_loca_table loca_table.lp + +static short cmap_n_segs; +static USHORT *cmap_seg_start, *cmap_seg_end; +static short *cmap_idDelta, *cmap_idRangeOffset; +static TTF_CMAP_FMT0 *encoding0; +static int enc_type; + +static char name_buffer[2000]; +static char *name_fields[8]; + +static int enc_found_ms, enc_found_mac; + +static char *mac_glyph_names[258] = { + ".notdef", ".null", "CR", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "Adieresis", + "Aring", "Ccedilla", "Eacute", "Ntilde", + "Odieresis", "Udieresis", "aacute", "agrave", + "acircumflex", "adieresis", "atilde", "aring", + "ccedilla", "eacute", "egrave", "ecircumflex", + "edieresis", "iacute", "igrave", "icircumflex", + "idieresis", "ntilde", "oacute", "ograve", + "ocircumflex", "odieresis", "otilde", "uacute", + "ugrave", "ucircumflex", "udieresis", "dagger", + "degree", "cent", "sterling", "section", + "bullet", "paragraph", "germandbls", "registered", + "copyright", "trademark", "acute", "dieresis", + "notequal", "AE", "Oslash", "infinity", + "plusminus", "lessequal", "greaterequal", "yen", + "mu", "partialdiff", "summation", "product", + "pi", "integral", "ordfeminine", "ordmasculine", + "Omega", "ae", "oslash", "questiondown", + "exclamdown", "logicalnot", "radical", "florin", + "approxequal", "increment", "guillemotleft", "guillemotright", + "ellipsis", "nbspace", "Agrave", "Atilde", + "Otilde", "OE", "oe", "endash", + "emdash", "quotedblleft", "quotedblright", "quoteleft", + "quoteright", "divide", "lozenge", "ydieresis", + "Ydieresis", "fraction", "currency", "guilsinglleft", + "guilsinglright", "fi", "fl", "daggerdbl", + "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", + "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", + "Egrave", "Iacute", "Icircumflex", "Idieresis", + "Igrave", "Oacute", "Ocircumflex", "applelogo", + "Ograve", "Uacute", "Ucircumflex", "Ugrave", + "dotlessi", "circumflex", "tilde", "macron", + "breve", "dotaccent", "ring", "cedilla", + "hungarumlaut", "ogonek", "caron", "Lslash", + "lslash", "Scaron", "scaron", "Zcaron", + "zcaron", "brokenbar", "Eth", "eth", + "Yacute", "yacute", "Thorn", "thorn", + "minus", "multiply", "onesuperior", "twosuperior", + "threesuperior", "onehalf", "onequarter", "threequarters", + "franc", "Gbreve", "gbreve", "Idot", + "Scedilla", "scedilla", "Cacute", "cacute", + "Ccaron", "ccaron", "dmacron" +}; + +/* other prototypes */ +static void draw_composite_glyf( GLYPH *g, GLYPH *glyph_list, int glyphno, + double *matrix, int level); +static void draw_simple_glyf( GLYPH *g, GLYPH *glyph_list, int glyphno, + double *matrix); +static double f2dot14( short x); + +/* get the TTF description table address and length for this index */ + +static void +get_glyf_table( + int glyphno, + TTF_GLYF **tab, + int *len +) +{ + if(tab!=NULL) { + if (long_offsets) { + *tab = (TTF_GLYF *) (glyf_start + ntohl(long_loca_table[glyphno])); + } else { + *tab = (TTF_GLYF *) (glyf_start + (ntohs(short_loca_table[glyphno]) << 1)); + } + } + if(len!=NULL) { + if (long_offsets) { + *len = ntohl(long_loca_table[glyphno + 1]) - ntohl(long_loca_table[glyphno]); + } else { + *len = (ntohs(short_loca_table[glyphno + 1]) - ntohs(short_loca_table[glyphno])) << 1; + } + } +} + +static void +handle_name(void) +{ + int j, k, lang, len, platform; + char *p, *string_area; + char *nbp = name_buffer; + int found3 = 0; + + string_area = (char *) name_table + ntohs(name_table->offset); + name_record = &(name_table->nameRecords); + + for (j = 0; j < 8; j++) { + name_fields[j] = ""; + } + + for (j = 0; j < ntohs(name_table->numberOfNameRecords); j++) { + + platform = ntohs(name_record->platformID); + + if (platform == 3) { + + found3 = 1; + lang = ntohs(name_record->languageID) & 0xff; + len = ntohs(name_record->stringLength); + if (lang == 0 || lang == 9) { + k = ntohs(name_record->nameID); + if (k < 8) { + name_fields[k] = nbp; + + p = string_area + ntohs(name_record->stringOffset); + for (k = 0; k < len; k++) { + if (p[k] != '\0') { + if (p[k] == '(') { + *nbp = '['; + } else if (p[k] == ')') { + *nbp = ']'; + } else { + *nbp = p[k]; + } + nbp++; + } + } + *nbp = '\0'; + nbp++; + } + } + } + name_record++; + } + + string_area = (char *) name_table + ntohs(name_table->offset); + name_record = &(name_table->nameRecords); + + if (!found3) { + for (j = 0; j < ntohs(name_table->numberOfNameRecords); j++) { + + platform = ntohs(name_record->platformID); + + if (platform == 1) { + + found3 = 1; + lang = ntohs(name_record->languageID) & 0xff; + len = ntohs(name_record->stringLength); + if (lang == 0 || lang == 9) { + k = ntohs(name_record->nameID); + if (k < 8) { + name_fields[k] = nbp; + + p = string_area + ntohs(name_record->stringOffset); + for (k = 0; k < len; k++) { + if (p[k] != '\0') { + if (p[k] == '(') { + *nbp = '['; + } else if (p[k] == ')') { + *nbp = ']'; + } else { + *nbp = p[k]; + } + nbp++; + } + } + *nbp = '\0'; + nbp++; + } + } + } + name_record++; + } + } + if (!found3) { + fprintf(stderr, "**** Cannot decode font name fields ****\n"); + exit(1); + } + if (name_fields[4][0] == 0) { /* Full Name empty, use Family Name */ + name_fields[4] = name_fields[1]; + } + if (name_fields[6][0] == 0) { /* Font Name empty, use Full Name */ + name_fields[6] = name_fields[4]; + if (name_fields[6][0] == 0) { /* oops, empty again */ + WARNING_1 fprintf(stderr, "Font name is unknown, setting to \"Unknown\"\n"); + name_fields[6] = "Unknown"; + } + } + p = name_fields[6]; + /* must not start with a digit */ + if(isdigit(*p)) + *p+= 'A'-'0'; /* change to a letter */ + while (*p != '\0') { + if (!isalnum(*p) || *p=='_') { + *p = '-'; + } + p++; + } +} + +static void +handle_head(void) +{ + long_offsets = ntohs(head_table->indexToLocFormat); + if (long_offsets != 0 && long_offsets != 1) { + fprintf(stderr, "**** indexToLocFormat wrong ****\n"); + exit(1); + } +} + +/* limit the recursion level to avoid cycles */ +#define MAX_COMPOSITE_LEVEL 20 + +static void +draw_composite_glyf( + GLYPH *g, + GLYPH *glyph_list, + int glyphno, + double *orgmatrix, + int level +) +{ + int len; + short ncontours; + USHORT flagbyte, glyphindex; + double arg1, arg2; + BYTE *ptr; + char *bptr; + SHORT *sptr; + double matrix[6], newmatrix[6]; + + get_glyf_table(glyphno, &glyf_table, &len); + + if(len<=0) /* nothing to do */ + return; + + ncontours = ntohs(glyf_table->numberOfContours); + if (ncontours >= 0) { /* simple case */ + draw_simple_glyf(g, glyph_list, glyphno, orgmatrix); + return; + } + + if(ISDBG(COMPOSITE) && level ==0) + fprintf(stderr, "* %s [ %.2f %.2f %.2f %.2f %.2f %.2f ]\n", g->name, + orgmatrix[0], orgmatrix[1], orgmatrix[2], orgmatrix[3], + orgmatrix[4], orgmatrix[5]); + + /* complex case */ + if(level >= MAX_COMPOSITE_LEVEL) { + WARNING_1 fprintf(stderr, + "*** Glyph %s: stopped (possibly infinite) recursion at depth %d\n", + g->name, level); + return; + } + + ptr = ((BYTE *) glyf_table + sizeof(TTF_GLYF)); + sptr = (SHORT *) ptr; + do { + flagbyte = ntohs(*sptr); + sptr++; + glyphindex = ntohs(*sptr); + sptr++; + + if (flagbyte & ARG_1_AND_2_ARE_WORDS) { + arg1 = (short)ntohs(*sptr); + sptr++; + arg2 = (short)ntohs(*sptr); + sptr++; + } else { + bptr = (char *) sptr; + arg1 = (signed char) bptr[0]; + arg2 = (signed char) bptr[1]; + sptr++; + } + matrix[1] = matrix[2] = 0.0; + + if (flagbyte & WE_HAVE_A_SCALE) { + matrix[0] = matrix[3] = f2dot14(*sptr); + sptr++; + } else if (flagbyte & WE_HAVE_AN_X_AND_Y_SCALE) { + matrix[0] = f2dot14(*sptr); + sptr++; + matrix[3] = f2dot14(*sptr); + sptr++; + } else if (flagbyte & WE_HAVE_A_TWO_BY_TWO) { + matrix[0] = f2dot14(*sptr); + sptr++; + matrix[2] = f2dot14(*sptr); + sptr++; + matrix[1] = f2dot14(*sptr); + sptr++; + matrix[3] = f2dot14(*sptr); + sptr++; + } else { + matrix[0] = matrix[3] = 1.0; + } + + /* + * See * + * http://fonts.apple.com/TTRefMan/RM06/Chap6g + * lyf.html * matrix[0,1,2,3,4,5]=a,b,c,d,m,n + */ + + if (fabs(matrix[0]) > fabs(matrix[1])) + matrix[4] = fabs(matrix[0]); + else + matrix[4] = fabs(matrix[1]); + if (fabs(fabs(matrix[0]) - fabs(matrix[2])) <= 33. / 65536.) + matrix[4] *= 2.0; + + if (fabs(matrix[2]) > fabs(matrix[3])) + matrix[5] = fabs(matrix[2]); + else + matrix[5] = fabs(matrix[3]); + if (fabs(fabs(matrix[2]) - fabs(matrix[3])) <= 33. / 65536.) + matrix[5] *= 2.0; + + /* + * fprintf (stderr,"Matrix Opp %hd + * %hd\n",arg1,arg2); + */ +#if 0 + fprintf(stderr, "Matrix: %f %f %f %f %f %f\n", + matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5]); + fprintf(stderr, "Offset: %f %f (%s)\n", + arg1, arg2, + ((flagbyte & ARGS_ARE_XY_VALUES) ? "XY" : "index")); +#endif + + if (flagbyte & ARGS_ARE_XY_VALUES) { + matrix[4] *= arg1; + matrix[5] *= arg2; + } else { + WARNING_1 fprintf(stderr, + "*** Glyph %s: reusing scale from another glyph is unsupported\n", + g->name); + /* + * must extract values from a glyph + * but it seems to be too much pain + * and it's not clear now that it + * would be really used in any + * interesting font + */ + } + + /* at this point arg1,arg2 contain what logically should be matrix[4,5] */ + + /* combine matrices */ + + newmatrix[0] = orgmatrix[0]*matrix[0] + orgmatrix[2]*matrix[1]; + newmatrix[1] = orgmatrix[0]*matrix[2] + orgmatrix[2]*matrix[3]; + + newmatrix[2] = orgmatrix[1]*matrix[0] + orgmatrix[3]*matrix[1]; + newmatrix[3] = orgmatrix[1]*matrix[2] + orgmatrix[3]*matrix[3]; + + newmatrix[4] = orgmatrix[0]*matrix[4] + orgmatrix[2]*matrix[5] + orgmatrix[4]; + newmatrix[5] = orgmatrix[1]*matrix[4] + orgmatrix[3]*matrix[5] + orgmatrix[5]; + + if(ISDBG(COMPOSITE)) { + fprintf(stderr, "%*c+-> %2d %s [ %.2f %.2f %.2f %.2f %.2f %.2f ]\n", + level+1, ' ', level, glyph_list[glyphindex].name, + matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5]); + fprintf(stderr, "%*c = [ %.2f %.2f %.2f %.2f %.2f %.2f ]\n", + level+1, ' ', + newmatrix[0], newmatrix[1], newmatrix[2], newmatrix[3], + newmatrix[4], newmatrix[5]); + } + draw_composite_glyf(g, glyph_list, glyphindex, newmatrix, level+1); + + } while (flagbyte & MORE_COMPONENTS); +} + +static void +draw_simple_glyf( + GLYPH *g, + GLYPH *glyph_list, + int glyphno, + double *matrix +) +{ + int i, j, k, k1, len, first, cs, ce; + /* We assume that hsbw always sets to(0, 0) */ + double xlast = 0, ylast = 0; + int finished, nguide, contour_start, contour_end; + short ncontours, n_inst, last_point; + USHORT *contour_end_pt; + BYTE *ptr; +#define GLYFSZ 2000 + short xabs[GLYFSZ], yabs[GLYFSZ], xrel[GLYFSZ], yrel[GLYFSZ]; + double xcoord[GLYFSZ], ycoord[GLYFSZ]; + BYTE flags[GLYFSZ]; + double tx, ty; + int needreverse = 0; /* transformation may require + * that */ + GENTRY *lge; + + lge = g->lastentry; + + get_glyf_table(glyphno, &glyf_table, &len); + + if (len <= 0) { + WARNING_1 fprintf(stderr, + "**** Composite glyph %s refers to non-existent glyph %s, ignored\n", + g->name, + glyph_list[glyphno].name); + return; + } + ncontours = ntohs(glyf_table->numberOfContours); + if (ncontours < 0) { + WARNING_1 fprintf(stderr, + "**** Composite glyph %s refers to composite glyph %s, ignored\n", + g->name, + glyph_list[glyphno].name); + return; + } + contour_end_pt = (USHORT *) ((char *) glyf_table + sizeof(TTF_GLYF)); + + last_point = ntohs(contour_end_pt[ncontours - 1]); + n_inst = ntohs(contour_end_pt[ncontours]); + + ptr = ((BYTE *) contour_end_pt) + (ncontours << 1) + n_inst + 2; + j = k = 0; + while (k <= last_point) { + flags[k] = ptr[j]; + + if (ptr[j] & REPEAT) { + for (k1 = 0; k1 < ptr[j + 1]; k1++) { + k++; + flags[k] = ptr[j]; + } + j++; + } + j++; + k++; + } + + for (k = 0; k <= last_point; k++) { + if (flags[k] & XSHORT) { + if (flags[k] & XSAME) { + xrel[k] = ptr[j]; + } else { + xrel[k] = -ptr[j]; + } + j++; + } else if (flags[k] & XSAME) { + xrel[k] = 0.0; + } else { + xrel[k] = (short)( ptr[j] * 256 + ptr[j + 1] ); + j += 2; + } + if (k == 0) { + xabs[k] = xrel[k]; + } else { + xabs[k] = xrel[k] + xabs[k - 1]; + } + + } + + for (k = 0; k <= last_point; k++) { + if (flags[k] & YSHORT) { + if (flags[k] & YSAME) { + yrel[k] = ptr[j]; + } else { + yrel[k] = -ptr[j]; + } + j++; + } else if (flags[k] & YSAME) { + yrel[k] = 0; + } else { + yrel[k] = ptr[j] * 256 + ptr[j + 1]; + j += 2; + } + if (k == 0) { + yabs[k] = yrel[k]; + } else { + yabs[k] = yrel[k] + yabs[k - 1]; + } + } + + if (matrix) { + for (i = 0; i <= last_point; i++) { + tx = xabs[i]; + ty = yabs[i]; + xcoord[i] = fscale(matrix[0] * tx + matrix[2] * ty + matrix[4]); + ycoord[i] = fscale(matrix[1] * tx + matrix[3] * ty + matrix[5]); + } + } else { + for (i = 0; i <= last_point; i++) { + xcoord[i] = fscale(xabs[i]); + ycoord[i] = fscale(yabs[i]); + } + } + + i = j = 0; + first = 1; + + while (i <= ntohs(contour_end_pt[ncontours - 1])) { + contour_end = ntohs(contour_end_pt[j]); + + if (first) { + fg_rmoveto(g, xcoord[i], ycoord[i]); + xlast = xcoord[i]; + ylast = ycoord[i]; + contour_start = i; + first = 0; + } else if (flags[i] & ONOROFF) { + fg_rlineto(g, xcoord[i], ycoord[i]); + xlast = xcoord[i]; + ylast = ycoord[i]; + } else { + cs = i - 1; + finished = nguide = 0; + while (!finished) { + if (i == contour_end + 1) { + ce = contour_start; + finished = 1; + } else if (flags[i] & ONOROFF) { + ce = i; + finished = 1; + } else { + i++; + nguide++; + } + } + + switch (nguide) { + case 0: + fg_rlineto(g, xcoord[ce], ycoord[ce]); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + break; + + case 1: + fg_rrcurveto(g, + (xcoord[cs] + 2.0 * xcoord[cs + 1]) / 3.0, + (ycoord[cs] + 2.0 * ycoord[cs + 1]) / 3.0, + (2.0 * xcoord[cs + 1] + xcoord[ce]) / 3.0, + (2.0 * ycoord[cs + 1] + ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + + break; + + case 2: + fg_rrcurveto(g, + (-xcoord[cs] + 4.0 * xcoord[cs + 1]) / 3.0, + (-ycoord[cs] + 4.0 * ycoord[cs + 1]) / 3.0, + (4.0 * xcoord[cs + 2] - xcoord[ce]) / 3.0, + (4.0 * ycoord[cs + 2] - ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + break; + + case 3: + fg_rrcurveto(g, + (xcoord[cs] + 2.0 * xcoord[cs + 1]) / 3.0, + (ycoord[cs] + 2.0 * ycoord[cs + 1]) / 3.0, + (5.0 * xcoord[cs + 1] + xcoord[cs + 2]) / 6.0, + (5.0 * ycoord[cs + 1] + ycoord[cs + 2]) / 6.0, + (xcoord[cs + 1] + xcoord[cs + 2]) / 2.0, + (ycoord[cs + 1] + ycoord[cs + 2]) / 2.0 + ); + + fg_rrcurveto(g, + (xcoord[cs + 1] + 5.0 * xcoord[cs + 2]) / 6.0, + (ycoord[cs + 1] + 5.0 * ycoord[cs + 2]) / 6.0, + (5.0 * xcoord[cs + 2] + xcoord[cs + 3]) / 6.0, + (5.0 * ycoord[cs + 2] + ycoord[cs + 3]) / 6.0, + (xcoord[cs + 3] + xcoord[cs + 2]) / 2.0, + (ycoord[cs + 3] + ycoord[cs + 2]) / 2.0 + ); + + fg_rrcurveto(g, + (xcoord[cs + 2] + 5.0 * xcoord[cs + 3]) / 6.0, + (ycoord[cs + 2] + 5.0 * ycoord[cs + 3]) / 6.0, + (2.0 * xcoord[cs + 3] + xcoord[ce]) / 3.0, + (2.0 * ycoord[cs + 3] + ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + ylast = ycoord[ce]; + xlast = xcoord[ce]; + + break; + + default: + k1 = cs + nguide; + fg_rrcurveto(g, + (xcoord[cs] + 2.0 * xcoord[cs + 1]) / 3.0, + (ycoord[cs] + 2.0 * ycoord[cs + 1]) / 3.0, + (5.0 * xcoord[cs + 1] + xcoord[cs + 2]) / 6.0, + (5.0 * ycoord[cs + 1] + ycoord[cs + 2]) / 6.0, + (xcoord[cs + 1] + xcoord[cs + 2]) / 2.0, + (ycoord[cs + 1] + ycoord[cs + 2]) / 2.0 + ); + + for (k = cs + 2; k <= k1 - 1; k++) { + fg_rrcurveto(g, + (xcoord[k - 1] + 5.0 * xcoord[k]) / 6.0, + (ycoord[k - 1] + 5.0 * ycoord[k]) / 6.0, + (5.0 * xcoord[k] + xcoord[k + 1]) / 6.0, + (5.0 * ycoord[k] + ycoord[k + 1]) / 6.0, + (xcoord[k] + xcoord[k + 1]) / 2.0, + (ycoord[k] + ycoord[k + 1]) / 2.0 + ); + + } + + fg_rrcurveto(g, + (xcoord[k1 - 1] + 5.0 * xcoord[k1]) / 6.0, + (ycoord[k1 - 1] + 5.0 * ycoord[k1]) / 6.0, + (2.0 * xcoord[k1] + xcoord[ce]) / 3.0, + (2.0 * ycoord[k1] + ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + + break; + } + } + if (i >= contour_end) { + g_closepath(g); + first = 1; + i = contour_end + 1; + j++; + } else { + i++; + } + } + + if (matrix) { + /* guess whether do we need to reverse the results */ + + double x[3], y[3]; + int max = 0, from, to; + + /* transform a triangle going in proper direction */ + /* + * the origin of triangle is in (0,0) so we know it in + * advance + */ + + x[0] = y[0] = 0; + x[1] = matrix[0] * 0 + matrix[2] * 300; + y[1] = matrix[1] * 0 + matrix[3] * 300; + x[2] = matrix[0] * 300 + matrix[2] * 0; + y[2] = matrix[1] * 300 + matrix[3] * 0; + + /* then find the topmost point */ + for (i = 0; i < 3; i++) + if (y[i] > y[max]) + max = i; + from = (max + 3 - 1) % 3; + to = (max + 1) % 3; + + needreverse = 0; + + /* special cases for horizontal lines */ + if (y[max] == y[from]) { + if (x[max] < y[from]) + needreverse = 1; + } else if (y[to] == y[from]) { + if (x[to] < x[max]) + needreverse = 1; + } else { /* generic case */ + if ((x[to] - x[max]) * (y[max] - y[from]) + > (x[max] - x[from]) * (y[to] - y[max])) + needreverse = 1; + } + + if (needreverse) { + if (lge) { + assertpath(lge->next, __FILE__, __LINE__, g->name); + reversepathsfromto(lge->next, NULL); + } else { + assertpath(g->entries, __FILE__, __LINE__, g->name); + reversepaths(g); + } + } + } +} + +static double +f2dot14( + short x +) +{ + short y = ntohs(x); + return (y >> 14) + ((y & 0x3fff) / 16384.0); +} + + +/* check that the pointer points within the file */ +/* returns 0 if pointer is good, 1 if bad */ +static int +badpointer( + void *ptr +) +{ + return (ptr < (void *)filebuffer || ptr >= (void *)filebuffer_end); +} + +/* + * Externally accessible methods + */ + +/* + * Open font and prepare to return information to the main driver. + * May print error and warning messages. + * Exit on error. + */ + +static void +openfont( + char *fname, + char *arg /* unused now */ +) +{ + int i, j; + struct stat statbuf; + static struct { + void **tbpp; /* pointer to pointer to the table */ + char name[5]; /* table name */ + char optional; /* flag: table may be missing */ + } tables[] = { + { (void **)&name_table, "name", 0 }, + { (void **)&head_table, "head", 0 }, + { (void **)&hhea_table, "hhea", 0 }, + { (void **)&post_table, "post", 0 }, + { (void **)&glyf_start, "glyf", 0 }, + { (void **)&cmap_table, "cmap", 0 }, + { (void **)&kern_table, "kern", 1 }, + { (void **)&maxp_table, "maxp", 0 }, + { (void **)&hmtx_table, "hmtx", 0 }, + { (void **)&long_loca_table, "loca", 0 }, + { NULL, "", 0 } /* end of table */ + }; + + if (stat(fname, &statbuf) == -1) { + fprintf(stderr, "**** Cannot access %s ****\n", fname); + exit(1); + } + if ((filebuffer = malloc(statbuf.st_size)) == NULL) { + fprintf(stderr, "**** Cannot malloc space for file ****\n"); + exit(1); + } + + filebuffer_end = filebuffer + statbuf.st_size; + + if ((ttf_file = fopen(fname, "rb")) == NULL) { + fprintf(stderr, "**** Cannot open file '%s'\n", fname); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Processing file %s\n", fname); + } + + if (fread(filebuffer, 1, statbuf.st_size, ttf_file) != statbuf.st_size) { + fprintf(stderr, "**** Could not read whole file \n"); + exit(1); + } + fclose(ttf_file); + + directory = (TTF_DIRECTORY *) filebuffer; + + if (ntohl(directory->sfntVersion) != 0x00010000) { + fprintf(stderr, + "**** Unknown File Version number [%x], or not a TrueType file\n", + directory->sfntVersion); + exit(1); + } + + /* clear the tables */ + for(j=0; tables[j].tbpp != NULL; j++) + *(tables[j].tbpp) = NULL; + + dir_entry = &(directory->list); + + for (i = 0; i < ntohs(directory->numTables); i++) { + + for(j=0; tables[j].tbpp != NULL; j++) + if (memcmp(dir_entry->tag, tables[j].name, 4) == 0) { + *(tables[j].tbpp) = (void *) (filebuffer + ntohl(dir_entry->offset)); + break; + } + + if (memcmp(dir_entry->tag, "EBDT", 4) == 0 || + memcmp(dir_entry->tag, "EBLC", 4) == 0 || + memcmp(dir_entry->tag, "EBSC", 4) == 0) { + WARNING_1 fprintf(stderr, "Font contains bitmaps\n"); + } + dir_entry++; + } + + for(j=0; tables[j].tbpp != NULL; j++) + if(!tables[j].optional && badpointer( *(tables[j].tbpp) )) { + fprintf(stderr, "**** File contains no required table '%s'\n", tables[j].name); + exit(1); + } + + handle_name(); + + handle_head(); + + ttf_nglyphs = ntohs(maxp_table->numGlyphs); + + enc_found_ms = enc_found_mac = 0; +} + +/* + * Close font. + * Exit on error. + */ + +static void +closefont( + void +) +{ + return; /* empty operation */ +} + +/* + * Get the number of glyphs in font. + */ + +static int +getnglyphs ( + void +) +{ + return ttf_nglyphs; +} + +/* + * Get the names of the glyphs. + * Returns 0 if the names were assigned, non-zero if the font + * provides no glyph names. + */ + +static int +glnames( + GLYPH *glyph_list +) +{ + int i, len, n, npost; + unsigned int format; + USHORT *name_index; + char *ptr, *p; + char **ps_name_ptr = (char **) malloc(ttf_nglyphs * sizeof(char *)); + int n_ps_names; + int ps_fmt_3 = 0; + + format = ntohl(post_table->formatType); + + if (format == 0x00010000) { + for (i = 0; i < 258 && i < ttf_nglyphs; i++) { + glyph_list[i].name = mac_glyph_names[i]; + } + } else if (format == 0x00020000) { + npost = ntohs(post_table->numGlyphs); + if (ttf_nglyphs != npost) { + /* This is an error in the font, but we can now cope */ + WARNING_1 fprintf(stderr, "**** Postscript table size mismatch %d/%d ****\n", + npost, ttf_nglyphs); + } + n_ps_names = 0; + name_index = &(post_table->glyphNameIndex); + + /* This checks the integrity of the post table */ + for (i=0; i n_ps_names + 257) { + n_ps_names = n - 257; + } + } + + ptr = (char *) post_table + 34 + (ttf_nglyphs << 1); + i = 0; + while (*ptr > 0 && i < n_ps_names) { + len = *ptr; + /* previously the program wrote nulls into the table. If the table + was corrupt, this could put zeroes anywhere, leading to obscure bugs, + so now I malloc space for the names. Yes it is much less efficient */ + + if ((p = malloc(len+1)) == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + + ps_name_ptr[i] = p; + strncpy(p, ptr+1, len); + p[len] = '\0'; + i ++; + ptr += len + 1; + } + + if (i != n_ps_names) + { + WARNING_2 fprintf (stderr, "** Postscript Name mismatch %d != %d **\n", + i, n_ps_names); + n_ps_names = i; + } + + /* + * for (i=0; inumGlyphs); + for (i = 0; i < ttf_nglyphs; i++) { + glyph_list[i].name = mac_glyph_names[i + ptr[i]]; + } + } else { + fprintf(stderr, + "**** Postscript table in wrong format %x ****\n", + format); + exit(1); + } + + return ps_fmt_3; +} + +/* + * Get the metrics of the glyphs. + */ + +static void +glmetrics( + GLYPH *glyph_list +) +{ + int i; + int n_hmetrics = ntohs(hhea_table->numberOfHMetrics); + GLYPH *g; + LONGHORMETRIC *hmtx_entry = hmtx_table; + FWORD *lsblist; + + for (i = 0; i < n_hmetrics; i++) { + g = &(glyph_list[i]); + g->width = ntohs(hmtx_entry->advanceWidth); + g->lsb = ntohs(hmtx_entry->lsb); + hmtx_entry++; + } + + lsblist = (FWORD *) hmtx_entry; + hmtx_entry--; + + for (i = n_hmetrics; i < ttf_nglyphs; i++) { + g = &(glyph_list[i]); + g->width = ntohs(hmtx_entry->advanceWidth); + g->lsb = ntohs(lsblist[i - n_hmetrics]); + } + + for (i = 0; i < ttf_nglyphs; i++) { + g = &(glyph_list[i]); + get_glyf_table(i, &glyf_table, &g->ttf_pathlen); + + g->xMin = (short)ntohs(glyf_table->xMin); + g->xMax = (short)ntohs(glyf_table->xMax); + g->yMin = (short)ntohs(glyf_table->yMin); + g->yMax = (short)ntohs(glyf_table->yMax); + } + +} + + +static void +handle_ms_encoding( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + int j, k, kk, set_ok; + USHORT start, end, ro; + short delta, n; + + for (j = 0; j < cmap_n_segs - 1; j++) { + start = ntohs(cmap_seg_start[j]); + end = ntohs(cmap_seg_end[j]); + delta = ntohs(cmap_idDelta[j]); + ro = ntohs(cmap_idRangeOffset[j]); + + for (k = start; k <= end; k++) { + if (ro == 0) { + n = k + delta; + } else { + n = ntohs(*((ro >> 1) + (k - start) + + &(cmap_idRangeOffset[j]))); + if (delta != 0) + { + /* Not exactly sure how to deal with this circumstance, + I suspect it never occurs */ + n += delta; + fprintf (stderr, + "rangeoffset and delta both non-zero - %d/%d", + ro, delta); + } + } + if(n<0 || n>=ttf_nglyphs) { + WARNING_1 fprintf(stderr, "Font contains a broken glyph code mapping, ignored\n"); + continue; + } + if (glyph_list[n].orig_code != -1) { +#if 0 + if (strcmp(glyph_list[n].name, ".notdef") != 0) { + WARNING_2 fprintf(stderr, + "Glyph %s has >= two encodings (A), %4.4x & %4.4x\n", + glyph_list[n].name, + glyph_list[n].orig_code, + k); + } +#endif + set_ok = 0; + } else { + set_ok = 1; + } + if (enc_type==1 || forcemap) { + kk = unicode_rev_lookup(k); + if(ISDBG(UNICODE)) + fprintf(stderr, "Unicode %s - 0x%04x\n",glyph_list[n].name,k); + if (set_ok) { + glyph_list[n].orig_code = k; + /* glyph_list[n].char_no = kk; */ + } + if (kk >= 0 && kk < ENCTABSZ && encoding[kk] == -1) + encoding[kk] = n; + } else { + if ((k & 0xff00) == 0xf000) { + if( encoding[k & 0x00ff] == -1 ) { + encoding[k & 0x00ff] = n; + if (set_ok) { + /* glyph_list[n].char_no = k & 0x00ff; */ + glyph_list[n].orig_code = k; + } + } + } else { + if (set_ok) { + /* glyph_list[n].char_no = k; */ + glyph_list[n].orig_code = k; + } + WARNING_2 fprintf(stderr, + "Glyph %s has non-symbol encoding %4.4x\n", + glyph_list[n].name, + k & 0xffff); + /* + * just use the code + * as it is + */ + if ((k & ~0xff) == 0 && encoding[k] == -1 ) + encoding[k] = n; + } + } + } + } +} + +static void +handle_mac_encoding( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + short n; + int j, size; + + size = ntohs(encoding0->length) - 6; + for (j = 0; j < size; j++) { + n = encoding0->glyphIdArray[j]; + if (glyph_list[n].char_no != -1) { + WARNING_2 fprintf(stderr, + "Glyph %s has >= two encodings (B), %4.4x & %4.4x\n", + glyph_list[n].name, + glyph_list[n].char_no, + j); + } else { + if (j < ENCTABSZ) { + if(encoding[j] == -1) { + glyph_list[n].char_no = j; + encoding[j] = n; + } + } + } + } +} + +/* + * Get the original encoding of the font. + * Returns 1 for if the original encoding is Unicode, 2 if the + * original encoding is other 16-bit, 0 if 8-bit. + */ + +static int +glenc( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + int num_tables = ntohs(cmap_table->numberOfEncodingTables); + BYTE *ptr; + int i, format, offset, seg_c2, found; + int platform, encoding_id; + TTF_CMAP_ENTRY *table_entry; + TTF_CMAP_FMT4 *encoding4; + + if(enc_found_ms) { + handle_ms_encoding(glyph_list, encoding, unimap); + return enc_type; + } else if(enc_found_mac) { + handle_mac_encoding(glyph_list, encoding, unimap); + return 0; + } + + if(force_pid != -1 && force_pid != 3) { + fputs("*** Only platform ID == 3 is supported\n", stderr); + exit(1); + } + + enc_type = 0; + found = 0; + + for (i = 0; i < num_tables && !found; i++) { + table_entry = &(cmap_table->encodingTable[i]); + offset = ntohl(table_entry->offset); + encoding4 = (TTF_CMAP_FMT4 *) ((BYTE *) cmap_table + offset); + format = ntohs(encoding4->format); + platform = ntohs(table_entry->platformID); + encoding_id = ntohs(table_entry->encodingID); + + if (platform == 3 && format == 4) { + if(force_pid == 3) { + if(encoding_id != force_eid) + continue; + WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n", + force_pid, force_eid); + enc_type = 1; + } else { + switch (encoding_id) { + case 0: + WARNING_1 fputs("Found Symbol Encoding\n", stderr); + break; + case 1: + WARNING_1 fputs("Found Unicode Encoding\n", stderr); + enc_type = 1; + break; + default: + WARNING_1 { + fprintf(stderr, + "****MS Encoding ID %d not supported****\n", + encoding_id); + fputs("Treating it like Symbol encoding\n", stderr); + } + break; + } + } + + found = 1; + seg_c2 = ntohs(encoding4->segCountX2); + cmap_n_segs = seg_c2 >> 1; + ptr = (BYTE *) encoding4 + 14; + cmap_seg_end = (USHORT *) ptr; + cmap_seg_start = (USHORT *) (ptr + seg_c2 + 2); + cmap_idDelta = (short *) (ptr + (seg_c2 * 2) + 2); + cmap_idRangeOffset = (short *) (ptr + (seg_c2 * 3) + 2); + enc_found_ms = 1; + + handle_ms_encoding(glyph_list, encoding, unimap); + } + } + + if (!found) { + if(force_pid != -1) { + fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n", + force_pid, force_eid); + exit(1); + } + + WARNING_1 fputs("No Microsoft encoding, looking for MAC encoding\n", stderr); + for (i = 0; i < num_tables && !found; i++) { + table_entry = &(cmap_table->encodingTable[i]); + offset = ntohl(table_entry->offset); + encoding0 = (TTF_CMAP_FMT0 *) ((BYTE *) cmap_table + offset); + format = ntohs(encoding0->format); + platform = ntohs(table_entry->platformID); + encoding_id = ntohs(table_entry->encodingID); + + if (format == 0) { + found = 1; + enc_found_mac = 1; + + handle_mac_encoding(glyph_list, encoding, unimap); + } + } + } + if (!found) { + fprintf(stderr, "**** No Recognised Encoding Table ****\n"); + exit(1); + } + + return enc_type; +} + +/* + * Get the font metrics + */ +static void +fnmetrics( + struct font_metrics *fm +) +{ + char *str; + static int fieldstocheck[]= {2,4,6}; + int i, j, len; + + fm->italic_angle = (short) (ntohs(post_table->italicAngle.upper)) + + ((short) ntohs(post_table->italicAngle.lower) / 65536.0); + fm->underline_position = (short) ntohs(post_table->underlinePosition); + fm->underline_thickness = (short) ntohs(post_table->underlineThickness); + fm->is_fixed_pitch = ntohl(post_table->isFixedPitch); + + fm->ascender = (short)ntohs(hhea_table->ascender); + fm->descender = (short)ntohs(hhea_table->descender); + + fm->units_per_em = ntohs(head_table->unitsPerEm); + + fm->bbox[0] = (short) ntohs(head_table->xMin); + fm->bbox[1] = (short) ntohs(head_table->yMin); + fm->bbox[2] = (short) ntohs(head_table->xMax); + fm->bbox[3] = (short) ntohs(head_table->yMax); + + fm->name_copyright = name_fields[0]; + fm->name_family = name_fields[1]; + fm->name_style = name_fields[2]; + fm->name_full = name_fields[4]; + fm->name_version = name_fields[5]; + fm->name_ps = name_fields[6]; + + /* guess the boldness from the font names */ + fm->force_bold=0; + + for(i=0; !fm->force_bold && i= len || !islower(str[j+4])) + ) { + fm->force_bold=1; + break; + } + } + } +} + +/* + * Get the path of contrours for a glyph. + */ + +static void +glpath( + int glyphno, + GLYPH *glyf_list +) +{ + double matrix[6]; + GLYPH *g; + + g = &glyph_list[glyphno]; + + matrix[0] = matrix[3] = 1.0; + matrix[1] = matrix[2] = matrix[4] = matrix[5] = 0.0; + draw_composite_glyf(g, glyf_list, glyphno, matrix, 0 /*level*/); +} + +/* + * Get the kerning data. + */ + +static void +kerning( + GLYPH *glyph_list +) +{ + TTF_KERN_SUB *subtable; + TTF_KERN_ENTRY *kern_entry; + int i, j; + int ntables; + int npairs; + char *ptr; + + if(kern_table == NULL) { + WARNING_1 fputs("No Kerning data\n", stderr); + return; + } + if(badpointer(kern_table)) { + fputs("**** Defective Kerning table, ignored\n", stderr); + return; + } + + ntables = ntohs(kern_table->nTables); + ptr = (char *) kern_table + 4; + + for (i = 0; i < ntables; i++) { + subtable = (TTF_KERN_SUB *) ptr; + if ((ntohs(subtable->coverage) & 0xff00) == 0) { + npairs = (short) ntohs(subtable->nPairs); + kern_entry = (TTF_KERN_ENTRY *) (ptr + sizeof(TTF_KERN_SUB)); + + kern_entry = (TTF_KERN_ENTRY *) (ptr + sizeof(TTF_KERN_SUB)); + for (j = 0; j < npairs; j++) { + if( kern_entry->value != 0) + addkernpair(ntohs(kern_entry->left), + ntohs(kern_entry->right), (short)ntohs(kern_entry->value)); + kern_entry++; + } + } + ptr += subtable->length; + } +} + Index: xc/extras/ttf2pt1/ttf.h =================================================================== RCS file: xc/extras/ttf2pt1/ttf.h diff -N xc/extras/ttf2pt1/ttf.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ttf.h 13 Apr 2004 02:45:08 -0000 @@ -0,0 +1,183 @@ +/* + * see COPYRIGHT + */ + +/* these definitions are mostly taken from Microsoft's True Type + documentation. +*/ + +#ifdef XP_PSTEXT +typedef unsigned char BYTE; +typedef signed char CHAR; +typedef unsigned short USHORT; +typedef signed short SHORT; +typedef unsigned int ULONG; +typedef signed int LONG; +typedef SHORT FWORD; +typedef USHORT UFWORD; +#else +#define BYTE unsigned char +#define CHAR signed char +#define USHORT unsigned short +#define SHORT signed short +#define ULONG unsigned int +#define LONG signed int +#define FWORD SHORT +#define UFWORD USHORT +#endif /* XP_PSTEXT */ + +#define ONOROFF 0x01 +#define XSHORT 0x02 +#define YSHORT 0x04 +#define REPEAT 0x08 +#define XSAME 0x10 +#define YSAME 0x20 + +#define ARG_1_AND_2_ARE_WORDS 0x0001 +#define ARGS_ARE_XY_VALUES 0x0002 +#define XY_BOUND_TO_GRID 0x0004 +#define WE_HAVE_A_SCALE 0x0008 +#define MORE_COMPONENTS 0x0020 +#define WE_HAVE_AN_X_AND_Y_SCALE 0x0040 +#define WE_HAVE_A_TWO_BY_TWO 0x0080 +#define WE_HAVE_INSTRUCTIONS 0x0100 +#define USE_MY_METRICS 0x0200 + +typedef struct short_2 { + SHORT upper; + USHORT lower; +} FIXED ; + +typedef struct longhormetric { + UFWORD advanceWidth; + FWORD lsb; +} LONGHORMETRIC; + +typedef struct ttf_hhea { + BYTE version[4]; + SHORT ascender, descender, lineGap; + USHORT advnaceWidthMax; + SHORT minLSB, minRSB, xMaxExtent; + SHORT caretSlopeRise, caretSlopeRun; + SHORT reserved[5]; + SHORT metricDataFormat; + USHORT numberOfHMetrics; +} TTF_HHEA; + +typedef struct ttf_dir_entry { + char tag[4]; + ULONG checksum; + ULONG offset; + ULONG length; +} TTF_DIR_ENTRY ; + +typedef struct ttf_directory { + ULONG sfntVersion; + USHORT numTables; + USHORT searchRange; + USHORT entrySelector; + USHORT rangeShift; + TTF_DIR_ENTRY list; +} TTF_DIRECTORY ; + +typedef struct ttf_name_rec { + USHORT platformID; + USHORT encodingID; + USHORT languageID; + USHORT nameID; + USHORT stringLength; + USHORT stringOffset; +} TTF_NAME_REC; + +typedef struct ttf_name { + USHORT format; + USHORT numberOfNameRecords; + USHORT offset; + TTF_NAME_REC nameRecords; +} TTF_NAME ; + +typedef struct ttf_head { + ULONG version; + ULONG fontRevision; + ULONG checksumAdjust; + ULONG magicNo; + USHORT flags; + USHORT unitsPerEm; + BYTE created[8]; + BYTE modified[8]; + FWORD xMin, yMin, xMax, yMax; + USHORT macStyle, lowestRecPPEM; + SHORT fontDirection, indexToLocFormat, glyphDataFormat; +} TTF_HEAD ; + +typedef struct ttf_kern { + USHORT version, nTables; +} TTF_KERN ; + +typedef struct ttf_kern_sub { + USHORT version, length, coverage; + USHORT nPairs, searchRange, entrySelector, rangeShift; +} TTF_KERN_SUB; + +typedef struct ttf_kern_entry { + USHORT left, right; + FWORD value; +} TTF_KERN_ENTRY; + +typedef struct ttf_cmap_fmt0 { + USHORT format; + USHORT length; + USHORT version; + BYTE glyphIdArray[256]; +} TTF_CMAP_FMT0; + +typedef struct ttf_cmap_fmt4 { + USHORT format; + USHORT length; + USHORT version; + USHORT segCountX2; + USHORT searchRange; + USHORT entrySelector; + USHORT rangeShift; +} TTF_CMAP_FMT4; + +typedef struct ttf_cmap_entry { + USHORT platformID; + USHORT encodingID; + ULONG offset; +} TTF_CMAP_ENTRY; + +typedef struct ttf_cmap { + USHORT version; + USHORT numberOfEncodingTables; + TTF_CMAP_ENTRY encodingTable[1]; +} TTF_CMAP ; + +typedef struct ttf_glyf { + SHORT numberOfContours; + FWORD xMin, yMin, xMax, yMax; +} TTF_GLYF ; + +typedef struct ttf_maxp { + ULONG version; + USHORT numGlyphs, maxPoints, maxContours; + USHORT maxCompositePoints, maxCompositeContours; + USHORT maxZones, maxTwilightPoints, maxStorage; + USHORT maxFunctionDefs, maxInstructionsDefs; + USHORT maxSizeOfInstructions, maxComponentElements; + USHORT maxComponentDepth; +} TTF_MAXP ; + +typedef struct ttf_post_head { + ULONG formatType; + FIXED italicAngle; + FWORD underlinePosition; + FWORD underlineThickness; + ULONG isFixedPitch; + ULONG minMemType42; + ULONG maxMemType42; + ULONG minMemType1; + ULONG maxMemType1; + USHORT numGlyphs; + USHORT glyphNameIndex; +} TTF_POST_HEAD ; Index: xc/extras/ttf2pt1/ttf2pt1.1 =================================================================== RCS file: xc/extras/ttf2pt1/ttf2pt1.1 diff -N xc/extras/ttf2pt1/ttf2pt1.1 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ttf2pt1.1 13 Apr 2004 02:45:08 -0000 @@ -0,0 +1,825 @@ +.rn '' }` +''' $RCSfile: ttf2pt1.1,v $$Revision: 1.1 $$Date: 2003/06/04 00:33:54 $ +''' +''' $Log: ttf2pt1.1,v $ +''' Revision 1.1 2003/06/04 00:33:54 roland +''' Fix for http://xprint.mozdev.org/bugs/show_bug.cgi?id=3846 - RFE: Upload Freetype --> PS Type1 font converter "ttf2pt1" ... +''' +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +''' \*(M", \*(S", \*(N" and \*(T" are the equivalent of +''' \*(L" and \*(R", except that they are used on ".xx" lines, +''' such as .IP and .SH, which do another additional levels of +''' double-quote interpretation +.ds M" """ +.ds S" """ +.ds N" """"" +.ds T" """"" +.ds L' ' +.ds R' ' +.ds M' ' +.ds S' ' +.ds N' ' +.ds T' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds M" `` +.ds S" '' +.ds N" `` +.ds T" '' +.ds L' ` +.ds R' ' +.ds M' ` +.ds S' ' +.ds N' ` +.ds T' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH TTF2PT1 1 "version 3.4.4-SNAP-030526" "May 26, 2003" "TTF2PT1 Font Converter" +.UC +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +TTF2PT1 \- A True Type to PostScript Type 1 Font Converter +.SH "SYNOPSIS" +\f(CWttf2pt1 \fI[-options] ttffont.ttf [Fontname]\fR\fR +.PP +or +.PP +\f(CWttf2pt1 \fI[-options] ttffont.ttf -\fR\fR +.SH "DESCRIPTION" +Ttf2pt1 is a font converter from the True Type format (and some other formats +supported by the FreeType library as well) to the Adobe Type1 format. +.PP +The versions 3.0 and later got rather extensive post-processing algorithm that +brings the converted fonts to the requirements of the Type1 standard, tries to +correct the rounding errors introduced during conversions and some simple +kinds of bugs that are typical for the public domain TTF fonts. It +also generates the hints that enable much better rendering of fonts in +small sizes that are typical for the computer displays. But everything +has its price, and some of the optimizations may not work well for certain +fonts. That's why the options were added to the converter, to control +the performed optimizations. +.SH "OPTIONS" +The first variant creates the file \f(CWFontname.pfa\fR (or \f(CWFontname.pfb\fR if the +option \*(L'\fB\-b\fR\*(R' was used) with the converted font and \f(CWFontname.afm\fR with the +font metrics, the second one prints the font or another file (if the option +\&\*(R'\fB\-G\fR\*(R' was used) on the standard output from where it can be immediately +piped through some filter. If no \f(CWFontname\fR is specified for the first +variant, the name is generated from \f(CWttffont\fR by replacing the \f(CW.ttf\fR +filename suffix. +.PP +Most of the time no options are neccessary (with a possible exception +of \*(L'\fB\-e\fR'). But if there are some troubles with the resulting font, they +may be used to control the conversion. +The \fBoptions\fR are: +.Ip "\(bu" 2 +\f(CW\fB-a\fR\fR \- Include all the glyphs from the source file into the converted +file. If this option is not specified then only the glyphs that have +been assigned some encoding are included, because the rest of glyphs +would be inaccessible anyway and would only consume the disk space. +But some applications are clever enough to change the encoding on +the fly and thus use the other glyphs, in this case they could +benefit from using this option. But there is a catch: the X11 library +has rather low limit for the font size. Including more glyphs increases +the file size and thus increases the chance of hitting this limit. +See \f(CWapp/X11/README\fR for the description of a +patch to X11 which fixes this problem. +.Ip "\(bu" 2 +\f(CW\fB-b\fR\fR \- Encode the resulting font to produce a ready \f(CW.pfb\fR file. +.Ip "\(bu" 2 +\f(CW\fB-d \fIsuboptions\fR\fR\fR \- Debugging options. The suboptions are: +.Sp +\f(CW\fBa\fR\fR \- Print out the absolute coordinates of dots in outlines. Such +a font can not be used by any program (that's why this option is +incompatible with \*(L'\fB\-e\fR') but it has proven to be a valuable debuging +information. +.Sp +\f(CW\fBr\fR\fR \- Do not reverse the direction of outlines. The \s-1TTF\s0 fonts have +the standard direction of outlines opposite to the Type1 fonts. So +they should be reversed during proper conversion. This option +may be used for debugging or to handle a \s-1TTF\s0 font with wrong +direction of outlines (possibly, converted in a broken way from +a Type1 font). The first signs of the wrong direction are the +letters like \*(L"P\*(R" or \*(L"B\*(R" without the unpainted \*(L"holes\*(R" inside. +.Ip "\(bu" 2 +\f(CW\fB-e\fR\fR \- Assemble the resulting font to produce a ready \f(CW.pfa\fR file. +.Sp +[ S.B.: Personally I don't think that this option is particularly useful. +The same result may be achieved by piping the unassembled data +through t1asm, the Type 1 assembler. And, anyways, it's good to +have the t1utils package handy. But Mark and many users think that +this functionality is good and it took not much time to add this option. ] +.Ip "\(bu" 2 +\f(CW\fB-F\fR\fR \- Force the Unicode encoding: any type of \s-1MS\s0 encoding specified +in the font is ignored and the font is treated like it has Unicode +encoding. \fB\s-1WARNING\s0:\fR this option is intended for buggy fonts +which actually are in Unicode but are marked as something else. The +effect on the other fonts is unpredictable. +.Ip "\(bu" 2 +\f(CW\fB-G \fIsuboptions\fR\fR\fR \- File generation options. The suboptions may be lowercase +or uppercase, the lowercase ones disable the generation of particular +files, the corresponding uppercase suboptions enable the generation of the +same kind of files. If the result of ttf2pt1 is requested to be printed on +the standard output, the last enabling suboption of \fB\-G\fR determines +which file will be written to the standard output and the rest of files +will be discarded. For example, \fB\-G A\fR will request the \s-1AFM\s0 file. +The suboptions to disable/enable the generation of the files are: +.Sp +\f(CW\fBf/F\fR\fR \- The font file. Depending on the other options this file +will have one of the suffixes \f(CW.t1a\fR, \f(CW.pfa\fR or \f(CW.pfb\fR. If the conversion result +is requested on the standard output ('\f(CW-\fR\*(R' is used as the output file name) +then the font file will also be written there by default, if not overwritten +by another suboption of \fB\-G\fR. +\fBDefault: enabled\fR +.Sp +\f(CW\fBa/A\fR\fR \- The Adobe font metrics file (\f(CW.afm\fR). +\fBDefault: enabled\fR +.Sp +\f(CW\fBe/E\fR\fR \- The dvips encoding file (\f(CW.enc\fR). +\fBDefault: disabled\fR +.Ip "\(bu" 2 +\f(CW\fB-l \fIlanguage\fR[+\fIargument\fR]\fR\fR \- Extract the fonts for the specified language from a +multi-language Unicode font. If this option is not used the converter +tries to guess the language by the values of the shell variable \s-1LANG\s0. +If it is not able to guess the language by \s-1LANG\s0 it tries all the +languages in the order they are listed. +.Sp +After the plus sign an optional argument for the language extractor +may be specified. The format of the argument is absolutely up to +the particular language converter. The primary purpose of the +argument is to support selection of planes for the multi-plane +Eastern encodings but it can also be used in any other way. The +language extractor may decide to add the plane name in some form +to the name of the resulting font. None of the currently supported +languages make any use of the argument yet. +.Sp +As of now the following languages are supported: +.Sp +\ \ \f(CWlatin1\fR \- for all the languages using the Latin-1 encoding +.Sp +\ \ \f(CWlatin2\fR \- for the Central European languages +.Sp +\ \ \f(CWlatin4\fR \- for the Baltic languages +.Sp +\ \ \f(CWlatin5\fR \- for the Turkish language +.Sp +\ \ \f(CWcyrillic\fR \- for the languages with Cyrillic alphabet +.Sp +\ \ \f(CWrussian\fR \- historic synonym for cyrillic +.Sp +\ \ \f(CWbulgarian\fR \- historic synonym for cyrillic +.Sp +\ \ \f(CWadobestd\fR \- for the AdobeStandard encoding used by TeX +.Sp +\ \ \f(CWplane+\fIargument\fR\fR \- to select one plane from a multi-byte encoding +.Sp +The argument of the \*(L"\f(CWplane\fR\*(R" language may be in one of three forms: +.Sp +\ \ \f(CWplane+\fBpid=\fR\fI\fR\fB,eid=\fR\fI\fR\fR +.Sp +\ \ \f(CWplane+\fBpid=\fR\fI\fR\fB,eid=\fR\fI\fR\fB,\fR\fI\fR\fR +.Sp +\ \ \f(CWplane+\fI\fR\fR +.Sp +Pid (\s-1TTF\s0 platform id) and eid (\s-1TTF\s0 encoding id) select a particular +\s-1TTF\s0 encoding table in the original font. They are specified as decimal +numbers. If this particular encoding table is not present in the font +file then the conversion fails. The native ("ttf") front-end parser supports +only pid=3 (Windows platform), the FreeType-based ("ft") front-end supports +any platform. If pid/eid is not specified then the \s-1TTF\s0 encoding table is +determined as usual: Unicode encoding if it's first or an 8-bit encoding +if not (and for an 8-bit encoding the plane number is silently ignored). +To prevent the converter from falling back to an 8-bit encoding, specify +the Unicode pid/eid value explicitly. +.Sp +Plane_number is a hexadecimal (if starts with \*(L"\fB0x\fR") or decimal number. +It gives the values of upper bytes for which 256 characters will be +selected. If not specified, defaults to 0. It is also used as a font +name suffix (the leading \*(L"0x\*(R" is not included into the suffix). +.Sp +\fB\s-1NOTE\s0:\fR +You may notice that the language names are not uniform: some are the +names of particular languages and some are names of encodings. This +is because of the different approaches. The original idea was to +implement a conversion from Unicode to the appropriate Windows +encoding for a given language. And then use the translation tables +to generate the fonts in whatever final encodings are needed. This +would allow to pile together the Unicode fonts and the non-Unicode +Windows fonts for that language and let the program to sort them out +automatically. And then generate fonts in all the possible encodings +for that language. An example of this approach is the Russian language +support. But if there is no multiplicity of encodings used for some +languages and if the non-Unicode fonts are not considered important +by the users, another way would be simpler to implement: just provide +only one table for extraction of the target encoding from Unicode +and don't bother with the translation tables. The latin* \*(L"languages\*(R" +are examples of this approach. If somebody feels that he needs the +Type1 fonts both in Latin-* and Windows encodings he or she is absolutely +welcome to submit the code to implement it. +.Sp +\fB\s-1WARNING\s0:\fR +Some of the glyphs included into the AdobeStandard encoding are not +included into the Unicode standard. The most typical examples of such +glyphs are ligatures like \*(L'fi\*(R', \*(L'fl\*(R' etc. Because of this the font +designers may place them at various places. The converter tries to +do its best, if the glyphs have honest Adobe names and/or are +placed at the same codes as in the Microsoft fonts they will be +picked up. Otherwise a possible solution is to use the option \*(L'\fB\-L\fR\*(R' +with an external map. +.Ip "\(bu" 2 +\f(CW\fB-L \fIfile\fR[+[pid=\fI\fR,eid=\fI\fR,][\fIplane\fR]]\fR\fR \- Extract the fonts for the specified +language from a multi-language font using the map from this file. This is +rather like the option \*(L'\fB\-l\fR\*(R' but the encoding map is not +compiled into the program, it's taken from that file, so it's +easy to edit. Examples of such files are provided in +\f(CWmaps/adobe-standard-encoding.map\fR, \f(CWCP1250.map\fR. (\fB\s-1NOTE\s0:\fR +the \*(L'standard encoding\*(R' map does not include all the glyphs of the +AdobeStandard encoding, it's provided only as an example.) The +description of the supported map formats is in the file +\f(CWmaps/unicode-sample.map\fR. +.Sp +Likewise to \*(L'\fB\-l\fR\*(R', an argument may be specified after the map file +name. But in this case the argument has fixed meaning: it selects the +original \s-1TTF\s0 encoding table (the syntax is the same as in \*(L'\fB\-l plane\fR') +and/or a plane of the map file. The plane name also gets added after dash +to the font name. The plane is a concept used in the Eastern fonts with big +number of glyphs: one \s-1TTF\s0 font gets divided into multiple Type1 fonts, +each containing one plane of up to 256 glyphs. But with a little +creativity this concept may be used for other purposes of combining +multiple translation maps into one file. To extract multiple planes +from a \s-1TTF\s0 font \f(CWttf2pt1\fR must be run multiple times, each time with +a different plane name specified. +.Sp +The default original \s-1TTF\s0 encoding table used for the option \*(L'\fB\-L\fR\*(R' is +Unicode. The map files may include directives to specify different original +\s-1TTF\s0 encodings. However if the pid/eid pair is specified with +it overrides any original encoding specified in the map file. +.Ip "\(bu" 2 +\f(CW\fB-m \fItype\fR=\fIvalue\fR\fR\fR \- Set maximal or minimal limits of resources. +These limits control the the font generation by limiting the resources +that the font is permitted to require from the PostScript interpreter. +The currently supported types of limits are: +.Sp +\f(CW\fBh\fR\fR \- the maximal hint stack depth for the substituted hints. +The default value is 128, according to the limitation in X11. This seems to +be the lowest (and thus the safest) widespread value. To display the +hint stack depth required by each glyph in a \f(CW.t1a\fR file use the script +\f(CWscripts/cntstems.pl\fR. +.Ip "\(bu" 2 +\f(CW\fB-O \fIsuboptions\fR\fR\fR \- Outline processing options. The suboptions +may be lowercase or uppercase, the lowercase ones disable the features, +the corresponding uppercase suboptions enable the same features. +The suboptions to disable/enable features are: +.Sp +\f(CW\fBb/B\fR\fR \- Guessing of the ForceBold parameter. This parameter helps +the Type1 engine to rasterize the bold fonts properly at small sizes. +But the algorithm used to guess the proper value of this flag makes +that guess based solely on the font name. In rare cases that may cause +errors, in these cases you may want to disable this guessing. +\fBDefault: enabled\fR +.Sp +\f(CW\fBh/H\fR\fR \- Autogeneration of hints. The really complex outlines +may confuse the algorithm, so theoretically it may be useful +sometimes to disable them. Although up to now it seems that +even bad hints are better than no hints at all. +\fBDefault: enabled\fR +.Sp +\f(CW\fBu/U\fR\fR \- Hint substitution. Hint substitution is a technique +permitting generation of more detailed hints for the rasterizer. It allows +to use different sets of hints for different parts of a glyph and change +these sets as neccessary during rasterization (that's why \*(L"substituted"). +So it should improve the quality of the fonts rendered at small sizes. +But there are two catches: First, the X11 library has rather low limit for +the font size. More detailed hints increase the file size and thus increase +the chance of hitting this limit (that does not mean that you shall hit it +but you may if your fonts are particularly big). This is especially +probable for Unicode fonts converted with option \*(L'\fB\-a\fR\*(R', so you may want to +use \*(L'\fB\-a\fR\*(R' together with \*(L'\fB\-Ou\fR\*(R'. See \f(CWapp/X11/README\fR for the description of +a patch to X11 which fixes this problem. Second, some rasterizers (again, +X11 is the typical example) have a limitation for total number of hints +used when drawing a glyph (also known as the hint stack depth). If that +stack overflows the glyph is ignored. Starting from version 3.22 \f(CWttf2pt1\fR +uses algorithms to minimizing this depth, with the trade-off of slightly +bigger font files. The glyphs which still exceed the limit set by option +\&\*(R'\fB\-mh\fR\*(R' have all the substituted hints removed and only base hints left. +The algorithms seem to have been refined far enough to make the fonts with +substituted hints look better than the fonts without them or at least the +same. Still if the original fonts are not well-designed the detailed +hinting may emphasize the defects of the design, such as non-even thickness +of lines. So provided that you are not afraid of the X11 bug the best idea +would be to generate a font with this feature and without it, then compare +the results using the program \f(CWother/cmpf\fR (see the description +in \f(CWother/README\fR) and decide which one looks better. +\fBDefault: enabled\fR +.Sp +\f(CW\fBo/O\fR\fR \- Space optimization of the outlines\*(R' code. This kind of optimization +never hurts, and the only reason to disable this feature is for comparison +of the generated fonts with the fonts generated by the previous versions of +converter. Well, it _almost_ never hurts. As it turned out there exist +some brain-damaged printers which don't understand it. Actually this +feature does not change the outlines at all. The Type 1 font manual +provides a set of redundant operators that make font description shorter, +such as \*(L'10 hlineto\*(R' instead of \*(L'0 10 rlineto\*(R' to describe a horizontal +line. This feature enables use of these operators. +\fBDefault: enabled\fR +.Sp +\f(CW\fBs/S\fR\fR \- Smoothing of outlines. If the font is broken in some +way (even the ones that are not easily noticeable), such smoothing +may break it further. So disabling this feature is the first thing to be +tried if some font looks odd. But with smoothing off the hint generation +algorithms may not work properly too. +\fBDefault: enabled\fR +.Sp +\f(CW\fBt/T\fR\fR \- Auto-scaling to the 1000x1000 Type1 standard matrix. The +\s-1TTF\s0 fonts are described in terms of an arbitrary matrix up to +4000x4000. The converted fonts must be scaled to conform to +the Type1 standard. But the scaling introduces additional rounding +errors, so it may be curious sometimes to look at the font in its +original scale. +\fBDefault: enabled\fR +.Sp +\f(CW\fBv/V\fR\fR \- Do vectorization on the bitmap fonts. Functionally +\*(L"vectorization\*(R" is the same thing as \*(L"autotracing\*(R", a different word is +used purely to differentiate it from the Autotrace library. It tries to +produce nice smooth outlines from bitmaps. This feature is still a work +in progress though the results are already mostly decent. +\fBDefault: disabled\fR +.Sp +\f(CW\fBw/W\fR\fR \- Glyphs\*(R' width corection. This option is designed to be +used on broken fonts which specify too narrow widths for the +letters. You can tell that a font can benefit from this option +if you see that the characters are smashed together without +any whitespace between them. This option causes the converter +to set the character widths to the actual width of this character +plus the width of a typical vertical stem. But on the other hand +the well-designed fonts may have characters that look better if +their widths are set slightly narrower. Such well-designed fonts +will benefit from disabling this feature. You may want to convert +a font with and without this feature, compare the results and +select the better one. This feature may be used only on proportional +fonts, it has no effect on the fixed-width fonts. +\fBDefault: disabled\fR +.Sp +\f(CW\fBz/Z\fR\fR \- Use the Autotrace library on the bitmap fonts. The results +are horrible and \fBthe use of this option is not recommended\fR. This option is +present for experimental purposes. It may change or be removed in the +future. The working tracing can be achieved with option \f(CW\fB-OV\fR\fR. +\fBDefault: disabled\fR +.Ip "\(bu" 2 +\f(CW\fB-p \fIparser_name\fR\fR\fR \- Use the specified front-end parser to read the font file. +If this option is not used, ttf2pt1 selects the parser automatically based +on the suffix of the font file name, it uses the first parser in its +list that supports this font type. Now two parsers are supported: +.Sp +\ \ \f(CWttf\fR \- built-in parser for the ttf files (suffix \f(CW.ttf\fR) +.Sp +\ \ \f(CWbdf\fR \- built-in parser for the \s-1BDF\s0 files (suffix \f(CW.bdf\fR) +.Sp +\ \ \f(CWft\fR \- parser based on the FreeType-2 library (suffixes \f(CW.ttf\fR, +\&\f(CW.otf\fR, \f(CW.pfa\fR, \f(CW.pfb\fR) +.Sp +The parser \f(CWft\fR is \fB\s-1NOT\s0\fR linked in by default. See \f(CWMakefile\fR +for instructions how to enable it. We do no support this parser on +Windows: probably it will work but nobody tried and nobody knows how +to build it. +.Sp +The conversion of the bitmap fonts (such as \s-1BDF\s0) is simplistic yet, +producing jagged outlines. When converting such fonts, it might be +a good idea to turn off the hint substitution (using option \fB\-Ou\fR) +because the hints produced will be huge but not adding much to the +quality of the fonts. +.Ip "\(bu" 2 +\f(CW\fB-u \fInumber\fR\fR\fR \- Mark the font with this value as its +UniqueID. The UniqueID is used by the printers with the hard disks +to cache the rasterized characters and thus significantly +speed-up the printing. Some of those printers just can't +store the fonts without UniqueID on their disk.The problem +is that the \s-1ID\s0 is supposed to be unique, as it name says. And +there is no easy way to create a guaranteed unique \s-1ID\s0. Adobe specifies +the range 4000000-4999999 for private IDs but still it's difficult +to guarantee the uniqueness within it. So if you don't really need the +UniqueID don't use it, it's optional. Luckily there are a few millions of +possible IDs, so the chances of collision are rather low. +If instead of the number a special value \*(L'\f(CW\fBA\fR\fR\*(R' is given +then the converter generates the value of UniqueID automatically, +as a hash of the font name. (\fB\s-1NOTE\s0:\fR in the version 3.22 the +algorithm for autogeneration of UniqueID was changed to fit the values +into the Adobe-spacified range. This means that if UniqueIDs were used +then the printer's cache may need to be flushed before replacing the +fonts converted by an old version with fonts converted by a newer version). +A simple way to find if any of the fonts in a given directory have +duplicated UniqueIDs is to use the command: +.Sp +\f(CW\ \ cat *.pf[ab] | grep UniqueID | sort | uniq -c | grep -v ' 1 '\fR +.Sp +Or if you use \f(CWscripts/convert\fR it will do that for you automatically +plus it will also give the exact list of files with duplicate UIDs. +.Ip "\(bu" 2 +\f(CW\fB-v \fIsize\fR\fR\fR \- Re-scale the font to get the size of a typical uppercase +letter somewhere around the specified size. Actually, it re-scales +the whole font to get the size of one language-dependent letter to be +at least of the specified size. Now this letter is \*(L"A\*(R" in all the +supported languages. The size is specified in the points of the +Type 1 coordinate grids, the maximal value is 1000. This is an +experimental option and should be used with caution. It tries to +increase the visible font size for a given point size and thus make +the font more readable. But if overused it may cause the fonts to +look out of scale. As of now the interesting values of size for +this option seem to be located mostly between 600 and 850. This +re-scaling may be quite useful but needs more experience to +understand the balance of its effects. +.Ip "\(bu" 2 +\f(CW\fB-W \fIlevel\fR\fR\fR \- Select the verbosity level of the warnings. +Currently the levels from 0 to 4 are supported. Level 0 means no warnings +at all, level 4 means all the possible warnings. The default level is 3. +Other levels may be added in the future, so using the level number 99 is +recommended to get all the possible warnings. Going below level 2 is +not generally recommended because you may miss valuable information about +the problems with the fonts being converted. +.Ip "\(bu" 2 +\fBObsolete option:\fR +\f(CW\fB-A\fR\fR \- Print the font metrics (.afm file) instead of the font on \s-1STDOUT\s0. +Use \fB\-\s-1GA\s0\fR instead. +.Ip "\(bu" 2 +\fBVery obsolete option:\fR +.Sp +The algorithm that implemented the forced fixed width had major +flaws, so it was disabled. The code is still in the program and +some day it will be refined and returned back. Meanwhile the +option name \*(L'\fB\-f\fR\*(R' was reused for another option. The old version was: +.Sp +\f(CW\fB-f\fR\fR \- Don't try to force the fixed width of font. Normally the converter +considers the fonts in which the glyph width deviates by not more +than 5% as buggy fixed width fonts and forces them to have really +fixed width. If this is undesirable, it can be disabled by this option. +.PP +The \f(CW.pfa\fR font format supposes that the description of the characters +is binary encoded and encrypted. This converter does not encode or +encrypt the data by default, you have to specify the option \*(L'\fB\-e\fR\*(R' +or use the \f(CWt1asm\fR program to assemble (that means, encode and +encrypt) the font program. The \f(CWt1asm\fR program that is included with +the converter is actually a part of the \f(CWt1utils\fR package, rather old +version of which may be obtained from +.PP +http://ttf2pt1.sourceforge.net/t1utils.tar.gz +.PP +Note that \f(CWt1asm\fR from the old version of that package won't work properly +with the files generated by \f(CWttf2pt1\fR version 3.20 and later. Please use +\f(CWt1asm\fR packaged with \f(CWttf2pt1\fR or from the new version \f(CWt1utils\fR +instead. For a newer version of \f(CWt1utils\fR please look at +.PP +http://www.lcdf.org/~eddietwo/type/ +.SH "EXAMPLES" +So, the following command lines: +.PP +\f(CWttf2pt1 -e ttffont.ttf t1font\fR +.PP +\f(CWttf2pt1 ttffont.ttf - | t1asm >t1font.pfa\fR +.PP +represent two ways to get a working font. The benefit of the second form +is that other filters may be applied to the font between the converter +and assembler. +.SH "FILES" +.Ip "\(bu" 2 +\s-1TTF2PT1_LIBXDIR/\s0t1asm +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR\s0/* +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/\s0scripts/* +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/\s0other/* +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/README\s0 +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/FONTS\s0 +.SH "SEE ALSO" +.Ip "\(bu" 4 +the \fIttf2pt1_convert(1)\fR manpage +.Ip "\(bu" 4 +the \fIttf2pt1_x2gs(1)\fR manpage +.Ip "\(bu" 4 +the \fIt1asm(1)\fR manpage +.Ip "\(bu" 4 +ttf2pt1-announce@lists.sourceforge.net +.Sp +The mailing list with announcements about ttf2pt1. It is a moderated mailing +with extremely low traffic. Everyone is encouraged to subscribe to keep in +touch with the current status of project. To subscribe use the Web interface +at http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-announce. +If you have only e-mail access to the Net then send a subscribe request to +the development mailing list ttf2pt1-devel@lists.sourceforge.net and somebody +will help you with subscription. +.Ip "\(bu" 4 +ttf2pt1-devel@lists.sourceforge.net +.Sp +ttf2pt1-users@lists.sourceforge.net +.Sp +The ttf2pt1 mailing lists for development and users issues. They have not +that much traffic either. To subscribe use the Web interface at +http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-devel +and http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-users. +If you have only e-mail access to the Net then send a subscribe request to +the development mailing list ttf2pt1-devel@lists.sourceforge.net and somebody +will help you with subscription. +.Ip "\(bu" 4 +http://ttf2pt1.sourceforge.net +.Sp +The main page of the project. +.Sp +http://www.netspace.net.au/~mheath/ttf2pt1/ +.Sp +The old main page of the project. +.SH "BUGS" +It seems that many Eastern fonts use features of the TTF format that are +not supported by the ttf2pt1's built-in front-end parser. Because of +this for now we recommend using the FreeType-based parser (option +\&\*(R'\fB\-p ft\fR') with the \*(L"\f(CWplane\fR\*(R" language. +.Sh "Troubleshooting and bug reports" +Have problems with conversion of some font ? The converter dumps core ? Or your +printer refuses to understand the converted fonts ? Or some characters are +missing ? Or some characters look strange ? +.PP +Send the bug reports to the ttf2pt1 development mailing list at +ttf2pt1-devel@lists.sourceforge.net. +.PP +Try to collect more information about the problem and include it into +the bug report. (Of course, even better if you would provide a ready +fix, but just a detailed bug report is also good). Provide detailed +information about your problem, this will speed up the response greatly. +Don't just write \*(L"this font looks strange after conversion\*(R" but describe +what's exactly wrong with it: for example, what characters look wrong +and what exactly is wrong about their look. Providing a link to the +original font file would be also a good idea. Try to do a little +troublehooting and report its result. This not only would help with +the fix but may also give you a temporary work-around for the bug. +.PP +First, enable full warnings with option \*(L'\fB\-W99\fR\*(R', save them to +a file and read carefully. Sometimes the prolem is with a not implemented +feature which is reported in the warnings. Still, reporting about such +problems may be a good idea: some features were missed to cut corners, +in hope that no real font is using them. So a report about a font using +such a feature may motivate someone to implement it. Of course, you +may be the most motivated person: after all, you are the one wishing +to convert that font. ;\-) Seriously, the philosophy \*(L"scrath your own itch\*(R" +seems to be the strongest moving force behind the Open Source software. +.PP +The next step is playing with the options. This serves a dual purpose: +on one hand, it helps to localize the bug, on the other hand you may be +able to get a working version of the font for the meantime while the +bug is being fixed. The typical options to try out are: first \*(L'\fB\-Ou\fR\*(R', if +it does not help then \*(L'\fB\-Os\fR\*(R', then \*(L'\fB\-Oh\fR\*(R', then \*(L'\fB\-Oo\fR\*(R'. +They are described in a bit more detail above. Try them one by one +and in combinations. See if with them the resulting fonts look better. +.PP +On some fonts ttf2pt1 just crashes. Commonly that happens because the +font being converted is highly defective (although sometimes the bug +is in ttf2pt1 itself). In any case it should not crash, so the reports +about such cases will help to handle these defects properly in future. +.PP +We try to respond to the bug reports in a timely fashion but alas, this +may not always be possible, especially if the problem is complex. +This is a volunteer project and its resources are limited. Because +of this we would appreciate bug reports as detailed as possible, +and we would appreciate the ready fixes and contributions even more. +.SH "HISTORY" +Based on ttf2pfa by Andrew Weeks, and help from Frank Siegert. +.PP +Modification by Mark Heath. +.PP +Further modification by Sergey Babkin. +.PP +The Type1 assembler by I. Lee Hetherington with modifications by +Kai-Uwe Herbing. + +.rn }` '' +.IX Title "TTF2PT1 1" +.IX Name "TTF2PT1 - A True Type to PostScript Type 1 Font Converter" + +.IX Header "NAME" + +.IX Header "SYNOPSIS" + +.IX Header "DESCRIPTION" + +.IX Header "OPTIONS" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "EXAMPLES" + +.IX Header "FILES" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "SEE ALSO" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "BUGS" + +.IX Subsection "Troubleshooting and bug reports" + +.IX Header "HISTORY" + Index: xc/extras/ttf2pt1/ttf2pt1.c =================================================================== RCS file: xc/extras/ttf2pt1/ttf2pt1.c diff -N xc/extras/ttf2pt1/ttf2pt1.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ttf2pt1.c 13 Apr 2004 02:45:10 -0000 @@ -0,0 +1,2721 @@ +/* + * True Type Font to Adobe Type 1 font converter + * By Mark Heath + * Based on ttf2pfa by Andrew Weeks + * With help from Frank M. Siegert + * + * see COPYRIGHT for full copyright notice + * +*********************************************************************** + * + * Sergey Babkin , + * + * Added post-processing of resulting outline to correct the errors + * both introduced during conversion and present in the original font, + * autogeneration of hints (has yet to be improved though) and BlueValues, + * scaling to 1000x1000 matrix, option to print the result on STDOUT, + * support of Unicode to CP1251 conversion, optimization of the + * resulting font code by space (that improves the speed too). Excluded + * the glyphs that are unaccessible through the encoding table from + * the output file. Added the built-in Type1 assembler (taken from + * the `t1utils' package). + * +*********************************************************************** + * + * Thomas Henlich + * + * Added generation of .afm file (font metrics) + * Read encoding information from encoding description file + * Fixed bug in error message about unknown language ('-l' option) + * Added `:' after %%!PS-AdobeFont-1.0 + * changed unused entries in ISOLatin1Encoding[] from .notdef to c127,c128... + * +*********************************************************************** + * + * Thomas Henlich + * + * Added generation of .afm file (font metrics) + * +*********************************************************************** + * + * Bug Fixes: +************************************************************************ + * + * Sun, 21 Jun 1998 Thomas Henlich + * 1. "width" should be "short int" because otherwise: + * characters with negative widths (e.g. -4) become *very* wide (65532) + * 2. the number of /CharStrings is numglyphs and not numglyphs+1 + * +*********************************************************************** + * + * + * + * The resultant font file produced by this program still needs to be ran + * through t1asm (from the t1utils archive) to produce a completely valid + * font. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _GNU_SOURCE +#include +#endif + +#ifndef WINDOWS +# include +# include +# define BITBUCKET "/dev/null" +# include +#else +# define WINDOWS_FUNCTIONS /* ask to define functions - in one file only */ +# include "windows.h" +# define BITBUCKET "NUL" +# define snprintf _snprintf +#endif + +#ifdef XP_PSTEXT +#include +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_BBOX_H +#include FT_GLYPH_H + +#include FT_CONFIG_CONFIG_H +#include FT_CONFIG_OPTIONS_H +#include FT_ERRORS_H +#include FT_SYSTEM_H +#include FT_IMAGE_H +#include FT_TYPES_H +#include FT_OUTLINE_H +#include FT_MODULE_H +#include FT_RENDER_H +#include FT_TYPE1_TABLES_H +#include FT_TRUETYPE_IDS_H +#include FT_TRUETYPE_TAGS_H +#include FT_MULTIPLE_MASTERS_H +#include FT_SFNT_NAMES_H + +#include "os.h" +#include "Xproto.h" +#include "font.h" +#include "fontstruct.h" +#include "fntfilst.h" +#include "fontutil.h" +#include "fontenc.h" +#include "ft.h" +#include "ftfuncs.h" +#endif /* XP_PSTEXT */ + +#include "pt1.h" +#include "global.h" +#include "version.h" + +/* globals */ + +/* table of front-ends */ + +#ifdef USE_TTF +extern struct frontsw ttf_sw; +#endif /* USE_TTF */ +#ifdef USE_BDF +extern struct frontsw bdf_sw; +#endif /* USE_BDF */ +#if defined(USE_FREETYPE) + extern struct frontsw freetype_sw; +#endif + +struct frontsw *frontswtab[] = { +#ifdef USE_BDF + &bdf_sw, +#endif /* USE_BDF */ +#if defined(USE_FREETYPE) && defined(PREFER_FREETYPE) + &freetype_sw, +#endif +#ifdef USE_TTF + &ttf_sw, +#endif /* USE_TTF */ +#if defined(USE_FREETYPE) && !defined(PREFER_FREETYPE) + &freetype_sw, +#endif + NULL /* end of table */ +}; + +struct frontsw *cursw=0; /* the active front end */ +char *front_arg=""; /* optional argument */ + +/* options */ +int encode = 0; /* encode the resulting file */ +int pfbflag = 0; /* produce compressed file */ +int wantafm=0; /* want to see .afm instead of .t1a on stdout */ +int correctvsize=0; /* try to correct the vertical size of characters */ +int wantuid = 0; /* user wants UniqueID entry in the font */ +int allglyphs = 0; /* convert all glyphs, not only 256 of them */ +int warnlevel = 3; /* the level of permitted warnings */ +int forcemap = 0; /* do mapping even on non-Unicode fonts */ +/* options - maximal limits */ +int max_stemdepth = 128; /* maximal depth of stem stack in interpreter (128 - limit from X11) */ +/* options - debugging */ +int absolute = 0; /* print out in absolute values */ +int reverse = 1; /* reverse font to Type1 path directions */ +/* options - suboptions of Outline Processing, defaults are set in table */ +int optimize; /* enables space optimization */ +int smooth; /* enable smoothing of outlines */ +int transform; /* enables transformation to 1000x1000 matrix */ +int hints; /* enables autogeneration of hints */ +int subhints; /* enables autogeneration of substituted hints */ +int trybold; /* try to guess whether the font is bold */ +int correctwidth; /* try to correct the character width */ +int vectorize; /* vectorize the bitmaps */ +int use_autotrace; /* use the autotrace library on bitmap */ +/* options - suboptions of File Generation, defaults are set in table */ +int gen_pfa; /* generate the font file */ +int gen_afm; /* generate the metrics file */ +int gen_dvienc; /* generate the dvips encoding file */ + +/* not quite options to select a particular source encoding */ +int force_pid = -1; /* specific platform id */ +int force_eid = -1; /* specific encoding id */ + +/* structure to define the sub-option lists controlled by the + * case: uppercase enables them, lowercase disables + */ +struct subo_case { + char disbl; /* character to disable - enforced lowercase */ + char enbl; /* character to enable - auto-set as toupper(disbl) */ + int *valp; /* pointer to the actual variable containing value */ + int dflt; /* default value */ + char *descr; /* description */ +}; + +#ifdef DEBUG +int debug = DEBUG; /* debugging flag */ +#else +int debug = 0; +#endif /* DEBUG */ + +FILE *null_file, *pfa_file, *afm_file, *dvienc_file; +int numglyphs; +struct font_metrics fontm; + +/* non-globals */ +static char *strUID = 0; /* user-supplied UniqueID */ +static unsigned long numUID; /* auto-generated UniqueID */ + +static int ps_fmt_3 = 0; +static double scale_factor, original_scale_factor; + +static char *glyph_rename[ENCTABSZ]; + +/* the names assigned if the original font + * does not specify any + */ + +static char *Fmt3Encoding[256] = { + "c0", "c1", "c2", "c3", + "c4", "c5", "c6", "c7", + "c8", "c9", "c10", "c11", + "c12", "CR", "c14", "c15", + "c16", "c17", "c18", "c19", + "c20", "c21", "c22", "c23", + "c24", "c25", "c26", "c27", + "c28", "c29", "c30", "c31", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "c127", + "c128", "c129", "quotesinglbase", "florin", + "quotedblbase", "ellipsis", "dagger", "daggerdbl", + "circumflex", "perthousand", "Scaron", "guilsinglleft", + "OE", "c141", "c142", "c143", + "c144", "quoteleft", "quoteright", "quotedblleft", + "quotedblright", "bullet", "endash", "emdash", + "tilde", "trademark", "scaron", "guilsinglright", + "oe", "c157", "c158", "Ydieresis", + "nbspace", "exclamdown", "cent", "sterling", + "currency", "yen", "brokenbar", "section", + "dieresis", "copyright", "ordfeminine", "guillemotleft", + "logicalnot", "sfthyphen", "registered", "macron", + "degree", "plusminus", "twosuperior", "threesuperior", + "acute", "mu", "paragraph", "periodcentered", + "cedilla", "onesuperior", "ordmasculine", "guillemotright", + "onequarter", "onehalf", "threequarters", "questiondown", + "Agrave", "Aacute", "Acircumflex", "Atilde", + "Adieresis", "Aring", "AE", "Ccedilla", + "Egrave", "Eacute", "Ecircumflex", "Edieresis", + "Igrave", "Iacute", "Icircumflex", "Idieresis", + "Eth", "Ntilde", "Ograve", "Oacute", + "Ocircumflex", "Otilde", "Odieresis", "multiply", + "Oslash", "Ugrave", "Uacute", "Ucircumflex", + "Udieresis", "Yacute", "Thorn", "germandbls", + "agrave", "aacute", "acircumflex", "atilde", + "adieresis", "aring", "ae", "ccedilla", + "egrave", "eacute", "ecircumflex", "edieresis", + "igrave", "iacute", "icircumflex", "idieresis", + "eth", "ntilde", "ograve", "oacute", + "ocircumflex", "otilde", "odieresis", "divide", + "oslash", "ugrave", "uacute", "ucircumflex", + "udieresis", "yacute", "thorn", "ydieresis" +}; + +#ifdef notdef /* { */ +/* This table is not used anywhere in the code + * so it's ifdef-ed out by default but left in + * the source code for reference purposes (and + * possibly for future use) + */ + +static char *ISOLatin1Encoding[256] = { + ".null", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "CR", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "c127", + "c128", "c129", "quotesinglbase", "florin", + "quotedblbase", "ellipsis", "dagger", "daggerdbl", + "circumflex", "perthousand", "Scaron", "guilsinglleft", + "OE", "c141", "c142", "c143", + "c144", "quoteleft", "quoteright", "quotedblleft", + "quotedblright", "bullet", "endash", "emdash", + "tilde", "trademark", "scaron", "guilsinglright", + "oe", "c157", "c158", "Ydieresis", + "nbspace", "exclamdown", "cent", "sterling", + "currency", "yen", "brokenbar", "section", + "dieresis", "copyright", "ordfeminine", "guillemotleft", + "logicalnot", "sfthyphen", "registered", "macron", + "degree", "plusminus", "twosuperior", "threesuperior", + "acute", "mu", "paragraph", "periodcentered", + "cedilla", "onesuperior", "ordmasculine", "guillemotright", + "onequarter", "onehalf", "threequarters", "questiondown", + "Agrave", "Aacute", "Acircumflex", "Atilde", + "Adieresis", "Aring", "AE", "Ccedilla", + "Egrave", "Eacute", "Ecircumflex", "Edieresis", + "Igrave", "Iacute", "Icircumflex", "Idieresis", + "Eth", "Ntilde", "Ograve", "Oacute", + "Ocircumflex", "Otilde", "Odieresis", "multiply", + "Oslash", "Ugrave", "Uacute", "Ucircumflex", + "Udieresis", "Yacute", "Thorn", "germandbls", + "agrave", "aacute", "acircumflex", "atilde", + "adieresis", "aring", "ae", "ccedilla", + "egrave", "eacute", "ecircumflex", "edieresis", + "igrave", "iacute", "icircumflex", "idieresis", + "eth", "ntilde", "ograve", "oacute", + "ocircumflex", "otilde", "odieresis", "divide", + "oslash", "ugrave", "uacute", "ucircumflex", + "udieresis", "yacute", "thorn", "ydieresis" +}; + +#endif /* } notdef */ + +static char *adobe_StandardEncoding[256] = { + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "quoteleft", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "exclamdown", "cent", "sterling", + "fraction", "yen", "florin", "section", + "currency", "quotesingle", "quotedblleft", "guillemotleft", + "guilsinglleft", "guilsinglright", "fi", "fl", + ".notdef", "endash", "dagger", "daggerdbl", + "periodcentered", ".notdef", "paragraph", "bullet", + "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", + "ellipsis", "perthousand", ".notdef", "questiondown", + ".notdef", "grave", "acute", "circumflex", + "tilde", "macron", "breve", "dotaccent", + "dieresis", ".notdef", "ring", "cedilla", + ".notdef", "hungarumlaut", "ogonek", "caron", + "emdash", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "AE", ".notdef", "ordfeminine", + ".notdef", ".notdef", ".notdef", ".notdef", + "Lslash", "Oslash", "OE", "ordmasculine", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "ae", ".notdef", ".notdef", + ".notdef", "dotlessi", ".notdef", ".notdef", + "lslash", "oslash", "oe", "germandbls", + ".notdef", ".notdef", ".notdef", ".notdef" +}; + +/* + * Decription of the supported conversions from Unicode + * + * SB + * Yes, I know that the compiled-in conversion is stupid but + * it is simple to implement and allows not to worry about the + * filesystem context. After all, the source is always available + * and adding another language to it is easy. + * + * The language name is expected to be the same as the subdirectory name + * in the `encodings' directory (for possible future extensions). + * The primary use of the aliases is for guessing based on the current + * locale. + */ + +#define MAXUNIALIAS 10 +#define MAXUNITABLES 3 + +/* the character used as the language argument separator */ +#define LANG_ARG_SEP '+' + + +/* + * Types of language-related routines. Arguments are: + * name is the glyph name + * arg is the user-specified language-dependent argument + * which can for example select the subfont plane for Eastern fonts. + * If none is supplied by user then an empty string ("") is passed. + * If no language is specified by user and auto-guessing happens + * then NULL is passed. + * when shows if the conversion by name was called before conversion by + * map or after (it's called twice) + */ + +/* type of the Unicode map initialization routine */ +typedef void uni_init_t(char *arg); + +/* type of Unicode converter-by-name function + * it's called for each glyph twice: one time for each glyph + * before doing conversion by map and one time after + */ +typedef int uni_conv_t(char *name, char *arg, int when); +#define UNICONV_BYNAME_BEFORE 0 +#define UNICONV_BYNAME_AFTER 1 + +struct uni_language { + uni_init_t *init[MAXUNITABLES]; /* map initialization routines */ + uni_conv_t *convbyname; /* the name-based conversion function */ + char *name; /* the language name */ + char *descr; /* description */ + char *alias[MAXUNIALIAS]; /* aliases of the language name */ + int sample_upper; /* code of some uppercase character for correctvsize() */ +}; + +/* the converter routines have an option of adding this suffix to the font name */ +static char *uni_font_name_suffix = ""; /* empty by default */ +/* this buffer may be used to store the suffix */ +#define UNI_MAX_SUFFIX_LEN 100 +static char uni_suffix_buf[UNI_MAX_SUFFIX_LEN+1]; + +/* + * Prototypes of the conversion routines + */ + +static uni_init_t unicode_latin1; +static uni_init_t unicode_latin2; +static uni_init_t unicode_latin4; +static uni_init_t unicode_latin5; +static uni_init_t unicode_cyrillic; +static uni_init_t unicode_adobestd; +static uni_init_t unicode_plane; +static uni_conv_t unicode_adobestd_byname; + +static uni_init_t unicode_init_user; + +/* + * The order of descriptions is important: if we can't guess the + * language we just call all the conversion routines in order until + * we find one that understands this glyph. + */ +static struct uni_language uni_lang[]= { + /* pseudo-language for all the languages using Latin1 */ + { + { unicode_latin1 }, + 0, /* no name-based mapping */ + "latin1", + "works for most of the Western languages", + { "en_", "de_", "fr_", "nl_", "no_", "da_", "it_" }, + 'A' + }, + { /* by Szalay Tamas */ + { unicode_latin2 }, + 0, /* no name-based mapping */ + "latin2", + "works for Central European languages", + { "hu_","pl_","cz_","si_","sk_" }, + 'A' + }, + { /* by Rièardas Èepas */ + { unicode_latin4 }, + 0, /* no name-based mapping */ + "latin4", + "works for Baltic languages", + { "lt_", "lv_" }, /* doubt about ee_ */ + 'A' + }, + { /* by Turgut Uyar */ + { unicode_latin5 }, + 0, /* no name-based mapping */ + "latin5", + "for Turkish", + { "tr_" }, + 'A' + }, + { /* by Zvezdan Petkovic */ + { unicode_cyrillic, unicode_latin1 }, + 0, /* no name-based mapping */ + "cyrillic", + "in Windows encoding", + { "bg_", "be_", "mk_", "ru_", "sr_", "su_", "uk_" }, + 'A' + }, + { + { unicode_cyrillic, unicode_latin1 }, + 0, /* no name-based mapping */ + "russian", + "obsolete, use cyrillic instead", + { 0 }, + 'A' + }, + { + { unicode_cyrillic, unicode_latin1 }, + 0, /* no name-based mapping */ + "bulgarian", + "obsolete, use cyrillic instead", + { 0 }, + 'A' + }, + { + { unicode_adobestd }, + unicode_adobestd_byname, + "adobestd", + "Adobe Standard, expected by TeX", + { NULL }, + 'A' + }, + { + { unicode_plane }, + 0, /* no name-based mapping */ + "plane", + "one plane of Unicode or other multi-byte encoding as is", + { NULL }, + 0 /* no easy way to predict the capital letters */ + }, +}; + +static struct uni_language uni_lang_user = { + { unicode_init_user }, + 0, /* no name-based mapping */ + 0, /* no name */ + 0, /* no description */ + { 0 }, + 0 /* no sample */ +}; + +static struct uni_language *uni_lang_selected=0; /* 0 means "unknown, try all" */ +static int uni_sample='A'; /* sample of an uppercase character */ +static char *uni_lang_arg=""; /* user-supplied language-dependent argument */ + +extern int runt1asm(int); + +/* + * user-defined loadable maps + */ + + +/* The idea begind buckets is to avoid comparing every code with all ENCTABSZ codes in table. + * All the 16-bit unicode space is divided between a number of equal-sized buckets. + * Initially all the buckets are marked with 0. Then if any code in the bucket is + * used it's marked with 1. Later during translation we check the code's bucket first + * and it it's 0 then return failure right away. This may be useful for + * Chinese fonts with many thousands of glyphs. + */ + +#define BUCKET_ID_BITS 11 +#define MARK_UNI_BUCKET(unicode) SET_BITMAP(uni_user_buckets, (unicode)>>(16-BUCKET_ID_BITS)) +#define IS_UNI_BUCKET(unicode) IS_BITMAP(uni_user_buckets, (unicode)>>(16-BUCKET_ID_BITS)) + +static DEF_BITMAP(uni_user_buckets, 1< UNI_MAX_SUFFIX_LEN-1) + arg = NULL; + else { + sprintf(uni_suffix_buf, "-%s", arg); + uni_font_name_suffix = uni_suffix_buf; + } + } + + /* now read in the encoding description file, if requested */ + if ((unicode_map_file = fopen(path, "r")) == NULL) { + fprintf(stderr, "**** Cannot access map file '%s' ****\n", path); + exit(1); + } + + sawplane = 0; + if(arg==NULL) + enabled = found = 1; + else + enabled = found = 0; + + lineno=0; curpos=0; + while (fgets (buffer, UNIBFSZ, unicode_map_file) != NULL) { + char name[UNIBFSZ]; + + lineno++; + + if(sscanf(buffer, "plane %s", name)==1) { + sawplane = 1; + if(arg == 0) { + fprintf(stderr, "**** map file '%s' requires plane name\n", path); + fprintf(stderr, "for example:\n"); + fprintf(stderr, " ttf2pt1 -L %s%c[pid=N,eid=N,]%s ...\n", + path, LANG_ARG_SEP, name); + fprintf(stderr, "to select plane '%s'\n", name); + exit(1); + } + if( !strcmp(arg, name) ) { + enabled = found = 1; + curpos = 0; + } else { + enabled = 0; + if(found) /* no need to read further */ + break; + } + continue; + } + + if(sscanf(buffer, "id %d %d", pid, eid)==2) { + if( !overid /* only if the user has not overriden */ + && (enabled || !sawplane) ) { + force_pid = pid; force_eid = eid; + forcemap = 1; + } + continue; + } + + if( !enabled ) + continue; /* skip to the next plane */ + + if( sscanf(buffer, "at %i", &curpos) == 1 ) { + if(curpos > 255) { + fprintf(stderr, "**** map file '%s' line %d: code over 255\n", path, lineno); + exit(1); + } + if(ISDBG(EXTMAP)) fprintf(stderr, "=== at 0x%x\n", curpos); + continue; + } + + /* try the format of Roman Czyborra's files */ + if ( sscanf (buffer, " =%x U+%4x", &code, &unicode) == 2 + /* try the format of Linux locale charmap file */ + || sscanf (buffer, " <%*s /x%x ", &code, &unicode) == 2 ) { + if (code < ENCTABSZ) { + if(code >= enctabsz) enctabsz=code+1; + unicode_map[code] = unicode; + glyph_rename[code] = NULL; + } + } + /* try the format with glyph renaming */ + else if (sscanf (buffer, " !%x U+%4x %128s", &code, + &unicode, name) == 3) { + if (code < ENCTABSZ) { + if(code >= enctabsz) enctabsz=code+1; + unicode_map[code] = unicode; + glyph_rename[code] = strdup(name); + } + } + /* try the compact sequence format */ + else if( (n=sscanf(buffer, " %i%n", &unicode, &cnt)) == 1 ) { + p = buffer; + do { + if(curpos > 255) { + fprintf(stderr, "**** map file '%s' line %d: code over 255 for unicode 0x%x\n", + path, lineno, unicode); + exit(1); + } + if(ISDBG(EXTMAP)) fprintf(stderr, "=== 0x%d -> 0x%x\n", curpos, unicode); + unicode_map[curpos++] = unicode; + p += cnt; + if( sscanf(p, " %[,-]%n", &next,&cnt) == 1 ) { + if(ISDBG(EXTMAP)) fprintf(stderr, "=== next: '%c'\n", next); + p += cnt; + if( next == '-' ) { /* range */ + if ( sscanf(p, " %i%n", &unicode2, &cnt) != 1 ) { + fprintf(stderr, "**** map file '%s' line %d: missing end of range\n", path, lineno); + exit(1); + } + p += cnt; + if(ISDBG(EXTMAP)) fprintf(stderr, "=== range 0x%x to 0x%x\n", unicode, unicode2); + for(unicode++; unicode <= unicode2; unicode++) { + if(curpos > 255) { + fprintf(stderr, "**** map file '%s' line %d: code over 255 in unicode range ...-0x%x\n", + path, lineno, unicode2); + exit(1); + } + if(ISDBG(EXTMAP)) fprintf(stderr, "=== 0x%x -> 0x%x\n", curpos, unicode); + unicode_map[curpos++] = unicode; + } + } + } + } while ( sscanf(p, " %i%n", &unicode, &cnt) == 1 ); + } + + } + + fclose (unicode_map_file); + + if( !found ) { + fprintf(stderr, "**** map file '%s' has no plane '%s'\n", path, arg); + exit(1); + } + + if(unicode_map['A'] == 'A') + uni_sample = 'A'; /* seems to be compatible with Latin */ + else + uni_sample = 0; /* don't make any assumptions */ +} + +/* + * by Zvezdan Petkovic + */ +static void +unicode_cyrillic( + char *arg +) +{ + int i; + static unsigned int cyrillic_unicode_map[] = { + 0x0402, 0x0403, 0x201a, 0x0453, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x20ac, 0x2030, 0x0409, 0x2039, 0x040a, 0x040c, 0x040b, 0x040f, /* 88 */ + 0x0452, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, 0x0459, 0x203a, 0x045a, 0x045c, 0x045b, 0x045f, /* 98 */ + 0x00a0, 0x040e, 0x045e, 0x0408, 0x00a4, 0x0490, 0x00a6, 0x00a7, /* A0 */ + 0x0401, 0x00a9, 0x0404, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0407, /* A8 */ + 0x00b0, 0x00b1, 0x0406, 0x0456, 0x0491, 0x00b5, 0x00b6, 0x00b7, /* B0 */ + 0x0451, 0x2116, 0x0454, 0x00bb, 0x0458, 0x0405, 0x0455, 0x0457, /* B8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0xBF; i++) + unicode_map[i] = cyrillic_unicode_map[i-0x80]; + + for(i=0xC0; i<=0xFF; i++) + unicode_map[i] = i+0x350; + +} + +static void +unicode_latin1( + char *arg +) +{ + int i; + static unsigned int latin1_unicode_map[] = { + 0x20ac, -1, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, /* 88 */ + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, /* 98 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0x9F; i++) + unicode_map[i] = latin1_unicode_map[i-0x80]; + + for(i=0xA0; i<=0xFF; i++) + unicode_map[i] = i; +} + +static void +unicode_adobestd( + char *arg +) +{ + int i; + static unsigned int adobestd_unicode_map[] = { + -1, 0x00a1, 0x00a2, 0x00a3, 0x2215, 0x00a5, 0x0192, 0x00a7, /* A0 */ + 0x00a4, 0x0027, 0x201c, 0x00ab, 0x2039, 0x203a, 0xfb01, 0xfb02, /* A8 */ + -1, 0x2013, 0x2020, 0x2021, 0x2219, -1, 0x00b6, 0x2022, /* B0 */ + 0x201a, 0x201e, 0x201d, 0x00bb, 0x2026, 0x2030, -1, 0x00bf, /* B8 */ + -1, 0x0060, 0x00b4, 0x02c6, 0x02dc, 0x02c9, 0x02d8, 0x02d9, /* C0 */ + 0x00a8, -1, 0x02da, 0x00b8, -1, 0x02dd, 0x02db, 0x02c7, /* C8 */ + 0x2014, -1, -1, -1, -1, -1, -1, -1, /* D0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* D8 */ + -1, 0x00c6, -1, 0x00aa, -1, -1, -1, -1, /* E0 */ + 0x0141, 0x00d8, 0x0152, 0x00ba, -1, -1, -1, -1, /* E8 */ + -1, 0x00e6, -1, -1, -1, 0x0131, -1, -1, /* F0 */ + 0x0142, 0x00f8, 0x0153, 0x00df, -1, -1, -1, -1, /* F8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + unicode_map[0x27] = 0x2019; + unicode_map[0x60] = -1; + + /* 0x80 to 0x9F is a hole */ + + for(i=0xA0; i<=0xFF; i++) + unicode_map[i] = adobestd_unicode_map[i-0xA0]; +} + +/* + * Not all of the Adobe glyphs are in the Unicode + * standard maps, so the font creators have + * different ideas about their codes. Because + * of this we try to map based on the glyph + * names instead of Unicode codes. If there are + * no glyph names (ps_fmt_3!=0) we fall back + * to the code-based scheme. + */ + +static int +unicode_adobestd_byname( + char *name, + char *arg, + int where +) +{ + int i; + + /* names always take precedence over codes */ + if(where == UNICONV_BYNAME_AFTER) + return -1; + + for(i=32; i<256; i++) { + if(!strcmp(name, adobe_StandardEncoding[i])) + return i; + } + return -1; + +} + +static void +unicode_latin2( + char *arg +) +{ + int i; + static unsigned int latin2_unicode_map[] = { + 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, /* A0 */ + 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, /* A8 */ + 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, /* B0 */ + 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, /* B8 */ + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, /* C0 */ + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, /* C8 */ + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, /* D0 */ + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, /* D8 */ + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, /* E0 */ + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, /* E8 */ + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, /* F0 */ + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, /* F8 */ + }; + + for(i=0; i<=0x7E; i++) + unicode_map[i] = i; + + /* 7F-9F are unused */ + + for(i=0xA0; i<=0xFF; i++) + unicode_map[i] = latin2_unicode_map[i-0xA0]; +} + +static void +unicode_latin4( + char *arg +) +{ + int i; + static unsigned int latin4_unicode_map[] = { + 0x0080, 0x0081, 0x201a, 0x0192, -1, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x02c6, 0x2030, -1, 0x2039, 0x0152, 0x008d, 0x008e, 0x008f, /* 88 */ + 0x201e, 0x201c, 0x2019, -1, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, -1, 0x203a, 0x0153, 0x009d, 0x009e, 0x0178, /* 98 */ + 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, /* A0 */ + 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, /* A8 */ + 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, /* B0 */ + 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, /* B8 */ + 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, /* C0 */ + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, /* C8 */ + 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, /* D0 */ + 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, /* D8 */ + 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, /* E0 */ + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, /* E8 */ + 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, /* F0 */ + 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, /* F8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0xFF; i++) + unicode_map[i] = latin4_unicode_map[i-0x80]; + +#if 0 /* for documentation purposes only */ + case 0x201e: return 0x90; /* these two quotes are a hack only */ + case 0x201c: return 0x91; /* these two quotes are a hack only */ + case 0x00A0: return 0xA0; /* NO-BREAK SPACE */ + case 0x0104: return 0xA1; /* LATIN CAPITAL LETTER A WITH OGONEK */ + case 0x0138: return 0xA2; /* LATIN SMALL LETTER KRA */ + case 0x0156: return 0xA3; /* LATIN CAPITAL LETTER R WITH CEDILLA */ + case 0x00A4: return 0xA4; /* CURRENCY SIGN */ + case 0x0128: return 0xA5; /* LATIN CAPITAL LETTER I WITH TILDE */ + case 0x013B: return 0xA6; /* LATIN CAPITAL LETTER L WITH CEDILLA */ + case 0x00A7: return 0xA7; /* SECTION SIGN */ + case 0x00A8: return 0xA8; /* DIAERESIS */ + case 0x0160: return 0xA9; /* LATIN CAPITAL LETTER S WITH CARON */ + case 0x0112: return 0xAA; /* LATIN CAPITAL LETTER E WITH MACRON */ + case 0x0122: return 0xAB; /* LATIN CAPITAL LETTER G WITH CEDILLA */ + case 0x0166: return 0xAC; /* LATIN CAPITAL LETTER T WITH STROKE */ + case 0x00AD: return 0xAD; /* SOFT HYPHEN */ + case 0x017D: return 0xAE; /* LATIN CAPITAL LETTER Z WITH CARON */ + case 0x00AF: return 0xAF; /* MACRON */ + case 0x00B0: return 0xB0; /* DEGREE SIGN */ + case 0x0105: return 0xB1; /* LATIN SMALL LETTER A WITH OGONEK */ + case 0x02DB: return 0xB2; /* OGONEK */ + case 0x0157: return 0xB3; /* LATIN SMALL LETTER R WITH CEDILLA */ + case 0x00B4: return 0xB4; /* ACUTE ACCENT */ + case 0x0129: return 0xB5; /* LATIN SMALL LETTER I WITH TILDE */ + case 0x013C: return 0xB6; /* LATIN SMALL LETTER L WITH CEDILLA */ + case 0x02C7: return 0xB7; /* CARON */ + case 0x00B8: return 0xB8; /* CEDILLA */ + case 0x0161: return 0xB9; /* LATIN SMALL LETTER S WITH CARON */ + case 0x0113: return 0xBA; /* LATIN SMALL LETTER E WITH MACRON */ + case 0x0123: return 0xBB; /* LATIN SMALL LETTER G WITH CEDILLA */ + case 0x0167: return 0xBC; /* LATIN SMALL LETTER T WITH STROKE */ + case 0x014A: return 0xBD; /* LATIN CAPITAL LETTER ENG */ + case 0x017E: return 0xBE; /* LATIN SMALL LETTER Z WITH CARON */ + case 0x014B: return 0xBF; /* LATIN SMALL LETTER ENG */ + case 0x0100: return 0xC0; /* LATIN CAPITAL LETTER A WITH MACRON */ + case 0x00C1: return 0xC1; /* LATIN CAPITAL LETTER A WITH ACUTE */ + case 0x00C2: return 0xC2; /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ + case 0x00C3: return 0xC3; /* LATIN CAPITAL LETTER A WITH TILDE */ + case 0x00C4: return 0xC4; /* LATIN CAPITAL LETTER A WITH DIAERESIS */ + case 0x00C5: return 0xC5; /* LATIN CAPITAL LETTER A WITH RING ABOVE */ + case 0x00C6: return 0xC6; /* LATIN CAPITAL LIGATURE AE */ + case 0x012E: return 0xC7; /* LATIN CAPITAL LETTER I WITH OGONEK */ + case 0x010C: return 0xC8; /* LATIN CAPITAL LETTER C WITH CARON */ + case 0x00C9: return 0xC9; /* LATIN CAPITAL LETTER E WITH ACUTE */ + case 0x0118: return 0xCA; /* LATIN CAPITAL LETTER E WITH OGONEK */ + case 0x00CB: return 0xCB; /* LATIN CAPITAL LETTER E WITH DIAERESIS */ + case 0x0116: return 0xCC; /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ + case 0x00CD: return 0xCD; /* LATIN CAPITAL LETTER I WITH ACUTE */ + case 0x00CE: return 0xCE; /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ + case 0x012A: return 0xCF; /* LATIN CAPITAL LETTER I WITH MACRON */ + case 0x0110: return 0xD0; /* LATIN CAPITAL LETTER D WITH STROKE */ + case 0x0145: return 0xD1; /* LATIN CAPITAL LETTER N WITH CEDILLA */ + case 0x014C: return 0xD2; /* LATIN CAPITAL LETTER O WITH MACRON */ + case 0x0136: return 0xD3; /* LATIN CAPITAL LETTER K WITH CEDILLA */ + case 0x00D4: return 0xD4; /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ + case 0x00D5: return 0xD5; /* LATIN CAPITAL LETTER O WITH TILDE */ + case 0x00D6: return 0xD6; /* LATIN CAPITAL LETTER O WITH DIAERESIS */ + case 0x00D7: return 0xD7; /* MULTIPLICATION SIGN */ + case 0x00D8: return 0xD8; /* LATIN CAPITAL LETTER O WITH STROKE */ + case 0x0172: return 0xD9; /* LATIN CAPITAL LETTER U WITH OGONEK */ + case 0x00DA: return 0xDA; /* LATIN CAPITAL LETTER U WITH ACUTE */ + case 0x00DB: return 0xDB; /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ + case 0x00DC: return 0xDC; /* LATIN CAPITAL LETTER U WITH DIAERESIS */ + case 0x0168: return 0xDD; /* LATIN CAPITAL LETTER U WITH TILDE */ + case 0x016A: return 0xDE; /* LATIN CAPITAL LETTER U WITH MACRON */ + case 0x00DF: return 0xDF; /* LATIN SMALL LETTER SHARP S */ + case 0x0101: return 0xE0; /* LATIN SMALL LETTER A WITH MACRON */ + case 0x00E1: return 0xE1; /* LATIN SMALL LETTER A WITH ACUTE */ + case 0x00E2: return 0xE2; /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ + case 0x00E3: return 0xE3; /* LATIN SMALL LETTER A WITH TILDE */ + case 0x00E4: return 0xE4; /* LATIN SMALL LETTER A WITH DIAERESIS */ + case 0x00E5: return 0xE5; /* LATIN SMALL LETTER A WITH RING ABOVE */ + case 0x00E6: return 0xE6; /* LATIN SMALL LIGATURE AE */ + case 0x012F: return 0xE7; /* LATIN SMALL LETTER I WITH OGONEK */ + case 0x010D: return 0xE8; /* LATIN SMALL LETTER C WITH CARON */ + case 0x00E9: return 0xE9; /* LATIN SMALL LETTER E WITH ACUTE */ + case 0x0119: return 0xEA; /* LATIN SMALL LETTER E WITH OGONEK */ + case 0x00EB: return 0xEB; /* LATIN SMALL LETTER E WITH DIAERESIS */ + case 0x0117: return 0xEC; /* LATIN SMALL LETTER E WITH DOT ABOVE */ + case 0x00ED: return 0xED; /* LATIN SMALL LETTER I WITH ACUTE */ + case 0x00EE: return 0xEE; /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ + case 0x012B: return 0xEF; /* LATIN SMALL LETTER I WITH MACRON */ + case 0x0111: return 0xF0; /* LATIN SMALL LETTER D WITH STROKE */ + case 0x0146: return 0xF1; /* LATIN SMALL LETTER N WITH CEDILLA */ + case 0x014D: return 0xF2; /* LATIN SMALL LETTER O WITH MACRON */ + case 0x0137: return 0xF3; /* LATIN SMALL LETTER K WITH CEDILLA */ + case 0x00F4: return 0xF4; /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ + case 0x00F5: return 0xF5; /* LATIN SMALL LETTER O WITH TILDE */ + case 0x00F6: return 0xF6; /* LATIN SMALL LETTER O WITH DIAERESIS */ + case 0x00F7: return 0xF7; /* DIVISION SIGN */ + case 0x00F8: return 0xF8; /* LATIN SMALL LETTER O WITH STROKE */ + case 0x0173: return 0xF9; /* LATIN SMALL LETTER U WITH OGONEK */ + case 0x00FA: return 0xFA; /* LATIN SMALL LETTER U WITH ACUTE */ + case 0x00FB: return 0xFB; /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ + case 0x00FC: return 0xFC; /* LATIN SMALL LETTER U WITH DIAERESIS */ + case 0x0169: return 0xFD; /* LATIN SMALL LETTER U WITH TILDE */ + case 0x016B: return 0xFE; /* LATIN SMALL LETTER U WITH MACRON */ + case 0x02D9: return 0xFF; /* DOT ABOVE */ +#endif +} + +static void +unicode_latin5( + char *arg +) +{ + int i; + static unsigned int latin5_unicode_map1[] = { + 0x0080, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x008e, 0x008f, /* 88 */ + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x009e, 0x0178, /* 98 */ + }; + static unsigned int latin5_unicode_map2[] = { + 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, /* D0 */ + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, /* D8 */ + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, /* E0 direct */ + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, /* E8 direct */ + 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, /* F0 */ + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, /* F8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0x9F; i++) + unicode_map[i] = latin5_unicode_map1[i-0x80]; + + for(i=0xA0; i<=0xCF; i++) + unicode_map[i] = i; + + for(i=0xD0; i<=0xFF; i++) + unicode_map[i] = latin5_unicode_map2[i-0xD0]; +} + +/* a way to select one 256-character plane from Unicode + * or other multi-byte encoding + */ + +static void +unicode_plane( + char *arg +) +{ + static unsigned plane; + int nchars; + int c1, c2, i; + + if(uni_lang_selected == 0) + return; /* don't participate in auto-guessing */ + + plane = 0; force_pid = force_eid = -1; + + c1 = sscanf(arg, "pid=%d,eid=%d%n", &force_pid, &force_eid, &nchars); + if(c1 == 2) { + arg += nchars; + if(*arg == ',') + arg++; + } + if(arg[0] == '0' && (arg[1]=='x' || arg[1]=='X') ) { + arg += 2; + c2 = sscanf(arg, "%x", &plane); + } else { + c2 = sscanf(arg, "%d", &plane); + } + + if( (c1!=2 && c1!=0) || (c1==0 && c2==0) ) { + fprintf(stderr, "**** option -l plane expects one of the following formats:\n"); + fprintf(stderr, " -l plane+0xNN - select hexadecimal number of plane of Unicode\n"); + fprintf(stderr, " -l plane+NN - select decimal number of plane of Unicode\n"); + fprintf(stderr, " -l plane+pid=N,eid=N - select plane 0 of specified encoding\n"); + fprintf(stderr, " -l plane+pid=N,eid=N,0xNN - select hex plane of TTF encoding with this PID/EID\n"); + fprintf(stderr, " -l plane+pid=N,eid=N,NN - select decimal plane of TTF encoding with this PID/EID\n"); + exit(1); + } + + if(c2!=0) { + if(strlen(arg) > sizeof(uni_suffix_buf)-2) { + fprintf(stderr, "**** plane number is too large\n"); + } + + sprintf(uni_suffix_buf, "-%s", arg); + uni_font_name_suffix = uni_suffix_buf; + } else { + uni_font_name_suffix = ""; + } + + plane <<= 8; + for(i=0; i<=0xFF; i++) + unicode_map[i] = plane | i; +} + +/* look up the 8-bit code by unicode */ + +int +unicode_rev_lookup( + int unival +) +{ + int res; + + if( ! IS_UNI_BUCKET(unival) ) + return -1; + + for (res = 0; res < enctabsz; res++) + if (unicode_map[res] == unival) + return res; + return -1; +} + +/* mark the buckets for quick lookup */ + +static void +unicode_prepare_buckets( + void +) +{ + int i; + + memset(uni_user_buckets, 0, sizeof uni_user_buckets); + for(i=0; i 126) { + sprintf(res+i, "\\x%02X", c); + i+=4; + } else { + res[i++] = c; + } + } + if(*s != 0) { + res[i++] = '.'; + res[i++] = '.'; + res[i++] = '.'; + } + res[i++] = 0; + return res; +} + +/* + * Scale the values according to the scale_factor + */ + +double +fscale( + double val +) +{ + return scale_factor * val; +} + +int +iscale( + int val +) +{ + return (int) (val > 0 ? scale_factor * val + 0.5 + : scale_factor * val - 0.5); +} + +/* + * Try to force fixed width of characters + */ + +static void +alignwidths(void) +{ + int i; + int n = 0, avg, max = 0, min = 3000, sum = 0, x; + + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + x = glyph_list[i].width; + + if (x != 0) { + if (x < min) + min = x; + if (x > max) + max = x; + + sum += x; + n++; + } + } + } + + if (n == 0) + return; + + avg = sum / n; + + WARNING_3 fprintf(stderr, "widths: max=%d avg=%d min=%d\n", max, avg, min); + + /* if less than 5% variation from average */ + /* force fixed width */ + if (20 * (avg - min) < avg && 20 * (max - avg) < avg) { + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) + glyph_list[i].width = avg; + } + fontm.is_fixed_pitch = 1; + } +} + +static void +convert_glyf( + int glyphno +) +{ + GLYPH *g; + int ncurves; + + g = &glyph_list[glyphno]; + + + g->scaledwidth = iscale(g->width); + + g->entries = 0; + g->lastentry = 0; + g->path = 0; + if (g->ttf_pathlen != 0) { + cursw->glpath(glyphno, glyph_list); + g->lastentry = 0; + + if(ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); + + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fclosepaths(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + /* float processing */ + if(smooth) { + ffixquadrants(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fsplitzigzags(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fforceconcise(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fstraighten(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + } + + pathtoint(g); + /* all processing past this point expects integer path */ + assertpath(g->entries, __FILE__, __LINE__, g->name); + +#if 0 + fixcontours(g); + testfixcvdir(g); +#endif + + /* int processing */ + if (smooth) { + smoothjoints(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + } + + ncurves = 0; + { + GENTRY *ge; + for(ge = g->entries; ge; ge = ge->next) + ncurves++; + } + if (ncurves > 200) { + WARNING_3 fprintf(stderr, + "** Glyph %s is too long, may display incorrectly\n", + g->name); + } + } else { + /* for buildstems */ + g->flags &= ~GF_FLOAT; + } +} + +static void +handle_gnames(void) +{ + int i, n, found, c, type; + + /* get the names from the font file */ + ps_fmt_3 = cursw->glnames(glyph_list); + +/* These checks are not required by Xprt's PS DDX... */ +#ifndef XP_PSTEXT + /* check for names with wrong characters */ + for (n = 0; n < numglyphs; n++) { + int c; + for (i = 0; (c = glyph_list[n].name[i]) != 0; i++) { + if (!(isalnum(c) || c == '.' || c == '_' || c == '-') + || i==0 && isdigit(c)) { /* must not start with a digit */ + WARNING_3 fprintf(stderr, "Glyph %d %s (%s), ", + n, isdigit(c) ? "name starts with a digit" : + "has bad characters in name", + nametoprint(glyph_list[n].name)); + glyph_list[n].name = malloc(16); + sprintf(glyph_list[n].name, "_b_%d", n); + WARNING_3 fprintf(stderr, "changing to %s\n", glyph_list[n].name); + break; + } + } + } + + if( !ps_fmt_3 ) { + /* check for duplicate names */ + for (n = 0; n < numglyphs; n++) { + found = 0; + for (i = 0; i < n && !found; i++) { + if (strcmp(glyph_list[i].name, glyph_list[n].name) == 0) { + if (( glyph_list[n].name = malloc(16) )==0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + sprintf(glyph_list[n].name, "_d_%d", n); + + /* if the font has no names in it (what the native parser + * recognises as ps_fmt_3), FreeType returns all the + * names as .notdef, so don't complain in this case + */ + if(strcmp(glyph_list[i].name, ".notdef")) { + WARNING_3 fprintf(stderr, + "Glyph %d has the same name as %d: (%s), changing to %s\n", + n, i, + glyph_list[i].name, + glyph_list[n].name); + } + found = 1; + } + } + } + + } +#endif /* !XP_PSTEXT */ + + /* start the encoding stuff */ + for (i = 0; i < ENCTABSZ; i++) { + encoding[i] = -1; + } + + /* do the 1st round of encoding by name */ + if(!ps_fmt_3 && uni_lang_selected && uni_lang_selected->convbyname) { + for (n = 0; n < numglyphs; n++) { + c = uni_lang_selected->convbyname(glyph_list[n].name, + uni_lang_arg, UNICONV_BYNAME_BEFORE); + if(c>=0 && cinit[i]; i++) { + for (n = 0; n < ENCTABSZ; n++) + unicode_map[n] = -1; + uni_lang_selected->init[i](uni_lang_arg); + unicode_prepare_buckets(); + type = cursw->glenc(glyph_list, encoding, unicode_map); + if( type == 0 ) + /* if we have an 8-bit encoding we don't need more tries */ + break; + } + } else { + /* language is unknown, try the first table of each */ + for(i=0; i < sizeof uni_lang/(sizeof uni_lang[0]); i++) { + if(uni_lang[i].init[0] == NULL) + continue; + for (n = 0; n < ENCTABSZ; n++) + unicode_map[n] = -1; + uni_lang[i].init[0](uni_lang_arg); + unicode_prepare_buckets(); + type = cursw->glenc(glyph_list, encoding, unicode_map); + if( type == 0 ) + /* if we have an 8-bit encoding we don't need more tries */ + break; + } + } + + if (ps_fmt_3) { + /* get rid of the old names, they are all "UNKNOWN" anyawy */ + for (i = 0; i < numglyphs; i++) { + glyph_list[i].name = 0; + } + if(type == 0) { + /* 8-bit - give 8859/1 names to the first 256 glyphs */ + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ + if (encoding[i] > 0) { + glyph_list[encoding[i]].name = Fmt3Encoding[i]; + } + } + } else if(type == 1) { + /* Unicode - give 8859/1 names to the first 256 glyphs of Unicode */ + for (n = 0; n < 256; n++) { /* here 256, not ENCTABSZ */ + i = unicode_rev_lookup(n); + if (i>=0 && encoding[i] > 0) { + glyph_list[encoding[i]].name = Fmt3Encoding[i]; + } + } + } /* for other types of encodings just give generated names */ + /* assign unique names to the rest of the glyphs */ + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].name == 0) { + if (( glyph_list[i].name = malloc(16) )==0) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + sprintf(glyph_list[i].name, "_d_%d", i); + } + } + } + + /* do the 2nd round of encoding by name */ + if(uni_lang_selected && uni_lang_selected->convbyname) { + for (n = 0; n < numglyphs; n++) { + c = uni_lang_selected->convbyname(glyph_list[n].name, + uni_lang_arg, UNICONV_BYNAME_AFTER); + if(c>=0 && c 0) + glyph_list[0].name = ".notdef"; + if(numglyphs > 1) + glyph_list[1].name = ".null"; +#endif /* !XP_PSTEXT */ + + for (i = 0; i < ENCTABSZ; i++) { + if ((encoding[i] != 0) && glyph_rename[i]) { + glyph_list[encoding[i]].name = glyph_rename[i]; + } + } +} + +static void +usage(void) +{ + +#ifdef XP_PSTEXT + fputs("ft2pt1: Internal startup error\n", stderr); +#else + +#ifdef _GNU_SOURCE +# define fplop(txt) fputs(txt, stderr); +#else +# define fplop(txt) +#endif + + fputs("Use:\n", stderr); + fputs("ttf2pt1 [-] [-l language | -L file] []\n", stderr); + fputs(" or\n", stderr); + fputs("ttf2pt1 [-] [-l language | -L file] -\n", stderr); + fputs(" or\n", stderr); + fputs("ttf2pt1 [-] [-l language | -L file] - | t1asm > \n", stderr); + + fplop("\n"); + fplop("This build supports both short and long option names,\n"); + fplop("the long options are listed before corresponding short ones\n"); + + fplop(" --all-glyphs\n"); + fputs(" -a - include all glyphs, even those not in the encoding table\n", stderr); + fplop(" --pfb\n"); + fputs(" -b - produce a compressed .pfb file\n", stderr); + fplop(" --debug dbg_suboptions\n"); + fputs(" -d dbg_suboptions - debugging options, run ttf2pt1 -d? for help\n", stderr); + fplop(" --encode\n"); + fputs(" -e - produce a fully encoded .pfa file\n", stderr); + fplop(" --force-unicode\n"); + fputs(" -F - force use of Unicode encoding even if other MS encoding detected\n", stderr); + fplop(" --generate suboptions\n"); + fputs(" -G suboptions - control the file generation, run ttf2pt1 -G? for help\n", stderr); + fplop(" --language language\n"); + fputs(" -l language - convert Unicode to specified language, run ttf2pt1 -l? for list\n", stderr); + fplop(" --language-map file\n"); + fputs(" -L file - convert Unicode according to encoding description file\n", stderr); + fplop(" --limit =\n"); + fputs(" -m = - set maximal limit of given type to value, types:\n", stderr); + fputs(" h - maximal hint stack depth in the PostScript interpreter\n", stderr); + fplop(" --processing suboptions\n"); + fputs(" -O suboptions - control outline processing, run ttf2pt1 -O? for help\n", stderr); + fplop(" --parser name\n"); + fputs(" -p name - use specific front-end parser, run ttf2pt1 -p? for list\n", stderr); + fplop(" --uid id\n"); + fputs(" -u id - use this UniqueID, -u A means autogeneration\n", stderr); + fplop(" --vertical-autoscale size\n"); + fputs(" -v size - scale the font to make uppercase letters >size/1000 high\n", stderr); + fplop(" --version\n"); + fputs(" -V - print ttf2pt1 version number\n", stderr); + fplop(" --warning number\n"); + fputs(" -W number - set the level of permitted warnings (0 - disable)\n", stderr); + fputs("Obsolete options (will be removed in future releases):\n", stderr); + fplop(" --afm\n"); + fputs(" -A - write the .afm file to STDOUT instead of the font, now -GA\n", stderr); + fputs(" -f - don't try to guess the value of the ForceBold hint, now -Ob\n", stderr); + fputs(" -h - disable autogeneration of hints, now -Oh\n", stderr); + fputs(" -H - disable hint substitution, now -Ou\n", stderr); + fputs(" -o - disable outline optimization, now -Oo\n", stderr); + fputs(" -s - disable outline smoothing, now -Os\n", stderr); + fputs(" -t - disable auto-scaling to 1000x1000 standard matrix, now -Ot\n", stderr); + fputs(" -w - correct the glyph widths (use only for buggy fonts), now -OW\n", stderr); + fputs("With no , write to with suffix replaced.\n", stderr); + fputs("The last '-' means 'use STDOUT'.\n", stderr); + +#undef fplop + +#endif /* XP_PSTEXT */ +} + +static void +printversion(void) +{ + fprintf(stderr, "ttf2pt1 %s\n", TTF2PT1_VERSION); +} + +/* initialize a table of suboptions */ +static void +init_subo_tbl( + struct subo_case *tbl +) +{ + int i; + + for(i=0; tbl[i].disbl != 0; i++) { + tbl[i].disbl = tolower(tbl[i].disbl); + tbl[i].enbl = toupper(tbl[i].disbl); + *(tbl[i].valp) = tbl[i].dflt; + } +} + +/* print the default value of the suboptions */ +static void +print_subo_dflt( + FILE *f, + struct subo_case *tbl +) +{ + int i; + + for(i=0; tbl[i].disbl != 0; i++) { + if(tbl[i].dflt) + putc(tbl[i].enbl, f); + else + putc(tbl[i].disbl, f); + } +} + +/* print the usage message for the suboptions */ +static void +print_subo_usage( + FILE *f, + struct subo_case *tbl +) +{ +#ifdef XP_PSTEXT + fputs("ft2pt1: Internal startup error\n", stderr); +#else + int i; + + fprintf(f,"The lowercase suboptions disable features, corresponding\n"); + fprintf(f,"uppercase suboptions enable them. The supported suboptions,\n"); + fprintf(f,"their default states and the features they control are:\n"); + for(i=0; tbl[i].disbl != 0; i++) { + fprintf(f," %c/%c - [%s] %s\n", tbl[i].disbl, tbl[i].enbl, + tbl[i].dflt ? "enabled" : "disabled", tbl[i].descr); + } +#endif /* XP_PSTEXT */ +} + +/* find and set the entry according to suboption, + * return the found entry (or if not found return NULL) + */ +struct subo_case * +set_subo( + struct subo_case *tbl, + int subopt +) +{ + int i; + + for(i=0; tbl[i].disbl != 0; i++) { + if(subopt == tbl[i].disbl) { + *(tbl[i].valp) = 0; + return &tbl[i]; + } else if(subopt == tbl[i].enbl) { + *(tbl[i].valp) = 1; + return &tbl[i]; + } + } + return NULL; +} + + +#ifdef XP_PSTEXT +FT_Face xp_pstext_ft_face = NULL; /* used by ft.c */ +FTFontPtr xp_xtf = NULL; +const char *xp_psfontname = NULL; +unsigned long xp_font_block_offset = 0UL; + +int +ft2pt1_main( + int argc, + char **argv, + FTFontPtr tf, + const char *download_psfontname, + unsigned long download_font_block_offset +) +#else +int +main( + int argc, + char **argv +) +#endif /* XP_PSTEXT */ +{ + long i, j; + time_t now; + char filename[4096]; + int c,nchars,nmetrics; + int ws; + int forcebold= -1; /* -1 means "don't know" */ + char *lang; + int oc; + int subid; + char *cmdline; +#ifdef _GNU_SOURCE +# define ttf2pt1_getopt(a, b, c, d, e) getopt_long(a, b, c, d, e) + static struct option longopts[] = { + { "afm", 0, NULL, 'A' }, + { "all-glyphs", 0, NULL, 'a' }, + { "pfb", 0, NULL, 'b' }, + { "debug", 1, NULL, 'd' }, + { "encode", 0, NULL, 'e' }, + { "force-unicode", 0, NULL, 'F' }, + { "generate", 1, NULL, 'G' }, + { "language", 1, NULL, 'l' }, + { "language-map", 1, NULL, 'L' }, + { "limit", 1, NULL, 'm' }, + { "processing", 1, NULL, 'O' }, + { "parser", 1, NULL, 'p' }, + { "uid", 1, NULL, 'u' }, + { "vertical-autoscale", 1, NULL, 'v' }, + { "version", 0, NULL, 'V' }, + { "warning", 1, NULL, 'W' }, + { NULL, 0, NULL, 0 } + }; +#else +# define ttf2pt1_getopt(a, b, c, d, e) getopt(a, b, c) +#endif + /* table of Outline Processing (may think also as Optimization) options */ + static struct subo_case opotbl[] = { + { 'b', 0/*auto-set*/, &trybold, 1, "guessing of the ForceBold hint" }, + { 'h', 0/*auto-set*/, &hints, 1, "autogeneration of hints" }, + { 'u', 0/*auto-set*/, &subhints, 1, "hint substitution technique" }, + { 'o', 0/*auto-set*/, &optimize, 1, "space optimization of font files" }, + { 's', 0/*auto-set*/, &smooth, 1, "smoothing and repair of outlines" }, + { 't', 0/*auto-set*/, &transform, 1, "auto-scaling to the standard matrix 1000x1000" }, + { 'w', 0/*auto-set*/, &correctwidth, 0, "correct the glyph widths (use only for buggy fonts)" }, + { 'v', 0/*auto-set*/, &vectorize, 0, "vectorize (trace) the bitmaps" }, +#ifdef USE_AUTOTRACE + { 'z', 0/*auto-set*/, &use_autotrace, 0, "use the autotrace library on bitmaps (works badly)" }, +#endif /*USE_AUTOTRACE*/ + { 0, 0, 0, 0, 0} /* terminator */ + }; + /* table of the File Generation options */ + static struct subo_case fgotbl[] = { + { 'f', 0/*auto-set*/, &gen_pfa, 1, "generate the font file (.t1a, .pfa or .pfb)" }, + { 'a', 0/*auto-set*/, &gen_afm, 1, "generate the Adobe metrics file (.afm)" }, + { 'e', 0/*auto-set*/, &gen_dvienc, 0, "generate the dvips encoding file (.enc)" }, + { 0, 0, 0, 0, 0} /* terminator */ + }; + int *genlast = NULL; + +#ifdef XP_PSTEXT + xp_pstext_ft_face = tf->instance->face->face; + xp_xtf = tf; + xp_psfontname = download_psfontname; + xp_font_block_offset = download_font_block_offset; +#endif /* XP_PSTEXT */ + + init_subo_tbl(opotbl); /* initialize sub-options of -O */ + init_subo_tbl(fgotbl); /* initialize sub-options of -G */ + + /* save the command line for the record + * (we don't bother about escaping the shell special characters) + */ + + j = 0; + for(i=1; ivalp) ) + genlast = s->valp; + } + break; + } + case 'h': + fputs("Warning: option -h is obsolete, use -Oh instead\n", stderr); + hints = 0; + break; + case 'H': + fputs("Warning: meaning of option -H has been changed to its opposite\n", stderr); + fputs("Warning: option -H is obsolete, use -Ou instead\n", stderr); + subhints = 0; + break; + case 'f': + fputs("Warning: option -f is obsolete, use -Ob instead\n", stderr); + trybold = 0; + break; + case 'w': + fputs("Warning: option -w is obsolete, use -OW instead\n", stderr); + correctwidth = 1; + break; + case 'u': + if(wantuid) { + fprintf(stderr, "**** UniqueID may be specified only once ****\n"); + exit(1); + } + wantuid = 1; + if(optarg[0]=='A' && optarg[1]==0) + strUID=0; /* will be generated automatically */ + else { + strUID=optarg; + for(i=0; optarg[i]!=0; i++) + if( !isdigit(optarg[i]) ) { + fprintf(stderr, "**** UniqueID must be numeric or A for automatic ****\n"); + exit(1); + } + } + break; + case 'v': + correctvsize = atoi(optarg); + if(correctvsize <= 0 && correctvsize > 1000) { + fprintf(stderr, "**** wrong vsize '%d', ignored ****\n", correctvsize); + correctvsize=0; + } + break; + case 'p': + if(cursw!=0) { + fprintf(stderr, "**** only one front-end parser be used ****\n"); + exit(1); + } + + { /* separate parser from parser-specific argument */ + char *p = strchr(optarg, LANG_ARG_SEP); + if(p != 0) { + *p = 0; + front_arg = p+1; + } else + front_arg = ""; + } + for(i=0; frontswtab[i] != NULL; i++) + if( !strcmp(frontswtab[i]->name, optarg) ) { + cursw = frontswtab[i]; + break; + } + + if(cursw==0) { + if (strcmp(optarg, "?")) + fprintf(stderr, "**** unknown front-end parser '%s' ****\n", optarg); + fputs("the following front-ends are supported now:\n", stderr); + for(i=0; frontswtab[i] != NULL; i++) { + fprintf(stderr," %s (%s)\n file suffixes: ", + frontswtab[i]->name, + frontswtab[i]->descr ? frontswtab[i]->descr : "no description" + ); + for(j=0; jsuffix[j]) + fprintf(stderr, "%s ", frontswtab[i]->suffix[j]); + fprintf(stderr, "\n"); + } + exit(1); + } + break; + case 'l': + if(uni_lang_selected!=0) { + fprintf(stderr, "**** only one language option may be used ****\n"); + exit(1); + } + + { /* separate language from language-specific argument */ + char *p = strchr(optarg, LANG_ARG_SEP); + if(p != 0) { + *p = 0; + uni_lang_arg = p+1; + } else + uni_lang_arg = ""; + } + for(i=0; i < sizeof uni_lang/(sizeof uni_lang[0]); i++) + if( !strcmp(uni_lang[i].name, optarg) ) { + uni_lang_selected = &uni_lang[i]; + uni_sample = uni_lang[i].sample_upper; + break; + } + + if(uni_lang_selected==0) { + if (strcmp(optarg, "?")) + fprintf(stderr, "**** unknown language '%s' ****\n", optarg); + fputs(" the following languages are supported now:\n", stderr); + for(i=0; i < sizeof uni_lang/(sizeof uni_lang[0]); i++) + fprintf(stderr," %s (%s)\n", + uni_lang[i].name, + uni_lang[i].descr ? uni_lang[i].descr : "no description" + ); + exit(1); + } + break; + case 'L': + if(uni_lang_selected!=0) { + fprintf(stderr, "**** only one language option may be used ****\n"); + exit(1); + } + uni_lang_selected = &uni_lang_user; + uni_lang_arg = optarg; + break; + case 'V': + printversion(); + exit(0); + break; + default: + usage(); + exit(1); + break; + } + } + argc-=optind-1; /* the rest of code counts from argv[0] */ + argv+=optind-1; + + if (absolute && encode) { + fprintf(stderr, "**** options -a and -e are incompatible ****\n"); + exit(1); + } + if ((argc != 2) && (argc != 3)) { + usage(); + exit(1); + } + + /* try to guess the language by the locale used */ + if(uni_lang_selected==0 && (lang=getenv("LANG"))!=0 ) { + for(i=0; i < sizeof uni_lang/sizeof(struct uni_language); i++) { + if( !strncmp(uni_lang[i].name, lang, strlen(uni_lang[i].name)) ) { + uni_lang_selected = &uni_lang[i]; + goto got_a_language; + } + } + /* no full name ? try aliases */ + for(i=0; i < sizeof uni_lang/sizeof(struct uni_language); i++) { + for(c=0; csuffix[j] + && !strcmp(p, frontswtab[i]->suffix[j]) ) { + cursw = frontswtab[i]; + WARNING_1 fprintf(stderr, "Auto-detected front-end parser '%s'\n", + cursw->name); + WARNING_1 fprintf(stderr, " (use ttf2pt1 -p? to get the full list of available front-ends)\n"); + break; + } + } + free(s); + } + + if(cursw==0) { + cursw = frontswtab[0]; + WARNING_1 fprintf(stderr, "Can't detect front-end parser, using '%s' by default\n", + cursw->name); + WARNING_1 fprintf(stderr, " (use ttf2pt1 -p? to get the full list of available front-ends)\n"); + } + } + + /* open the input file */ + cursw->open(argv[1], front_arg); + + /* Get base name of output file (if not specified) + * by removing (known) suffixes + */ + if (argc == 2) { + char *p; + argv[2] = strdup (argv[1]); + p = strrchr(argv[2], '.'); + if (p != NULL) + for (j = 0; (j < MAXSUFFIX) && (cursw->suffix[j]); j++) + if (!strcmp(p+1, cursw->suffix[j])) { + *p = '\0'; + break; + } + } + + if ((null_file = fopen(BITBUCKET, "w")) == NULL) { + fprintf(stderr, "**** Cannot open %s ****\n", + BITBUCKET); + exit(1); + } + + if (argv[2][0] == '-' && argv[2][1] == 0) { +#ifdef WINDOWS + if(encode) { + fprintf(stderr, "**** can't write encoded file to stdout ***\n"); + exit(1); + } +#endif /* WINDOWS */ + pfa_file = afm_file = dvienc_file = null_file; + + if(wantafm || genlast == &gen_afm) { /* print .afm instead of .pfa */ + afm_file=stdout; + } else if(genlast == &gen_dvienc) { /* print .enc instead of .pfa */ + dvienc_file=stdout; + } else { + pfa_file=stdout; + } + } else { +#ifndef WINDOWS + snprintf(filename, sizeof filename, "%s.%s", argv[2], encode ? (pfbflag ? "pfb" : "pfa") : "t1a" ); +#else /* WINDOWS */ + snprintf(filename, sizeof filename, "%s.t1a", argv[2]); +#endif /* WINDOWS */ + if(gen_pfa) { + if ((pfa_file = fopen(filename, "w+b")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Creating file %s\n", filename); + } + } else + pfa_file = null_file; + + if(gen_afm) { + snprintf(filename, sizeof filename, "%s.afm", argv[2]) ; + if ((afm_file = fopen(filename, "w+")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } + } else + afm_file = null_file; + + if(gen_dvienc) { + snprintf(filename, sizeof filename, "%s.enc", argv[2]) ; + if ((dvienc_file = fopen(filename, "w+")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } + } else + dvienc_file = null_file; + } + + /* + * Now check whether we want a fully encoded .pfa file + */ +#ifndef WINDOWS + if (encode && pfa_file != null_file) { + int p[2]; + extern FILE *ifp, *ofp; /* from t1asm.c */ + + ifp=stdin; + ofp=stdout; + + if (pipe(p) < 0) { + perror("**** Cannot create pipe ****\n"); + exit(1); + } + ofp = pfa_file; + ifp = fdopen(p[0], "r"); + if (ifp == NULL) { + perror("**** Cannot use pipe for reading ****\n"); + exit(1); + } + pfa_file = fdopen(p[1], "w"); + if (pfa_file == NULL) { + perror("**** Cannot use pipe for writing ****\n"); + exit(1); + } + switch (fork()) { + case -1: + perror("**** Cannot fork the assembler process ****\n"); + exit(1); + case 0: /* child */ + fclose(pfa_file); + exit(runt1asm(pfbflag)); + default: /* parent */ + fclose(ifp); fclose(ofp); + } + } +#endif /* WINDOWS */ + + numglyphs = cursw->nglyphs(); + + WARNING_3 fprintf(stderr, "numglyphs = %d\n", numglyphs); + + glyph_list = (GLYPH *) calloc(numglyphs, sizeof(GLYPH)); + + /* initialize non-0 fields */ + for (i = 0; i < numglyphs; i++) { + GLYPH *g; + + g = &glyph_list[i]; + g->char_no = -1; + g->orig_code = -1; + g->name = "UNKNOWN"; + g->flags = GF_FLOAT; /* we start with float representation */ + } + + handle_gnames(); + + cursw->glmetrics(glyph_list); + cursw->fnmetrics(&fontm); + + original_scale_factor = 1000.0 / (double) fontm.units_per_em; + + if(transform == 0) + scale_factor = 1.0; /* don't transform */ + else + scale_factor = original_scale_factor; + + if(correctvsize && uni_sample!=0) { /* only for known languages */ + /* try to adjust the scale factor to make a typical + * uppercase character of hight at least (correctvsize), this + * may improve the appearance of the font but also + * make it weird, use with caution + */ + int ysz; + + ysz = iscale(glyph_list[encoding[uni_sample]].yMax); + if( yszmapping, i); + + if( ftindex < numglyphs ) { + glyph_list[ftindex].flags |= GF_USED; + } + } + + /* also always include .notdef */ + { + int notdef_found = FALSE; + + for (i = 0; i < numglyphs; i++) { + if(!strcmp(glyph_list[i].name, ".notdef")) { + glyph_list[i].flags |= GF_USED; + notdef_found = TRUE; + break; + } + } + + if( !notdef_found ) + { + /* No ".notdef" found ? + * Then copy outlines of char 0 to the first "free" slot and make + * it our ".notdef" char. + */ + for (i = 0; i < numglyphs; i++) { + if((glyph_list[i].flags & GF_USED) == 0) { + glyph_list[i] = glyph_list[0]; + glyph_list[i].flags |= GF_USED; + glyph_list[i].name = ".notdef"; + notdef_found = TRUE; + break; + } + } + } + + if( !notdef_found ) + { + /* This shoudl never happen... */ + fprintf(stderr, "ft2pt1: '.notdef' missing in generated font.\n"); + } + } +#else + if(allglyphs) { + for (i = 0; i < numglyphs; i++) { + glyph_list[i].flags |= GF_USED; + } + } else { + for (i = 0; i < ENCTABSZ; i++) { + glyph_list[encoding[i]].flags |= GF_USED; + } + + /* also always include .notdef */ + for (i = 0; i < numglyphs; i++) + if(!strcmp(glyph_list[i].name, ".notdef")) { + glyph_list[i].flags |= GF_USED; + break; + } + } +#endif /* XP_ONLY_BLOCKS */ + + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + DBG_TO_GLYPH(&glyph_list[i]); + convert_glyf(i); + DBG_FROM_GLYPH(&glyph_list[i]); + } + } + + italic_angle = fontm.italic_angle; + + if (italic_angle > 45.0 || italic_angle < -45.0) + italic_angle = 0.0; /* consider buggy */ + + if (hints) { + findblues(); + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + DBG_TO_GLYPH(&glyph_list[i]); + buildstems(&glyph_list[i]); + assertpath(glyph_list[i].entries, __FILE__, __LINE__, glyph_list[i].name); + DBG_FROM_GLYPH(&glyph_list[i]); + } + } + stemstatistics(); + } else { + for(i=0; i<4; i++) + bbox[i] = iscale(fontm.bbox[i]); + } + /* don't touch the width of fixed width fonts */ + if( fontm.is_fixed_pitch ) + correctwidth=0; + docorrectwidth(); /* checks correctwidth inside */ + if (reverse) + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + DBG_TO_GLYPH(&glyph_list[i]); + reversepaths(&glyph_list[i]); + assertpath(glyph_list[i].entries, __FILE__, __LINE__, glyph_list[i].name); + DBG_FROM_GLYPH(&glyph_list[i]); + } + } + + +#if 0 + /* + ** It seems to bring troubles. The problem is that some + ** styles of the font may be recognized as fixed-width + ** while other styles of the same font as proportional. + ** So it's better to be commented out yet. + */ + if (tryfixed) + alignwidths(); +#endif + + if(trybold) { + forcebold = fontm.force_bold; + } + +#ifdef XP_PSTEXT + fprintf(pfa_file, "%%!PS-AdobeFont-1.0: %s\n", fontm.name_ps); +#else + fprintf(pfa_file, "%%!PS-AdobeFont-1.0: %s %s\n", fontm.name_ps, fontm.name_copyright); + time(&now); + fprintf(pfa_file, "%%%%CreationDate: %s", ctime(&now)); + fprintf(pfa_file, "%% Converted by ttf2pt1 %s/%s\n", TTF2PT1_VERSION, cursw->name); + fprintf(pfa_file, "%% Args: %s\n", cmdline); + fprintf(pfa_file, "%%%%EndComments\n"); +#endif /* XP_PSTEXT */ + fprintf(pfa_file, "12 dict begin\n/FontInfo 9 dict dup begin\n"); + + WARNING_3 fprintf(stderr, "FontName %s%s\n", fontm.name_ps, uni_font_name_suffix); + + + fprintf(pfa_file, " /version (%s) readonly def\n", fontm.name_version); + + fprintf(pfa_file, " /Notice (%s) readonly def\n", fontm.name_copyright); + + fprintf(pfa_file, " /FullName (%s) readonly def\n", fontm.name_full); + fprintf(pfa_file, " /FamilyName (%s) readonly def\n", fontm.name_family); + + if(wantuid) { + if(strUID) + fprintf(pfa_file, " /UniqueID %s def\n", strUID); + else { + numUID=0; + for(i=0; fontm.name_full[i]!=0; i++) { + numUID *= 37; /* magic number, good for hash */ + numUID += fontm.name_full[i]-' '; + /* if the name is long the first chars + * may be lost forever, so re-insert + * them thus making kind of CRC + */ + numUID += (numUID>>24) & 0xFF; + } + /* the range for private UIDs is 4 000 000 - 4 999 999 */ + fprintf(pfa_file, " /UniqueID %lu def\n", numUID%1000000+4000000); + } + } + + fprintf(pfa_file, " /Weight (%s) readonly def\n", fontm.name_style); + + fprintf(pfa_file, " /ItalicAngle %f def\n", italic_angle); + fprintf(pfa_file, " /isFixedPitch %s def\n", + fontm.is_fixed_pitch ? "true" : "false"); + + /* we don't print out the unused glyphs */ + nchars = 0; + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + nchars++; + } + } + + fprintf(afm_file, "StartFontMetrics 4.1\n"); + fprintf(afm_file, "FontName %s%s\n", fontm.name_ps, uni_font_name_suffix); + fprintf(afm_file, "FullName %s\n", fontm.name_full); + fprintf(afm_file, "Notice %s\n", fontm.name_copyright); + fprintf(afm_file, "EncodingScheme FontSpecific\n"); + fprintf(afm_file, "FamilyName %s\n", fontm.name_family); + fprintf(afm_file, "Weight %s\n", fontm.name_style); + fprintf(afm_file, "Version %s\n", fontm.name_version); + fprintf(afm_file, "Characters %d\n", nchars); + fprintf(afm_file, "ItalicAngle %.1f\n", italic_angle); + + fprintf(afm_file, "Ascender %d\n", iscale(fontm.ascender)); + fprintf(afm_file, "Descender %d\n", iscale(fontm.descender)); + + fprintf(pfa_file, " /UnderlinePosition %d def\n", + iscale(fontm.underline_position)); + + fprintf(pfa_file, " /UnderlineThickness %hd def\n", + iscale(fontm.underline_thickness)); + + fprintf(pfa_file, "end readonly def\n"); + + fprintf(afm_file, "UnderlineThickness %d\n", + iscale(fontm.underline_thickness)); + + fprintf(afm_file, "UnderlinePosition %d\n", + iscale(fontm.underline_position)); + + fprintf(afm_file, "IsFixedPitch %s\n", + fontm.is_fixed_pitch ? "true" : "false"); + fprintf(afm_file, "FontBBox %d %d %d %d\n", + bbox[0], bbox[1], bbox[2], bbox[3]); + + fprintf(pfa_file, "/FontName /%s%s def\n", fontm.name_ps, uni_font_name_suffix); + fprintf(pfa_file, "/PaintType 0 def\n/StrokeWidth 0 def\n"); + /* I'm not sure if these are fixed */ + fprintf(pfa_file, "/FontType 1 def\n"); + + if (transform) { + fprintf(pfa_file, "/FontMatrix [0.001 0 0 0.001 0 0] def\n"); + } else { + fprintf(pfa_file, "/FontMatrix [%9.7f 0 0 %9.7f 0 0] def\n", + original_scale_factor / 1000.0, original_scale_factor / 1000.0); + } + + fprintf(pfa_file, "/FontBBox {%d %d %d %d} readonly def\n", + bbox[0], bbox[1], bbox[2], bbox[3]); + + fprintf(pfa_file, "/Encoding 256 array\n"); + /* determine number of elements for metrics table */ + nmetrics = 256; + for (i = 0; i < numglyphs; i++) { + if( glyph_list[i].flags & GF_USED + && glyph_list[i].char_no == -1 ) { + nmetrics++; + } + } + fprintf(afm_file, "StartCharMetrics %d\n", nmetrics); + + fprintf(dvienc_file, "/%s%sEncoding [\n", + fontm.name_ps, uni_font_name_suffix); + +#ifdef XP_PSTEXT + { + int linewidth = 0; + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ + linewidth += strlen(glyph_list[encoding[i]].name) + 14 + 8; + fprintf(pfa_file, "dup %d /%s put%s", + i, + glyph_list[encoding[i]].name, + (linewidth > 70 || i == 255)?(linewidth = 0, "\n"):("\t")); + if( glyph_list[encoding[i]].flags & GF_USED ) { + print_glyph_metrics(i, encoding[i]); + } + if (encoding[i]) + fprintf (dvienc_file, "/index0x%04X\n", encoding[i]); + else + fprintf (dvienc_file, "/.notdef\n"); + } + } +#else + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ + fprintf(pfa_file, + "dup %d /%s put\n", i, glyph_list[encoding[i]].name); + if( glyph_list[encoding[i]].flags & GF_USED ) { + print_glyph_metrics(i, encoding[i]); + } + if (encoding[i]) + fprintf (dvienc_file, "/index0x%04X\n", encoding[i]); + else + fprintf (dvienc_file, "/.notdef\n"); + } +#endif /* XP_PSTEXT */ + /* print the metrics for glyphs not in encoding table */ + for(i=0; ikerning(glyph_list); + print_kerning(afm_file); + } + + fprintf(afm_file, "EndFontMetrics\n"); + if(afm_file != null_file) + fclose(afm_file); + + fprintf(dvienc_file, "] def\n"); + if(dvienc_file != null_file) + fclose(dvienc_file); + + WARNING_1 fprintf(stderr, "Finished - font files created\n"); + + cursw->close(); + +#ifndef WINDOWS + while (wait(&ws) > 0) { + } +#else + if (encode && pfa_file != null_file) { + extern FILE *ifp, *ofp; /* from t1asm.c */ + + snprintf(filename, sizeof filename, "%s.%s", argv[2], pfbflag ? "pfb" : "pfa" ); + + if ((ofp = fopen(filename, "w+b")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Creating file %s\n", filename); + } + + snprintf(filename, sizeof filename, "%s.t1a", argv[2]); + + if ((ifp = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "**** Cannot read %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Converting file %s\n", filename); + } + + runt1asm(pfbflag); + + WARNING_2 fprintf(stderr, "Removing file %s\n", filename); + if(unlink(filename) < 0) + WARNING_1 fprintf(stderr, "Unable to remove file %s\n", filename); + } +#endif /* WINDOWS */ + + fclose(null_file); + return 0; +} Index: xc/extras/ttf2pt1/ttf2pt1_convert.1 =================================================================== RCS file: xc/extras/ttf2pt1/ttf2pt1_convert.1 diff -N xc/extras/ttf2pt1/ttf2pt1_convert.1 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ttf2pt1_convert.1 13 Apr 2004 02:45:11 -0000 @@ -0,0 +1,509 @@ +.rn '' }` +''' $RCSfile: ttf2pt1_convert.1,v $$Revision: 1.1 $$Date: 2003/06/04 00:33:54 $ +''' +''' $Log: ttf2pt1_convert.1,v $ +''' Revision 1.1 2003/06/04 00:33:54 roland +''' Fix for http://xprint.mozdev.org/bugs/show_bug.cgi?id=3846 - RFE: Upload Freetype --> PS Type1 font converter "ttf2pt1" ... +''' +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +''' \*(M", \*(S", \*(N" and \*(T" are the equivalent of +''' \*(L" and \*(R", except that they are used on ".xx" lines, +''' such as .IP and .SH, which do another additional levels of +''' double-quote interpretation +.ds M" """ +.ds S" """ +.ds N" """"" +.ds T" """"" +.ds L' ' +.ds R' ' +.ds M' ' +.ds S' ' +.ds N' ' +.ds T' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds M" `` +.ds S" '' +.ds N" `` +.ds T" '' +.ds L' ` +.ds R' ' +.ds M' ` +.ds S' ' +.ds N' ` +.ds T' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH TTF2PT1_CONVERT 1 "version 3.4.4-SNAP-030526" "May 26, 2003" "TTF2PT1 Font Converter" +.UC +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +\fBttf2pt1_convert\fR \- convenience font conversion script +.SH "SYNOPSIS" +ttf2pt1_convert \fB[config-file]\fR +.SH "DESCRIPTION" +`\fBConvert\fR\*(R' is the master conversion script provided with ttf2pt1. +When installed into a public directory it's named `\fBttf2pt1_convert\fR\*(R' +to avoid name collisions with the other programs. +.PP +If the configuration file is not specified as an argument then the file +`\f(CWconvert.cfg\fR\*(R' in the current directory is used. This file contains +a set of configuration variables. The distribution contains a sample file +file `\f(CWconvert.cfg.sample\fR\*(R'. Please copy it to `\f(CWconvert.cfg\fR\*(R', +look inside it and change the configuration variables. The more stable +configuration variables, such as the path names of the scripts and +encoding files are located in `\f(CWconvert\fR\*(R' itself, they are +automatically updated when installing \fBttf2pt1\fR. +.PP +Put all the TTF fonts you want to convert into some directory (this +may be just the directory that already contains all the Windows +fonts on a mounted FAT filesystem). If you have fonts in different +source encoding then put the fonts in each of the encodings +into a separate directory. Up to 10 source directories are +supported. If you (in a rather unlikely case) have more source +directories then you can make two separate runs of the converter, +converting up to 10 directories at a time. +.PP +The variables in the configuration file are: +.Ip "\(bu" 2 +\fB\f(CWSRCDIRS\fR\fR \- the list of directories (with absolute paths) with +\s-1TTF\s0 fonts. Each line contains at least 3 fields: the name of the directory, +the language of the fonts in it (if you have fonts for different +languages you have to put them into the separate directories) and the +encoding of the fonts. Again, if you have some of the \s-1TTF\s0 typefaces in +one encoding, and some in another (say, \s-1CP\s0\-1251 and \s-1KOI\s0\-8), you have +to put them into the separate source directories. Some lines may contain +4 fields. Then the fourth field is the name of the external map to +convert the Unicode fonts into the desirable encoding. This map is +used instead of the built-in map for the specified language. +.Sp +*8* +An interesting thing is that some languages have more than one +widely used character encodings. For example, the widely used +encodings for Russian are \s-1IBM\s0 \s-1CP\s0\-866 (\s-1MS\s0\-\s-1DOS\s0 and Unix), \s-1KOI\s0\-8 +(Unix and \s-1VAX\s0, also the standard Internet encoding), \s-1IBM\s0 \s-1CP\s0\-1251 (\s-1MS\s0 Windows). +That's why I have provided the means to generate the converted fonts +in more than one encoding. See the file encodings/\s-1README\s0 for +details about the encoding tables. Actually, if you plan to use +these fonts with Netscape Navigator better use the aliases +cp-866 instead of ibm-866 and windows-1251 instead of ibm-1251 +because that's what Netscape wants. +.Ip "\(bu" 2 +\fB\f(CWDSTDIR\fR\fR \- directory for the resulting Type1 fonts. Be careful! +This directory gets completely wiped out before conversion, +so don't use any already existing directory for this purpose. +.Ip "\(bu" 2 +\fB\f(CWDSTENC\fI{language}\fR\fR\fR \- the list of encodings in which the destination +fonts will be generated for each language. Each font of that +language will be generated in each of the specified +encodings. If you don't want any translation, just specify both +\f(CWSRCENC\fR and \f(CWDSTENC\fR as iso8859-1 (or if you want any other encoding +specified in the fonts.dir, copy the description of 8859-1 with +new name and use this new name for \f(CWSRCENC\fR and \f(CWDSTENC\fR). +.Ip "\(bu" 2 +\fB\f(CWFOUNDRY\fR\fR \- the foundry name to be used in the fonts.dir file. I have +set it to `fromttf\*(R' to avoid name conflicts with any existing font for +sure. But this foundry name is not registered in X11 standards and +if you want to get the full standard compliance or have a font server +that enforces such a compliance, use `misc\*(R'. +.PP +The next few parameters control the general behavior of the converter. +They default values are set to something reasonable. +.Ip "\(bu" 2 +\fB\f(CWCORRECTWIDTH\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then use the +converter option \f(CW\fB-w\fR\fR, otherwise don't use it. See the description of +this option in the \s-1README\s0 file. +.Ip "\(bu" 2 +\fB\f(CWREMOVET1A\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then after +conversion remove the un-encoded \f(CW.t1a\fR font files and the +intermediate \f(CW.xpfa\fR font metric files. +.Ip "\(bu" 2 +\fB\f(CWINSTALLFONTMAP\fR\fR \- a Ghostscript parameter, if the value is set to +\fB\f(CWYES\fR\fR then install the entries for the new fonts +right into the main \f(CWFontmap\fR file. Otherwise just leave +the file \f(CWFontmap.ttf\fR in the Ghostscript configuration +directory. +.Ip "\(bu" 2 +\fB\f(CWHINTSUBST\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR use the option +\f(CW\fB-H\fR\fR, otherwise don't use it. This option enables the +hint substitution technique. If you have not installed the X11 patch +described above, use this option with great caution. See further +description of this option in the \s-1README\s0 file. +.Ip "\(bu" 2 +\fB\f(CWENFORCEISO\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then +disguise the resulting fonts as the fonts in ISOLatin1 encoding. Historically +this was neccessary due to the way the installer scripts created the +X11 font configuration files. It is not neccessary any more for this +purpose. But if you plan to use these fonts with some other application +that expects ISOLatin1 encoding then better enable this option. +.Ip "\(bu" 2 +\fB\f(CWALLGLYPHS\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then +include all the glyphs from the source fonts into the resulting fonts, even +if these glyphs are inaccessible. If it's set to \fB\f(CWNO\fR\fR then +include only the glyphs which have codes assigned to them. The glyphs +without codes can not be used directly. But some clever programs, +such as the Type 1 library from XFree86 3.9 and higher can change +the encoding on the fly and use another set of glyphs. If you have not +installed the X11 patch described above, use this option with great +caution. See further description of the option option \f(CW\fB-a\fR\fR in the +\s-1README\s0 file. +.Ip "\(bu" 2 +\fB\f(CWGENUID\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then use +the option \f(CW\fB-uA\fR\fR of the converter to generate UniqueIDs for +the converted fonts. The standard X11 Type 1 library does not use +this \s-1ID\s0, so it may only be neccessary for the other applications. +The script is clever enough to generate different UniqueID for the +same font converted to multiple encodings. Also after conversion it +checks all the fonts generacted during the session for duplicated +UniqueID and shows those. Still, this does not quarantee that these +UniqueIDs won't overlap with some other fonts. The UniqueIDs are +generated as hash values from the font names, so it's guaranteed +that if the `\f(CWconvert\fR\*(R' script runs multiple times it will +generate the same UniqueIDs during each run. See further description +of this option in the \s-1README\s0 file. +.Ip "\(bu" 2 +\fB\f(CWGENUID\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then create +the \f(CW.pfb\fR files, otherwise the \f(CW.pfa\fR files. The \f(CW.pfb\fR +files are more compact but contain binary data, so you may experience some +troubles when transferring them through the network. +.PP +The following parameters are used to locate the other scripts and +configuration files. By default the scripts do a bit of guessing for them: +they search in the \fBttf2pt1\fR installation directory if \fBttf2pt1\fR +was installed or otherwise suppose that you are running `\f(CWconvert\fR\*(R' with +`\f(CWscripts\fR\*(R' subdirectory being the current directory. +.Ip "\(bu" 2 +\fB\f(CWENCDIR\fR\fR \- directory containing the descriptions of encodings +.Ip "\(bu" 2 +\fB\f(CWMAPDIR\fR\fR \- directory containing the external map files +.PP +Besides that a few parameters are built into the `\f(CWconvert\fR\*(R' script itself. +You probably won't need to change them: +.Ip "\(bu" 2 +\f(CW\fBT1ASM\fR\fR, \f(CW\fBTTF2PT1\fR\fR, \f(CW\fBTRANS\fR\fR, \f(CW\fBT1FDIR\fR\fR, \f(CW\fBFORCEISO\fR\fR \- paths to the other script +.PP +Also there are a few parameters controlling the installation of +fonts for Ghostscript. Please look at their description in the +Ghostscript section of documentation or in the \fBttf2pt1_x2gs(1)\fR +manual page before running `\f(CWconvert\fR\*(R'. If these parameters are +set, `\f(CWconvert\fR\*(R' will call the `\f(CWx2gs\fR\*(R' script automatically +to install the newly converted fonts in Ghostscript. +.PP +After creating the configuration file run the `\f(CWconvert\fR\*(R' script. Look at +the result and the log file in \f(CWDSTDIR\fR. +.PP +Add the directory with newly converted fonts to the configuration +of X server or font server. For most of the systems this step is +very straightforward. For \s-1HP\s0\-\s-1UX\s0 it's rather tricky and poorly +documented, so the file \s-1FONTS\s0.hpux gives a short description. +.PP +If you don't have the privileges of the root user, you still can +configure your private font server. Just use some non-standard +port number (see \s-1FONTS\s0.hpux for an example, exept that you won't +need all the \s-1HP\s0\-related stuff on any other system). +.SH "FILES" +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/\s0scripts/convert.cfg.sample +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/\s0scripts/* +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/README\s0 +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/FONTS\s0 +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR\s0/* +.Ip "\(bu" 2 +\s-1TTF2PT1_BINDIR/\s0ttf2pt1 +.SH "SEE ALSO" +.Ip "\(bu" 4 +the \fIttf2pt1(1)\fR manpage +.Ip "\(bu" 4 +the \fIttf2pt1_x2gs(1)\fR manpage +.Ip "\(bu" 4 +the \fIt1asm(1)\fR manpage +.SH "BUGS" +.Sh "Known problems" +.Ip "\(bu" 4 +One catch is that the X11 Type 1 font library has a rather low limit +on the font size. Because of this the fonts with more complicated +outlines and the enabled hint substitution may not fit into +this limit. The same applies to the fonts with very complicated +outlines or with very many glyphs (especially the fonts with +over 256 glyphs). So you will need to excercise caution with +these options if you plan using these fonts with X11. Some vendors +such as \s-1HP\s0 provide the Type 1 implementation licensed from Adobe +which should have no such problem. +.Sp +But there is a solution even for the generic X11. A patch located +in the subdirectory `\f(CWapp/X11\fR\*(R' fixes this problem as well +as some other minor problems. Its description is provided in +app/X11/\s-1README\s0. +.Sp +To fix the X11 font library, you have to get the X11 sources. I +can recommend the ftp sites of the XFree86 project ftp://ftp.xfree86.org +or of the Open Group ftp://ftp.x.org. This patch was made on the sources +of XFree86 so you may have better success with applying it to the +XFree86 distribution. After you have got the sources, make sure +that you can compile them. Then apply the patch as described. +Make sure that it was applied properly. Compile the sources again +(actually, you need only the fonts library, the fonts server, and +possibly the X server). It would be prudent now to save your old +font library, font server and, possibly, X server. Then install +the new recently compiled versions of these files. Of course, +if you know someone who already has compiled these files for the +same \s-1OS\s0 as yours, you can just copy the binary fles from him. +.Sp +Alas, building the X11 system from the source code is not the +easiest thing in the world and if you have no experience it +can be quite difficult. In this case just avoid the aforementioned +features or check each converted font to make sure that it +works properly. +.Ip "\(bu" 4 +The Type1 font library from the standard X11 distribution +does not work on \s-1HP\s0\-\s-1UX\s0 (at least, up to 10.01). The font server +supplied with \s-1HP\s0\-\s-1UX\s0 up to 10.01 is also broken. Starting from +\s-1HP\s0\-\s-1UX\s0 10.20 (I don't know about 10.10) they supply a proprietary font +library and the converted fonts work fine with it, provided that +they are configured properly (see the file \s-1FONTS\s0.hpux). +.Ip "\(bu" 4 +The \f(CWfonts.scale\fR files created by the older versions of the +\f(CWttf2pt1\fR installation program (up to release 3.1) have conflicted +with the language definitions of the \f(CWXfsft\fR font server and +parts of it included into XFree86. To overcome this incompatibility +the never versions creats the \f(CWfonts.scale\fR file describing all the +fonts as belonging to the \f(CWadobe-fontspecific\fR encoding and +the \f(CWfonts.alias\fR file with the proper names. The drawback of +this solution is that \f(CWxlsfonts\fR gives the list of twice more +fonts. But as a side effect the option \f(CW\fBENFORCEISO\fR\fR in +`\f(CWconvert.cfg\fR\*(R' is not required for X11 any more. +.Ip "\(bu" 4 +The conversion script has no support for Eastern multi-plane fonts. +Contribution of such a support would be welcome. + +.rn }` '' +.IX Title "TTF2PT1_CONVERT 1" +.IX Name "B - convenience font conversion script" + +.IX Header "NAME" + +.IX Header "SYNOPSIS" + +.IX Header "DESCRIPTION" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "FILES" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "SEE ALSO" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "BUGS" + +.IX Subsection "Known problems" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + Index: xc/extras/ttf2pt1/ttf2pt1_x2gs.1 =================================================================== RCS file: xc/extras/ttf2pt1/ttf2pt1_x2gs.1 diff -N xc/extras/ttf2pt1/ttf2pt1_x2gs.1 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/ttf2pt1_x2gs.1 13 Apr 2004 02:45:11 -0000 @@ -0,0 +1,316 @@ +.rn '' }` +''' $RCSfile: ttf2pt1_x2gs.1,v $$Revision: 1.1 $$Date: 2003/06/04 00:33:54 $ +''' +''' $Log: ttf2pt1_x2gs.1,v $ +''' Revision 1.1 2003/06/04 00:33:54 roland +''' Fix for http://xprint.mozdev.org/bugs/show_bug.cgi?id=3846 - RFE: Upload Freetype --> PS Type1 font converter "ttf2pt1" ... +''' +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +''' \*(M", \*(S", \*(N" and \*(T" are the equivalent of +''' \*(L" and \*(R", except that they are used on ".xx" lines, +''' such as .IP and .SH, which do another additional levels of +''' double-quote interpretation +.ds M" """ +.ds S" """ +.ds N" """"" +.ds T" """"" +.ds L' ' +.ds R' ' +.ds M' ' +.ds S' ' +.ds N' ' +.ds T' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds M" `` +.ds S" '' +.ds N" `` +.ds T" '' +.ds L' ` +.ds R' ' +.ds M' ` +.ds S' ' +.ds N' ` +.ds T' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH TTF2PT1_X2GS 1 "version 3.4.4-SNAP-030526" "May 26, 2003" "TTF2PT1 Font Converter" +.UC +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +\fBttf2pt1_x2gs\fR \- font installer for Ghostscript +.SH "SYNOPSIS" +ttf2pt1_x2gs \fB[config-file]\fR +.SH "DESCRIPTION" +The fonts generated with \fBttf2pt1\fR work fine with Ghostscript by +themselves. The script `\fBx2gs\fR\*(R' (or `\fBttf2pt1_x2gs\fR\*(R' when installed +into a public directory, to avoid name conflicts with other +programs) links the font files from the X11 direcotry into the Ghostscript +directory and automatically creates the description file (\f(CWFontmap\fR) +in Ghostscript format. +.PP +If the configuration file is not specified as an argument then the file +`\f(CWconvert.cfg\fR\*(R' in the current directory is used, just like the +`\f(CWconvert\fR\*(R' script does. Indeed, this configuration file is used for +both scripts. +.PP +The Ghostscript-related parameters in the configuration file are: +.PP +\fB\f(CWDSTDIR\fR\fR \- the X11 font directory used by `\f(CWx2gs\fR\*(R' as the +source of the fonts. This parameter is common with the X11 +configuration. +.PP +\fB\f(CWGSDIR\fR\fR \- the base directory of Ghostsript. If this +parameter is set to an empty string then `\f(CWconvert\fR\*(R' won't +call `\f(CWx2gs\fR\*(R'. So if you want to get only the X11 fonts +installed then set this parameter to an empty string. This +directory may vary on various system, so please check your +system and set this value accordingly before running the script. +.PP +\fB\f(CWGSFONTDIR\fR\fR \- the font directory of Ghostscript. In the standard +Ghostscript installation it's a subdirectory of \f(CWGSDIR\fR +but some systems may use completely different directories. +.PP +\fB\f(CWGSCONFDIR\fR\fR \- the configuration subdirectory of Ghostscript +that contains the \f(CWFontmap\fR file. +.PP +\fB\f(CWINSTALLFONTMAP\fR\fR \- if the value is set to \fB\f(CWYES\fR\fR then +install the entries for the new fonts right into the main +\f(CWFontmap\fR file. Otherwise just leave the file \f(CWFontmap.ttf\fR +in the Ghostscript configuration directory. +.PP +After preparing the configuration file run the script. It symbolicaly links +all the font files and creates the description file \f(CWFontmap.ttf\fR in +\f(CWGSCONDFIR\fR. After that there are two choices. +.PP +If the option \f(CWINSTALLFONTMAP\fR was set to \f(CWYES\fR then +the font descriptions are also automatically installed into the +master \f(CWFontmap\fR file. The script is clever enough to +detect if it was run multiple times with the same directories +and if so it replaces the old \f(CWFontmap\fR entries with +the new ones instead of just accumulating all of them. You +may also run it multiple times for multiple X11 directories +and all the results will be properly collected in the \f(CWFontmap\fR. +But it's your responsibility to watch that the names of the +font files don't overlap. If the X11 font directory gets +renamed then you have to remove its font entries from the +\f(CWFontmap\fR and only after that re-run `\f(CWx2gs\fR\*(R' +for the new directory. +.PP +On the other hand if the option \f(CWINSTALLFONTMAP\fR was set to +\f(CWNO\fR then go to the \f(CWGSCONFDIR\fR directory and insert the +contents of \f(CWFontmap.ttf\fR into the \f(CWFontmap\fR file +manually. This step may be left manual to make the installation +a little bit more safe. +.PP +After that you may also want to redefine some of the aliases in +\f(CWFontmap\fR to refer to the newly installed fonts. +But the redefinition of the aliases may be dangerous if the width of +characters in the new font will be different from the old font. +Alas, there is no visible solution of this problem yet. +.SH "FILES" +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/\s0scripts/convert.cfg.sample +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/\s0scripts/* +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/README\s0 +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR/FONTS\s0 +.Ip "\(bu" 2 +\s-1TTF2PT1_SHAREDIR\s0/* +.Ip "\(bu" 2 +\s-1TTF2PT1_BINDIR/\s0ttf2pt1 +.SH "SEE ALSO" +.Ip "\(bu" 4 +the \fIttf2pt1(1)\fR manpage +.Ip "\(bu" 4 +the \fIttf2pt1_convert(1)\fR manpage +.Ip "\(bu" 4 +the \fIt1asm(1)\fR manpage + +.rn }` '' +.IX Title "TTF2PT1_X2GS 1" +.IX Name "B - font installer for Ghostscript" + +.IX Header "NAME" + +.IX Header "SYNOPSIS" + +.IX Header "DESCRIPTION" + +.IX Header "FILES" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Header "SEE ALSO" + +.IX Item "\(bu" + +.IX Item "\(bu" + +.IX Item "\(bu" + Index: xc/extras/ttf2pt1/version.h =================================================================== RCS file: xc/extras/ttf2pt1/version.h diff -N xc/extras/ttf2pt1/version.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/version.h 13 Apr 2004 02:45:11 -0000 @@ -0,0 +1,7 @@ +/* + * see COPYRIGHT + */ + + +/* version number */ +#define TTF2PT1_VERSION "3.4.4-SNAP-030526" Index: xc/extras/ttf2pt1/winbuild.bat =================================================================== RCS file: xc/extras/ttf2pt1/winbuild.bat diff -N xc/extras/ttf2pt1/winbuild.bat --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/winbuild.bat 13 Apr 2004 02:45:11 -0000 @@ -0,0 +1,11 @@ +rem file to build ttf2pt1 with Visual C++ + +cl -DWINDOWS -c bdf.c +cl -DWINDOWS -c ttf2pt1.c +cl -DWINDOWS -c pt1.c +cl -DWINDOWS -c ttf.c +cl -DWINDOWS -c t1asm.c +cl -DWINDOWS -c bitmap.c +cl -o ttf2pt1 ttf2pt1.obj pt1.obj t1asm.obj ttf.obj bdf.obj bitmap.obj +cl -o t1asm -DWINDOWS -DSTANDALONE t1asm.c + Index: xc/extras/ttf2pt1/windows.h =================================================================== RCS file: xc/extras/ttf2pt1/windows.h diff -N xc/extras/ttf2pt1/windows.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ xc/extras/ttf2pt1/windows.h 13 Apr 2004 02:45:11 -0000 @@ -0,0 +1,93 @@ +/* + * Implementation of things missing in Windows + */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#undef ntohs +#undef ntohl +#undef htonl + +#ifdef WINDOWS_FUNCTIONS +/* byte order */ + +static unsigned short StoM(unsigned short inv) { + union iconv { + unsigned short ui; + unsigned char uc[2]; + } *inp, outv; + + inp = (union iconv *)&inv; + + outv.uc[0] = inp->uc[1]; + outv.uc[1] = inp->uc[0]; + + return (outv.ui); +} + +static unsigned int ItoM(unsigned int inv) { + union iconv { + unsigned int ui; + unsigned char uc[4]; + } *inp, outv; + + inp = (union iconv *)&inv; + + outv.uc[0] = inp->uc[3]; + outv.uc[1] = inp->uc[2]; + outv.uc[2] = inp->uc[1]; + outv.uc[3] = inp->uc[0]; + + return (outv.ui); +} + +unsigned short ntohs(unsigned short inv) { return StoM(inv); } +unsigned long ntohl(unsigned long inv) { return ItoM(inv); } +unsigned long htonl(unsigned long inv) { return ItoM(inv); } + +char *optarg; +int optind=1; + +char getopt(int argc, char **argv, char *args) { + int n,nlen=strlen(args),nLen=0; + char nCmd; + + if (argv[optind] && *argv[optind]=='-') { + nCmd=*((argv[optind]+1)); + + for (n=0;n