aboutsummaryrefslogtreecommitdiff
path: root/nx-X11/extras/ttf2pt1
diff options
context:
space:
mode:
authorReinhard Tartler <siretart@tauware.de>2011-10-10 17:43:39 +0200
committerReinhard Tartler <siretart@tauware.de>2011-10-10 17:43:39 +0200
commitf4092abdf94af6a99aff944d6264bc1284e8bdd4 (patch)
tree2ac1c9cc16ceb93edb2c4382c088dac5aeafdf0f /nx-X11/extras/ttf2pt1
parenta840692edc9c6d19cd7c057f68e39c7d95eb767d (diff)
downloadnx-libs-f4092abdf94af6a99aff944d6264bc1284e8bdd4.tar.gz
nx-libs-f4092abdf94af6a99aff944d6264bc1284e8bdd4.tar.bz2
nx-libs-f4092abdf94af6a99aff944d6264bc1284e8bdd4.zip
Imported nx-X11-3.1.0-1.tar.gznx-X11/3.1.0-1
Summary: Imported nx-X11-3.1.0-1.tar.gz Keywords: Imported nx-X11-3.1.0-1.tar.gz into Git repository
Diffstat (limited to 'nx-X11/extras/ttf2pt1')
-rw-r--r--nx-X11/extras/ttf2pt1/CHANGES.html805
-rw-r--r--nx-X11/extras/ttf2pt1/COPYRIGHT87
-rw-r--r--nx-X11/extras/ttf2pt1/FONTS.hpux.html197
-rw-r--r--nx-X11/extras/ttf2pt1/FONTS.html708
-rw-r--r--nx-X11/extras/ttf2pt1/Makefile279
-rw-r--r--nx-X11/extras/ttf2pt1/README.FIRST4
-rw-r--r--nx-X11/extras/ttf2pt1/README.html1182
-rw-r--r--nx-X11/extras/ttf2pt1/bdf.c660
-rw-r--r--nx-X11/extras/ttf2pt1/bitmap.c2633
-rw-r--r--nx-X11/extras/ttf2pt1/byteorder.h24
-rw-r--r--nx-X11/extras/ttf2pt1/cygbuild.sh8
-rw-r--r--nx-X11/extras/ttf2pt1/ft.c808
-rw-r--r--nx-X11/extras/ttf2pt1/global.h174
-rw-r--r--nx-X11/extras/ttf2pt1/pt1.c7374
-rw-r--r--nx-X11/extras/ttf2pt1/pt1.h257
-rw-r--r--nx-X11/extras/ttf2pt1/runt1asm.c61
-rw-r--r--nx-X11/extras/ttf2pt1/t1asm.c606
-rw-r--r--nx-X11/extras/ttf2pt1/ttf.c1480
-rw-r--r--nx-X11/extras/ttf2pt1/ttf.h183
-rw-r--r--nx-X11/extras/ttf2pt1/ttf2pt1.1832
-rw-r--r--nx-X11/extras/ttf2pt1/ttf2pt1.c2722
-rw-r--r--nx-X11/extras/ttf2pt1/ttf2pt1_convert.1516
-rw-r--r--nx-X11/extras/ttf2pt1/ttf2pt1_x2gs.1323
-rw-r--r--nx-X11/extras/ttf2pt1/version.h7
-rw-r--r--nx-X11/extras/ttf2pt1/winbuild.bat11
-rw-r--r--nx-X11/extras/ttf2pt1/windows.h93
26 files changed, 22034 insertions, 0 deletions
diff --git a/nx-X11/extras/ttf2pt1/CHANGES.html b/nx-X11/extras/ttf2pt1/CHANGES.html
new file mode 100644
index 000000000..9b51d1bc8
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/CHANGES.html
@@ -0,0 +1,805 @@
+<HTML>
+<HEAD>
+<TITLE>
+TTF2PT1 - CHANGES history
+</TITLE>
+</HEAD>
+<BODY>
+<H2>
+TTF2PT1 - CHANGES history
+</H2>
+
+<!
+(Do not edit this file, it is generated from CHANGES.html!!!)
+>
+
+<H4>
+3.4.4-SNAP-030526
+</H4>
+<!
+-------
+>
+
+<b>New features:</b>
+<ul>
+<li> Improved the auto-vectoring (-OV) alrogithm.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Fix to build all the features on Windows MS C++, by Tomoo Amano.
+</ul>
+
+<H4>
+3.4.3 -- December 2, 2002
+</H4>
+<!
+-------
+>
+
+<b>New features:</b>
+<ul>
+<li> <tt>scripts/forceiso</tt> got an optional argument to select the
+ format of the names for glyphs without standard Latin-1 names.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Changed the glyph names in scripts/forceiso to match those in ttf2pt1.
+<li> Included the missing directory app/TeX.
+</ul>
+
+<H4>
+3.4.2 -- August 30, 2002
+</H4>
+<!
+-------
+>
+
+<b>New features:</b>
+<ul>
+<li> New map for T2A_compat encoding (for Cyrillic LaTeX) by Mikhail
+ Umorin.
+<li> Scripts supporting font conversion for CJK-LaTeX, by Mike Fabian
+ from SuSE.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Explicit owner/group/permissions are used to install directories.
+<li> In scripts/convert fixed the addition of encoding name to the font
+ name for the external encoding maps, was missing "/" at the start.
+<li> Fixed the divergence between two copies of UniqueID.
+<li> Fixed the recovery after defective empty contours.
+</ul>
+
+<H4>
+3.4.1 -- June 13, 2002
+</H4>
+<!
+-------
+>
+
+<b>New features:</b>
+<ul>
+<li> Added Autotrace support for the bitmap fonts (-OZ). It's horrible.
+<li> Added vectorization of bitmap fonts (-OV) - functionally the same thing as
+ autotrace but home-grown. Works mostly decently but still with large
+ space for impprovement.
+<li> Relaxed the conditions for warnings about long glyphs.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Fix by Rob Kolstad for a crash in the new outline smoothing
+ code (on small thin contours) and diagnostic for another crash.
+<li> Fix by Holger Huesing for a crash on degenerate contours.
+<li> Fix for bitmaps of zero dimensions.
+<li> The BDF reader does not fail on redefintion of the properties.
+<li> Fix for reading of BDF glyphs with 0 size.
+<li> Fix for a hang when guessing the boldness of some fonts.
+<li> Fix by Adriano Konzen for scaling coefficients in composite glyphs.
+</ul>
+
+<H4>
+3.4.0 -- November 24, 2001
+</H4>
+<!
+-------
+>
+
+<b>New features:</b>
+<ul>
+<li> Parser for the BDF bitmap fonts.
+<li> Vastly improved the smoothing of the outlines.
+<li> The options are saved as a comment in the output file.
+<li> New script <tt>other/showdf</tt> for visual comparison of the fonts.
+<li> New option <b>-G</b> to select the file types to generate.
+<li> Creation of the dvips encoding files (by Rigel).
+<li> More glyphs in the Chinese maps (by Rigel).
+<li> Made the assignment of ISO8859/1 glyph names to the glyphs in the
+ fonts without PostScript names in them dependent on the original
+ encoding: no change for the 8-bit encodings, for the Unicode encoding
+ the names are assigned to the glyph with the codes 0-255 in Unicode,
+ and for the other 16-bit encodings the 8859/1 names are not assigned
+ at all.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Added a check for spaces in the PostScript font name in the FreeType
+ parser.
+<li> Made "-" a valid character in the glyph names.
+<li> Fixed handling of the Unicode names returned by FreeType, though
+ not perfectly.
+<li> Changed the build for FreeType-2.0.4.
+<li> Fixed the handling and printing of bad glyph names.
+<li> Fixed the bug with duplicated glyph names when more than 256 glyphs are
+ extracted from a font that has no PostScript glyph names defined.
+<li> Added ability to map a glyph to more than one code when unisng the
+ native parser (-pttf).
+</ul>
+
+<H4>
+3.3.5 -- September 12, 2001
+</H4>
+<!
+-------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Fixed the scaling of Ascender and Descender in the AFM file.
+<li> Fixed the brekage of "-l adobestd".
+</ul>
+
+<H4>
+3.3.4 -- June 4, 2001
+</H4>
+<!
+-------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Cyrillic (full set of glyphs) language tables (by Zvezdan Petkovic).
+ Now the languages "russian" and "bulgarian" are provided for compatibility
+ only, use the common language "cyrillic" instead.
+<li> More information in <a href="FONTS.html">FONTS</a> on using Cyrillic fonts with
+ Netscape (by Zvezdan Petkovic)
+<li> In the Netscape print filter added removal of the clipping path command:
+ otherwise Netscape tends to cut off a large piece of the rightmost column
+ of the tables.
+<li> One more script for printing from Netscape (by Zvezdan Petkovic).
+<li> Added selection of the base TTF encoding by pid/eid in the external maps.
+<li> Improved the recognition of substituted stems for intersecting contours.
+<li> Improved the substituted hints to make the horizontal positioning of
+ the points at the same height more uniform at small pixel sizes.
+<li> Made the algorithm for calculation of standard stem widths more
+ selective.
+<li> Added link to the GnuWin32 project.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> TH: Print out metrics of un-encoded glyphs even without "-a" option.
+<li> Added missing "/" in Fontmap generation in convert (by Zvezdan Petkovic).
+<li> Removed unneccessary "\n" in messages in x2gs.
+<li> Removed the broken overoptimisation of "0 0 rmoveto".
+<li> Removed the useless warnings about multiple codes for a glyph.
+<li> Changed the FreeType2 include directory in the Makefile to match the
+ FreeType's default.
+</ul>
+
+<H4>
+3.3.3 -- March 4, 2001
+</H4>
+<!
+-------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> TH: Added printing of front-end parser in the header of the font file.
+<li> Tested build with FreeType 2.0 Release.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Changed the installation script which on some versions of bash
+ copied all files into the share directory.
+<li> Fixed the close sequences of html2man comments in the HTML files,
+ now they should display correctly with <tt>lynx</tt>.
+<li> Restored the ability to include un-encoded characters into the
+ customised maps (those with codes over 255).
+<li> Fixed the Unicode mapping of the Cyrillic letters "YO" and "yo"
+ (by Yuri Shemanin).
+<li> Fixed the spurious aborts when the conversion-by-plane function
+ gets called for auto-guessing of encoding.
+</ul>
+
+<H4>
+3.3.2 -- November 20, 2000
+</H4>
+<!
+--------------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added generation of man pages.
+<li> Added "make install" and "make uninstall".
+<li> Added language option "-l plane".
+<li> In <tt>other/showg</tt> added better support of comparison files:
+<ul>
+ <li> printing of the comparison file legend;
+ <li> guessing of missing glyph names in a comparison file by code;
+ <li> bounding boxes of all comparison files are used for page layout.
+</ul>
+<li> Added ability to use external t1asm instead of compiling it in.
+<li> Renamed the fonts installation guide from INSTALL*html to FONTS*html
+ to avoid confusion with installation of ttf2pt1 itself.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Removed erroneous extra fclose(pfa_file).
+<li> Fixed random memory corruption that manifested with crash on Linux
+ when converting fonts not containing glyph names.
+<li> Removed from the output file the comments that confused dvips. Changed
+ <tt>other/showg</tt> to work without them.
+<li> In <tt>other/showg</tt> added better checks for missing glyphs, now it
+ gives warnings about them and the output file does not crash PostScript.
+</ul>
+
+<b>Other:</b>
+<ul>
+<li> <tt>ttf2pfa</tt> is no longer included, people interested in history
+ should look for it in the older versions.
+</ul>
+
+<H4>
+3.3.1 -- October 22, 2000
+</H4>
+<!
+-------------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added front-end parser based on the FreeType-2 library. See Makefile
+for build instructions.
+<li> Changed the handling of encodings to accomodate the FreeType model.
+<li> Further cleaned up the front-end parser interface.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Fixed a bug that caused core dump on Alpha machines.
+<li> Fixed a bug in the outline smoothing that occasionally caused core dump.
+<li> Cleaned up warnings from picky compilers
+<li> Fixed more bugs in the Windows port (by Stefan Bauer).
+<li> Fixed the RPM spec file (suggested by Brian Armstrong).
+</ul>
+<p>
+
+<H4>
+3.3.0 -- September 22, 2000
+</H4>
+<!
+---------------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Converted most of the outlines' processing to floating point
+arithmetic.
+<li> Added splitting of curves crossing the quadrant boundaries (no gross
+damage is done any more to the Marvosym font and others like it).
+<li> Added modular interface for front-end font parsers and option to control
+their selection at run time.
+<li> Grouped the outline processing control options into one to reduce the
+options namespace pollution.
+<li> Thomas moved the Chinese maps into a separate module, chinese-maps.
+<li> Thomas added option -V to print version number. In addition, the version
+number is put in the header of the font file.
+<li> Added long option names (suggested by Thomas).
+<li> Added support for multi-level composite glyphs.
+<li> TH: Made &lt;fontname&gt; command-line argument optional; default to &lt;ttf-file&gt;
+with suffix replaced.
+<li> In <tt>other/showg</tt> added more ways to specify glyphs and the comparison option.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Fixed the VC++ batch file, added batch file for Cygnus GCC on Windows.
+<li> Removed parentheses from the Version string in AFM files because it does
+not help StarOffice anyway. StarOffice 5.2 has been reported to have this
+bug fixed. Added paragraph on StarOffice in FONTS.html.
+<li> Made messages on the '?' option parameter more meaningful (by Johan Vromans).
+<li> Changed the latin1 encoding table to include the Euro sign, Z and z with
+caron (by Thomas Henlich).
+<li> Improved the smoothing code which occasionally had problems with
+joining curves. Also fixed a few minor bugs in it.
+</ul>
+
+<H4>
+3.22 -- May 23, 2000
+</H4>
+<!
+--------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Included windows support by Frank Siegert (somewhat amended)
+<li> Added control over verbosity of warnings.
+<li> Added arguments and initialization functions to the language
+translation routines.
+<li> Added support of planes determined by arguments to the external
+maps.
+<li> Added compact external maps format (primarily for Eastern fonts).
+<li> Added external maps for Chinese GBK and Big5 encodings (converted
+from ttf2pfb) as well as maps for other Chinese encodings by Wang Lei.
+<li> Added the idea of buckets to speed up the search in external maps.
+<li> Changed the grouping algorithm for substituted hints: now it creates
+a bit bigger files but requires smaller hint stack when being rendered.
+<li> Added maximal limit of hint stack depth, glyphs requiring bigger
+stack get generation of substituted hints disabled. This makes substituted
+hints safe to use, no more lost glyphs due to hint stack overflow.
+<li> Added the font dump program <tt>other/dumpf</tt>.
+<li> Changed the testing HTML generator <tt>other/lst.pl</tt> to use tables.
+<li> Added debugging script <tt>other/cntstems.pl</tt> to count required hint
+stack depth for the glyphs.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> Fixed printing of UID in script/trans. Changed the auto-generated UID to
+be in range 4000000-4999999 which is reserved by Adobe for private use.
+<li> Fixed handling of "cleartomark" in built-in t1asm.
+<li> Added handling of "can't happen" case in straighten() routine
+which actually happened on strange fonts and caused failure on assertion.
+<li> Made it always include the glyph .notdef in the resulting font.
+<li> Placed the version string in AFM file in parentheses, hopefully
+that would fix the problem with StarOffice.
+<li> Improved the smoothing code which occasionally had problems with
+joining curves.
+</ul>
+
+<H4>
+3.21 -- March 1, 2000
+</H4>
+<!
+---------------------
+>
+
+Sergey Babkin: committed the changes by Petr Titera and
+my bugfixes.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> New Unicode map format with glyph names, by Petr Titera.
+<li> Option to force the Unicode encoding by Petr Titera
+ (I changed it to work on any MS encoding, not only Symbol).
+<li> Slightly tweaked the calculation of hints, should be better now.
+</ul>
+
+<b>Bug fixes:</b>
+<ul>
+<li> The unicode-sample.map with description of the map formats
+ was lost in the release process, restored and enhanced.
+<li> Renamed the table ISOLatin1Encoding to Fmt3Encoding to reflect
+ the way it is used. Saved the original one for reference
+ purposes. In the new table renamed "quoteright" to "quotesingle"
+ as Thomas Henlich suggested (and he were right).
+<li> In the ISOLatinEncoding table renamed the glyph "grave"
+ at octal 0140 to "quoteleft", "quotesingle" at octal 047 to
+ "quoteright" to conform to the standard as suggested by
+ Martin Trautner).
+<li> Fixed bug in scripts/trans that corrupted the UniqueID record
+ in the translated fonts.
+<li> Fixed bug in interaction of substituted hints with BlueZones.
+ Now the fonts with hint substitution seem to be always at least
+ not worse than without it (well, when they fit in the X11
+ file size limit).
+</ul>
+
+
+<H4>
+3.2 -- January 15, 2000
+</H4>
+<!
+-----------------------
+>
+
+Sergey Babkin: combined my changes with the changes by
+Thomas Henlich. The result deserves a not-so-minor version
+increase.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Support of the external Unicode re-encoding maps
+ (by Thomas).
+<li> Support for inclusion of all the glyphs from the
+ source file into the resulting file (inspired by
+ Thomas but I re-implemented it to remove the limitation
+ of his implementation: not more than 1024 glyphs).
+<li> The hints substitution. It's an experimental feature
+ yet and needs further work.
+<li> Support for UniqueID and its auto-generation.
+<li> Support for the name-based conversions from Unicode
+ in general and the adobestd "language" in particular.
+<li> Started the split of the source code into multiple
+ files. This needs more work to do it in a cleaner
+ way.
+<li> Better framework for the debugging printout
+ in the converter.
+<li> Utilities to install the fonts in Netscape
+ Navigator/Communicator 4.x.
+<li> Patches for bigger font files in the X11 rasterizer.
+<li> Linux RPM spec-file (by Johan Vromans).
+<li> Added the COPYRIGHT file (BSD-style, as we discussed
+ on the mailing list earlier) and the CHANGES file.
+<li> Creation of the <tt>.pfb</tt> files from the <tt>convert</tt>
+ script.
+<li> Changed the <tt>.notdef</tt>-s in the built-in
+ ISOLatin1Encoding table to some valid names (by Thomas).
+ Thomas also suggested replacing `<tt>quoteright</tt>' by
+ `<tt>quotesingle</tt>' but this seems to be against the
+ Adobe ISOLatin1 table.
+<li> New aliases <tt>windows-1251</tt> and <tt>cp-866</tt> for
+ the Russian encodings: those are expected by Netscape
+ navigator.
+<li> The font comparison program <tt>other/cmpf</tt>.
+<li> The "magnifying glass" program for glyph outlines:
+ <tt>other/showg</tt>.
+<li> Other updates of the tools in the `<tt>other</tt>' subdirectory.
+<li> Added a link to T1LIB in README.
+<li> A few new options in <tt>convert.cfg</tt>.
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> A bug in the outline smoothing code that corrupted some
+ of the fonts (for example, Microsoft Verdana).
+<li> Added explicit `<tt>cleartomark</tt>' to the end of file,
+ this seems to be compatible with both old and new version
+ of <tt>t1asm</tt> (suggested by Thomas).
+<li> Added the <tt>FontEncoding</tt> statement to the AFM files
+ (techincally this was not a bug because this statement
+ is optional but some programs want it).
+<li> A coredump when the converter tried to print a warning
+ (rather ironically) about a weird glyph width.
+<li> Changed the underscores in the font names to dashes (this
+ has been proposed long time ago by Johan Vromans).
+<li> No more glyph names of font names staring with a digit.
+<li> The names of the fonts in font and AFM files are now the
+ same as in the generated Ghostscript <tt>Fontmap</tt> file.<br>
+ <b>Warning:</b> the names in <tt>Fontmap</tt> have been
+ changed.
+<li> The <tt>forceiso</tt> script does not corrupt the character
+ and kerning pairs counts any more, and is optional at all.
+<li> Fix for a loop going to 254 instead of 255 (by Thomas).
+<li> Added ':' in the font header (by Thomas).
+<li> A coredump when wrong language name is given (this was
+ also fixed by Thomas but I noticed it too late, after
+ I already fixed it by myself).
+<li> Fixed the links to the Adobe documents in README.
+</ul>
+
+
+<H4>
+3.13 -- October 18, 1999
+</H4>
+<!
+------------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> New option -v for automatic re-scaling based on the vertical size of the font
+<li> Changed the code to use getopt() instead of a home-made version of it.
+<li> Latin2 language support by Szalay Tamas.
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> Fix for the bug that made possible calls of malloc(0).
+<li> Refinement of the option -w to prevent extra wide spacing
+</ul>
+
+<H4>
+3.12 -- October 2, 1999
+</H4>
+<!
+-----------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added support for the Bulgarian language (actually, for now just an alias
+of Russian).
+<li> Added option -w that tries to make sure that the character widths are not
+too narrow.
+<li> Added the concept of aliased encodings.
+<li> Now the conversion scripts create and install the .afm files too.
+<li> The conversion script removes the intermediate files after installation.
+<li> Added tunables to the conversion script.
+<li> Installation of the Ghostscript fonts can now be done automatically
+together with the X11 fonts.
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> (FINALLY!!!) A correct fix for the infamous Red Hat 6.0 stdio "feature".
+<li> A number of little bugs discovered by a picky SGI compiler (well, maybe
+some day I'll try to run it through the UnixWare lint and see what happens).
+<li> A diagnostic message about the empty encodings in the convert script was
+made less cryptic and a bug in the awk sub-script was fixed.
+<li> The .afm creation code now considers the option -t.
+</ul>
+
+<H4>
+3.11 -- May 24, 1999
+</H4>
+<!
+--------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> It includes the Turkish (Latin5, ISO8859/9)
+language support by Turgut Uyar and Baltic (ISO8859/4) languages support by
+Rihardas Hepas.
+<li> Also the installation script got updated: the configuration parameters
+are moved to a separate file and the generated fonts.dir files should now be
+compatible with Xfsft.
+</ul>
+
+<H4>
+3.1 -- March 28, 1999
+</H4>
+<!
+---------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Improved the interaction of the character-level hints and font-level hints
+</ul>
+
+
+<H4>
+3.0 -- March 6, 1999
+</H4>
+<!
+--------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added HTML documents.
+</ul>
+
+<H4>
+3.0beta2 -- February 14, 1999
+</H4>
+<!
+-----------------------------
+>
+
+Packaged by Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added ability to print the .afm file instead of the font to STDOUT.
+<li> Added the guessing of the /ForceBold parameter that proved to be useful.
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> Removed the force-fixed option that proved to be troublesome.
+</ul>
+
+<H4>
+3.0beta1 -- December 11, 1998
+</H4>
+<!
+-----------------------------
+>
+
+By Andrew Weeks.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added option (passed to t1asm) to create a compressed binary
+version of the font (A PFB file).
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> Versions of handle_post and handle_cmap that deal with some
+problems with buggy fonts.
+<li> Minor Bug Fixes.
+</ul>
+
+<H4>
+3.0beta-afm -- December 5, 1998
+</H4>
+<!
+-------------------------------
+>
+
+By Thomas Henlich.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Integration of AFM file creation.
+</ul>
+
+<H4>
+3.0beta -- November 15, 1998
+</H4>
+<!
+----------------------------
+>
+
+By Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Added the auto-calculation of the italic angle.
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> Fixed a couple of bugs.
+</ul>
+
+<H4>
+3.0alpha -- October 19, 1998
+</H4>
+<!
+----------------------------
+>
+
+By Sergey Babkin.
+<p>
+
+<b>New features:</b>
+<ul>
+<li> Improved (although still not perfect) handling of
+scaling in composite glyphs
+<li> Automatic correction of outlines to make them more
+smooth (to correct both rounding errors introduced
+during conversion and present in the original font)
+<li> Automatic generation of hints (still has lots of
+space for improvement)
+<li> Automatic generation of BlueValues etc.
+</ul>
+
+<b>Bux fixes:</b>
+<ul>
+<li> Scaling of fonts to 1000x1000 M-square required by
+Type1 standard
+<li> Printing out the contours in reverse direction, because
+TTF directions are different from Type1 ones (that was
+the major reason why the fonts generated by
+version 2.2 were rendered so badly in small sizes)
+</ul>
+
+<H4>
+June 22, 1998 (AKA 2.2)
+</H4>
+<!
+-------------
+>
+
+By Thomas Henlich.
+<p>
+
+<b>Bux fixes:</b>
+<ul>
+<li> "width" should be "short int" because otherwise:
+characters with negative widths (e.g. -4) become *very* wide (65532)
+<li> The number of /CharStrings is numglyphs and not numglyphs+1
+</ul>
+
+<H4>
+February 13, 1998
+</H4>
+<!
+-----------------
+>
+
+By Mark Heath.
+<p>
+
+<b>Bux fixes:</b>
+<ul>
+<li> An original Bug Reported by Frank, which was just incorrect syntax in the
+Type 1 header, managed to creep back into the Feb 04 Version. This has been
+Fixed in the Feb 13 Version.
+</ul>
+
+<H4>
+February 4, 1998
+</H4>
+<!
+----------------
+>
+
+By Mark Heath.
+<p>
+
+<b>Bux fixes:</b>
+<ul>
+<li> A workaround was implemented in ttf2pfa by altering the matrix. I suspect
+I will have to calculate the correct values, as matrix ops are probably not
+allowed in Type 1 format.
+</ul>
+
+<!
+---------------------------------------------
+>
+<H4>
+The older history seems to be lost.
+</H4>
+
+<I>
+(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 "<tt>ttf2pt1-22</tt>". 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.)
+</I>
+
+</BODY>
+</HTML>
diff --git a/nx-X11/extras/ttf2pt1/COPYRIGHT b/nx-X11/extras/ttf2pt1/COPYRIGHT
new file mode 100644
index 000000000..85950856b
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/COPYRIGHT
@@ -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 <ccsaw@bath.ac.uk>
+ Frank M. Siegert <fms@this.net>
+ Mark Heath <mheath@netspace.net.au>
+ Thomas Henlich <thenlich@rcs.urz.tu-dresden.de>
+ Sergey Babkin <babkin@users.sourceforge.net>, <sab123@hotmail.com>
+ Turgut Uyar <uyar@cs.itu.edu.tr>
+ Rihardas Hepas <rch@WriteMe.Com>
+ Szalay Tamas <tomek@elender.hu>
+ Johan Vromans <jvromans@squirrel.nl>
+ Petr Titera <P.Titera@sh.cvut.cz>
+ Lei Wang <lwang@amath8.amt.ac.cn>
+ Chen Xiangyang <chenxy@sun.ihep.ac.cn>
+ Zvezdan Petkovic <z.petkovic@computer.org>
+ Rigel <rigel863@yahoo.com>
+ 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 <uyar@cs.itu.edu.tr>
+ The Unicode translation table for the Turkish language.
+
+Rihardas Hepas <rch@WriteMe.Com>
+ The Unicode translation table for the Baltic languages.
+
+Szalay Tamas <tomek@elender.hu>
+ The Unicode translation table for the Central European languages.
+
+Johan Vromans <jvromans@squirrel.nl>
+ The RPM file.
+
+Petr Titera <P.Titera@sh.cvut.cz>
+ The Unicode map format with names, the forced Unicode option.
+
+Frank M. Siegert <frank@this.net>
+ Port to Windows
+
+Lei Wang <lwang@amath8.amt.ac.cn>
+Chen Xiangyang <chenxy@sun.ihep.ac.cn>
+ Translation maps for Chinese fonts.
+
+Zvezdan Petkovic <z.petkovic@computer.org>
+ The Unicode translation tables for the Cyrillic alphabet.
+
+Rigel <rigel863@yahoo.com>
+ Generation of the dvips encoding files, modification to the Chinese maps.
+
+I. Lee Hetherington <ilh@lcs.mit.edu>
+ 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.
+
diff --git a/nx-X11/extras/ttf2pt1/FONTS.hpux.html b/nx-X11/extras/ttf2pt1/FONTS.hpux.html
new file mode 100644
index 000000000..80889cae2
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/FONTS.hpux.html
@@ -0,0 +1,197 @@
+<HTML>
+<HEAD>
+<TITLE>
+How to install new Type1 fonts on an HP-UX 10.20 machine
+</TITLE>
+</HEAD>
+<BODY>
+Sergey A. Babkin
+<br>
+<A HREF="mailto:babkin@bellatlantic.net">
+&lt;babkin@bellatlantic.net&gt;</A> or <A HREF="mailto:sab123@hotmail.com">&lt;sab123@hotmail.com&gt;</A>
+<p>
+<!
+(Do not edit this file, it is generated from FONTS.hpux.html!!!)
+>
+
+<H3>
+How to install new Type1 fonts on an HP-UX 10.20 machine
+</H3>
+<!
+--------------------------------------------------------
+>
+
+1. Add the font files to <tt>/usr/lib/X11/fonts/type1.st/typefaces</tt>.
+<p>
+
+2. Add the font descriptions to
+<tt>/usr/lib/X11/fonts/type1.st/typefaces/fonts.scale</tt>. Run `mkfontdir'
+in <tt>/usr/lib/X11/fonts/type1.st/typefaces</tt>. In the descriptions
+you have to specify the font manufacturer as `misc', like:
+<p>
+
+<tt>
+&nbsp;&nbsp;-misc-courier-...
+</tt>
+<p>
+
+3. Copy <tt>/usr/lib/X11/fonts/type1.st/typefaces/fonts.dir</tt> to
+<tt>/usr/lib/X11/fonts/type1.st/licenses/STSYSTEM/DISPLAYS/fonts.dir</tt>.
+Better yet, create a symbolic link.
+<p>
+
+4. For each font encoding you are going to use create a description
+file in <tt>/usr/lib/X11/fonts/stadmin/type1/charsets</tt>. 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 <tt>cp.iso8859-1</tt>
+to <tt>cp.<i>&lt;your-encoding-name&gt;</i></tt>, like <tt>cp.koi8-r</tt>.
+<p>
+
+5. Restart you X server and/or font server.
+<p>
+
+<H4>
+What if you don't have the `root' privileges ?
+</H4>
+<!
+----------------------------------------------
+>
+
+You still can run the font server and configure your X server
+to get the fonts from it.
+<p>
+
+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.
+<p>
+
+First, choose some unused port. Numbers around 9000 are a good
+choice. Verify that this port is not used by somebody else
+by entering
+<p>
+
+<blockquote><tt>
+ netstat -naf inet |grep 9000
+</tt></blockquote>
+
+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.
+<p>
+
+Go to you home directory <tt>$HOME</tt> and create some directory for
+your font server, say, <tt>$HOME/fs</tt>. Copy the directory structure
+of <tt>/usr/lib/X11/fonts/type1.st</tt> into <tt>$HOME/fs</tt>, so that in result
+you get <tt>$HOME/fs/type1.st/<i>&lt;whatever was there&gt;</i></tt>. Copy the directory
+structure of <tt>/usr/lib/X11/fonts/stadmin/type1/charsets</tt> into <tt>$HOME/fs</tt>,
+so that in result you get <tt>$HOME/fs/charsets/<i>&lt;whatever was there&gt;</i></tt>.
+Install the new fonts in these directorues as described above.
+<p>
+
+Then create the fontserver configuration file, say, <tt>$HOME/fs/xfs.cfg</tt>.
+The sample contents (supposing that my <tt>$HOME</tt> is equal to <tt>/home/babkin</tt>)
+is:
+<p>
+
+<!
+--------------8&lt;----------- cut here -----------------------------
+>
+<hr>
+<tt>
+# font server configuration file
+<br>
+# $XConsortium: config.cpp,v 1.7 91/08/22 11:39:59 rws Exp $
+<br>
+
+<br>
+rasterizers = /usr/lib/X11/fs/ufstrast.sl,/usr/lib/X11/fs/iforast.sl
+<br>
+
+<br>
+clone-self = off
+<br>
+use-syslog = off
+<br>
+catalogue = /home/babkin/fs/type1.st
+<br>
+# in decipoints
+<br>
+default-point-size = 120
+<br>
+default-resolutions = 100,100,75,75
+<br>
+port=9000
+<br>
+error-file=/home/babkin/fs/fs.err
+</tt>
+<hr>
+<!
+--------------8&lt;----------- cut here -----------------------------
+>
+<p>
+
+Then create the script to start your font server, say, <tt>$HOME/fs/runme</tt>:
+<p>
+
+<!
+--------------8&lt;----------- cut here -----------------------------
+>
+<hr>
+<tt>
+TYPE1_CODEPAGE_DIR=$HOME/fs/charsets
+<br>
+export TYPE1_CODEPAGE_DIR
+<br>
+kill `ps -ef | grep $HOME/\[f\]s/xfs.cfg | awk '{print $2}'`;
+<br>
+nohup xfs -config $HOME/fs/xfs.cfg &
+</tt>
+<hr>
+<!
+--------------8&lt;----------- cut here -----------------------------
+>
+<p>
+
+Don't forget to make <tt>$HOME/fs/runme</tt> executable. Then you can
+execute it manually or from you .profile.
+<p>
+
+After you get your font server running, just execute the following
+command (with proper host name and port number) in your X session
+<p>
+
+<blockquote><tt>
+ xset fp+ tcp/somehost:9000
+</tt></blockquote>
+
+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:
+<p>
+
+<!
+--------------8&lt;----------- cut here -----------------------------
+>
+<hr>
+<tt>
+...
+<br>
+$HOME/fs/runme
+<br>
+sleep 2 # give it some time to start
+<br>
+xset fp+ tcp/somehost:9000
+<br>
+...
+</tt>
+<hr>
+<!
+--------------8&lt;----------- cut here -----------------------------
+>
+<p>
+</BODY>
+</HTML>
diff --git a/nx-X11/extras/ttf2pt1/FONTS.html b/nx-X11/extras/ttf2pt1/FONTS.html
new file mode 100644
index 000000000..352bd0693
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/FONTS.html
@@ -0,0 +1,708 @@
+<HTML>
+<HEAD>
+<TITLE>
+The ttf2pt1 font installation guide
+</TITLE>
+</HEAD>
+<BODY>
+Sergey A. Babkin
+<br>
+<A HREF="mailto:babkin@users.sourceforge.net">
+&lt;babkin@bellatlantic.net&gt;</A> or <A HREF="mailto:sab123@hotmail.com">&lt;sab123@hotmail.com&gt;</A>
+<p>
+<!
+(Do not edit this file, it is generated from FONTS.html!!!)
+>
+
+<!-- =defdoc cv ttf2pt1_convert 1 -->
+<!-- =defdoc gs ttf2pt1_x2gs 1 -->
+<H2>
+THE FONT INSTALLATION GUIDE
+<br>
+for the TTF to Type1 converter and fonts generated by it
+</H2>
+<!
+========================================================
+>
+
+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 <FONT COLOR="#3333FF"><FONT SIZE=-1>*8*</FONT></FONT>.
+<p>
+
+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.
+<p>
+
+This guide considers the following issues of installation of the
+fonts:
+<p>
+
+<b>
+<ul>
+<li> <A HREF="#X11">X11</A><br>
+<li> <A HREF="#gs">Ghostscript</A><br>
+<li> <A HREF="#win">MS Windows</A><br>
+<li> <A HREF="#netscape">Netscape Navigator/Communicator</A><br>
+<li> <A HREF="#rpm">Linux RPM package</A><br>
+<li> <A HREF="#framemaker">FrameMaker</A><br>
+<li> <A HREF="#soffice">StarOffice</A><br>
+</ul>
+</b><p>
+
+<A NAME="X11"></A>
+<H3>
+X11
+</H3>
+<!
+===
+>
+
+<!-- =section cv NAME -->
+<!-- =text B&lt;ttf2pt1_convert&gt; - convenience font conversion script -->
+<!-- =stop -->
+To simplify the conversion a set of scripts is provided with <b>ttf2pt1</b>.
+They are collected in the `<TT>scripts</TT>' subdirectory.
+<p>
+
+<!-- =section cv DESCRIPTION -->
+`<b>Convert</b>' is the master conversion script provided with ttf2pt1.
+When installed into a public directory it's named `<b>ttf2pt1_convert</b>'
+to avoid name collisions with the other programs.
+<p>
+<!-- =stop -->
+
+It's called as:
+<p>
+
+<!-- =section cv SYNOPSIS -->
+<!-- =text ttf2pt1_convert B&lt;[config-file]&gt; -->
+<!-- =stop -->
+<blockquote>
+ convert <i>[config-file]</i>
+</blockquote>
+
+<!-- =section cv DESCRIPTION -->
+If the configuration file is not specified as an argument then the file
+`<TT>convert.cfg</TT>' in the current directory is used. This file contains
+a set of configuration variables. The distribution contains a sample file
+file `<TT>convert.cfg.sample</TT>'. Please copy it to `<TT>convert.cfg</TT>',
+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 `<TT>convert</TT>' itself, they are
+automatically updated when installing <b>ttf2pt1</b>.
+<p>
+
+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.
+<p>
+
+The variables in the configuration file are:
+<p>
+
+<!-- ==over 2 -->
+<!-- ==item * -->
+<B><tt>SRCDIRS</tt></B> - 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.
+<p>
+
+<FONT COLOR="#3333FF"><FONT SIZE=-1>*8*</FONT></FONT>
+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 <A HREF="encodings/README.html">encodings/README</A> 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.
+<p>
+
+<!-- ==item * -->
+<b><tt>DSTDIR</tt></b> - 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.
+<p>
+
+<!-- ==item * -->
+<b><tt>DSTENC<i>{language}</i></tt></b> - 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
+ <tt>SRCENC</tt> and <tt>DSTENC</tt> 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 <tt>SRCENC</tt> and <tt>DSTENC</tt>).
+<p>
+
+<!-- ==item * -->
+<b><tt>FOUNDRY</tt></b> - 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'.
+<p>
+<!-- ==back -->
+
+The next few parameters control the general behavior of the converter.
+They default values are set to something reasonable.
+<p>
+
+<!-- ==over 2 -->
+<!-- ==item * -->
+<b><tt>CORRECTWIDTH</tt></b> - if the value is set to <b><tt>YES</tt></b> then use the
+ converter option <tt><b>-w</b></tt>, otherwise don't use it. See the description of
+ this option in the <A HREF="README.html">README</A> file.
+<p>
+
+<!-- ==item * -->
+<b><tt>REMOVET1A</tt></b> - if the value is set to <b><tt>YES</tt></b> then after
+ conversion remove the un-encoded <tt>.t1a</tt> font files and the
+ intermediate <tt>.xpfa</tt> font metric files.
+<p>
+
+<!-- ==item * -->
+<b><tt>INSTALLFONTMAP</tt></b> - a Ghostscript parameter, if the value is set to
+ <b><tt>YES</tt></b> then install the entries for the new fonts
+ right into the main <tt>Fontmap</tt> file. Otherwise just leave
+ the file <tt>Fontmap.ttf</tt> in the Ghostscript configuration
+ directory.
+<p>
+
+<!-- ==item * -->
+<b><tt>HINTSUBST</tt></b> - if the value is set to <b><tt>YES</tt></b> use the option
+ <tt><b>-H</b></tt>, 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 <A HREF="README.html">README</A> file.
+<p>
+
+<!-- ==item * -->
+<b><tt>ENFORCEISO</tt></b> - if the value is set to <b><tt>YES</tt></b> 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.
+<p>
+
+<!-- ==item * -->
+<b><tt>ALLGLYPHS</tt></b> - if the value is set to <b><tt>YES</tt></b> then
+ include all the glyphs from the source fonts into the resulting fonts, even
+ if these glyphs are inaccessible. If it's set to <b><tt>NO</tt></b> 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 <tt><b>-a</b></tt> in the
+ <A HREF="README.html">README</A> file.
+<p>
+
+<!-- ==item * -->
+<b><tt>GENUID</tt></b> - if the value is set to <b><tt>YES</tt></b> then use
+ the option <tt><b>-uA</b></tt> 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 `<tt>convert</tt>' script runs multiple times it will
+ generate the same UniqueIDs during each run. See further description
+ of this option in the <A HREF="README.html">README</A> file.
+<p>
+
+<!-- ==item * -->
+<b><tt>GENUID</tt></b> - if the value is set to <b><tt>YES</tt></b> then create
+ the <tt>.pfb</tt> files, otherwise the <tt>.pfa</tt> files. The <tt>.pfb</tt>
+ files are more compact but contain binary data, so you may experience some
+ troubles when transferring them through the network.
+<p>
+<!-- ==back -->
+
+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 <b>ttf2pt1</b> installation directory if <b>ttf2pt1</b>
+was installed or otherwise suppose that you are running `<tt>convert</tt>' with
+`<tt>scripts</tt>' subdirectory being the current directory.
+<p>
+
+<!-- ==over 2 -->
+<!-- ==item * -->
+<b><tt>ENCDIR</tt></b> - directory containing the descriptions of encodings
+<br>
+<!-- ==item * -->
+<b><tt>MAPDIR</tt></b> - directory containing the external map files
+<p>
+<!-- ==back -->
+
+Besides that a few parameters are built into the `<tt>convert</tt>' script itself.
+You probably won't need to change them:
+<p>
+
+<!-- ==over 2 -->
+<!-- ==item * -->
+<tt><b>T1ASM</b></tt>, <tt><b>TTF2PT1</b></tt>, <tt><b>TRANS</b></tt>, <tt><b>T1FDIR</b></tt>, <tt><b>FORCEISO</b></tt> - paths to the other script
+<p>
+<!-- ==back -->
+
+Also there are a few parameters controlling the installation of
+fonts for Ghostscript. Please look at their description in the
+<A HREF="#gs">Ghostscript</a> section of documentation or in the <b>ttf2pt1_x2gs(1)</b>
+manual page before running `<tt>convert</tt>'. If these parameters are
+set, `<tt>convert</tt>' will call the `<tt>x2gs</tt>' script automatically
+to install the newly converted fonts in Ghostscript.
+<p>
+
+After creating the configuration file run the `<tt>convert</tt>' script. Look at
+the result and the log file in <tt>DSTDIR</tt>.
+<p>
+
+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 <A HREF="FONTS.hpux.html">FONTS.hpux</A> gives a short description.
+<p>
+
+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 <A HREF="FONTS.hpux.html">FONTS.hpux</A> for an example, exept that you won't
+need all the HP-related stuff on any other system).
+<p>
+<!-- =stop -->
+
+<H4>
+Known Problems
+</H4>
+<!
+--------------
+>
+<!-- =section cv BUGS -->
+<!-- ==head2 Known problems -->
+
+<ul>
+<li> 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.
+<p>
+
+ But there is a solution even for the generic X11. A patch located
+ in the subdirectory `<tt>app/X11</tt>' fixes this problem as well
+ as some other minor problems. Its description is provided in
+ <A HREF="app/X11/README.html">app/X11/README</A>.
+<p>
+
+ To fix the X11 font library, you have to get the X11 sources. I
+ can recommend the ftp sites of the XFree86 project <A HREF="ftp://ftp.xfree86.org">ftp://ftp.xfree86.org</A>
+ or of the Open Group <A HREF="ftp://ftp.x.org">ftp://ftp.x.org</A>. 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.
+<p>
+
+ 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.
+<p>
+
+<li> 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 <A HREF="FONTS.hpux.html">FONTS.hpux</A>).
+<p>
+
+<li> The <tt>fonts.scale</tt> files created by the older versions of the
+ <tt>ttf2pt1</tt> installation program (up to release 3.1) have conflicted
+ with the language definitions of the <tt>Xfsft</tt> font server and
+ parts of it included into XFree86. To overcome this incompatibility
+ the never versions creats the <tt>fonts.scale</tt> file describing all the
+ fonts as belonging to the <tt>adobe-fontspecific</tt> encoding and
+ the <tt>fonts.alias</tt> file with the proper names. The drawback of
+ this solution is that <tt>xlsfonts</tt> gives the list of twice more
+ fonts. But as a side effect the option <tt><b>ENFORCEISO</b></tt> in
+ `<tt>convert.cfg</tt>' is not required for X11 any more.
+<p>
+
+<li> The conversion script has no support for Eastern multi-plane fonts.
+ Contribution of such a support would be welcome.
+<p>
+</ul>
+<!-- =stop -->
+<!-- =section cv FILES -->
+<!-- ==over 2 -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/scripts/convert.cfg.sample -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/scripts/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/README -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/FONTS -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_BINDIR/ttf2pt1 -->
+<!-- ==back -->
+<!-- =stop -->
+<!-- =section cv SEE ALSO -->
+<!-- ==over 4 -->
+<!-- ==item * -->
+<!-- =text L&lt;ttf2pt1(1)&gt; -->
+<!-- ==item * -->
+<!-- =text L&lt;ttf2pt1_x2gs(1)&gt; -->
+<!-- ==item * -->
+<!-- =text L&lt;t1asm(1)&gt; -->
+<!-- ==back -->
+<!-- =stop -->
+
+<A NAME="gs"></A>
+<H3>
+Ghostscript
+</H3>
+<!
+===========
+>
+<!-- =section gs NAME -->
+<!-- =text B&lt;ttf2pt1_x2gs&gt; - font installer for Ghostscript -->
+<!-- =stop -->
+
+<!-- =section gs DESCRIPTION -->
+The fonts generated with <b>ttf2pt1</b> work fine with Ghostscript by
+themselves. The script `<b>x2gs</b>' (or `<b>ttf2pt1_x2gs</b>' 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 (<tt>Fontmap</tt>)
+in Ghostscript format.
+<!-- =stop -->
+
+It's called as:
+<p>
+
+<!-- =section gs SYNOPSIS -->
+<!-- =text ttf2pt1_x2gs B&lt;[config-file]&gt; -->
+<!-- =stop -->
+<blockquote>
+ x2gs <i>[config-file]</i>
+</blockquote>
+
+<!-- =section gs DESCRIPTION -->
+If the configuration file is not specified as an argument then the file
+`<TT>convert.cfg</TT>' in the current directory is used, just like the
+`<tt>convert</tt>' script does. Indeed, this configuration file is used for
+both scripts.
+<p>
+
+The Ghostscript-related parameters in the configuration file are:
+<p>
+
+<b><tt>DSTDIR</tt></b> - the X11 font directory used by `<tt>x2gs</tt>' as the
+ source of the fonts. This parameter is common with the X11
+ configuration.
+<p>
+
+<b><tt>GSDIR</tt></b> - the base directory of Ghostsript. If this
+ parameter is set to an empty string then `<tt>convert</tt>' won't
+ call `<tt>x2gs</tt>'. 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.
+<p>
+
+<b><tt>GSFONTDIR</tt></b> - the font directory of Ghostscript. In the standard
+ Ghostscript installation it's a subdirectory of <tt>GSDIR</tt>
+ but some systems may use completely different directories.
+<p>
+
+<b><tt>GSCONFDIR</tt></b> - the configuration subdirectory of Ghostscript
+ that contains the <tt>Fontmap</tt> file.
+<p>
+
+<b><tt>INSTALLFONTMAP</tt></b> - if the value is set to <b><tt>YES</tt></b> then
+ install the entries for the new fonts right into the main
+ <tt>Fontmap</tt> file. Otherwise just leave the file <tt>Fontmap.ttf</tt>
+ in the Ghostscript configuration directory.
+<p>
+
+
+After preparing the configuration file run the script. It symbolicaly links
+all the font files and creates the description file <tt>Fontmap.ttf</tt> in
+<tt>GSCONDFIR</tt>. After that there are two choices.
+<p>
+
+If the option <tt>INSTALLFONTMAP</tt> was set to <tt>YES</tt> then
+the font descriptions are also automatically installed into the
+master <tt>Fontmap</tt> 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 <tt>Fontmap</tt> 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 <tt>Fontmap</tt>.
+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
+<tt>Fontmap</tt> and only after that re-run `<tt>x2gs</tt>'
+for the new directory.
+<p>
+
+On the other hand if the option <tt>INSTALLFONTMAP</tt> was set to
+<tt>NO</tt> then go to the <tt>GSCONFDIR</tt> directory and insert the
+contents of <tt>Fontmap.ttf</tt> into the <tt>Fontmap</tt> file
+manually. This step may be left manual to make the installation
+a little bit more safe.
+<p>
+
+After that you may also want to redefine some of the aliases in
+<tt>Fontmap</tt> 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.
+<p>
+<!-- =stop -->
+<!-- =section gs FILES -->
+<!-- ==over 2 -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/scripts/convert.cfg.sample -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/scripts/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/README -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/FONTS -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_BINDIR/ttf2pt1 -->
+<!-- ==back -->
+<!-- =stop -->
+<!-- =section gs SEE ALSO -->
+<!-- ==over 4 -->
+<!-- ==item * -->
+<!-- =text L&lt;ttf2pt1(1)&gt; -->
+<!-- ==item * -->
+<!-- =text L&lt;ttf2pt1_convert(1)&gt; -->
+<!-- ==item * -->
+<!-- =text L&lt;t1asm(1)&gt; -->
+<!-- ==back -->
+<!-- =stop -->
+
+<A NAME="win"></A>
+<H3>
+MS Windows
+</H3>
+<!
+===========
+>
+
+<b>Ttf2pt1</b> can be built on Windows either with native compiler or in
+POSIX emulation mode.
+<p>
+
+Native MS Windows compilers require a different way to build the converter
+instead of the Makefile (their <tt>make</tt> programs commonly are quite weird
+and limited in capabilities). An example of batch file <tt>winbuild.bat</tt>
+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.
+<p>
+
+Cygnus <tt>make</tt> 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 <tt>cygbuild.sh</tt> 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 <tt>CYGWIN1.DLL</tt> should be copied first into
+<tt>C:\WINDOWS</tt>.
+<p>
+
+To run the accompanying scripts Perl for Windows will be required as well as
+other tools from the Cygnus set.
+<p>
+
+The Windows support was not particularly tested, so in case of problems with
+building or running the converter please let us know.
+<p>
+
+The pre-built code (possibly of an older version) of ttf2pt1 for MS Windows is
+available from the GnuWin32 project from
+
+<A HREF="http://gnuwin32.sourceforge.net/packages/ttf2pt1.htm">http://gnuwin32.sourceforge.net/packages/ttf2pt1.htm</A>
+<p>
+
+<A NAME="netscape"></a>
+<H3>
+Netscape Navigator/Communicator
+</H3>
+<!
+===============================
+>
+
+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 ? (<A HREF="#nsfn1">*</a>)
+<p>
+
+Luckily, we have solutions for all of these problems. They are
+located in the subdirectory `<tt>app/netscape</tt>' and described
+in <A HREF="app/netscape/README.html">app/netscape/README</a>.
+<p>
+
+<A NAME="nsfn1"></a>
+&nbsp;&nbsp;-------<br>
+&nbsp;&nbsp;<FONT SIZE=-1>*) See Monty Python's Flying Circus, episode 15</FONT></FONT>
+<p>
+
+<FONT COLOR="#3333FF"><FONT SIZE=-1>*8*</FONT></FONT>
+<H4>
+Netscape and cyrillic fonts<br>
+<!
+---------------------------
+>
+(courtesy of Zvezdan Petkovic)
+</H4>
+
+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.
+<p>
+
+<blockquote><tt>
+ Netscape*documentFonts.charset*koi8-r: iso-8859-5
+</tt></blockquote>
+<p>
+
+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.
+<p>
+
+<b>Note:</b> <i>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.</i>
+<p>
+
+<A NAME="rpm"></a>
+<H3>
+Linux RPM package
+</H3>
+<!
+=================
+>
+
+The spec file for the creation of a Linux RPM package is located in
+<tt>app/RPM</tt>. It has been contributed by Johan Vromans. When
+<tt>make all</tt> is ran in the main directory it among the other
+things creates the version of itself adapted to Linux in <tt>app/RPM</tt>,
+you may want to copy that version back to the main directory.
+<p>
+
+<B>Warning:</B> Please note that the install section is incomplete, and
+the installed scripts won't work until the paths inside them
+are corrected.
+<p>
+
+<A NAME="framemaker"></a>
+<H3>
+FrameMaker
+</H3>
+<!
+==========
+>
+
+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:
+<p>
+
+&nbsp;&nbsp;<tt>EncodingScheme FontSpecific</tt>
+<p>
+
+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.
+<p>
+
+<B>NOTE by Jason Baietto:</B> 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).
+<p>
+
+<A NAME="soffice"></a>
+<H3>
+StarOffice
+</H3>
+<!
+==========
+>
+
+StarOffice 5.1x has been reported to crash if the <tt>.afm</tt> file contains
+spaces in the values of such statements as <b>Version</b>, <b>Weight</b> etc.
+These spaces are permitted by the Adobe spec, so this is a problem of
+StarOffice. The easiest way to fix these <tt>.afm</tt> 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 <tt>sed</tt>
+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 <tt>ttf2pt1</tt>.
+<p>
+
+</BODY>
+</HTML>
diff --git a/nx-X11/extras/ttf2pt1/Makefile b/nx-X11/extras/ttf2pt1/Makefile
new file mode 100644
index 000000000..2616d4687
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/Makefile
@@ -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 . . <README.html
+
+ttf2pt1_convert.1 ttf2pt1_x2gs.1: FONTS.html
+ scripts/html2man . . <FONTS.html
+
+app/RPM/Makefile: Makefile
+ sed 's/^CFLAGS_SYS.*=.*$$/CFLAGS_SYS= -O2 -D_GNU_SOURCE/;/warning:/,/^$$/s/^/#/' <Makefile >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`'/' <app/RPM/ttf2pt1.spec.src >$@
+
+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.html >CHANGES
+
+README: README.html
+ scripts/unhtml <README.html >README
+
+encodings/README: encodings/README.html
+ scripts/unhtml <encodings/README.html >encodings/README
+
+other/README: other/README.html
+ scripts/unhtml <other/README.html >other/README
+
+app/X11/README: app/X11/README.html
+ scripts/unhtml <app/X11/README.html >app/X11/README
+
+app/netscape/README: app/netscape/README.html
+ scripts/unhtml <app/netscape/README.html >app/netscape/README
+
+app/TeX/README: app/TeX/README.html
+ scripts/unhtml <app/TeX/README.html >app/TeX/README
+
+FONTS: FONTS.html
+ scripts/unhtml <FONTS.html >FONTS
+
+FONTS.hpux: FONTS.hpux.html
+ scripts/unhtml <FONTS.hpux.html >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)|;' <scripts/convert >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
diff --git a/nx-X11/extras/ttf2pt1/README.FIRST b/nx-X11/extras/ttf2pt1/README.FIRST
new file mode 100644
index 000000000..f89db2010
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/README.FIRST
@@ -0,0 +1,4 @@
+To get the plain-text README and installation guides run:
+
+ make docs
+
diff --git a/nx-X11/extras/ttf2pt1/README.html b/nx-X11/extras/ttf2pt1/README.html
new file mode 100644
index 000000000..b1d6c9c22
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/README.html
@@ -0,0 +1,1182 @@
+<HTML>
+<HEAD>
+<TITLE>
+TTF2PT1 - A True Type to PostScript Type 1 Converter
+</TITLE>
+</HEAD>
+<BODY>
+<!-- =defdoc t1 ttf2pt1 1 -->
+<H2>
+<!-- =section t1 NAME -->
+TTF2PT1 - A True Type to PostScript Type 1 Font Converter
+<!-- =stop -->
+</H2>
+
+<!
+(Do not edit this file, it is generated from README.html!!!)
+>
+<TT>
+[
+<blockquote>
+<!-- =section t1 HISTORY -->
+ Based on ttf2pfa by Andrew Weeks, and help from Frank Siegert.
+<BR>
+ Modification by Mark Heath.
+<BR>
+ Further modification by Sergey Babkin.
+<BR>
+ The Type1 assembler by I. Lee Hetherington with modifications by
+ Kai-Uwe Herbing.
+<!-- =stop -->
+</blockquote>
+]
+</TT>
+<p>
+
+Ever wanted to install a particular font on your XServer but only could find
+the font you are after in True Type Format?
+<p>
+
+Ever asked <TT>comp.fonts</TT> for a True Type to Type 1 converter and got a List
+of Commercial software that doesn't run on your Operating System?
+<p>
+
+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.
+<p>
+
+<!-- =section t1 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.
+<p>
+
+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.
+<p>
+<!-- =stop -->
+
+The converter is simple to run, just:
+<p>
+
+<!-- =section t1 SYNOPSIS -->
+<blockquote>
+ <tt>ttf2pt1 <i>[-options] ttffont.ttf [Fontname]</i></tt>
+</blockquote>
+or
+<blockquote>
+ <tt>ttf2pt1 <i>[-options] ttffont.ttf -</i></tt>
+</blockquote>
+<!-- =stop -->
+<p>
+
+<!-- =section t1 OPTIONS -->
+The first variant creates the file <tt>Fontname.pfa</tt> (or <tt>Fontname.pfb</tt> if the
+option '<b>-b</b>' was used) with the converted font and <tt>Fontname.afm</tt> with the
+font metrics, the second one prints the font or another file (if the option
+'<b>-G</b>' was used) on the standard output from where it can be immediately
+piped through some filter. If no <tt>Fontname</tt> is specified for the first
+variant, the name is generated from <tt>ttffont</tt> by replacing the <tt>.ttf</tt>
+filename suffix.
+<p>
+
+Most of the time no options are neccessary (with a possible exception
+of '<b>-e</b>'). But if there are some troubles with the resulting font, they
+may be used to control the conversion.
+The <B>options</B> are:
+<p>
+
+<!-- ==over 2 -->
+<!-- ==item * -->
+<TT><B>-a</TT></B> - 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 <A HREF="app/X11/README.html"><tt>app/X11/README</tt></A> for the description of a
+ patch to X11 which fixes this problem.
+<p>
+
+<!-- ==item * -->
+<TT><B>-b</TT></B> - Encode the resulting font to produce a ready <tt>.pfb</tt> file.
+<p>
+
+<!-- ==item * -->
+<TT><B>-d <i>suboptions</i></TT></B> - Debugging options. The suboptions are:
+<p>
+
+<blockquote>
+ <TT><B>a</TT></B> - 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 '<b>-e</b>') but it has proven to be a valuable debuging
+ information.
+<p>
+
+ <TT><B>r</TT></B> - 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.
+<p>
+</blockquote>
+
+<!-- ==item * -->
+<TT><B>-e</TT></B> - Assemble the resulting font to produce a ready <tt>.pfa</tt> file.
+<I>
+ [ </I>S.B.<I>: 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. ]
+</I>
+<p>
+
+<!-- ==item * -->
+<TT><B>-F</TT></B> - 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. <B>WARNING:</B> <I>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.</I>
+<p>
+
+<!-- ==item * -->
+<TT><B>-G <i>suboptions</i></TT></B> - 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 <b>-G</b> determines
+ which file will be written to the standard output and the rest of files
+ will be discarded. For example, <b>-G A</b> will request the AFM file.
+ The suboptions to disable/enable the generation of the files are:
+<p>
+
+<blockquote>
+ <TT><B>f/F</TT></B> - The font file. Depending on the other options this file
+ will have one of the suffixes <tt>.t1a</tt>, <tt>.pfa</tt> or <tt>.pfb</tt>. If the conversion result
+ is requested on the standard output ('<tt>-</tt>' 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 <b>-G</b>.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>a/A</TT></B> - The Adobe font metrics file (<tt>.afm</tt>).
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>e/E</TT></B> - The dvips encoding file (<tt>.enc</tt>).
+ <b>Default: disabled</b>
+<p>
+
+</blockquote>
+
+<!-- ==item * -->
+<TT><B>-l <I>language</I>[+<I>argument</I>]</TT></B> - 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.
+<p>
+
+ 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.
+<p>
+
+ As of now the following languages are supported:
+<br>
+ &nbsp;&nbsp;<TT>latin1</TT> - for all the languages using the Latin-1 encoding
+<br>
+ &nbsp;&nbsp;<TT>latin2</TT> - for the Central European languages
+<br>
+ &nbsp;&nbsp;<TT>latin4</TT> - for the Baltic languages
+<br>
+ &nbsp;&nbsp;<TT>latin5</TT> - for the Turkish language
+<br>
+ &nbsp;&nbsp;<TT>cyrillic</TT> - for the languages with Cyrillic alphabet
+<br>
+ &nbsp;&nbsp;<TT>russian</TT> - historic synonym for cyrillic
+<br>
+ &nbsp;&nbsp;<TT>bulgarian</TT> - historic synonym for cyrillic
+<br>
+ &nbsp;&nbsp;<TT>adobestd</TT> - for the AdobeStandard encoding used by TeX
+<br>
+ &nbsp;&nbsp;<TT>plane+<i>argument</i></TT> - to select one plane from a multi-byte encoding
+<p>
+
+ The argument of the "<tt>plane</tt>" language may be in one of three forms:
+<p>
+ &nbsp;&nbsp;<tt>plane+<b>pid=</b><i>&lt;pid&gt;</i><b>,eid=</b><i>&lt;eid&gt;</i></tt>
+<br>
+ &nbsp;&nbsp;<tt>plane+<b>pid=</b><i>&lt;pid&gt;</i><b>,eid=</b><i>&lt;eid&gt;</i><b>,</b><i>&lt;plane_number&gt;</i></tt>
+<br>
+ &nbsp;&nbsp;<tt>plane+<i>&lt;plane_number&gt;</i></tt>
+<p>
+
+ 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.
+<p>
+
+ Plane_number is a hexadecimal (if starts with "<b>0x</b>") 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).
+<p>
+
+<!-- =stop -->
+ <B>NOTE:</B>
+<!-- =section t1 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
+ '<b>-p ft</b>') with the "<tt>plane</tt>" language.
+<p>
+<!-- =stop -->
+
+<!-- =section t1 OPTIONS -->
+<I>
+ <B>NOTE:</B>
+ 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 </I>latin*<I> "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.
+</I><p>
+
+ <B>WARNING:</B>
+ 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 '<b>-L</b>'
+ with an external map.
+<p>
+
+<!-- ==item * -->
+<TT><B>-L <I>file</I>[+[pid=<I>&lt;pid&gt;</I>,eid=<I>&lt;eid&gt;</I>,][<I>plane</I>]]</TT></B> - Extract the fonts for the specified
+ language from a multi-language font using the map from this file. This is
+ rather like the option '<b>-l</b>' 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
+ <tt>maps/adobe-standard-encoding.map</tt>, <tt>CP1250.map</tt>. (<b>NOTE:</b>
+ <I>the 'standard encoding' map does not include all the glyphs of the
+ AdobeStandard encoding, it's provided only as an example</I>.) The
+ description of the supported map formats is in the file
+ <tt>maps/unicode-sample.map</tt>.
+<p>
+
+ Likewise to '<b>-l</b>', 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 '<b>-l plane</b>')
+ 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 <tt>ttf2pt1</tt> must be run multiple times, each time with
+ a different plane name specified.
+<p>
+
+ The default original TTF encoding table used for the option '<b>-L</b>' 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.
+<p>
+
+<!-- ==item * -->
+<TT><B>-m <i>type</i>=<i>value</i></TT></B> - 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:
+<p>
+
+<blockquote>
+ <TT><B>h</TT></B> - 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 <tt>.t1a</tt> file use the script
+ <tt>scripts/cntstems.pl</tt>.
+<p>
+</blockquote>
+
+<!-- ==item * -->
+<TT><B>-O <i>suboptions</i></TT></B> - 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:
+<p>
+
+<blockquote>
+ <TT><B>b/B</TT></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.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>h/H</TT></B> - 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.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>u/U</TT></B> - 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 '<b>-a</b>', so you may want to
+ use '<b>-a</b>' together with '<b>-Ou</b>'. See <A HREF="app/X11/README.html"><tt>app/X11/README</tt></A> 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 <tt>ttf2pt1</tt>
+ 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
+ '<b>-mh</b>' 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 <tt>other/cmpf</tt> (see the description
+ in <A HREF="other/README.html"><tt>other/README</tt></A>) and decide which one looks better.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>o/O</TT></B> - 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.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>s/S</TT></B> - 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.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>t/T</TT></B> - 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.
+ <b>Default: enabled</b>
+<p>
+
+ <TT><B>v/V</TT></B> - 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.
+ <b>Default: disabled</b>
+<p>
+
+ <TT><B>w/W</TT></B> - 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.
+ <b>Default: disabled</b>
+<p>
+
+ <TT><B>z/Z</TT></B> - Use the Autotrace library on the bitmap fonts. The results
+ are horrible and <b>the use of this option is not recommended</b>. This option is
+ present for experimental purposes. It may change or be removed in the
+ future. The working tracing can be achieved with option <tt><b>-OV</b></tt>.
+ <b>Default: disabled</b>
+<p>
+</blockquote>
+
+<!-- ==item * -->
+<TT><B>-p <I>parser_name</I></TT></B> - 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:
+<p>
+
+ &nbsp;&nbsp;<TT>ttf</TT> - built-in parser for the ttf files (suffix <tt>.ttf</tt>)
+<br>
+ &nbsp;&nbsp;<TT>bdf</TT> - built-in parser for the BDF files (suffix <tt>.bdf</tt>)
+<br>
+ &nbsp;&nbsp;<TT>ft</TT> - parser based on the FreeType-2 library (suffixes <tt>.ttf</tt>,
+ <tt>.otf</tt>, <tt>.pfa</tt>, <tt>.pfb</tt>)
+<p>
+
+ The parser <tt>ft</tt> is <b>NOT</b> linked in by default. See <tt>Makefile</tt>
+ 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.
+<p>
+
+ 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 <b>-Ou</b>)
+ because the hints produced will be huge but not adding much to the
+ quality of the fonts.
+<p>
+
+<!-- ==item * -->
+<TT><B>-u <I>number</I></TT></B> - 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 '<tt><b>A</b></tt>' is given
+ then the converter generates the value of UniqueID automatically,
+ as a hash of the font name. (<b>NOTE:</b> <i> 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</i>).
+ A simple way to find if any of the fonts in a given directory have
+ duplicated UniqueIDs is to use the command:
+<p>
+
+ <tt>&nbsp;&nbsp;cat *.pf[ab] | grep UniqueID | sort | uniq -c | grep -v ' 1 '</tt>
+<p>
+
+ Or if you use <tt>scripts/convert</tt> it will do that for you automatically
+ plus it will also give the exact list of files with duplicate UIDs.
+<p>
+
+<!-- ==item * -->
+<TT><B>-v <I>size</I></TT></B> - 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.
+<p>
+
+<!-- ==item * -->
+<TT><B>-W <i>level</i></TT></B> - 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.
+<p>
+
+<!-- ==item * -->
+<B>Obsolete option:</B>
+<TT><B>-A</TT></B> - Print the font metrics (.afm file) instead of the font on STDOUT.
+ Use <b>-GA</b> instead.
+<p>
+
+<!-- ==item * -->
+<B>Very obsolete option:</B>
+<br>
+ 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 '<b>-f</b>' was reused for another option. The old version was:
+<br>
+<TT><B>-f</TT></B> - 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.
+<p>
+<!-- ==back -->
+
+The <tt>.pfa</tt> 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 '<b>-e</b>'
+or use the <tt>t1asm</tt> program to assemble (that means, encode and
+encrypt) the font program. The <tt>t1asm</tt> program that is included with
+the converter is actually a part of the <tt>t1utils</tt> package, rather old
+version of which may be obtained from
+<p>
+
+<blockquote>
+<A HREF="http://ttf2pt1.sourceforge.net/t1utils.tar.gz">
+ http://ttf2pt1.sourceforge.net/t1utils.tar.gz
+</A>
+</blockquote>
+<p>
+
+Note that <tt>t1asm</tt> from the old version of that package won't work properly
+with the files generated by <tt>ttf2pt1</tt> version 3.20 and later. Please use
+<tt>t1asm</tt> packaged with <tt>ttf2pt1</tt> or from the new version <tt>t1utils</tt>
+instead. For a newer version of <tt>t1utils</tt> please look at
+<p>
+
+<blockquote>
+<A HREF="http://www.lcdf.org/~eddietwo/type/">
+ http://www.lcdf.org/~eddietwo/type/
+</A>
+</blockquote>
+<p>
+<!-- =stop -->
+
+<!-- =section t1 EXAMPLES -->
+So, the following command lines:
+<p>
+
+<blockquote>
+ <tt>ttf2pt1 -e ttffont.ttf t1font</tt>
+<br>
+ <tt>ttf2pt1 ttffont.ttf - | t1asm &gt;t1font.pfa</tt>
+</blockquote>
+<p>
+
+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.
+<p>
+<!-- =stop -->
+
+<H4>
+Installation and deinstallation of the converter
+</H4>
+<!
+------------------------------------------------
+>
+
+The converter may be easily installed systemwide with
+
+<blockquote>
+ <tt>make install</tt>
+</blockquote>
+
+and uninstalled with
+
+<blockquote>
+ <tt>make uninstall</tt>
+</blockquote>
+
+By default the <tt>Makefile</tt> is configured to install in the hierarchy
+of directory <tt>/usr/local</tt>. This destination directory as well as
+the structure of the hierarchy may be changed by editing the <tt>Makefile</tt>.
+
+<H4>
+Installation of the fonts
+</H4>
+<!
+-------------------------
+>
+
+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
+<tt>fonts.scale</tt> and/or <tt>Fontmap</tt> files. The <A HREF="FONTS.html"><tt>FONTS</tt></A> 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.
+<p>
+
+<H4>
+Other utilities
+</H4>
+<!
+---------------
+>
+
+A few other small interesting programs that allow a cloase look at
+the fonts are located in the subdirectory '<tt>other</tt>'. They
+are described shortly in <A HREF="other/README.html">others/README</a>.
+<p>
+
+<H4>
+Optional packages
+</H4>
+<!
+-----------------
+>
+
+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:
+<p>
+
+&nbsp;&nbsp;<b>ttf2pt1-chinese</b> - contains the Chinese conversion maps
+<p>
+
+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.
+<p>
+
+<H4>
+TO DO:
+</H4>
+<!
+------
+>
+
+<ul>
+<li> Improve hinting.
+<li> Improve the auto-tracing of bitmaps.
+<li> Implement the family-level hints.
+<li> Add generation of CID-fonts.
+<li> Handle the composite glyphs with relative base points.
+<li> Preserve the relative width of stems during scaling to 1000x1000 matrix.
+<li> Add support for bitmap TTF fonts.
+<li> Implement better support of Asian encodings.
+<li> Implement automatic creation of ligatures.
+</ul>
+
+<H4>
+TROUBLESHOOTING AND BUG REPORTS
+</H4>
+<!
+-------------------------------
+>
+<!-- =section t1 BUGS -->
+<!-- ==head2 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 ?
+<p>
+
+Send the bug reports to the ttf2pt1 development mailing list at
+<A HREF="mailto:ttf2pt1-devel@lists.sourceforge.net">ttf2pt1-devel@lists.sourceforge.net</A>.
+<p>
+
+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.
+<p>
+
+First, enable full warnings with option '<b>-W99</b>', 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.
+<p>
+
+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 '<b>-Ou</b>', if
+it does not help then '<b>-Os</b>', then '<b>-Oh</b>', then '<b>-Oo</b>'.
+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.
+<p>
+
+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.
+<p>
+
+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.
+<p>
+<!-- =stop -->
+<!-- =section t1 FILES -->
+<!-- ==over 2 -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_LIBXDIR/t1asm -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/scripts/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/other/* -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/README -->
+<!-- ==item * -->
+<!-- =text TTF2PT1_SHAREDIR/FONTS -->
+<!-- ==back -->
+<!-- =stop -->
+
+<H4>
+CONTACTS
+</H4>
+<!
+--------
+>
+<!-- =section t1 SEE ALSO -->
+<!-- ==over 4 -->
+<!-- ==item * -->
+<!-- =text L&lt;ttf2pt1_convert(1)&gt; -->
+<!-- ==item * -->
+<!-- =text L&lt;ttf2pt1_x2gs(1)&gt; -->
+<!-- ==item * -->
+<!-- =text L&lt;t1asm(1)&gt; -->
+
+<!-- ==item * -->
+<A HREF="http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-announce">
+ttf2pt1-announce@lists.sourceforge.net
+</A><br>
+ 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 <A HREF="http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-announce">http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-announce</A>.
+ 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.
+<p>
+
+<!-- ==item * -->
+<A HREF="mailto:ttf2pt1-devel@lists.sourceforge.net">
+ttf2pt1-devel@lists.sourceforge.net
+</A><br>
+<A HREF="mailto:ttf2pt1-users@lists.sourceforge.net">
+ttf2pt1-users@lists.sourceforge.net
+</A><br>
+ The ttf2pt1 mailing lists for development and users issues. They have not
+ that much traffic either. To subscribe use the Web interface at
+ <A HREF="http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-devel">http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-devel</A>
+ and <A HREF="http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-users">http://lists.sourceforge.net/mailman/listinfo/ttf2pt1-users</A>.
+ 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.
+<p>
+
+<!-- =stop -->
+<A HREF="mailto:mheath@netspace.net.au">
+mheath@netspace.net.au
+</A><br>
+ Mark Heath
+<p>
+
+<A HREF="mailto:A.Weeks@mcc.ac.uk">
+A.Weeks@mcc.ac.uk
+</A><br>
+ Andrew Weeks
+<p>
+
+<A HREF="mailto:babkin@users.sourceforge.net">
+babkin@users.sourceforge.net</A> (preferred)<br>
+<A HREF="mailto:sab123@hotmail.com">
+sab123@hotmail.com
+</A><br>
+<A HREF="http://members.bellatlantic.net/~babkin">
+http://members.bellatlantic.net/~babkin
+</A><br>
+ Sergey Babkin
+<p>
+
+<H4>
+SEE ALSO
+</H4>
+<!
+--------
+>
+
+<!-- =section t1 SEE ALSO -->
+<!-- ==item * -->
+<A HREF="http://ttf2pt1.sourceforge.net">
+http://ttf2pt1.sourceforge.net
+</A><br>
+ The main page of the project.
+<p>
+
+<A HREF="http://www.netspace.net.au/~mheath/ttf2pt1/">
+http://www.netspace.net.au/~mheath/ttf2pt1/
+</A><br>
+ The old main page of the project.
+<p>
+<!-- ==back -->
+<!-- =stop -->
+
+<A HREF="http://sourceforge.net/projects/gnuwin32">
+http://sourceforge.net/projects/gnuwin32
+</A><br>
+ Precompiled binaries for Windows.
+<p>
+
+<A HREF="http://www.lcdf.org/~eddietwo/type/">
+http://www.lcdf.org/~eddietwo/type/
+</a><br>
+ The home page of the Type 1 utilities package.
+<p>
+
+<A HREF="http://www.rightbrain.com/pages/books.html">
+http://www.rightbrain.com/pages/books.html
+</a><br>
+ The first book about PostScript on the Web, "Thinking in PostScript".
+<p>
+
+<A HREF="http://fonts.apple.com/TTRefMan/index.html">
+http://fonts.apple.com/TTRefMan/index.html
+</a><br>
+ The True Type reference manual.
+<p>
+
+<A HREF="http://partners.adobe.com/asn/developer/PDFS/TN/PLRM.pdf">
+http://partners.adobe.com/asn/developer/PDFS/TN/PLRM.pdf
+</a><br>
+ Adobe PostScript reference manual.
+<p>
+
+<A HREF="http://partners.adobe.com/asn/developer/PDFS/TN/T1_SPEC.PDF">
+http://partners.adobe.com/asn/developer/PDFS/TN/T1_SPEC.PDF
+</a><br>
+ Specification of the Type 1 font format.
+<p>
+
+<A HREF="http://partners.adobe.com/asn/developer/PDFS/TN/5015.Type1_Supp.pdf">
+http://partners.adobe.com/asn/developer/PDFS/TN/5015.Type1_Supp.pdf
+</a><br>
+ The Type 1 font format supplement.
+<p>
+
+<A HREF="http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf">
+http://partners.adobe.com/asn/developer/PDFS/TN/5004.AFM_Spec.pdf
+</A><BR>
+ Specification of the Adobe font metrics file format.
+<p>
+
+<A HREF="http://www.cs.wpi.edu/~matt/courses/cs563/talks/surface/bez_surf.html">
+http://www.cs.wpi.edu/~matt/courses/cs563/talks/surface/bez_surf.html
+</A><BR>
+<A HREF="http://www.cs.wpi.edu/~matt/courses/cs563/talks/curves.html">
+http://www.cs.wpi.edu/~matt/courses/cs563/talks/curves.html
+</A><BR>
+ Information about the Bezier curves.
+<p>
+
+<A HREF="http://www.neuroinformatik.ruhr-uni-bochum.de/ini/PEOPLE/rmz/t1lib/t1lib.html">
+http://www.neuroinformatik.ruhr-uni-bochum.de/ini/PEOPLE/rmz/t1lib/t1lib.html
+</A><br>
+ A stand-alone library supporting the Type1 fonts. Is neccessary
+ to compile the programs <tt>other/cmpf</tt> and <tt>other/dmpf</tt>.
+<p>
+
+<A HREF="http://www.freetype.org">
+http://www.freetype.org
+</A><br>
+ A library supporting the TTF fonts. Also many useful TTF programs
+ are included with it.
+<p>
+
+<A HREF="http://heliotrope.homestead.com/files/printsoft.html">
+http://heliotrope.homestead.com/files/printsoft.html
+</A><br>
+ Moses Gold's collection of links to printing software.
+<p>
+
+<A HREF="http://linuxartist.org/fonts/">
+http://linuxartist.org/fonts/
+</A><br>
+ Collection of font-related links.
+<p>
+
+<HR>
+<HR>
+<!
+----------------------------------------------------------------------
+----------------------------------------------------------------------
+>
+
+Following is the Readme of <tt>ttf2pfa</tt> (true type to type 3 font converter) It
+covers other issues regarding the use of this software. Please note that
+although <tt>ttf2pfa</tt> is a public domain software, <tt>ttf2pt1</tt>
+is instead covered by an Open Source license. See the <tt>COPYRIGHT</tt>
+file for details.
+<p>
+
+Please note also that <tt>ttf2pfa</tt> has not been maintained for a long time.
+All of its functionality has been integrated into <tt>ttf2pt1</tt> and all the
+development moved to <tt>ttf2pt1</tt>, including Andrew Weeks, the author of
+<tt>ttf2pfa</tt>. <tt>Ttf2pfa</tt> is provided for historical reasons only. Please use
+<tt>ttf2pt1</tt> instead.
+
+<HR>
+<!
+----------------------------------------------------------------------
+>
+
+<H3>
+True Type to Postscript Font converter
+</H3>
+<!
+--------------------------------------
+>
+
+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. '<I>_A_</I> postscript font', that is, not necessarily
+the same font, you understand, but a fair imitation.
+<p>
+
+Run it like this:
+<p>
+
+<blockquote><tt>
+ ttf2pfa fontfile.ttf fontname
+</tt></blockquote>
+<p>
+
+The first parameter is the truetype filename, the second is a stem for
+the output file names. The program will create a <tt>fontname.pfa</tt> containing
+the Postscript font and a <tt>fontname.afm</tt> containing the metrics.
+<p>
+
+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.
+<p>
+
+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.
+<p>
+
+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.
+<p>
+
+<H4>
+Copyright issues
+</H4>
+<!
+----------------
+>
+
+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.
+<p>
+
+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.
+<p>
+
+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.
+<p>
+
+I am not a lawyer, however, so this is not a legal opinion, and may be
+garbage.
+<p>
+
+There shouldn't be a any problem with public domain fonts.
+<p>
+
+<H4>
+About the Program
+</H4>
+<!
+-----------------
+>
+
+It was written in C on a IBM PC running Linux.
+<p>
+
+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.
+<p>
+
+I doubt whether it will work on a DOS-based PC though.
+<p>
+
+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.
+<p>
+
+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.
+<p>
+
+<H4>
+Problems that may occur
+</H4>
+<!
+-----------------------
+>
+
+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 <tt>.pfa</tt> 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.
+<p>
+
+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.
+<p>
+
+<H4>
+Encodings
+</H4>
+<!
+---------
+>
+
+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.
+<p>
+
+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:
+<p>
+ 1) Microsoft uses Unicode, a 16-bit system, to encode the font.
+<br>
+ 2) that more than one glyph is given the same Postscript name.
+<p>
+
+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.
+<p>
+
+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).
+<p>
+
+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.
+<p>
+
+(almost every TrueType font has three glyphs called <tt>.notdef</tt>, 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 <tt>.notdef</tt> characters are only
+used for unprintable characters, which shouldn't occur in your documents
+anyway).
+<p>
+</BODY>
+</HTML>
diff --git a/nx-X11/extras/ttf2pt1/bdf.c b/nx-X11/extras/ttf2pt1/bdf.c
new file mode 100644
index 000000000..9f6c72720
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/bdf.c
@@ -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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.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 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<MAXLINE-1) {
+ if(c!='\r')
+ str[len++]=c;
+ } else {
+ fprintf(stderr, "**** bdf: line %d is too long (>%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(i<len) {
+ c=str[i++];
+ if(!isxdigit(c)) {
+ fprintf(stderr,"**** non-hex digit in bitmap at line %d\n", lineno);
+ exit(1);
+ }
+ if(c<='9')
+ c-='0';
+ else
+ c= tolower(c)-'a'+10;
+
+ for(plim=p+4; p<psz && p<plim; c<<=1)
+ *p++ = (( c & 0x08 )!=0);
+ }
+ if(p<psz) {
+ fprintf(stderr,"**** bitmap line is too short at line %d\n", lineno);
+ exit(1);
+ }
+ curln--;
+ }
+ return 0;
+}
+
+/*
+ * Read all the possible information about the glyphs
+ */
+
+static void
+readglyphs(
+ GLYPH *glyph_list
+)
+{
+ int i;
+ GLYPH *g;
+
+ if(got_glyphs)
+ return;
+
+ /* pass them to handle_glyphs() through statics */
+ glyphs = glyph_list;
+ curgl = 2; /* skip the empty glyph and .notdef */
+
+ /* initialize the empty glyph and .notdef */
+
+ for(i=0; i<2; i++) {
+ g = &glyphs[i];
+ g->lsb = 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<nglyphs; i++)
+ fontenc[i] = -1;
+ if(( glpaths = calloc(nglyphs, sizeof *glpaths) )==NULL) {
+ fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
+ exit(255);
+ }
+}
+
+/*
+ * Close font.
+ * Exit on error.
+ */
+
+static void
+closefont(
+ void
+)
+{
+ if(fclose(bdf_file) < 0) {
+ WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n");
+ }
+}
+
+/*
+ * Get the number of glyphs in font.
+ */
+
+static int
+getnglyphs (
+ void
+)
+{
+ return 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
+)
+{
+ readglyphs(glyph_list);
+ return 0;
+}
+
+/*
+ * 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 i, douni, e;
+
+ if(is_unicode || forcemap)
+ douni = 1;
+ else
+ douni = 0;
+
+ for(i=0; i<nglyphs; i++) {
+ e = fontenc[i];
+ if(douni)
+ e = unicode_rev_lookup(e);
+ if(e>=0 && e<ENCTABSZ && encoding[e] == -1)
+ encoding[e] = i;
+ }
+
+ if(is_unicode)
+ return 1;
+ else if(maxenc > 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 */
+}
diff --git a/nx-X11/extras/ttf2pt1/bitmap.c b/nx-X11/extras/ttf2pt1/bitmap.c
new file mode 100644
index 000000000..d2334e433
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/bitmap.c
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#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 <autotrace/autotrace.h>
+
+/*
+ * 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; i<ysz; i++, j+=xsz+2) {
+ xbmap[j] = 0; /* left margin */
+ memcpy(&xbmap[j+1], &bmap[xsz*(ysz-1-i)], xsz); /* a line of bitmap */
+ xbmap[j+xsz+1] = 0; /* right margin */
+ }
+ memset(xbmap+j, 0, xsz+2); /* bottom margin */
+ xoff--; yoff-=2; /* compensate for the margins */
+
+ atoptsp = at_fitting_opts_new();
+
+ atb.width = xsz+2;
+ atb.height = ysz+2;
+ atb.np = 1;
+ atb.bitmap = xbmap;
+
+ atsp = at_splines_new(&atb, atoptsp);
+
+ lastx = lasty = -1.;
+ for(i=0; i<atsp->length; 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; j<slp->length; 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; x<xsz; x++) {
+ if(ip[x])
+ op[x]=L_ON;
+ }
+ ip+=xsz; op+=xsz;
+ /* internal rows */
+ for(y=1; y<ysz; y++) {
+ for(x=0; x<xsz; x++) {
+ if(ip[x]) {
+ if(!ip[x-xsz])
+ op[x]=L_ON;
+ } else {
+ if(ip[x-xsz])
+ op[x]=L_OFF;
+ }
+ }
+ ip+=xsz; op+=xsz;
+ }
+
+ /* last row */
+ ip-=xsz;
+ for(x=0; x<xsz; x++) {
+ if(ip[x])
+ op[x]=L_OFF;
+ }
+
+ /* find the vertical limits */
+ ip=bmap; op=vlm;
+ for(y=0; y<ysz; y++) {
+ if(ip[0])
+ op[0]=L_ON;
+ for(x=1; x<xsz; x++) {
+ if(ip[x]) {
+ if(!ip[x-1])
+ op[x]=L_ON;
+ } else {
+ if(ip[x-1])
+ op[x]=L_OFF;
+ }
+ }
+ if(ip[xsz-1])
+ op[xsz]=L_OFF;
+ ip+=xsz; op+=xsz+1;
+ }
+
+ /*
+ * Ambiguous points are the nodes of the grids
+ * that are between two white and two black pixels
+ * located in a checkerboard style. Actually
+ * there are only two patterns that may be
+ * around an ambiguous point:
+ *
+ * X|. .|X
+ * -*- -*-
+ * .|X X|.
+ *
+ * where "|" and "-" represent the grid (respectively members
+ * of vlm and hlm), "*" represents an ambiguous point
+ * and "X" and "." represent black and white pixels.
+ *
+ * If these sample pattern occur in the lower left corner
+ * of the bitmap then this ambiguous point will be
+ * located at amp[1][1] or in other words amp[1*xsz+1].
+ *
+ * These points are named "ambiguous" because it's
+ * not easy to guess what did the font creator mean
+ * at these points. So we are going to treat them
+ * specially, doing no aggressive smoothing.
+ */
+
+ /* find the ambiguous points */
+ for(y=ysz-1; y>0; 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<xsz; x++)
+ if(hlm[y*xsz+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; i<len; i++, pge = pge->frwd)
+ 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; i<len; i++, pge = pge->frwd)
+ 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<xsz; x++)
+ putchar( bmap[y*xsz+x] ? 'X' : '.' );
+ if(-yoff==y)
+ putchar('_'); /* mark the baseline */
+ putchar('\n');
+ }
+ putchar(' '); putchar(' ');
+ for(x=0; x<xsz; x++)
+ putchar( x%10+'0' );
+ putchar('\n'); putchar(' '); putchar(' ');
+ for(x=0; x<xsz; x++)
+ putchar( (x%10==0) ? x/10+'0' : ' ' );
+ putchar('\n');
+}
+
+/* print out the limits of outlines */
+printlimits(hlm, vlm, amp, xsz, ysz)
+ char *hlm, *vlm, *amp;
+ int xsz, ysz;
+{
+ int x, y;
+ static char h_char[]={ ' ', '~', '^' };
+ static char v_char[]={ ' ', '(', ')' };
+
+ for(y=ysz-1; y>=0; y--) {
+ for(x=0; x<xsz; x++) {
+ if(amp[y*xsz+x])
+ putchar('!'); /* ambigouos point is always on a limit */
+ else
+ putchar(v_char[ vlm[y*(xsz+1)+x] & (L_ON|L_OFF) ]);
+ putchar(h_char[ hlm[(y+1)*xsz+x] & (L_ON|L_OFF) ]);
+ }
+ putchar(v_char[ vlm[y*(xsz+1)+x] & (L_ON|L_OFF) ]);
+ putchar('\n');
+ }
+ /* last line */
+ for(x=0; x<xsz; x++) {
+ putchar(' ');
+ putchar(h_char[ hlm[x] & (L_ON|L_OFF) ]);
+ }
+ putchar(' ');
+ putchar('\n');
+}
+#endif /* 0 */
diff --git a/nx-X11/extras/ttf2pt1/byteorder.h b/nx-X11/extras/ttf2pt1/byteorder.h
new file mode 100644
index 000000000..c139817e5
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/byteorder.h
@@ -0,0 +1,24 @@
+/*
+ * see COPYRIGHT
+ */
+
+/* This defines the macroes ntohs and ntohl, which convert short and long
+ ints from network order (used on 68000 chips, and in TrueType font
+ files) to whatever order your computer uses. #define _BIG_ENDIAN or not
+ to control which set of definitions apply. If you don't know, try both. If
+ you have a peculiar machine you're on your own.
+*/
+
+#if defined(_BIG_ENDIAN)
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#else
+#define ntohs(x) \
+ ((USHORT)((((USHORT)(x) & 0x00ff) << 8) | \
+ (((USHORT)(x) & 0xff00) >> 8)))
+#define ntohl(x) \
+ ((ULONG)((((ULONG)(x) & 0x000000ffU) << 24) | \
+ (((ULONG)(x) & 0x0000ff00U) << 8) | \
+ (((ULONG)(x) & 0x00ff0000U) >> 8) | \
+ (((ULONG)(x) & 0xff000000U) >> 24)))
+#endif
diff --git a/nx-X11/extras/ttf2pt1/cygbuild.sh b/nx-X11/extras/ttf2pt1/cygbuild.sh
new file mode 100644
index 000000000..df109f81e
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/cygbuild.sh
@@ -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
+
diff --git a/nx-X11/extras/ttf2pt1/ft.c b/nx-X11/extras/ttf2pt1/ft.c
new file mode 100644
index 000000000..60a429654
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ft.c
@@ -0,0 +1,808 @@
+/*
+ * The font parser using the FreeType library version 2.
+ *
+ * see COPYRIGHT
+ *
+ */
+
+#ifdef USE_FREETYPE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#include <ft2build.h>
+#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"
+#define NOT_IN_FTFUNCS
+#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; i<ENCTABSZ; i++) {
+ if(encoding[i] != -1)
+ continue;
+
+ /* Remap X11 font index to FreeType font index */
+ code = FTRemap(face, &xp_xtf->mapping, 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<ENCTABSZ; i++) {
+ if(encoding[i] != -1)
+ continue;
+ if(enc_type == 1 || forcemap) {
+ code = unimap[i];
+ if(code == (unsigned) -1)
+ continue;
+ } else
+ code = i;
+
+ code = FT_Get_Char_Index(face, code);
+ if(0 && ISDBG(FT)) fprintf(stderr, "code of %3d is %3d\n", i, code);
+ if(code == 0)
+ continue; /* .notdef */
+ encoding[i] = code;
+ }
+
+ return enc_type;
+#endif /* XP_PSTEXT */
+}
+
+/* duplicate a string with counter to a 0-terminated string */
+static char *
+dupcnstring(
+ unsigned char *s,
+ int len
+)
+{
+ char *res, *out;
+ int i, c;
+ static int warned=0;
+
+ if(( res = malloc(len+1) )==NULL) {
+ fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
+ exit(255);
+ }
+
+ out = res;
+ for(i=0; i<len; i++) {
+ if(( c=s[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 && i<sizeof fieldstocheck /sizeof(fieldstocheck[0]); i++) {
+ str=fieldstocheck[i];
+ for(i=0; str[i]!=0; i++) {
+ if( (str[i]=='B'
+ || str[i]=='b'
+ && ( i==0 || !isalpha(str[i-1]) )
+ )
+ && !strncmp("old",&str[i+1],3)
+ && !islower(str[i+4])
+ ) {
+ fm->force_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<nglyphs; i++) {
+ if( (glyph_list[i].flags & GF_USED) ==0)
+ continue;
+ for(j=0; j<nglyphs; j++) {
+ if( (glyph_list[j].flags & GF_USED) ==0)
+ continue;
+ if( FT_Get_Kerning(face, i, j, ft_kerning_unscaled, &k) )
+ continue;
+ if( k.x == 0 )
+ continue;
+
+ addkernpair(i, j, k.x);
+ }
+ }
+}
+
+#endif
diff --git a/nx-X11/extras/ttf2pt1/global.h b/nx-X11/extras/ttf2pt1/global.h
new file mode 100644
index 000000000..4d53878f7
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/global.h
@@ -0,0 +1,174 @@
+/*
+ * see COPYRIGHT
+ */
+
+
+/* options */
+
+extern int encode; /* encode the resulting file */
+extern int pfbflag; /* produce compressed file */
+extern int wantafm; /* want to see .afm instead of .t1a on stdout */
+extern int correctvsize; /* try to correct the vertical size of characters */
+extern int wantuid; /* user wants UniqueID entry in the font */
+extern int allglyphs; /* convert all glyphs, not only 256 of them */
+extern int warnlevel; /* the level of permitted warnings */
+extern int forcemap; /* do mapping even on non-Unicode fonts */
+/* options - maximal limits */
+extern int max_stemdepth; /* maximal depth of stem stack in interpreter */
+/* options - debugging */
+extern int absolute; /* print out in absolute values */
+extern int reverse; /* reverse font to Type1 path directions */
+/* options - suboptions of Outline Processing */
+extern int optimize; /* enables space optimization */
+extern int smooth; /* enable smoothing of outlines */
+extern int transform; /* enables transformation to 1000x1000 matrix */
+extern int hints; /* enables autogeneration of hints */
+extern int subhints; /* enables autogeneration of substituted hints */
+extern int trybold; /* try to guess whether the font is bold */
+extern int correctwidth; /* try to correct the character width */
+extern int vectorize; /* vectorize the bitmaps */
+extern int use_autotrace; /* use the autotrace library on bitmap */
+/* options - suboptions of File Generation */
+extern int gen_pfa; /* generate the font file */
+extern int gen_afm; /* generate the metrics file */
+extern int gen_dvienc; /* generate the dvips encoding file */
+
+/* not quite options to select a particular source encoding */
+extern int force_pid; /* specific platform id */
+extern int force_eid; /* specific encoding id */
+
+/* other globals */
+extern FILE *null_file, *pfa_file, *afm_file, *dvienc_file;
+extern int numglyphs;
+
+/* warnings */
+
+#define WARNING_1 if(warnlevel >= 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 */
+};
diff --git a/nx-X11/extras/ttf2pt1/pt1.c b/nx-X11/extras/ttf2pt1/pt1.c
new file mode 100644
index 000000000..b1c46d57a
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/pt1.c
@@ -0,0 +1,7374 @@
+/*
+ * see COPYRIGHT
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifndef WINDOWS
+# include <netinet/in.h>
+# include <unistd.h>
+#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);
+#if 0
+static double fcvarea( GENTRY *ge);
+#endif
+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
+** <EQUAL - goes under the line connecting the ends
+**
+** See CVDIR_* for exact definitions.
+*/
+
+static void
+fixcvdir(
+ GENTRY * ge,
+ int dir
+)
+{
+ int a, b, c, d;
+ double kk, kk1, kk2;
+ int changed;
+ int fdir, rdir;
+
+ if(ge->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<nblues && i<2; i+=2) { /* baseline */
+ s[n].value=bluevalues[i];
+ s[n].flags=ST_UP|ST_ZONE;
+ /* don't overlap with anything */
+ s[n].origin=s[n].from=s[n].to= -10000+i;
+ n++;
+ s[n].value=bluevalues[i+1];
+ s[n].flags=ST_ZONE;
+ /* don't overlap with anything */
+ s[n].origin=s[n].from=s[n].to= -10000+i+1;
+ n++;
+ }
+ for(i=2; i<nblues; i+=2) { /* top zones */
+ s[n].value=bluevalues[i];
+ s[n].flags=ST_UP|ST_ZONE|ST_TOPZONE;
+ /* don't overlap with anything */
+ s[n].origin=s[n].from=s[n].to= -10000+i;
+ n++;
+ s[n].value=bluevalues[i+1];
+ s[n].flags=ST_ZONE|ST_TOPZONE;
+ /* don't overlap with anything */
+ s[n].origin=s[n].from=s[n].to= -10000+i+1;
+ n++;
+ }
+ for(i=0; i<notherb; i+=2) { /* bottom zones */
+ s[n].value=otherblues[i];
+ s[n].flags=ST_UP|ST_ZONE;
+ /* don't overlap with anything */
+ s[n].origin=s[n].from=s[n].to= -10000+i+nblues;
+ n++;
+ s[n].value=otherblues[i+1];
+ s[n].flags=ST_ZONE;
+ /* don't overlap with anything */
+ s[n].origin=s[n].from=s[n].to= -10000+i+1+nblues;
+ n++;
+ }
+ return n;
+}
+
+/* sort stems in array */
+static void
+sortstems(
+ STEM * s,
+ int n
+)
+{
+ int i, j;
+ STEM x;
+
+
+ /* a simple sorting */
+ /* hm, the ordering criteria are not quite simple :-)
+ * if the values are tied
+ * ST_UP always goes under not ST_UP
+ * ST_ZONE goes on the most outer side
+ * ST_END goes towards inner side after ST_ZONE
+ * ST_FLAT goes on the inner side
+ */
+
+ for (i = 0; i < n; i++)
+ for (j = i + 1; j < n; j++) {
+ if(s[i].value < s[j].value)
+ continue;
+ if(s[i].value == s[j].value) {
+ if( (s[i].flags & ST_UP) < (s[j].flags & ST_UP) )
+ continue;
+ if( (s[i].flags & ST_UP) == (s[j].flags & ST_UP) ) {
+ if( s[i].flags & ST_UP ) {
+ if(
+ ((s[i].flags & (ST_ZONE|ST_FLAT|ST_END)) ^ ST_FLAT)
+ >
+ ((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<notherb; i++) {
+ if( val>=otherblues[i] && val<=otherblues[i+1] )
+ return 1;
+ }
+ } else {
+ /* painted side down, look at upper zones */
+ for(i=2; i<nblues; i++) {
+ if( val>=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<nblues; i+=2) {
+ a=bluevalues[i]; b=bluevalues[i+1];
+ if(ISDBG(BLUESTEMS))
+ fprintf(pfa_file, "%% looking at blue zone %d...%d\n", a, b);
+ for(j=nold-1; j>=0; j--) {
+ if( s[j].flags & (ST_ZONE|ST_UP|ST_END) )
+ continue;
+ c=s[j].value;
+ if(c<a) /* too low */
+ break;
+ if(c<=b) { /* found the topmost stem border */
+ /* mark all the stems with the same value */
+ if(ISDBG(BLUESTEMS))
+ fprintf(pfa_file, "%% found D 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>=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; j<nold; j++) {
+ if( (s[j].flags & (ST_ZONE|ST_UP|ST_END))!=ST_UP )
+ continue;
+ c=s[j].value;
+ if(c>b) /* 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<nold && s[j].value==c
+ && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++)
+ s[j].flags |= ST_BLUE;
+ break;
+ }
+ }
+ }
+ /* bottom zones: the logic is the same as for baseline */
+ for(i=0; i<notherb; i+=2) {
+ a=otherblues[i]; b=otherblues[i+1];
+ for(j=0; j<nold; j++) {
+ if( (s[j].flags & (ST_UP|ST_ZONE|ST_END))!=ST_UP )
+ continue;
+ c=s[j].value;
+ if(c>b) /* 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<nold && s[j].value==c
+ && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++)
+ s[j].flags |= ST_BLUE;
+ break;
+ }
+ }
+ }
+}
+
+/* Eliminate invalid stems, join equivalent lines and remove nested stems
+ * to build the main (non-substituted) set of stems.
+ * XXX add consideration of the italic angle
+ */
+static int
+joinmainstems(
+ STEM * s,
+ int nold,
+ int useblues /* do we use the blue values ? */
+)
+{
+#define MAX_STACK 1000
+ STEM stack[MAX_STACK];
+ int nstack = 0;
+ int sbottom = 0;
+ int nnew;
+ int i, j, k;
+ int a, b, c, w1, w2, w3;
+ int fw, fd;
+ /*
+ * priority of the last found stem:
+ * 0 - nothing found yet
+ * 1 - has ST_END in it (one or more)
+ * 2 - has no ST_END and no ST_FLAT, can override only one stem
+ * with priority 1
+ * 3 - has no ST_END and at least one ST_FLAT, can override one
+ * stem with priority 2 or any number of stems with priority 1
+ * 4 (handled separately) - has ST_BLUE, can override anything
+ */
+ int readystem = 0;
+ int pri;
+ int nlps = 0; /* number of non-committed
+ * lowest-priority stems */
+
+
+ for (i = 0, nnew = 0; i < nold; i++) {
+ if (s[i].flags & (ST_UP|ST_ZONE)) {
+ if(s[i].flags & ST_BLUE) {
+ /* 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<nold && s[a].value==s[i].value
+ && (s[i].flags & ST_BLUE); i++)
+ {}
+ b=i; /* our range is a <= i < b */
+ c= -1; /* index of our best guess up to now */
+ pri=0;
+ /* try to find a match, don't cross blue zones */
+ for(; i<nold && (s[i].flags & ST_BLUE)==0; i++) {
+ if(s[i].flags & ST_UP) {
+ if(s[i].flags & ST_TOPZONE)
+ break;
+ else
+ continue;
+ }
+ for(j=a; j<b; j++) {
+ if(!stemoverlap(&s[j], &s[i]) )
+ continue;
+ /* consider priorities */
+ if( ( (s[j].flags|s[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) {
+ c=i;
+ goto bluematch;
+ }
+ if( ((s[j].flags|s[i].flags) & ST_END)==0 ) {
+ if(pri < 2) {
+ c=i; pri=2;
+ }
+ } else {
+ if(pri == 0) {
+ c=i; pri=1;
+ }
+ }
+ }
+ }
+ bluematch:
+ /* clean up the stack */
+ nstack=sbottom=0;
+ readystem=0;
+ /* add this stem */
+ s[nnew++]=s[a];
+ if(c<0) { /* make one-dot-wide stem */
+ if(nnew>=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<nold && s[a].value==s[i].value
+ && (s[i].flags & ST_BLUE); i++)
+ {}
+ b=i; /* our range is a <= i < b */
+ c= -1; /* index of our best guess up to now */
+ pri=0;
+ /* try to find a match */
+ for (i = nstack - 1; 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<b; j++) {
+ if(!stemoverlap(&s[j], &stack[i]) )
+ continue;
+ /* consider priorities */
+ if( ( (s[j].flags|stack[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) {
+ c=i;
+ goto bluedownmatch;
+ }
+ if( ((s[j].flags|stack[i].flags) & ST_END)==0 ) {
+ if(pri < 2) {
+ c=i; pri=2;
+ }
+ } else {
+ if(pri == 0) {
+ c=i; pri=1;
+ }
+ }
+ }
+ }
+ bluedownmatch:
+ /* if found no match make a one-dot-wide stem */
+ if(c<0) {
+ c=0;
+ stack[0]=s[b-1];
+ stack[0].flags |= ST_UP;
+ stack[0].flags &= ~ST_BLUE;
+ }
+ /* remove all the stems conflicting with this one */
+ readystem=0;
+ for(j=nnew-2; 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; i<nnew; i+=2) {
+ if(s[i].value != s[i+1].value)
+ continue;
+ if( ((s[i].flags ^ s[i+1].flags) & ST_BLUE)==0 )
+ continue;
+ if( s[i].flags & ST_BLUE ) {
+ if(nnew>i+2 && s[i+2].value<s[i].value+22)
+ s[i+1].value=s[i+2].value-2; /* compensate for fuzziness */
+ else
+ s[i+1].value+=20;
+ } else {
+ if(i>0 && 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<nnew; i+=2) {
+ a=10000; /* lowest border of top zone crosing the stem */
+ b= -10000; /* highest border of bottom zone crossing the stem */
+
+ for(j=2; j<nblues; j++) {
+ c=bluevalues[j];
+ if( c>=s[i].value && c<=s[i+1].value && c<a )
+ a=c;
+ }
+ if(nblues>=2) {
+ c=bluevalues[1];
+ if( c>=s[i].value && c<=s[i+1].value && c>b )
+ b=c;
+ }
+ for(j=1; j<notherb; j++) {
+ c=otherblues[j];
+ if( c>=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<xblast; i+=2)
+ printf("%d-%d ", xbstem[i], xbstem[i+1]);
+ printf(") %d\n", xblast);
+}
+
+/*
+ * Join the stem borders to build the sets of substituted stems
+ * XXX add consideration of the italic angle
+ */
+static void
+joinsubstems(
+ STEM * s,
+ short *pairs,
+ int nold,
+ int useblues /* do we use the blue values ? */
+)
+{
+ int i, j, x;
+ static unsigned char mx[MAX_STEMS][MAX_STEMS];
+
+ /* we do the substituted groups of stems first
+ * and it looks like it's going to be REALLY SLOW
+ * AND PAINFUL but let's bother about it later
+ */
+
+ /* for the substituted stems we don't bother about [hv]stem3 -
+ * anyway the X11R6 rasterizer does not bother about hstem3
+ * at all and is able to handle only one global vstem3
+ * per glyph
+ */
+
+ /* clean the used part of matrix */
+ for(i=0; i<nold; i++)
+ for(j=0; j<nold; j++)
+ mx[i][j]=0;
+
+ /* build the matrix of stem pairs */
+ for(i=0; i<nold; i++) {
+ if( s[i].flags & ST_ZONE )
+ continue;
+ if(s[i].flags & ST_BLUE)
+ mx[i][i]=1; /* allow to pair with itself if no better pair */
+ if(s[i].flags & ST_UP) { /* the down-stems are already matched */
+ setbasestem(s[i].from, s[i].to);
+ for(j=i+1; j<nold; j++) {
+ if(s[i].value==s[j].value
+ || s[j].flags & ST_ZONE ) {
+ continue;
+ }
+ x=subfrombase(s[j].from, s[j].to);
+
+ if(s[j].flags & ST_UP) /* match only up+down pairs */
+ continue;
+
+ mx[i][j]=mx[j][i]=x;
+
+ if(isbaseempty()) /* nothing else to do */
+ break;
+ }
+ }
+ }
+
+ if(ISDBG(SUBSTEMS)) {
+ fprintf(pfa_file, "%% ");
+ for(j=0; j<nold; j++)
+ putc( j%10==0 ? '0'+(j/10)%10 : ' ', pfa_file);
+ fprintf(pfa_file, "\n%% ");
+ for(j=0; j<nold; j++)
+ putc('0'+j%10, pfa_file);
+ putc('\n', pfa_file);
+ for(i=0; i<nold; i++) {
+ fprintf(pfa_file, "%% %3d ",i);
+ for(j=0; j<nold; j++)
+ putc( mx[i][j] ? 'X' : '.', pfa_file);
+ putc('\n', pfa_file);
+ }
+ }
+
+ /* now use the matrix to find the best pair for each stem */
+ for(i=0; i<nold; i++) {
+ int pri, lastpri, v, f;
+
+ x= -1; /* best pair: none */
+ lastpri=0;
+
+ v=s[i].value;
+ f=s[i].flags;
+
+ if(f & ST_ZONE) {
+ pairs[i]= -1;
+ continue;
+ }
+
+ if(f & ST_UP) {
+ for(j=i+1; j<nold; 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 || 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; i<nold; i++) {
+ j=pairs[i];
+ if(j>0)
+ 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<ns; from=to) {
+ prevpri[0] = prevpri[1] = 0;
+ prevwd[0] = prevwd[1] = 0;
+ prevbest[0] = prevbest[1] = -1;
+ val = s[from].value;
+
+ for(to = from; to<ns && s[to].value == val; to++) {
+ dir = ((s[to].flags & ST_UP)!=0);
+
+ i=pairs[to]; /* the other side of this stem */
+ if(i<0 || i==to)
+ continue; /* oops, no other side */
+ wd=abs(s[i].value-val);
+ if(wd == 0)
+ continue;
+ pri=1;
+ if( (s[to].flags | s[i].flags) & ST_END )
+ pri=0;
+ if( prevbest[dir] == -1 || pri > prevpri[dir] || wd<prevwd[dir] ) {
+ prevbest[dir]=i;
+ prevpri[dir]=pri;
+ prevwd[dir]=wd;
+ }
+ }
+
+ for(i=from; i<to; i++) {
+ dir = ((s[i].flags & ST_UP)!=0);
+ if(prevbest[dir] >= 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(v<value)
+ min=i+1;
+ else if(v>value)
+ 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(; si<ns && sp[si].value==value; si++) {
+ if(sp[si].origin != origin)
+ continue;
+ if(sp[si].ge != ge) {
+ if(ISDBG(SUBSTEMS)) {
+ fprintf(stderr,
+ "dbg: possible self-intersection at v=%d o=%d exp_ge=0x%x ge=0x%x\n",
+ value, origin, ge, sp[si].ge);
+ }
+ continue;
+ }
+ i=pairs[si]; /* the other side of this stem */
+ if(i<0)
+ continue; /* oops, no other side */
+ pri=1;
+ if( (sp[si].flags | sp[i].flags) & ST_END )
+ pri=0;
+ wd=abs(sp[i].value-value);
+ if( prevbest == -1 || pri >prevpri
+ || (pri==prevpri && prevwd==0) || (wd!=0 && wd<prevwd) ) {
+ prevbest=si;
+ prevpri=pri;
+ prevwd=wd;
+ continue;
+ }
+ }
+
+ return prevbest;
+}
+
+/* add the substems for one glyph entry
+ * (called from groupsubstems())
+ * returns 0 if all OK, 1 if too many groups
+ */
+
+static int gssentry_lastgrp=0; /* reset to 0 for each new glyph */
+
+static int
+gssentry( /* crazy number of parameters */
+ GENTRY *ge,
+ STEM *hs, /* horizontal stems, sorted by value */
+ short *hpairs,
+ int nhs,
+ STEM *vs, /* vertical stems, sorted by value */
+ short *vpairs,
+ int nvs,
+ STEMBOUNDS *s,
+ short *egp,
+ int *nextvsi,
+ int *nexthsi /* -2 means "check by yourself" */
+) {
+ enum {
+ SI_VP, /* vertical primary */
+ SI_HP, /* horizontal primary */
+ SI_SIZE /* size of the array */
+ };
+ int si[SI_SIZE]; /* indexes of relevant stems */
+
+ /* the bounds of the existing relevant stems */
+ STEMBOUNDS r[ sizeof(si) / sizeof(si[0]) * 2 ];
+ char rexpand; /* by how much we need to expand the group */
+ int nr; /* and the number of them */
+
+ /* yet more temporary storage */
+ short lb, hb, isvert;
+ int conflict, grp;
+ int i, j, x, y;
+
+
+ /* for each line or curve we try to find a horizontal and
+ * a vertical stem corresponding to its first point
+ * (corresponding to the last point of the previous
+ * glyph entry), because the directions of the lines
+ * will be eventually reversed and it will then become the last
+ * point. And the T1 rasterizer applies the hints to
+ * the last point.
+ *
+ */
+
+ /* start with the common part, the first point */
+ x=ge->prev->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<SI_HP) {
+ r[nr].isvert=1; sp=vs; pairs=vpairs;
+ } else {
+ r[nr].isvert=0; sp=hs; pairs=hpairs;
+ }
+
+ r[nr].low=sp[ si[i] ].value;
+ r[nr].high=sp[ pairs[ si[i] ] ].value;
+
+ if(r[nr].low > 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<nhs; j++)
+ if( (sp[j].flags & (ST_ZONE|ST_TOPZONE))
+ == (ST_ZONE|ST_TOPZONE) ) {
+ if(r[nr].high > 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<nr; j++)
+ r[j].already=0;
+
+ /* check if it fits into the last group */
+ grp = gssentry_lastgrp;
+ i = (grp==0)? 0 : egp[grp-1];
+ for(; i<egp[grp]; i++) {
+ lb=s[i].low; hb=s[i].high; isvert=s[i].isvert;
+ for(j=0; j<nr; j++)
+ if( r[j].isvert==isvert /* intersects */
+ && r[j].low <= hb && r[j].high >= 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<nr; j++)
+ r[j].already=0;
+
+ for(i=0, grp=0; i<egp[NSTEMGRP-1]; i++) {
+ if(i == egp[grp]) { /* checked all stems in a group */
+ if(conflict) {
+ grp++; conflict=0; /* check the next group */
+ for(j=0; j<nr; j++)
+ r[j].already=0;
+ } else
+ break; /* insert into this group */
+ }
+
+ lb=s[i].low; hb=s[i].high; isvert=s[i].isvert;
+ for(j=0; j<nr; j++)
+ if( r[j].isvert==isvert /* intersects */
+ && r[j].low <= hb && r[j].high >= 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<nr; j++)
+ r[j].already=0;
+ }
+
+ if(conflict) { /* oops, can't find any group to fit */
+ return 1;
+ }
+
+ /* OK, add stems to this group */
+
+ rexpand = nr;
+ for(j=0; j<nr; j++)
+ rexpand -= r[j].already;
+
+ if(rexpand > 0) {
+ for(i=egp[NSTEMGRP-1]-1; i>=egp[grp]; i--)
+ s[i+rexpand]=s[i];
+ for(i=0; i<nr; i++)
+ if(!r[i].already)
+ s[egp[grp]++]=r[i];
+ for(i=grp+1; i<NSTEMGRP; i++)
+ egp[i]+=rexpand;
+ }
+
+ ge->stemid = 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; i<NSTEMGRP; i++)
+ egp[i]=0;
+
+ nextvsi=nexthsi= -2; /* processed no horiz/vert line */
+
+ gssentry_lastgrp = 0; /* reset the last group for new glyph */
+
+ for (ge = g->entries; 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; i<NSTEMGRP && egp[i]!=egp[i-1]; i++)
+ {}
+ g->nsg=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->nsg<NSTEMGRP ? egp[g->nsg] : -1 );
+ j=0;
+ for(i=0; i<g->nsg; i++) {
+ fprintf(pfa_file, "%% grp %3d: ", i);
+ for(; j<egp[i]; j++) {
+ fprintf(pfa_file, " %4d...%-4d %c ", s[j].low, s[j].high,
+ s[j].isvert ? 'v' : 'h');
+ }
+ fprintf(pfa_file, "\n");
+ }
+ }
+
+ if(g->nsg==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; j<np; j++) {
+ dir[j] = i;
+ if(ISDBG(QUAD))
+ fprintf(stderr, "%g ", sp[j]);
+ if(sp[j] < 0.03) { /* front end of curve */
+ if(ge->fpoints[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<np; i++)
+ fprintf(stderr, "%g(%c) ", sp[i], dir[i] ? 'y':'x');
+ fprintf(stderr, "\n");
+ }
+
+ /* sort the points ascending */
+ for(i=0; i<np; i++)
+ for(j=i+1; j<np; j++)
+ if(sp[i] > sp[j]) {
+ a = sp[i]; sp[i] = sp[j]; sp[j] = a;
+ }
+
+ /* now finally do the split on each point */
+ for(j=0; j<np; j++) {
+ double k1, k2, c;
+
+ k1 = sp[j];
+ k2 = 1 - k1;
+
+ if(ISDBG(QUAD)) fprintf(stderr, " 0x%x %g/%g\n", ge, k1, k2);
+
+ nge = newgentry(GEF_FLOAT);
+ (*nge) = (*ge);
+
+#define SPLIT(pt1, pt2) ( (pt1) + k1*((pt2)-(pt1)) ) /* order is important! */
+ for(i=0; i<2; i++) { /* for x and y */
+ a = ge->fpoints[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; i<np; i++)
+ sp[i] = (sp[i]-k1) / k2;
+ }
+ }
+
+}
+
+/* check if a curve is a zigzag */
+
+static int
+iiszigzag(
+ GENTRY *ge
+)
+{
+ double k, k1, k2;
+ int a, b;
+
+ if (ge->type != 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; i<NAPSECT; i++) {
+ t += step;
+ nt = 1 - t;
+ t2 = t*t;
+ nt2 = nt*nt;
+ tt[i][0] = nt2*nt; /* (1-t)^3 */
+ tt[i][1] = 3*nt2*t; /* 3*(1-t)^2*t */
+ tt[i][2] = 3*nt*t2; /* 3*(1-t)*t^2 */
+ tt[i][3] = t2*t; /* t^3 */
+ }
+ }
+
+ for(i=0; i<NWSECT; i++) {
+ sum[i] = 0.;
+ count[i] = 0;
+ }
+
+ /* split the curve into segments */
+ for(d=0; d<2; d++) { /* X and Y */
+ cvd[0][d] = curve[0][d]; /* endpoints */
+ cvd[NAPSECT][d] = curve[3][d];
+ for(i=1; i<NAPSECT; i++) {
+ cvd[i][d] = curve[0][d] * tt[i][0]
+ + curve[1][d] * tt[i][1]
+ + curve[2][d] * tt[i][2]
+ + curve[3][d] * tt[i][3];
+ }
+ }
+
+ for(d=0; d<ndots; d++) {
+#ifdef DEBUG_DOTCURVE
+ printf("dot %d ", d); printdot(dots[d].p); printf(":\n");
+
+ /* for debugging */
+ for(i=0; i< NAPSECT; i++) {
+ dist1 = fdotsegdist2(&cvd[i], dots[d].p);
+ printf(" seg %d ",i); printseg(&cvd[i]); printf(" dist=%g\n", sqrt(dist1));
+ }
+#endif
+
+ x = dots[d].p[X];
+ y = dots[d].p[Y];
+
+ /* find the nearest dot on the curve
+ * there may be up to 2 local minimums - so we start from the
+ * ends of curve and go to the center
+ */
+
+ id1 = 0;
+ dx = x - cvd[0][X];
+ dy = y - cvd[0][Y];
+ dist1 = dx*dx + dy*dy;
+#ifdef DEBUG_DOTCURVE
+ printf(" dot 0 "); printdot(cvd[id1]); printf(" dist=%g\n", sqrt(dist1));
+#endif
+ for(i = 1; i<=NAPSECT; 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 < dist1) {
+ dist1 = dist3;
+ id1 = i;
+ } else
+ break;
+ }
+
+ if(id1 < NAPSECT-1) {
+ id2 = NAPSECT;
+ dx = x - cvd[NAPSECT][X];
+ dy = y - cvd[NAPSECT][Y];
+ dist2 = dx*dx + dy*dy;
+#ifdef DEBUG_DOTCURVE
+ printf(" +dot %d ", id2); printdot(cvd[id2]); printf(" dist=%g\n", sqrt(dist2));
+#endif
+ for(i = NAPSECT-1; i>id1+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; i<NWSECT; i++) {
+ if(count[i]==0)
+ continue;
+ id1++;
+ dist1 += sum[i]/count[i];
+ }
+ if(maxp)
+ *maxp = max;
+ if(id1==0) /* no dots, strange */
+ return 0.;
+ else
+ return dist1/id1; /* to get the average distance apply sqrt() */
+}
+
+/*
+ * Approximate a curve matching the giving set of points and with
+ * middle reference points going along the given segments (and no farther
+ * than these segments).
+ */
+
+void
+fapproxcurve(
+ double cv[4][2 /*X,Y*/ ], /* points 0-3 are passed in, points 1,2 - out */
+ struct dot_dist *dots, /* the dots to approximate - distances returned
+ * there may be invalid */
+ int ndots
+)
+{
+ /* b and c are the middle control points */
+#define B 0
+#define C 1
+ /* maximal number of sections on each axis - used for the first step */
+#define MAXSECT 2
+ /* number of sections used for the other steps */
+#define NORMSECT 2
+ /* when the steps become less than this many points, it's time to stop */
+#define STEPEPS 1.
+ double from[2 /*B,C*/], to[2 /*B,C*/];
+ double middf[2 /*B,C*/][2 /*X,Y*/], df;
+ double coef[2 /*B,C*/][MAXSECT];
+ double res[MAXSECT][MAXSECT], thisres, bestres, goodres;
+ int ncoef[2 /*B,C*/], best[2 /*B,C*/], good[2 /*B,C*/];
+ int i, j, k, keepsym;
+ char bc[]="BC";
+ char xy[]="XY";
+
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, "Curve points:");
+ for(i=0; i<4; i++) {
+ fprintf(stderr, " ");
+ printdot(cv[i]);
+ }
+ fprintf(stderr, "\nDots:");
+ for(i=0; i<ndots; i++) {
+ fprintf(stderr, " ");
+ printdot(dots[i].p);
+ }
+ fprintf(stderr, "\n");
+#endif
+
+ /* load the endpoints and calculate differences */
+ for(i=0; i<2; i++) {
+ /* i is X, Y */
+ middf[B][i] = cv[1][i]-cv[0][i];
+ middf[C][i] = cv[2][i]-cv[3][i];
+
+ /* i is B, C */
+ from[i] = 0.;
+ to[i] = 1.;
+ ncoef[i] = MAXSECT;
+ }
+
+ while(ncoef[B] != 1 || ncoef[C] != 1) {
+ /* prepare the values of coefficients */
+ for(i=0; i<2; i++) { /*B,C*/
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, "Coefficients by %c(%g,%g):", bc[i], from[i], to[i]);
+#endif
+ df = (to[i]-from[i]) / (ncoef[i]*2);
+ for(j=0; j<ncoef[i]; j++) {
+ coef[i][j] = from[i] + df*(2*j+1);
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, " %g", coef[i][j]);
+#endif
+ }
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, "\n");
+#endif
+ }
+ bestres = FBIGVAL;
+ /* i iterates by ncoef[B], j iterates by ncoef[C] */
+ for(i=0; i<ncoef[B]; i++) {
+ for(j=0; j<ncoef[C]; j++) {
+ for(k=0; k<2; k++) { /*X, Y*/
+ cv[1][k] = cv[0][k] + middf[B][k]*coef[B][i];
+ cv[2][k] = cv[3][k] + middf[C][k]*coef[C][j];
+ }
+ res[i][j] = thisres = fdotcurvdist2(cv, dots, ndots, NULL);
+ if(thisres < bestres) {
+ goodres = bestres;
+ good[B] = best[B];
+ good[C] = best[C];
+ bestres = thisres;
+ best[B] = i;
+ best[C] = j;
+ } else if(thisres < goodres) {
+ goodres = thisres;
+ good[B] = i;
+ good[C] = j;
+ }
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, " at (%g,%g) dist=%g %s\n", coef[B][i], coef[C][j], sqrt(thisres),
+ (best[B]==i && best[C]==j)? "(BEST)":"");
+#endif
+ }
+ }
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, " best: at (%g, %g) dist=%g\n",
+ coef[B][best[B]], coef[C][best[C]], sqrt(bestres));
+ fprintf(stderr, " B:%d,%d C:%d,%d -- 2nd best: at (%g, %g) dist=%g\n",
+ best[B], good[B], best[C], good[C], coef[B][good[B]], coef[C][good[C]], sqrt(goodres));
+#endif
+
+ if(bestres < (0.1*0.1)) { /* consider it close enough */
+ /* calculate the coordinates to return */
+ for(k=0; k<2; k++) { /*X, Y*/
+ cv[1][k] = cv[0][k] + middf[B][k]*coef[B][best[B]];
+ cv[2][k] = cv[3][k] + middf[C][k]*coef[C][best[C]];
+ }
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, "quick approximated middle points "); printdot(cv[1]);
+ fprintf(stderr, " "); printdot(cv[2]); fprintf(stderr, "\n");
+#endif
+ return;
+ }
+ keepsym = 0;
+ if(best[B] != best[C] && best[B]-best[C] == good[C]-good[B]) {
+ keepsym = 1;
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, "keeping symmetry!\n");
+#endif
+ }
+ for(i=0; i<2; i++) { /*B,C*/
+ if(ncoef[i]==1)
+ continue;
+ if(keepsym) {
+ /* try to keep the symmetry */
+ if(best[i] < good[i]) {
+ from[i] = coef[i][best[i]];
+ to[i] = coef[i][good[i]];
+ } else {
+ from[i] = coef[i][good[i]];
+ to[i] = coef[i][best[i]];
+ }
+ } else {
+ df = (to[i]-from[i]) / ncoef[i];
+ from[i] += df*best[i];
+ to[i] = from[i] + df;
+ }
+ if( fabs(df*middf[i][0]) < STEPEPS && fabs(df*middf[i][1]) < STEPEPS) {
+ /* this side has converged */
+ from[i] = to[i] = (from[i]+to[i]) / 2.;
+ ncoef[i] = 1;
+ } else
+ ncoef[i] = NORMSECT;
+ }
+
+ }
+ /* calculate the coordinates to return */
+ for(k=0; k<2; k++) { /*X, Y*/
+ cv[1][k] = cv[0][k] + middf[B][k]*from[B];
+ cv[2][k] = cv[3][k] + middf[C][k]*from[C];
+ }
+#ifdef DEBUG_APPROXCV
+ fprintf(stderr, "approximated middle points "); printdot(cv[1]);
+ fprintf(stderr, " "); printdot(cv[2]); fprintf(stderr, "\n");
+#endif
+#undef B
+#undef C
+#undef MAXSECT
+#undef NORMSECT
+#undef STEPEPS
+}
+
+/*
+ * Find the squared value of the sinus of the angle between the
+ * end of ge1 and the beginning of ge2
+ * The curve must be normalized.
+ */
+
+static double
+fjointsin2(
+ GENTRY *ge1,
+ GENTRY *ge2
+)
+{
+ double d[3][2 /*X,Y*/];
+ double scale1, scale2, len1, len2;
+ int axis;
+
+ if(ge1->rtg < 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; i<ndots; i++) {
+ t = (i+1)/nf;
+ for(axis=0; axis<2; axis++)
+ dots[i][axis] = fcvval(ge, axis, t);
+ }
+ } else { /* line */
+ d[0] = ge->fx3 - ge->prev->fx3;
+ d[1] = ge->fy3 - ge->prev->fy3;
+ for(i=0; i<ndots; i++) {
+ t = (i+1)/nf;
+ for(axis=0; axis<2; axis++)
+ dots[i][axis] = ge->prev->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)<FEPS && fabs(ge->fy1-ge->fy2)<FEPS);
+ frontsame = (fabs(ge->fx1-ge->prev->fx3)<FEPS && fabs(ge->fy1-ge->prev->fy3)<FEPS);
+ rearsame = (fabs(ge->fx3-ge->fx2)<FEPS && fabs(ge->fy3-ge->fy2)<FEPS);
+
+ if(midsame && (frontsame || rearsame) ) {
+ /* essentially a line */
+ ge->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; i<NREFDOTS; i++) {
+ for(j=0; j<2; j++)
+ dots[ndots].p[j] = nngex->dots[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<ndots; i++) {
+ avsd2 = fdotsegdist2(apcv, dots[i].p);
+ if(avsd2 > CVEPS2)
+ break;
+ }
+ if(i<ndots) { /* failed to join */
+ if(ISDBG(FCONCISE))
+ fprintf(stderr, "failed to join prev %p ", pge);
+ ndots--;
+ pgex->flags &= ~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<ndots; i++) {
+ avsd2 = fdotsegdist2(apcv, dots[i].p);
+ if(avsd2 > CVEPS2)
+ break;
+ }
+ if(i<ndots) { /* failed to join */
+ if(ISDBG(FCONCISE))
+ fprintf(stderr, "failed to join next %p ", nge->frwd);
+ 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; grp<g->nsg; grp++) {
+ fprintf(pfa_file, "dup %d {\n", startid++);
+ for(i= (grp==0)? 0 : g->nsbs[grp-1]; i<g->nsbs[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; k<MINDIST-1; k++) {
+ hyst[w+MINDIST + k] += MINDIST-1-k;
+ hyst[w+MINDIST - k] += MINDIST-1-k;
+ }
+ }
+ }
+ }
+ }
+
+ /* find 12 most frequent values */
+ ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdhw);
+
+ /* store data in stemsnaph */
+ for (i = 0; i < ns; i++)
+ stemsnaph[i] = best[i];
+ if (ns < 12)
+ stemsnaph[ns] = 0;
+
+ /* build the hystogram of vertical stem widths */
+ memset(hyst, 0, sizeof hyst);
+
+ for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
+ if (g->flags & 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; k<MINDIST-1; k++) {
+ hyst[w+MINDIST + k] += MINDIST-1-k;
+ hyst[w+MINDIST - k] += MINDIST-1-k;
+ }
+ }
+ }
+ }
+ }
+
+ /* find 12 most frequent values */
+ ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdvw);
+
+ /* store data in stemsnaph */
+ for (i = 0; i < ns; i++)
+ stemsnapv[i] = best[i];
+ if (ns < 12)
+ stemsnapv[ns] = 0;
+
+#undef MINDIST
+}
+
+/*
+ * SB
+ * A funny thing: TTF paths are going in reverse direction compared
+ * to Type1. So after all (because the rest of logic uses TTF
+ * path directions) we have to reverse the paths.
+ *
+ * It was a big headache to discover that.
+ */
+
+/* works on both int and float paths */
+
+void
+reversepathsfromto(
+ GENTRY * from,
+ GENTRY * to
+)
+{
+ GENTRY *ge, *nge, *pge;
+ GENTRY *cur, *next;
+ int i, n, ilast[2];
+ double flast[2], f;
+
+ for (ge = from; ge != 0 && ge != to; ge = ge->next) {
+ 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; i<numglyphs; i++) {
+ g = &glyph_list[i];
+ if( (g->flags & 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
+
+/*
+ *
+ */
+
diff --git a/nx-X11/extras/ttf2pt1/pt1.h b/nx-X11/extras/ttf2pt1/pt1.h
new file mode 100644
index 000000000..4f65f8847
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/pt1.h
@@ -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);
diff --git a/nx-X11/extras/ttf2pt1/runt1asm.c b/nx-X11/extras/ttf2pt1/runt1asm.c
new file mode 100644
index 000000000..58c4cad8c
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/runt1asm.c
@@ -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 <stdio.h>
+#include <errno.h>
+
+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
diff --git a/nx-X11/extras/ttf2pt1/t1asm.c b/nx-X11/extras/ttf2pt1/t1asm.c
new file mode 100644
index 000000000..cf13f2005
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/t1asm.c
@@ -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 <fcntl.h>
+ #include <getopt.h>
+ #include <io.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#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;
+}
diff --git a/nx-X11/extras/ttf2pt1/ttf.c b/nx-X11/extras/ttf2pt1/ttf.c
new file mode 100644
index 000000000..b5cc5bdf8
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ttf.c
@@ -0,0 +1,1480 @@
+/*
+ * True Type Font to Adobe Type 1 font converter
+ * By Mark Heath <mheath@netspace.net.au>
+ * Based on ttf2pfa by Andrew Weeks <ccsaw@bath.ac.uk>
+ * With help from Frank M. Siegert <fms@this.net>
+ *
+ * see COPYRIGHT
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifndef WINDOWS
+# include <unistd.h>
+# include <netinet/in.h>
+#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<npost; i++) {
+ n = ntohs(name_index[i]);
+ if (n > 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; i<n_ps_names; i++) { fprintf(stderr, "i=%d,
+ * len=%d, name=%s\n", i, ps_name_len[i], ps_name_ptr[i]); }
+ */
+
+ for (i = 0; i < npost; i++) {
+ n = ntohs(name_index[i]);
+ if (n < 258) {
+ glyph_list[i].name = mac_glyph_names[n];
+ } else if (n < 258 + n_ps_names) {
+ glyph_list[i].name = ps_name_ptr[n - 258];
+ } else {
+ glyph_list[i].name = malloc(16);
+ sprintf(glyph_list[i].name, "_g_%d", i);
+ WARNING_2 fprintf(stderr,
+ "Glyph No. %d has no postscript name, becomes %s\n",
+ i, glyph_list[i].name);
+ }
+ }
+ /* Now fake postscript names for all those beyond the end of the table */
+ if (npost < ttf_nglyphs) {
+ for (i=npost; i<ttf_nglyphs; i++) {
+ if ((glyph_list[i].name = malloc(16)) == NULL)
+ {
+ fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
+ exit(255);
+ }
+ sprintf(glyph_list[i].name, "_g_%d", i);
+ WARNING_2 fprintf(stderr,
+ "Glyph No. %d has no postscript name, becomes %s\n",
+ i, glyph_list[i].name);
+ }
+ }
+ } else if (format == 0x00030000) {
+ WARNING_3 fputs("No postscript table, using default\n", stderr);
+ ps_fmt_3 = 1;
+ } else if (format == 0x00028000) {
+ ptr = (char *) &(post_table->numGlyphs);
+ 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<sizeof fieldstocheck /sizeof(int); i++) {
+ str = name_fields[fieldstocheck[i]];
+ len = strlen(str);
+ for(j=0; j<len; j++) {
+ if( (str[j]=='B'
+ || str[j]=='b'
+ && ( j==0 || !isalpha(str[j-1]) )
+ )
+ && !strncmp("old",&str[j+1],3)
+ && (j+4 >= 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;
+ }
+}
+
diff --git a/nx-X11/extras/ttf2pt1/ttf.h b/nx-X11/extras/ttf2pt1/ttf.h
new file mode 100644
index 000000000..297dcaf10
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ttf.h
@@ -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 ;
diff --git a/nx-X11/extras/ttf2pt1/ttf2pt1.1 b/nx-X11/extras/ttf2pt1/ttf2pt1.1
new file mode 100644
index 000000000..500a7d2ec
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ttf2pt1.1
@@ -0,0 +1,832 @@
+.rn '' }`
+''' $RCSfile: ttf2pt1.1,v $$Revision: 1.2 $$Date: 2004/04/23 18:42:57 $
+'''
+''' $Log: ttf2pt1.1,v $
+''' Revision 1.2 2004/04/23 18:42:57 eich
+''' 2004-04-23 Egbert Eich <eich@freedesktop.org>
+''' Merging XORG-CURRENT into trunk
+'''
+''' Revision 1.1.4.1 2004/04/21 10:03:13 gisburn
+''' Fix for http://pdx.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=530 - Land XPRINT branch on XORG-CURRENT
+'''
+''' 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<pid>\fR\fB,eid=\fR\fI<eid>\fR\fR
+.Sp
+\ \ \f(CWplane+\fBpid=\fR\fI<pid>\fR\fB,eid=\fR\fI<eid>\fR\fB,\fR\fI<plane_number>\fR\fR
+.Sp
+\ \ \f(CWplane+\fI<plane_number>\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<pid>\fR,eid=\fI<eid>\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"
+
diff --git a/nx-X11/extras/ttf2pt1/ttf2pt1.c b/nx-X11/extras/ttf2pt1/ttf2pt1.c
new file mode 100644
index 000000000..a85a41658
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ttf2pt1.c
@@ -0,0 +1,2722 @@
+/*
+ * True Type Font to Adobe Type 1 font converter
+ * By Mark Heath <mheath@netspace.net.au>
+ * Based on ttf2pfa by Andrew Weeks <ccsaw@bath.ac.uk>
+ * With help from Frank M. Siegert <fms@this.net>
+ *
+ * see COPYRIGHT for full copyright notice
+ *
+***********************************************************************
+ *
+ * Sergey Babkin <babkin@users.sourceforge.net>, <sab123@hotmail.com>
+ *
+ * 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 <thenlich@rcs.urz.tu-dresden.de>
+ *
+ * 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 <thenlich@rcs.urz.tu-dresden.de>
+ *
+ * Added generation of .afm file (font metrics)
+ *
+***********************************************************************
+ *
+ * Bug Fixes:
+************************************************************************
+ *
+ * Sun, 21 Jun 1998 Thomas Henlich <thenlich@Rcs1.urz.tu-dresden.de>
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef _GNU_SOURCE
+#include <getopt.h>
+#endif
+
+#ifndef WINDOWS
+# include <unistd.h>
+# include <netinet/in.h>
+# define BITBUCKET "/dev/null"
+# include <sys/wait.h>
+#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 <ft2build.h>
+#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"
+#define NOT_IN_FTFUNCS
+#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 <tomek@elender.hu> */
+ { unicode_latin2 },
+ 0, /* no name-based mapping */
+ "latin2",
+ "works for Central European languages",
+ { "hu_","pl_","cz_","si_","sk_" },
+ 'A'
+ },
+ { /* by Rièardas Èepas <rch@WriteMe.Com> */
+ { unicode_latin4 },
+ 0, /* no name-based mapping */
+ "latin4",
+ "works for Baltic languages",
+ { "lt_", "lv_" }, /* doubt about ee_ */
+ 'A'
+ },
+ { /* by Turgut Uyar <uyar@cs.itu.edu.tr> */
+ { unicode_latin5 },
+ 0, /* no name-based mapping */
+ "latin5",
+ "for Turkish",
+ { "tr_" },
+ 'A'
+ },
+ { /* by Zvezdan Petkovic <z.petkovic@computer.org> */
+ { 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<<BUCKET_ID_BITS);
+
+static unsigned int unicode_map[ENCTABSZ]; /* font-encoding to unicode map */
+static int enctabsz = 256; /* actual number of codes used */
+
+static void
+unicode_init_user(
+ char *path
+)
+{
+ FILE *unicode_map_file;
+#define UNIBFSZ 256
+ char buffer[UNIBFSZ];
+ unsigned code, unicode, curpos, unicode2;
+ char *arg, *p;
+ int enabled, found, sawplane;
+ int lineno, cnt, n, nchars;
+ char next;
+ int pid, eid, overid=0;
+
+ /* check if we have an argument (plane name) */
+ arg = strrchr(path, LANG_ARG_SEP);
+ if(arg != 0) {
+ *arg++ = 0;
+ if( sscanf(arg, "pid=%d,eid=%d%n", &pid, &eid, &nchars) == 2 ) {
+ force_pid = pid; force_eid = eid; overid = 1;
+ WARNING_1 fprintf(stderr, "User override of the source encoding: pid=%d eid=%d\n", pid, eid);
+ forcemap = 1;
+ arg += nchars;
+ if(*arg == ',')
+ arg++;
+ }
+ if( *arg == 0 || strlen(arg) > 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 <U%4x>", &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 <z.petkovic@computer.org>
+ */
+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<enctabsz; i++) {
+ if(unicode_map[i] != (unsigned) -1)
+ MARK_UNI_BUCKET(unicode_map[i]);
+ }
+}
+
+/*
+ * When we print errors about bad names we want to print these names in
+ * some decent-looking form
+ */
+
+static char *
+nametoprint(
+ unsigned char *s
+)
+{
+ static char res[50];
+ int c, i;
+
+ for(i=0; ( c =* s )!=0 && i<sizeof(res)-8; s++) {
+ if(c < ' ' || c > 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 && c<ENCTABSZ && encoding[c] == -1)
+ encoding[c] = n;
+ }
+ }
+
+ /* now do the encoding by table */
+ if(uni_lang_selected) {
+ for(i=0; i < MAXUNITABLES && uni_lang_selected->init[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<ENCTABSZ && encoding[c] == -1)
+ encoding[c] = n;
+ }
+ }
+ /* all the encoding things are done */
+
+ for (i = 0; i < ENCTABSZ; i++)
+ if(encoding[i] == -1) {
+ /* check whether this character might be a duplicate
+ * (in which case it would be missed by unicode_rev_lookup())
+ */
+ c = unicode_map[i];
+ if((type != 0 || forcemap) && c != -1) {
+ for(n = 0; n < i; n++) {
+ if(unicode_map[n] == c) {
+ encoding[i] = encoding[n];
+ }
+ }
+ }
+ if(encoding[i] == -1) /* still not found, defaults to .notdef */
+ encoding[i] = 0;
+ }
+
+ for (i = 0; i < 256; i++) /* here 256, not ENCTABSZ */
+ glyph_list[encoding[i]].char_no = i;
+
+#ifndef XP_PSTEXT
+ /* enforce two special cases defined in TTF manual */
+ if(numglyphs > 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 [-<opts>] [-l language | -L file] <ttf-file> [<fontname>]\n", stderr);
+ fputs(" or\n", stderr);
+ fputs("ttf2pt1 [-<opts>] [-l language | -L file] <ttf-file> -\n", stderr);
+ fputs(" or\n", stderr);
+ fputs("ttf2pt1 [-<opts>] [-l language | -L file] <ttf-file> - | t1asm > <pfa-file>\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 <type>=<value>\n");
+ fputs(" -m <type>=<value> - 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 <fontname>, write to <ttf-file> 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; i<argc; i++) {
+ j += strlen(argv[i])+1;
+ }
+ if ((cmdline = malloc(j+1)) == NULL) {
+ fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
+ exit(255);
+ }
+ cmdline[0] = 0;
+ for(i=1; i<argc; i++) {
+ strcat(cmdline, argv[i]);
+ strcat(cmdline, " ");
+ }
+ for(i=0; (j=cmdline[i])!=0; i++)
+ if(j == '\n')
+ cmdline[i] = ' ';
+
+
+ while(( oc=ttf2pt1_getopt(argc, argv, "FaoebAsthHfwVv:p:l:d:u:L:m:W:O:G:",
+ longopts, NULL) )!= -1) {
+ switch(oc) {
+ case 'W':
+ if(sscanf(optarg, "%d", &warnlevel) < 1 || warnlevel < 0) {
+ fprintf(stderr, "**** warning level must be a positive number\n");
+ exit(1);
+ }
+ break;
+ case 'F':
+ forcemap = 1;
+ break;
+ case 'o':
+ fputs("Warning: option -o is obsolete, use -Oo instead\n", stderr);
+ optimize = 0;
+ break;
+ case 'e':
+ encode = 1;
+ break;
+ case 'b':
+ encode = pfbflag = 1;
+ break;
+ case 'A':
+ fputs("Warning: option -A is obsolete, use -GA instead\n", stderr);
+ wantafm = 1;
+ break;
+ case 'a':
+ allglyphs = 1;
+ break;
+ case 's':
+ fputs("Warning: option -s is obsolete, use -Os instead\n", stderr);
+ smooth = 0;
+ break;
+ case 't':
+ fputs("Warning: option -t is obsolete, use -Ot instead\n", stderr);
+ transform = 0;
+ break;
+ case 'd':
+ for(i=0; optarg[i]!=0; i++)
+ switch(optarg[i]) {
+ case 'a':
+ absolute = 1;
+ break;
+ case 'r':
+ reverse = 0;
+ break;
+ default:
+ if (optarg[i] != '?')
+ fprintf(stderr, "**** Unknown debugging option '%c' ****\n", optarg[i]);
+ fputs("The recognized debugging options are:\n", stderr);
+ fputs(" a - enable absolute coordinates\n", stderr);
+ fputs(" r - do not reverse font outlines directions\n", stderr);
+ exit(1);
+ break;
+ };
+ break;
+ case 'm':
+ {
+ char subopt;
+ int val;
+
+ if(sscanf(optarg, "%c=%d", &subopt, &val) !=2) {
+ fprintf(stderr, "**** Misformatted maximal limit ****\n");
+ fprintf(stderr, "spaces around the equal sign are not allowed\n");
+ fprintf(stderr, "good examples: -mh=100 -m h=100\n");
+ fprintf(stderr, "bad examples: -mh = 100 -mh= 100\n");
+ exit(1);
+ break;
+ }
+ switch(subopt) {
+ case 'h':
+ max_stemdepth = val;
+ break;
+ default:
+ if (subopt != '?')
+ fprintf(stderr, "**** Unknown limit type '%c' ****\n", subopt);
+ fputs("The recognized limit types are:\n", stderr);
+ fputs(" h - maximal hint stack depth in the PostScript interpreter\n", stderr);
+ exit(1);
+ break;
+ }
+ break;
+ }
+ case 'O':
+ {
+ char *p;
+ for(p=optarg; *p != 0; p++) {
+ if(set_subo(opotbl, *p) == NULL) { /* found no match */
+ if (*p != '?')
+ fprintf(stderr, "**** Unknown outline processing suboption '%c' ****\n", *p);
+ fprintf(stderr,"The general form of the outline processing option is:\n");
+ fprintf(stderr," -O suboptions\n");
+ fprintf(stderr,"(To remember easily -O may be also thought of as \"optimization\").\n");
+ print_subo_usage(stderr, opotbl);
+ fprintf(stderr, "The default state corresponds to the option -O ");
+ print_subo_dflt(stderr, opotbl);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ }
+ break;
+ }
+ case 'G':
+ {
+ char *p;
+ struct subo_case *s;
+
+ for(p=optarg; *p != 0; p++) {
+ if(( s = set_subo(fgotbl, *p) )==NULL) { /* found no match */
+ if (*p != '?')
+ fprintf(stderr, "**** Unknown outline processing suboption '%c' ****\n", *p);
+ fprintf(stderr,"The general form of the file generation option is:\n");
+ fprintf(stderr," -G suboptions\n");
+ print_subo_usage(stderr, fgotbl);
+ fprintf(stderr, "The default state corresponds to the option -G ");
+ print_subo_dflt(stderr, fgotbl);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "If the result is written to STDOUT, the last specified enabling suboption of -G\n");
+ fprintf(stderr, "selects the file to be written to STDOUT (the font file by default).\n");
+ exit(1);
+ }
+ if( *(s->valp) )
+ 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; j<MAXSUFFIX; j++)
+ if(frontswtab[i]->suffix[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; c<MAXUNIALIAS; c++)
+ if( uni_lang[i].alias[c]!=0
+ && !strncmp(uni_lang[i].alias[c], lang, strlen(uni_lang[i].alias[c])) ) {
+ uni_lang_selected = &uni_lang[i];
+ goto got_a_language;
+ }
+ }
+ got_a_language:
+ if(uni_lang_selected!=0) {
+ WARNING_1 fprintf(stderr, "Using language '%s' for Unicode fonts\n", uni_lang[i].name);
+ uni_sample = uni_lang[i].sample_upper;
+ }
+ }
+
+ /* try to guess the front-end parser by the file name suffix */
+ if(cursw==0) {
+ char *p = strrchr(argv[1], '.');
+ char *s;
+
+ if(p!=0 && (s = strdup(p+1))!=0) {
+ for(p=s; *p; p++)
+ *p = tolower(*p);
+ p = s;
+
+ for(i=0; frontswtab[i] != 0 && cursw == 0; i++) {
+ for(j=0; j<MAXSUFFIX; j++)
+ if(frontswtab[i]->suffix[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( ysz<correctvsize ) {
+ scale_factor *= (double)correctvsize / ysz;
+ }
+ }
+
+#ifdef XP_ONLY_BLOCKS
+ /* All glyphs from |xp_font_block_offset| to |xp_font_block_offset+255|*/
+ for( i = xp_font_block_offset ; i < (xp_font_block_offset+256) ; i++ ) {
+ unsigned long ftindex;
+
+ /* Remap X11 font index to FreeType font index */
+ ftindex = FTRemap(xp_pstext_ft_face, &xp_xtf->mapping, 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; i<numglyphs; i++) {
+ if( (glyph_list[i].flags & GF_USED)
+ && glyph_list[i].char_no == -1 ) {
+ print_glyph_metrics(-1, i);
+ }
+ }
+
+ fprintf(pfa_file, "readonly def\ncurrentdict end\ncurrentfile eexec\n");
+ fprintf(pfa_file, "dup /Private 16 dict dup begin\n");
+
+ fprintf(pfa_file, "/RD{string currentfile exch readstring pop}executeonly def\n");
+ fprintf(pfa_file, "/ND{noaccess def}executeonly def\n");
+ fprintf(pfa_file, "/NP{noaccess put}executeonly def\n");
+
+ /* UniqueID must be shown twice, in both font and Private dictionary */
+ if(wantuid) {
+ if(strUID)
+ fprintf(pfa_file, "/UniqueID %s def\n", strUID);
+ else
+ /* the range for private UIDs is 4 000 000 - 4 999 999 */
+ fprintf(pfa_file, "/UniqueID %lu def\n", numUID%1000000+4000000);
+ }
+
+ if(forcebold==0)
+ fprintf(pfa_file, "/ForceBold false def\n");
+ else if(forcebold==1)
+ fprintf(pfa_file, "/ForceBold true def\n");
+
+ fprintf(pfa_file, "/BlueValues [ ");
+ for (i = 0; i < nblues; i++)
+ fprintf(pfa_file, "%d ", bluevalues[i]);
+ fprintf(pfa_file, "] def\n");
+
+ fprintf(pfa_file, "/OtherBlues [ ");
+ for (i = 0; i < notherb; i++)
+ fprintf(pfa_file, "%d ", otherblues[i]);
+ fprintf(pfa_file, "] def\n");
+
+ if (stdhw != 0)
+ fprintf(pfa_file, "/StdHW [ %d ] def\n", stdhw);
+ if (stdvw != 0)
+ fprintf(pfa_file, "/StdVW [ %d ] def\n", stdvw);
+ fprintf(pfa_file, "/StemSnapH [ ");
+ for (i = 0; i < 12 && stemsnaph[i] != 0; i++)
+ fprintf(pfa_file, "%d ", stemsnaph[i]);
+ fprintf(pfa_file, "] def\n");
+ fprintf(pfa_file, "/StemSnapV [ ");
+ for (i = 0; i < 12 && stemsnapv[i] != 0; i++)
+ fprintf(pfa_file, "%d ", stemsnapv[i]);
+ fprintf(pfa_file, "] def\n");
+
+ fprintf(pfa_file, "/MinFeature {16 16} def\n");
+ /* Are these fixed also ? */
+ fprintf(pfa_file, "/password 5839 def\n");
+
+ /* calculate the number of subroutines */
+
+ subid=5;
+ for (i = 0; i < numglyphs; i++) {
+ if (glyph_list[i].flags & GF_USED) {
+ subid+=glyph_list[i].nsg;
+ }
+ }
+
+ fprintf(pfa_file, "/Subrs %d array\n", subid);
+ /* standard subroutines */
+ fprintf(pfa_file, "dup 0 {\n\t3 0 callothersubr pop pop setcurrentpoint return\n\t} NP\n");
+ fprintf(pfa_file, "dup 1 {\n\t0 1 callothersubr return\n\t} NP\n");
+ fprintf(pfa_file, "dup 2 {\n\t0 2 callothersubr return\n\t} NP\n");
+ fprintf(pfa_file, "dup 3 {\n\treturn\n\t} NP\n");
+ /* our sub to make the hint substitution code shorter */
+ fprintf(pfa_file, "dup 4 {\n\t1 3 callothersubr pop callsubr return\n\t} NP\n");
+
+ if(pfa_file != null_file) { /* save time if the output would be wasted */
+ /* print the hinting subroutines */
+ subid=5;
+ for (i = 0; i < numglyphs; i++) {
+ if (glyph_list[i].flags & GF_USED) {
+ subid+=print_glyph_subs(i, subid);
+ }
+ }
+
+ fprintf(pfa_file, "ND\n");
+
+ fprintf(pfa_file, "2 index /CharStrings %d dict dup begin\n", nchars);
+
+ for (i = 0; i < numglyphs; i++) {
+ if (glyph_list[i].flags & GF_USED) {
+ print_glyph(i);
+ }
+ }
+ }
+
+
+ fprintf(pfa_file, "end\nend\nreadonly put\n");
+ fprintf(pfa_file, "noaccess put\n");
+ fprintf(pfa_file, "dup/FontName get exch definefont pop\n");
+ fprintf(pfa_file, "mark currentfile closefile\n");
+ fprintf(pfa_file, "cleartomark\n");
+ if(pfa_file != null_file)
+ fclose(pfa_file);
+
+ fprintf(afm_file, "EndCharMetrics\n");
+
+ if(afm_file != null_file) { /* save time if the output would be wasted */
+ /* print the kerning data if present */
+ cursw->kerning(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;
+}
diff --git a/nx-X11/extras/ttf2pt1/ttf2pt1_convert.1 b/nx-X11/extras/ttf2pt1/ttf2pt1_convert.1
new file mode 100644
index 000000000..8e07150aa
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ttf2pt1_convert.1
@@ -0,0 +1,516 @@
+.rn '' }`
+''' $RCSfile: ttf2pt1_convert.1,v $$Revision: 1.2 $$Date: 2004/04/23 18:42:57 $
+'''
+''' $Log: ttf2pt1_convert.1,v $
+''' Revision 1.2 2004/04/23 18:42:57 eich
+''' 2004-04-23 Egbert Eich <eich@freedesktop.org>
+''' Merging XORG-CURRENT into trunk
+'''
+''' Revision 1.1.4.1 2004/04/21 10:03:13 gisburn
+''' Fix for http://pdx.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=530 - Land XPRINT branch on XORG-CURRENT
+'''
+''' 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<ttf2pt1_convert> - 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"
+
diff --git a/nx-X11/extras/ttf2pt1/ttf2pt1_x2gs.1 b/nx-X11/extras/ttf2pt1/ttf2pt1_x2gs.1
new file mode 100644
index 000000000..9ac5caad7
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/ttf2pt1_x2gs.1
@@ -0,0 +1,323 @@
+.rn '' }`
+''' $RCSfile: ttf2pt1_x2gs.1,v $$Revision: 1.2 $$Date: 2004/04/23 18:42:57 $
+'''
+''' $Log: ttf2pt1_x2gs.1,v $
+''' Revision 1.2 2004/04/23 18:42:57 eich
+''' 2004-04-23 Egbert Eich <eich@freedesktop.org>
+''' Merging XORG-CURRENT into trunk
+'''
+''' Revision 1.1.4.1 2004/04/21 10:03:13 gisburn
+''' Fix for http://pdx.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=530 - Land XPRINT branch on XORG-CURRENT
+'''
+''' 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<ttf2pt1_x2gs> - 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"
+
diff --git a/nx-X11/extras/ttf2pt1/version.h b/nx-X11/extras/ttf2pt1/version.h
new file mode 100644
index 000000000..d1b3525ba
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/version.h
@@ -0,0 +1,7 @@
+/*
+ * see COPYRIGHT
+ */
+
+
+/* version number */
+#define TTF2PT1_VERSION "3.4.4-SNAP-030526"
diff --git a/nx-X11/extras/ttf2pt1/winbuild.bat b/nx-X11/extras/ttf2pt1/winbuild.bat
new file mode 100644
index 000000000..0e8c09341
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/winbuild.bat
@@ -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
+
diff --git a/nx-X11/extras/ttf2pt1/windows.h b/nx-X11/extras/ttf2pt1/windows.h
new file mode 100644
index 000000000..a90ecf7e5
--- /dev/null
+++ b/nx-X11/extras/ttf2pt1/windows.h
@@ -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<nlen;n++) {
+ if (args[n] == ':') continue;
+ if (args[n] == nCmd) {
+ if (args[n+1]==':') {
+ char retVal;
+ retVal=*(argv[optind]+1);
+ optarg=argv[optind+1];
+ if (!optarg) optarg="";
+ optind+=2;
+ return retVal;
+ } else {
+ char retVal;
+ retVal=*(argv[optind]+1);
+ optarg=NULL;
+ optind+=1;
+ return retVal;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+#else
+
+unsigned short ntohs(unsigned short inv);
+unsigned long ntohl(unsigned long inv);
+unsigned long htonl(unsigned long inv);
+
+extern char *optarg;
+extern int optind;
+
+char getopt(int argc, char **argv, char *args);
+#endif