]> git.kernelconcepts.de Git - gbdfed.git/commitdiff
Initial import of upstream V1.6 from
authorNils Faerber <nils.faerber@dpin.de>
Sun, 5 May 2013 01:44:32 +0000 (03:44 +0200)
committerNils Faerber <nils.faerber@dpin.de>
Sun, 5 May 2013 01:44:32 +0000 (03:44 +0200)
http://sofia.nmsu.edu/~mleisher/Software/gbdfed/
by Mark Leisher

46 files changed:
GNUMakefile [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
bdf.c [new file with mode: 0644]
bdf.h [new file with mode: 0644]
bdfP.h [new file with mode: 0644]
bdfcons.c [new file with mode: 0644]
bdffnt.c [new file with mode: 0644]
bdfgname.c [new file with mode: 0644]
bdfgrab.c [new file with mode: 0644]
bdfgrid.c [new file with mode: 0644]
bdfotf.c [new file with mode: 0644]
bdfpkgf.c [new file with mode: 0644]
bdfpsf.c [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
fontgrid.c [new file with mode: 0644]
fontgrid.h [new file with mode: 0644]
gbdfed.c [new file with mode: 0644]
gbdfed.h [new file with mode: 0644]
gbdfed.man [new file with mode: 0644]
gectrl.c [new file with mode: 0644]
gectrl.h [new file with mode: 0644]
gectrlbmaps.h [new file with mode: 0644]
glyphedit.c [new file with mode: 0644]
glyphedit.h [new file with mode: 0644]
glyphtest.c [new file with mode: 0644]
glyphtest.h [new file with mode: 0644]
grayswatch.c [new file with mode: 0644]
grayswatch.h [new file with mode: 0644]
gtkcompat.h [new file with mode: 0644]
guiedit.c [new file with mode: 0644]
guifile.c [new file with mode: 0644]
guigedit.c [new file with mode: 0644]
guihelp.c [new file with mode: 0644]
guiops.c [new file with mode: 0644]
guipref.c [new file with mode: 0644]
guiutil.c [new file with mode: 0644]
hbf.c [new file with mode: 0644]
hbf.h [new file with mode: 0644]
htext.h [new file with mode: 0644]
labcon.c [new file with mode: 0644]
labcon.h [new file with mode: 0644]
mkinstalldirs [new file with mode: 0755]

diff --git a/GNUMakefile b/GNUMakefile
new file mode 100644 (file)
index 0000000..2b668e0
--- /dev/null
@@ -0,0 +1,78 @@
+CC = gcc
+CFLAGS = -Wall -g
+DEFS = -DHAVE_XLIB -DHAVE_HBF -DHAVE_FREETYPE -DG_DISABLE_DEPRECATED \
+       -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED \
+       -DGTK_DISABLE_DEPRECATED
+INCS = -I. `pkg-config gtk+-2.0 --cflags freetype2 --cflags`
+LIBS = `pkg-config gtk+-2.0 --libs freetype2 --libs`
+
+HBF=
+
+SRCS = bdf.c bdfcons.c bdffnt.c bdfgname.c bdfgrab.c bdfgrid.c bdfotf.c \
+       bdfpkgf.c bdfpsf.c $(HBF) \
+       labcon.c \
+       grayswatch.c \
+       glyphedit.c \
+       glyphtest.c \
+       fontgrid.c \
+       gectrl.c \
+       gbdfed.c \
+       guiedit.c \
+       guigedit.c \
+       guifile.c \
+       guihelp.c \
+       guiops.c \
+       guipref.c \
+       guiutil.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+all: gbdfed
+
+gbdfed: $(OBJS)
+       $(CC) $(STATIC) $(CFLAGS) $(OBJS) -o gbdfed $(LIBS)
+
+.c.o:
+       $(CC) $(CFLAGS) $(DEFS) $(INCS) -c $< -o $@
+
+clean:
+       /bin/rm -f *~ *BAK *CKP *.o
+
+distclean: clean
+       /bin/rm -f gbdfed
+
+realclean: distclean
+
+deps:
+       gcc -I. -MM $(SRCS) > deps
+
+#
+# Dependencies.
+#
+bdf.o: bdf.c bdfP.h bdf.h
+bdfcons.o: bdfcons.c bdfP.h bdf.h
+bdffnt.o: bdffnt.c bdfP.h bdf.h
+bdfgname.o: bdfgname.c bdfP.h bdf.h
+bdfgrab.o: bdfgrab.c bdfP.h bdf.h
+bdfgrid.o: bdfgrid.c bdfP.h bdf.h
+bdfotf.o: bdfotf.c
+bdfpkgf.o: bdfpkgf.c bdfP.h bdf.h
+bdfpsf.o: bdfpsf.c bdfP.h bdf.h
+hbf.o: hbf.c hbf.h
+labcon.o: labcon.c labcon.h
+grayswatch.o: grayswatch.c grayswatch.h
+glyphedit.o: glyphedit.c glyphedit.h bdfP.h bdf.h
+glyphtest.o: glyphtest.c glyphtest.h bdfP.h bdf.h
+fontgrid.o: fontgrid.c fontgrid.h bdfP.h bdf.h
+gectrl.o: gectrl.c gectrl.h bdfP.h bdf.h gectrlbmaps.h
+gbdfed.o: gbdfed.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h
+guiedit.o: guiedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \
+  labcon.h
+guigedit.o: guigedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \
+  glyphedit.h labcon.h gectrl.h
+guifile.o: guifile.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \
+  labcon.h
+guihelp.o: guihelp.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h htext.h
+guiops.o: guiops.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h
+guipref.o: guipref.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h
+guiutil.o: guiutil.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..b482958
--- /dev/null
@@ -0,0 +1,152 @@
+#
+# Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+RM = @RM@
+CP = @CP@
+MKINSTALLDIRS = ./mkinstalldirs
+
+CC = @CC@
+CFLAGS = @XX_CFLAGS@ @CFLAGS@
+
+DEFINES = @DEFINES@ -DG_DISABLE_DEPRECATED \
+       -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED \
+       -DGTK_DISABLE_DEPRECATED
+
+SRCS = bdf.c \
+       bdfcons.c \
+       bdffnt.c \
+       bdfgname.c \
+       @BDFGRABSRC@ \
+       bdfgrid.c \
+       bdfotf.c \
+       bdfpkgf.c \
+       bdfpsf.c \
+       fontgrid.c \
+       gbdfed.c \
+       gectrl.c \
+       glyphedit.c \
+       glyphtest.c \
+       grayswatch.c \
+       guiedit.c \
+       guifile.c \
+       guigedit.c \
+       guihelp.c \
+       guiops.c \
+       guipref.c \
+       guiutil.c \
+       @HBFSRC@ \
+       labcon.c
+
+OBJS = bdf.o \
+       bdfcons.o \
+       bdffnt.o \
+       bdfgname.o \
+       @BDFGRABOBJ@ \
+       bdfgrid.o \
+       bdfotf.o \
+       bdfpkgf.o \
+       bdfpsf.o \
+       fontgrid.o \
+       gbdfed.o \
+       gectrl.o \
+       glyphedit.o \
+       glyphtest.o \
+       grayswatch.o \
+       guiedit.o \
+       guifile.o \
+       guigedit.o \
+       guihelp.o \
+       guiops.o \
+       guipref.o \
+       guiutil.o \
+       @HBFOBJ@ \
+       labcon.o
+
+#
+# Point these at the FreeType source directories.
+#
+INCS = @CPPFLAGS@
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+datarootdir = @datarootdir@
+mandir = @mandir@
+
+all: gbdfed
+
+gbdfed: $(OBJS)
+       $(CC) $(STATIC) $(LDFLAGS) -o gbdfed $(OBJS) $(LIBS)
+
+clean:
+       $(RM) -f *.o *BAK *CKP *~ core
+
+realclean: clean
+       $(RM) -f gbdfed
+
+distclean: clean
+       $(RM) -rf gbdfed config.* Makefile autom4te.cache
+
+.c.o:
+       $(CC) $(DEFINES) $(CFLAGS) $(INCS) -c $< -o $@
+
+install: gbdfed
+       $(MKINSTALLDIRS) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1
+       $(CP) gbdfed $(DESTDIR)$(bindir)/gbdfed
+       $(CP) gbdfed.man $(DESTDIR)$(mandir)/man1/gbdfed.1
+
+uninstall:
+       $(RM) -f $(DESTDIR)$(bindir)/gbdfed
+       $(RM) -f $(DESTDIR)$(mandir)/man1/gbdfed.1
+
+#
+# Dependencies.
+#
+bdf.o: bdf.c bdfP.h bdf.h
+bdfcons.o: bdfcons.c bdfP.h bdf.h
+bdffnt.o: bdffnt.c bdfP.h bdf.h
+bdfgname.o: bdfgname.c bdfP.h bdf.h
+bdfgrab.o: bdfgrab.c bdfP.h bdf.h
+bdfgrid.o: bdfgrid.c bdfP.h bdf.h
+bdfotf.o: bdfotf.c
+bdfpkgf.o: bdfpkgf.c bdfP.h bdf.h
+bdfpsf.o: bdfpsf.c bdfP.h bdf.h
+fontgrid.o: fontgrid.c fontgrid.h bdfP.h bdf.h
+gbdfed.o: gbdfed.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h
+gectrl.o: gectrl.c gectrl.h bdfP.h bdf.h gectrlbmaps.h
+glyphedit.o: glyphedit.c glyphedit.h bdfP.h bdf.h
+glyphtest.o: glyphtest.c glyphtest.h bdfP.h bdf.h
+grayswatch.o: grayswatch.c grayswatch.h
+guiedit.o: guiedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h
+guifile.o: guifile.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h
+guigedit.o: guigedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \
+glyphedit.h labcon.h gectrl.h
+guihelp.o: guihelp.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h htext.h
+guiops.o: guiops.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h
+guipref.o: guipref.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h
+guiutil.o: guiutil.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h
+hbf.o: hbf.c hbf.h
+labcon.o: labcon.c labcon.h
+
+# end of Makefile
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..89ebedc
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,117 @@
+Changes from 1.5 to 1.6
+-----------------------
+1. Fixed a problem with implicit dynamic links which are becoming a no-no.
+
+2. Fixed a name collision with glib 2.10.
+
+3. Updated to work with GTK+ 2.20.
+
+Changes from 1.4 to 1.5
+-----------------------
+1. Fixed some documentation and Font Grid selection problems.
+
+2. Fixed a problem with the device width being unnecessarily adjusted before
+   editing glyphs in a proportional spacing font.
+
+3. Fixed a problem of not adding a SPACING property when the font spacing is
+   changed. This forces the correct spacing even if the font does not have an
+   XLFD value yet.
+
+4. Fixed a problem with deletions from the PSF mapping table not being
+   recorded.
+
+5. Removed some leftover deprecated sections.
+
+6. Added glyph navigation buttons to the GlyphEditor.
+
+Changes from 1.3 to 1.4
+-----------------------
+1. Changed last instance of FileSelection to FileChooser.
+
+2. Updated to work with GTK 2.12 (GtkTooltip).
+
+3. Fixed a problem when X11 is not available and bdfgrab.c won't compile as a
+   result.
+
+4. Updated Makefile.in to work with later versions of autoconf without
+   complaining.
+
+5. Improved the on-line documentation.
+
+6. Remove the _XMBDFED_INFO font property when fonts are loaded or grabbed
+   from the X server.
+
+7. Fixed a problem with renaming glyphs using the Adobe Glyph Name List.
+
+8. Fixed a warning about checking for a point in a NULL region.
+
+9. Fixed a crash that happened because some file filter objects were being
+   dereferenced when the import dialog was popped up multiple times.
+
+10. Added up to 10 recent fonts to the File menu.
+
+11. Changed the keyboard activate key for Exit from 'e' to 'x' and the
+    activate key for Export from 'x' to 'p'.
+
+12. Fixed a problem with fonts grabbed from the X server always being loaded
+    into the FontGrid that popped up the dialog.
+
+13. Fixed a couple problems with integer sign promotion affecting import of
+    OTF fonts.
+
+Changes from 1.2 to 1.3
+-----------------------
+1. Minor make file fix.
+
+2. Fixes for argument passing on 64-bit architectures.
+
+3. Changes to reduce the memory footprint on 64-bit architectures.
+
+Changes from 1.1 to 1.2
+-----------------------
+1. Some fixes for 64-bit systems.
+
+2. Some improvements in the help text.
+
+3. Added HBF support.
+
+Changes from 1.0 to 1.1
+-----------------------
+1. Fixed a problem opening other editors when multiple fonts are provided
+   on the command line.
+
+2. Fixed a memory allocation problem.
+
+3. Fixed a problem with setting and inverting pixels in the glyph editor.
+
+4. Added the missing Delete and BackSpace keys for deleting selections.
+
+5. Big improvements to the on-line help text. More readable now.
+
+Changes from 1.0 Beta to 1.0
+----------------------------
+1. Fixed a compilation error and a problem displaying the -1 encoding for the
+   unencoded pages.
+
+2. Changed several dialogs to make more regular use of the existing GTK dialog
+   facilities.
+
+3. Added a default icon list in 48x48, 32x32, and 16x16 order. (gbdfed.c)
+
+4. Added support for 2, 4 and 8 bits per pixel fonts back in.
+
+5. Reduced the size of an empty fontgrid. It looked too big. (fontgrid.c)
+
+6. Removed some unecessary code. (gbdfed.c)
+
+7. Disabled the cursor font preference until the automask generation code
+   and hotspot selection is done.
+
+8. Changed the fontgrid_new() function to accept a vararg list.
+
+9. Changed the glyphedit_new() function to accept a vararg list.
+
+10. Fixed a problem with the glyph image not being updated sometimes
+   (guigedit.c).
+
+11. Several dialog changes to work more naturally.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..6f8d0cc
--- /dev/null
+++ b/README
@@ -0,0 +1,262 @@
+gbdfed 1.6
+
+INTRO
+-----
+
+gbdfed is a GTK-based BDF font editor with the following features:
+
+  o  Multiple fonts can be loaded from the command line.
+  o  Multiple fonts can be open at the same time.
+  o  Cutting and pasting glyphs between fonts.
+  o  Multiple glyph bitmap editors can be open at the same time.
+  o  Cutting and pasting between glyph bitmap editors.
+  o  Export of XBM files from glyph bitmap editors.
+  o  Automatic correction of certain metrics when a font is loaded.
+  o  Generation of XLFD font names for fonts without XLFD names.
+  o  Update an XLFD font name from the font properties.
+  o  Update the font properties from an XLFD font name.
+  o  Font property editor.
+  o  Font comment editor.
+  o  Supports unencoded glyphs (ENCODING of -1).
+  o  Display of glyph encodings in octal, decimal, or hex.
+  o  Builtin on-line help.
+  o  Imports PK/GF fonts.
+  o  Imports HBF (Han Bitmap Font) fonts.
+  o  Imports Linux console fonts (PSF, CP, and FNT).
+  o  Imports Sun console fonts (vfont format).
+  o  Imports fonts from the X server.
+  o  Imports Windows FON/FNT fonts.
+  o  Imports OpenType/TrueType fonts and collections.
+  o  Exports PSF fonts.
+  o  Exports HEX fonts.
+
+A few things missing from this font editor:
+
+  o  No way to create space glyphs in monowidth or character cell fonts.
+  o  No support for scaling fonts.
+  o  Fonts with right-to-left direction (negative widths) not supported.
+  o  No way to edit comments appearing in the properties list.
+
+Known problems:
+
+  o  Selecting the original font editor from the "Windows" menu does not
+     deiconify, set focus, and place it on top like it should.
+  o  Deleting glyphs from or inserting glyphs into a font grid do not update
+     the glyph editors if they happen to be editing a deleted glyph or a glyph
+     that moved due to an insertion.
+
+COMMAND LINE OPTIONS
+--------------------
+  gbdfed [options] [font1 font2 ...]
+
+  -nc      - do not preserve comments
+  -nu      - do not preserve unencoded glyphs
+  -nm      - do not make metrics corrections
+  -np      - do not pad character-cell bitmaps
+  -bp      - allow blank pages
+  -ed      - do not present the "Really Exit?" dialog
+  -ps n    - set default point size
+  -hres n  - set default horizontal resolution
+  -vres n  - set default vertical resolution
+  -res n   - set both default resolutions
+  -sp s    - set font spacing ("p" for proportional, "m" for monospace,
+             or "c" for charcell).
+  -eol e   - set the default end-of-line char(s) ("u" for Unix, "d" for DOS,
+             or "m" for Mac).
+  -g code  - set the initial glyph code (in decimal, hex or octal).
+  -cb base - set the code base for display of glyph codes, can be "octal,"
+             "decimal," or "hexadecimal." The first letter of these names
+             work as well.
+
+  By default, gbdfed will set its point size to 12, the horizontal and
+  vertical resolution to that of the display (e.g. 90x90 dpi for Sun's), and
+  the font spacing to proportional.  Also by default, gbdfed will preserve
+  comments, preserve unencoded glyphs, and make metrics corrections when
+  loading fonts.
+
+COMPILING
+---------
+
+To build gbdfed, GTK 2.6 or greater is required. It may be that versions as
+early as 2.3 will work, but only various versions of 2.7 through 2.20 have been
+tested.
+
+Optional:
+
+  Freetype2 support is optional and can be found at:
+
+    http://www.freetype.org
+
+Step 1.
+
+  Run the "configure" script to generate the Makefile. By default, the program
+  installs in /usr/local. You can change that by using the --prefix option of
+  the "configure" script.
+
+  To disable the File->Import->X Server Font feature, use the --without-x
+  command line option to the "configure" script.
+
+Step 2.
+
+  Compile the program
+
+WARNINGS
+--------
+
+1. When compiling on HP/UX, the htext.h file has long concatenated strings that
+   require the -H option (noted by W. Chao).
+
+2. Compiling with the gcc -pedantic option complains about strings being too
+   long in the help text file. The help system will be changed in later
+   versions.
+
+ACKNOWLEDGEMENTS
+----------------
+
+Thanks go to the following people:
+
+  Ross Patterson for his HBF code.
+
+  der Mouse for his "getbdf" code.
+
+  K. Carothers and A. Korobka for their "fnt2bdf" code in Wine.
+
+  Mike Stroyan <mike_stroyan@fc.hp.com> for patches.
+
+  Primoz Peterlin <primoz.peterlin@biofiz.mf.uni-lj.si> for the man page and
+  some changes for building on HP/UX.
+
+  Danny Backx <u27113@kb.be> for the LessTif Imakefile.
+
+  Donald Page <donaldp@sco.com> for patches.
+
+  Michal Szymanski <msz@sirius.astrouw.edu.pl> for problem reports.
+
+  Werner Lemberg <a7971428@unet.univie.ac.at> for pointing out a problem
+  with the HBF code and other problem reports.
+
+  William F. Maton <wmaton@enterprise.ic.gc.ca> for pointing out a
+  problem with padding character cell fonts.
+
+  Ivan Nejgebauer <ian@uns.ns.ac.yu> for reporting a problem with glyph
+  names on imported console fonts.
+
+  Solofo <solofo@mpi-sb.mpg.de> for reporting a problem when creating an
+  XLFD name.  The old name was saved in the FONT property and some
+  versions of "mkfontdir" use that instead of the first FONT field.  Also
+  for recommending that the Ctrl+F4 accelerator be configurable if
+  it does not suit the user.
+
+  Dave Bodenstab <imdave@mcs.net> for providing a patch for a problem
+  with the HBF code that was not allowing gzipped HBF files to be
+  loaded, and a patch to get rid of some extraneous code.  Also a patch to fix
+  some size problems with GlyphEditors.
+
+  W. Chao <wchao@HRZ.Uni-Bielefeld.DE> for providing the Makefile changes
+  needed to compile on HP/UX and pointing out a problem with the builtin
+  documentation.
+
+  Andreas Reuter <ar205@bonzo.geowiss.nat.tu-bs.de> for pointing out a problem
+  with importing TrueType fonts.
+
+  Leonard Dickens <leonard@saul.hipgraphics.com> for providing the Makefile
+  changes needed for IRIX 6.3.
+
+  Markus Kuhn <Markus.Kuhn@cl.cam.ac.uk> for a handful of good suggestions.
+
+  Jim Knoble <jmknoble@pobox.com> for some geometry improvements in some
+  dialogs.
+
+  Darren Stuart Embry <dsembr01@ox.slug.louisville.edu> for the donation of
+  another HP/UX 10.20 X11R6 compilation setup.
+
+  Vladimir Volovich <vvv@vvv.vsu.ru> for pointing out something I forgot to
+  test.
+
+  Ben Fry <fry@media.mit.edu> for IRIX 6.5.2 variables for the Makefile.
+
+  J.H.M. Dassen (Ray) <jdassen@debian.org> for fixing bugs with the PK/GF
+  import feature.
+
+  Robert Brady <rwb197@ecs.soton.ac.uk> for pointing out a problem with the
+  length of _XFREE86_GLYPH_RANGES properties and an Exceed font compiler.
+
+  Stefan Monnier <monnier@cs.yale.edu> for a bug report on highlight thickness
+  of 0.
+
+  Humphrey Clerx <humphrey.clerx@eurocontrol.be> for pointing out some
+  compilation problem on Digit Unix 4.0 and some compilation problems with
+  Traditional C compilers.
+
+  Rudolf Cejka <cejkar@dcse.fee.vutbr.cz> for providing patches to fix
+  problems with grabbing fonts from the X server and alerting me to
+  uninitialized variable warnings.
+
+  Baruch Even <baruch@ev-en.org> for providing a fix for a bug that should
+  have shown up long ago dealing with font properties.
+
+  Sergey Vlasov <vsu@mivlgu.murom.ru> for pointing out a serious problem with
+  naming glyphs from the Unicode Character Database and for providing a fix
+  for a potential buffer overflow problem.
+
+  Daniel Neuburger <daniel.neuburger@lmco.com> for providing a patch that
+  fixes a display problem in the Font Grid.
+
+  Pierre HANSER <Pierre.Hanser@sxb.bsf.alcatel.fr> for a patch to fix a
+  problem loading font names that are not NULL terminated from the end of
+  FON/FNT files.
+
+  Patrick Hagglund <patrik.hagglund@bredband.net> for providing the patches to
+  use FreeType 2.
+
+  James Cloos <cloos@jhcloos.com> for finding problems with gbdfed.
+
+  Ming Hua <minghua@rice.edu> for locating a problem with editing glyphs.
+
+  Sergio Martins <smartins@students.dei.uc.pt> for finding a typo and several
+  dialog related bugs.
+
+  Viktor Urban <viktor@icc-atcsolutions.com> for locating a problem when
+  saving and moving to the next or previous glyph.
+
+  Jiri "BlueBear" Dluhos <modry.medved@seznam.cz> for producing crash fixes on
+  64-bit machines.
+
+  Jan Engelhardt <jengelh@linux01.gwdg.de> for providing an improvement on the
+  help text, a missing prototype, and an improvement in Makefile.in.
+
+  Daniel Richard G. <skunk@iSKUNK.ORG> for diagnosing problems on 64-bit
+  architectures.
+
+  Baruch Even <baruch@ev-en.org> for diagnosing problems on 64-bit
+  architectures.
+
+  Ming Hua <minghua.debian@gmail.com> for pointing out a warning about testing
+  a NULL region.
+
+  Ryan Hill <dirtyepic@gentoo.org> for pointing out a crashing problem with
+  filename filters when the import dialog was popped up multiple times.
+
+  Don Knuth for reporting problems with the documentation and a highlighting
+  problem (https://bugs.launchpad.net/ubuntu/+source/gbdfed/+bug/172836).
+
+  Tim Allen <screwtape@froup.com> for reporting an obscure bug with glyph
+  spacing which led to fixing another spacing related bug. Also for the idea
+  of preserving device width offsets.
+
+  Daniel Quarras <dqarras@yahoo.com> for discovering that deleting PSF
+  unicode map entries was being ignored.
+
+  Bertrand Janin <tamentis@neopulsar.org> for adding glyph navigation buttons
+  to the GlyphEditors.
+
+  Peter Volkov <pva@gentoo.org> for fixing a name collision with Glib 2.10.
+
+  Tom "spot" Callaway <tcallawa@redhat.com> for the configuration addition
+  that fixes a problem with implicit dynamic linking that showed up with newer
+  versions of gcc.
+
+AUTHOR
+------
+  Mark Leisher <mleisher@gmail.com>
+  15 April 2010
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..c707181
--- /dev/null
@@ -0,0 +1,56 @@
+dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
+dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
+dnl also defines GSTUFF_PKG_ERRORS on error
+AC_DEFUN(PKG_CHECK_MODULES, [
+  succeeded=no
+
+  if test -z "$PKG_CONFIG"; then
+    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  fi
+
+  if test "$PKG_CONFIG" = "no" ; then
+     echo "*** The pkg-config script could not be found. Make sure it is"
+     echo "*** in your path, or set the PKG_CONFIG environment variable"
+     echo "*** to the full path to pkg-config."
+     echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
+  else
+     PKG_CONFIG_MIN_VERSION=0.9.0
+     if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
+        AC_MSG_CHECKING(for $2)
+
+        if $PKG_CONFIG --exists "$2" ; then
+            AC_MSG_RESULT(yes)
+            succeeded=yes
+
+            AC_MSG_CHECKING($1_CFLAGS)
+            $1_CFLAGS=`$PKG_CONFIG --cflags "$2"`
+            AC_MSG_RESULT($$1_CFLAGS)
+
+            AC_MSG_CHECKING($1_LIBS)
+            $1_LIBS=`$PKG_CONFIG --libs "$2"`
+            AC_MSG_RESULT($$1_LIBS)
+        else
+            $1_CFLAGS=""
+            $1_LIBS=""
+            ## If we have a custom action on failure, don't print errors, but 
+            ## do set a variable so people can do so.
+            $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+            ifelse([$4], ,echo $$1_PKG_ERRORS,)
+        fi
+
+        AC_SUBST($1_CFLAGS)
+        AC_SUBST($1_LIBS)
+     else
+        echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
+        echo "*** See http://www.freedesktop.org/software/pkgconfig"
+     fi
+  fi
+
+  if test $succeeded = yes; then
+     ifelse([$3], , :, [$3])
+  else
+     ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4])
+  fi
+])
+
+
diff --git a/bdf.c b/bdf.c
new file mode 100644 (file)
index 0000000..cf0e9a7
--- /dev/null
+++ b/bdf.c
@@ -0,0 +1,7049 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "bdfP.h"
+
+#ifdef HAVE_HBF
+#include "hbf.h"
+#endif
+
+#undef MAX
+#define MAX(h, i) ((h) > (i) ? (h) : (i))
+
+#undef MIN
+#define MIN(l, o) ((l) < (o) ? (l) : (o))
+
+/**************************************************************************
+ *
+ * Masks used for checking different bits per pixel cases.
+ *
+ **************************************************************************/
+
+unsigned char bdf_onebpp[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+unsigned char bdf_twobpp[] = { 0xc0, 0x30, 0x0c, 0x03 };
+unsigned char bdf_fourbpp[] = { 0xf0, 0x0f };
+unsigned char bdf_eightbpp[] = { 0xff };
+
+/**************************************************************************
+ *
+ * Default BDF font options.
+ *
+ **************************************************************************/
+
+static bdf_options_t _bdf_opts = {
+#ifdef HAVE_FREETYPE
+    FT_LOAD_DEFAULT,     /* OTF flags - hinting on.        */
+#else
+    0,                   /* OTF flags                      */
+#endif /* HAVE_FREETYPE */
+    1,                   /* Correct metrics.               */
+    1,                   /* Preserve unencoded glyphs.     */
+    1,                   /* Preserve comments.             */
+    1,                   /* Pad character-cells.           */
+    BDF_PROPORTIONAL,    /* Default spacing.               */
+    12,                  /* Default point size.            */
+    0,                   /* Default horizontal resolution. */
+    0,                   /* Default vertical resolution.   */
+    1,                   /* Bits per pixel.                */
+    BDF_UNIX_EOL,        /* Line separator.                */
+    BDF_PSF_ALL,         /* PSF font export options.       */
+    0,                   /* An X cursor font.              */
+};
+
+/**************************************************************************
+ *
+ * Builtin BDF font properties.
+ *
+ **************************************************************************/
+
+/*
+ * List of most properties that might appear in a font.  Doesn't include the
+ * AXIS_* properties in X11R6 polymorphic fonts.
+ */
+static bdf_property_t _bdf_properties[] = {
+    {"ADD_STYLE_NAME",          BDF_ATOM,     1},
+    {"AVERAGE_WIDTH",           BDF_INTEGER,  1},
+    {"AVG_CAPITAL_WIDTH",       BDF_INTEGER,  1},
+    {"AVG_LOWERCASE_WIDTH",     BDF_INTEGER,  1},
+    {"CAP_HEIGHT",              BDF_INTEGER,  1},
+    {"CHARSET_COLLECTIONS",     BDF_ATOM,     1},
+    {"CHARSET_ENCODING",        BDF_ATOM,     1},
+    {"CHARSET_REGISTRY",        BDF_ATOM,     1},
+    {"COMMENT",                 BDF_ATOM,     1},
+    {"COPYRIGHT",               BDF_ATOM,     1},
+    {"DEFAULT_CHAR",            BDF_CARDINAL, 1},
+    {"DESTINATION",             BDF_CARDINAL, 1},
+    {"DEVICE_FONT_NAME",        BDF_ATOM,     1},
+    {"END_SPACE",               BDF_INTEGER,  1},
+    {"FACE_NAME",               BDF_ATOM,     1},
+    {"FAMILY_NAME",             BDF_ATOM,     1},
+    {"FIGURE_WIDTH",            BDF_INTEGER,  1},
+    {"FONT",                    BDF_ATOM,     1},
+    {"FONTNAME_REGISTRY",       BDF_ATOM,     1},
+    {"FONT_ASCENT",             BDF_INTEGER,  1},
+    {"FONT_DESCENT",            BDF_INTEGER,  1},
+    {"FOUNDRY",                 BDF_ATOM,     1},
+    {"FULL_NAME",               BDF_ATOM,     1},
+    {"ITALIC_ANGLE",            BDF_INTEGER,  1},
+    {"MAX_SPACE",               BDF_INTEGER,  1},
+    {"MIN_SPACE",               BDF_INTEGER,  1},
+    {"NORM_SPACE",              BDF_INTEGER,  1},
+    {"NOTICE",                  BDF_ATOM,     1},
+    {"PIXEL_SIZE",              BDF_INTEGER,  1},
+    {"POINT_SIZE",              BDF_INTEGER,  1},
+    {"QUAD_WIDTH",              BDF_INTEGER,  1},
+    {"RAW_ASCENT",              BDF_INTEGER,  1},
+    {"RAW_AVERAGE_WIDTH",       BDF_INTEGER,  1},
+    {"RAW_AVG_CAPITAL_WIDTH",   BDF_INTEGER,  1},
+    {"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER,  1},
+    {"RAW_CAP_HEIGHT",          BDF_INTEGER,  1},
+    {"RAW_DESCENT",             BDF_INTEGER,  1},
+    {"RAW_END_SPACE",           BDF_INTEGER,  1},
+    {"RAW_FIGURE_WIDTH",        BDF_INTEGER,  1},
+    {"RAW_MAX_SPACE",           BDF_INTEGER,  1},
+    {"RAW_MIN_SPACE",           BDF_INTEGER,  1},
+    {"RAW_NORM_SPACE",          BDF_INTEGER,  1},
+    {"RAW_PIXEL_SIZE",          BDF_INTEGER,  1},
+    {"RAW_POINT_SIZE",          BDF_INTEGER,  1},
+    {"RAW_PIXELSIZE",           BDF_INTEGER,  1},
+    {"RAW_POINTSIZE",           BDF_INTEGER,  1},
+    {"RAW_QUAD_WIDTH",          BDF_INTEGER,  1},
+    {"RAW_SMALL_CAP_SIZE",      BDF_INTEGER,  1},
+    {"RAW_STRIKEOUT_ASCENT",    BDF_INTEGER,  1},
+    {"RAW_STRIKEOUT_DESCENT",   BDF_INTEGER,  1},
+    {"RAW_SUBSCRIPT_SIZE",      BDF_INTEGER,  1},
+    {"RAW_SUBSCRIPT_X",         BDF_INTEGER,  1},
+    {"RAW_SUBSCRIPT_Y",         BDF_INTEGER,  1},
+    {"RAW_SUPERSCRIPT_SIZE",    BDF_INTEGER,  1},
+    {"RAW_SUPERSCRIPT_X",       BDF_INTEGER,  1},
+    {"RAW_SUPERSCRIPT_Y",       BDF_INTEGER,  1},
+    {"RAW_UNDERLINE_POSITION",  BDF_INTEGER,  1},
+    {"RAW_UNDERLINE_THICKNESS", BDF_INTEGER,  1},
+    {"RAW_X_HEIGHT",            BDF_INTEGER,  1},
+    {"RELATIVE_SETWIDTH",       BDF_CARDINAL, 1},
+    {"RELATIVE_WEIGHT",         BDF_CARDINAL, 1},
+    {"RESOLUTION",              BDF_INTEGER,  1},
+    {"RESOLUTION_X",            BDF_CARDINAL, 1},
+    {"RESOLUTION_Y",            BDF_CARDINAL, 1},
+    {"SETWIDTH_NAME",           BDF_ATOM,     1},
+    {"SLANT",                   BDF_ATOM,     1},
+    {"SMALL_CAP_SIZE",          BDF_INTEGER,  1},
+    {"SPACING",                 BDF_ATOM,     1},
+    {"STRIKEOUT_ASCENT",        BDF_INTEGER,  1},
+    {"STRIKEOUT_DESCENT",       BDF_INTEGER,  1},
+    {"SUBSCRIPT_SIZE",          BDF_INTEGER,  1},
+    {"SUBSCRIPT_X",             BDF_INTEGER,  1},
+    {"SUBSCRIPT_Y",             BDF_INTEGER,  1},
+    {"SUPERSCRIPT_SIZE",        BDF_INTEGER,  1},
+    {"SUPERSCRIPT_X",           BDF_INTEGER,  1},
+    {"SUPERSCRIPT_Y",           BDF_INTEGER,  1},
+    {"UNDERLINE_POSITION",      BDF_INTEGER,  1},
+    {"UNDERLINE_THICKNESS",     BDF_INTEGER,  1},
+    {"WEIGHT",                  BDF_CARDINAL, 1},
+    {"WEIGHT_NAME",             BDF_ATOM,     1},
+    {"X_HEIGHT",                BDF_INTEGER,  1},
+    {"_MULE_BASELINE_OFFSET",   BDF_INTEGER,  1},
+    {"_MULE_RELATIVE_COMPOSE",  BDF_INTEGER,  1},
+    /*
+     * Throw this in to make it clear.
+     */
+    {"_XMBDFED_INFO",           BDF_ATOM,     1},
+};
+
+static unsigned int _num_bdf_properties =
+sizeof(_bdf_properties) / sizeof(_bdf_properties[0]);
+
+/*
+ * User defined properties.
+ */
+static bdf_property_t *user_props;
+static unsigned int nuser_props = 0;
+
+/**************************************************************************
+ *
+ * Hash table utilities for the properties.
+ *
+ **************************************************************************/
+
+#define INITIAL_HT_SIZE 241
+
+typedef struct {
+    char *key;
+    void *data;
+} _hashnode, *hashnode;
+
+typedef struct {
+    int limit;
+    int size;
+    int used;
+    hashnode *table;
+} hashtable;
+
+typedef void (*hash_free_func)(hashnode node);
+
+static hashnode *
+hash_bucket(char *key, hashtable *ht)
+{
+    char *kp = key;
+    unsigned int res = 0;
+    hashnode *bp = ht->table, *ndp;
+
+    /*
+     * Mocklisp hash function.
+     */
+    while (*kp)
+      res = (res << 5) - res + *kp++;
+
+    ndp = bp + (res % ht->size);
+    while (*ndp) {
+        kp = (*ndp)->key;
+        if (kp[0] == key[0] && strcmp(kp, key) == 0)
+          break;
+        ndp--;
+        if (ndp < bp)
+          ndp = bp + (ht->size - 1);
+    }
+    return ndp;
+}
+
+static void
+hash_rehash(hashtable *ht)
+{
+    hashnode *obp = ht->table, *bp, *nbp;
+    int i, sz = ht->size;
+
+    ht->size <<= 1;
+    ht->limit = ht->size / 3;
+    ht->table = (hashnode *) malloc(sizeof(hashnode) * ht->size);
+    (void) memset((char *) ht->table, 0, sizeof(hashnode) * ht->size);
+
+    for (i = 0, bp = obp; i < sz; i++, bp++) {
+        if (*bp) {
+            nbp = hash_bucket((*bp)->key, ht);
+            *nbp = *bp;
+        }
+    }
+    free((char *) obp);
+}
+
+static void
+hash_init(hashtable *ht)
+{
+    int sz = INITIAL_HT_SIZE;
+
+    ht->size = sz;
+    ht->limit = sz / 3;
+    ht->used = 0;
+    ht->table = (hashnode *) malloc(sizeof(hashnode) * sz);
+    (void) memset((char *) ht->table, 0, sizeof(hashnode) * sz);
+}
+
+static void
+hash_free(hashtable *ht)
+{
+    int i, sz = ht->size;
+    hashnode *bp = ht->table;
+
+    for (i = 0; i < sz; i++, bp++) {
+        if (*bp)
+          free((char *) *bp);
+    }
+    if (sz > 0)
+      free((char *) ht->table);
+}
+
+static void
+hash_insert(char *key, void *data, hashtable *ht)
+{
+    hashnode nn, *bp = hash_bucket(key, ht);
+
+    nn = *bp;
+    if (!nn) {
+        *bp = nn = (hashnode) malloc(sizeof(_hashnode));
+        nn->key = key;
+        nn->data = data;
+
+        if (ht->used >= ht->limit)
+          hash_rehash(ht);
+        ht->used++;
+    } else
+      nn->data = data;
+}
+
+static hashnode
+hash_lookup(char *key, hashtable *ht)
+{
+    hashnode *np = hash_bucket(key, ht);
+    return *np;
+}
+
+static void
+hash_delete(char *name, hashtable *ht)
+{
+    hashnode *hp;
+
+    hp = hash_bucket(name, ht);
+    if (*hp) {
+        free((char *) *hp);
+        *hp = 0;
+    }
+}
+
+/*
+ * The builtin property table.
+ */
+static hashtable proptbl;
+
+/**************************************************************************
+ *
+ * Utility types and functions.
+ *
+ **************************************************************************/
+
+/*
+ * Function type for parsing lines of a BDF font.
+ */
+typedef int (*_bdf_line_func_t)(
+    char *line,
+    unsigned int linelen,
+    unsigned int lineno,
+    void *call_data,
+    void *client_data
+);
+
+/*
+ * List structure for splitting lines into fields.
+ */
+typedef struct {
+    char **field;
+    unsigned int size;
+    unsigned int used;
+    char *bfield;
+    unsigned int bsize;
+    unsigned int bused;
+} _bdf_list_t;
+
+/*
+ * Structure used while loading BDF fonts.
+ */
+typedef struct {
+    unsigned int flags;
+    unsigned int cnt;
+    unsigned int row;
+    unsigned int bpr;
+    short minlb;
+    short maxlb;
+    short maxrb;
+    short maxas;
+    short maxds;
+    short rbearing;
+    char *glyph_name;
+    int glyph_enc;
+    bdf_font_t *font;
+    bdf_options_t *opts;
+    void *client_data;
+    bdf_callback_t callback;
+    bdf_callback_struct_t cb;
+    unsigned int have[2048];
+    _bdf_list_t list;
+} _bdf_parse_t;
+
+#define setsbit(m, cc) (m[(cc) >> 3] |= (1 << ((cc) & 7)))
+#define sbitset(m, cc) (m[(cc) >> 3] & (1 << ((cc) & 7)))
+
+/*
+ * An empty string for empty fields.
+ */
+static char empty[1] = { 0 };
+
+/*
+ * Assume the line is NULL terminated and that the `list' parameter was
+ * initialized the first time it was used.
+ */
+static void
+_bdf_split(char *separators, char *line, unsigned int linelen,
+           _bdf_list_t *list)
+{
+    int mult, final_empty;
+    char *sp, *ep, *end;
+    unsigned char seps[32];
+
+    /*
+     * Initialize the list.
+     */
+    list->used = list->bused = 0;
+
+    /*
+     * If the line is empty, then simply return.
+     */
+    if (linelen == 0 || line[0] == 0)
+      return;
+
+    /*
+     * If the `separators' parameter is NULL or empty, split the list into
+     * individual bytes.
+     */
+    if (separators == 0 || *separators == 0) {
+        if (linelen > list->bsize) {
+            if (list->bsize)
+              list->bfield = (char *) malloc(linelen);
+            else
+              list->bfield = (char *) realloc(list->bfield, linelen);
+            list->bsize = linelen;
+        }
+        list->bused = linelen;
+        (void) memcpy(list->bfield, line, linelen);
+        return;
+    }
+
+    /*
+     * Prepare the separator bitmap.
+     */
+    (void) memset((char *) seps, 0, 32);
+
+    /*
+     * If the very last character of the separator string is a plus, then set
+     * the `mult' flag to indicate that multiple separators should be
+     * collapsed into one.
+     */
+    for (mult = 0, sp = separators; sp && *sp; sp++) {
+        if (*sp == '+' && *(sp + 1) == 0)
+          mult = 1;
+        else
+          setsbit(seps, *sp);
+    }
+
+    /*
+     * Break the line up into fields.
+     */
+    for (final_empty = 0, sp = ep = line, end = sp + linelen;
+         sp < end && *sp;) {
+        /*
+         * Collect everything that is not a separator.
+         */
+        for (; *ep && !sbitset(seps, *ep); ep++) ;
+
+        /*
+         * Resize the list if necessary.
+         */
+        if (list->used == list->size) {
+            if (list->size == 0)
+              list->field = (char **) malloc(sizeof(char *) * 5);
+            else
+              list->field = (char **)
+                  realloc((char *) list->field,
+                          sizeof(char *) * (list->size + 5));
+
+            list->size += 5;
+        }
+
+        /*
+         * Assign the field appropriately.
+         */
+        list->field[list->used++] = (ep > sp) ? sp : empty;
+
+        sp = ep;
+        if (mult) {
+            /*
+             * If multiple separators should be collapsed, do it now by
+             * setting all the separator characters to 0.
+             */
+            for (; *ep && sbitset(seps, *ep); ep++)
+              *ep = 0;
+        } else if (*ep != 0)
+          /*
+           * Don't collapse multiple separators by making them 0, so just
+           * make the one encountered 0.
+           */
+          *ep++ = 0;
+        final_empty = (ep > sp && *ep == 0);
+        sp = ep;
+    }
+
+    /*
+     * Finally, NULL terminate the list.
+     */
+    if (list->used + final_empty + 1 >= list->size) {
+        if (list->used == list->size) {
+            if (list->size == 0)
+              list->field = (char **) malloc(sizeof(char *) * 5);
+            else
+              list->field = (char **)
+                  realloc((char *) list->field,
+                          sizeof(char *) * (list->size + 5));
+            list->size += 5;
+        }
+    }
+    if (final_empty)
+      list->field[list->used++] = empty;
+
+    if (list->used == list->size) {
+        if (list->size == 0)
+          list->field = (char **) malloc(sizeof(char *) * 5);
+        else
+          list->field = (char **)
+              realloc((char *) list->field,
+                      sizeof(char *) * (list->size + 5));
+        list->size += 5;
+    }
+    list->field[list->used] = 0;
+}
+
+static void
+_bdf_shift(unsigned int n, _bdf_list_t *list)
+{
+    unsigned int i, u;
+
+    if (list == 0 || list->used == 0 || n == 0)
+      return;
+
+    if (n >= list->used) {
+        list->used = 0;
+        return;
+    }
+    for (u = n, i = 0; u < list->used; i++, u++)
+      list->field[i] = list->field[u];
+    list->used -= n;
+}
+
+static char *
+_bdf_join(int c, unsigned int *len, _bdf_list_t *list)
+{
+    unsigned int i, j;
+    char *fp, *dp;
+
+    if (list == 0 || list->used == 0)
+      return 0;
+
+    *len = 0;
+
+    dp = list->field[0];
+    for (i = j = 0; i < list->used; i++) {
+        fp = list->field[i];
+        while (*fp)
+          dp[j++] = *fp++;
+        if (i + 1 < list->used)
+          dp[j++] = c;
+    }
+    dp[j] = 0;
+
+    *len = j;
+    return dp;
+}
+
+/*
+ * High speed file reader that passes each line to a callback.
+ */
+static int
+_bdf_readlines(int fd, _bdf_line_func_t callback, void *client_data,
+               unsigned int *lno)
+{
+    _bdf_line_func_t cb;
+    unsigned int lineno;
+    int n, res, done, refill, bytes, hold;
+    char *ls, *le, *pp, *pe, *hp;
+    char buf[65536];
+
+    if (callback == 0)
+      return -1;
+
+    cb = callback;
+    lineno = 1;
+    buf[0] = 0;
+    res = done = 0;
+    pp = ls = le = buf;
+    bytes = 65536;
+    while (!done && (n = read(fd, pp, bytes)) > 0) {
+        /*
+         * Determine the new end of the buffer pages.
+         */
+        pe = pp + n;
+
+        for (refill = 0; done == 0 && refill == 0; ) {
+            while (le < pe && *le != '\n' && *le != '\r')
+              le++;
+
+            if (le == pe) {
+                /*
+                 * Hit the end of the last page in the buffer.  Need to find
+                 * out how many pages to shift and how many pages need to be
+                 * read in.  Adjust the line start and end pointers down to
+                 * point to the right places in the pages.
+                 */
+                pp = buf + (((ls - buf) >> 13) << 13);
+                n = pp - buf;
+                ls -= n;
+                le -= n;
+                n = pe - pp;
+                memmove(buf, pp, n);
+#if 0
+                memcpy(buf, pp, n);
+#endif
+                pp = buf + n;
+                bytes = 65536 - n;
+                refill = 1;
+            } else {
+                /*
+                 * Temporarily NULL terminate the line.
+                 */
+                hp = le;
+                hold = *le;
+                *le = 0;
+
+                if (callback && *ls != '#' && *ls != 0x1a && le > ls &&
+                    (res = (*cb)(ls, le - ls, lineno, (void *) &cb,
+                                 client_data)) != 0)
+                  done = 1;
+                else {
+                    ls = ++le;
+                    /*
+                     * Handle the case of DOS crlf sequences.
+                     */
+                    if (le < pe && hold == '\n' && *le =='\r')
+                      ls = ++le;
+                }
+
+                /*
+                 * Increment the line number.
+                 */
+                lineno++;
+
+                /*
+                 * Restore the character at the end of the line.
+                 */
+                *hp = hold;
+            }
+        }
+    }
+    *lno = lineno;
+    return res;
+}
+
+unsigned char *
+_bdf_strdup(unsigned char *s, unsigned int len)
+{
+    unsigned char *ns;
+
+    if (s == 0 || len == 0)
+      return 0;
+
+    ns = (unsigned char *) malloc(len);
+    (void) memcpy((char *) ns, (char *) s, len);
+    return ns;
+}
+
+void
+_bdf_memmove(char *dest, char *src, unsigned int bytes)
+{
+    int i, j;
+
+    i = (int) bytes;
+    j = i & 7;
+    i = (i + 7) >> 3;
+
+    /*
+     * Do a memmove using Ye Olde Duff's Device for efficiency.
+     */
+    if (src < dest) {
+        src += bytes;
+        dest += bytes;
+
+        switch (j) {
+          case 0: do {
+              *--dest = *--src;
+            case 7: *--dest = *--src;
+            case 6: *--dest = *--src;
+            case 5: *--dest = *--src;
+            case 4: *--dest = *--src;
+            case 3: *--dest = *--src;
+            case 2: *--dest = *--src;
+            case 1: *--dest = *--src;
+          } while (--i > 0);
+        }
+    } else if (src > dest) {
+        switch (j) {
+          case 0: do {
+              *dest++ = *src++;
+            case 7: *dest++ = *src++;
+            case 6: *dest++ = *src++;
+            case 5: *dest++ = *src++;
+            case 4: *dest++ = *src++;
+            case 3: *dest++ = *src++;
+            case 2: *dest++ = *src++;
+            case 1: *dest++ = *src++;
+          } while (--i > 0);
+        }
+    }
+}
+
+static unsigned char a2i[128] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char odigits[32] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static unsigned char ddigits[32] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static unsigned char hdigits[32] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
+    0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+#define isdigok(m, d) (m[(d) >> 3] & (1 << ((d) & 7)))
+
+/*
+ * Routine to convert an ASCII string into an unsigned int integer.
+ */
+unsigned int
+_bdf_atoul(char *s, char **end, int base)
+{
+    unsigned int v;
+    unsigned char *dmap;
+
+    if (s == 0 || *s == 0)
+      return 0;
+
+    /*
+     * Make sure the radix is something recognizable.  Default to 10.
+     */
+    switch (base) {
+      case 8: dmap = odigits; break;
+      case 16: dmap = hdigits; break;
+      default: base = 10; dmap = ddigits; break;
+    }
+
+    /*
+     * Check for the special hex prefixes of 0[xX] or [Uu][+-].
+     */
+    if ((*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) ||
+        ((*s == 'U' || *s == 'u') && (*(s + 1) == '+' || *(s + 1) == '-'))) {
+        base = 16;
+        dmap = hdigits;
+        s += 2;
+    }
+
+    for (v = 0; isdigok(dmap, *s); s++)
+      v = (v * base) + a2i[(int) *s];
+
+    if (end != 0)
+      *end = s;
+
+    return v;
+}
+
+/*
+ * Routine to convert an ASCII string into an signed int integer.
+ */
+int
+_bdf_atol(char *s, char **end, int base)
+{
+    int v, neg;
+    unsigned char *dmap;
+
+    if (s == 0 || *s == 0)
+      return 0;
+
+    /*
+     * Make sure the radix is something recognizable.  Default to 10.
+     */
+    switch (base) {
+      case 8: dmap = odigits; break;
+      case 16: dmap = hdigits; break;
+      default: base = 10; dmap = ddigits; break;
+    }
+
+    /*
+     * Check for a minus sign.
+     */
+    neg = 0;
+    if (*s == '-') {
+        s++;
+        neg = 1;
+    }
+
+    /*
+     * Check for the special hex prefix.
+     */
+    if ((*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) ||
+        ((*s == 'U' || *s == 'u') && (*(s + 1) == '+' || *(s + 1) == '-'))) {
+        base = 16;
+        dmap = hdigits;
+        s += 2;
+    }
+
+    for (v = 0; isdigok(dmap, *s); s++)
+      v = (v * base) + a2i[(int) *s];
+
+    if (end != 0)
+      *end = s;
+
+    return (!neg) ? v : -v;
+}
+
+/*
+ * Routine to convert an ASCII string into an signed short integer.
+ */
+short
+_bdf_atos(char *s, char **end, int base)
+{
+    short v, neg;
+    unsigned char *dmap;
+
+    if (s == 0 || *s == 0)
+      return 0;
+
+    /*
+     * Make sure the radix is something recognizable.  Default to 10.
+     */
+    switch (base) {
+      case 8: dmap = odigits; break;
+      case 16: dmap = hdigits; break;
+      default: base = 10; dmap = ddigits; break;
+    }
+
+    /*
+     * Check for a minus.
+     */
+    neg = 0;
+    if (*s == '-') {
+        s++;
+        neg = 1;
+    }
+
+    /*
+     * Check for the special hex prefix.
+     */
+    if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) {
+        base = 16;
+        dmap = hdigits;
+        s += 2;
+    }
+
+    for (v = 0; isdigok(dmap, *s); s++)
+      v = (v * base) + a2i[(int) *s];
+
+    if (end != 0)
+      *end = s;
+
+    return (!neg) ? v : -v;
+}
+
+/*
+ * Routine to compare two glyphs by encoding so they can be sorted.
+ */
+static int
+by_encoding(const void *a, const void *b)
+{
+    bdf_glyph_t *c1, *c2;
+
+    c1 = (bdf_glyph_t *) a;
+    c2 = (bdf_glyph_t *) b;
+    if (c1->encoding < c2->encoding)
+      return -1;
+    else if (c1->encoding > c2->encoding)
+      return 1;
+    return 0;
+}
+
+/**************************************************************************
+ *
+ * BDF font file parsing flags and functions.
+ *
+ **************************************************************************/
+
+/*
+ * Parse flags.
+ */
+#define _BDF_START     0x0001
+#define _BDF_FONT_NAME 0x0002
+#define _BDF_SIZE      0x0004
+#define _BDF_FONT_BBX  0x0008
+#define _BDF_PROPS     0x0010
+#define _BDF_GLYPHS    0x0020
+#define _BDF_GLYPH     0x0040
+#define _BDF_ENCODING  0x0080
+#define _BDF_SWIDTH    0x0100
+#define _BDF_DWIDTH    0x0200
+#define _BDF_BBX       0x0400
+#define _BDF_BITMAP    0x0800
+
+#define _BDF_SWIDTH_ADJ 0x1000
+
+#define _BDF_GLYPH_BITS (_BDF_GLYPH|_BDF_ENCODING|_BDF_SWIDTH|\
+                         _BDF_DWIDTH|_BDF_BBX|_BDF_BITMAP)
+
+#define _BDF_GLYPH_WIDTH_CHECK 0x40000000
+#define _BDF_GLYPH_HEIGHT_CHECK 0x80000000
+
+/*
+ * Auto correction messages.
+ */
+#define ACMSG1 "FONT_ASCENT property missing.  Added \"FONT_ASCENT %hd\"."
+#define ACMSG2 "FONT_DESCENT property missing.  Added \"FONT_DESCENT %hd\"."
+#define ACMSG3 "Font width != actual width.  Old: %hd New: %hd."
+#define ACMSG4 "Font left bearing != actual left bearing.  Old: %hd New: %hd."
+#define ACMSG5 "Font ascent != actual ascent.  Old: %hd New: %hd."
+#define ACMSG6 "Font descent != actual descent.  Old: %hd New: %hd."
+#define ACMSG7 "Font height != actual height. Old: %hd New: %hd."
+#define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made."
+#define ACMSG9 "SWIDTH field missing at line %d.  Set automatically."
+#define ACMSG10 "DWIDTH field missing at line %d.  Set to glyph width."
+#define ACMSG11 "SIZE bits per pixel field adjusted to %hd."
+#define ACMSG12 "Duplicate encoding %d (%s) changed to unencoded."
+#define ACMSG13 "Glyph %d extra rows removed."
+#define ACMSG14 "Glyph %d extra columns removed."
+#define ACMSG15 "Incorrect glyph count: %d indicated but %d found."
+
+/*
+ * Error messages.
+ */
+#define ERRMSG1 "[line %d] Missing \"%s\" line."
+#define ERRMSG2 "[line %d] Font header corrupted or missing fields."
+#define ERRMSG3 "[line %d] Font glyphs corrupted or missing fields."
+
+void
+_bdf_add_acmsg(bdf_font_t *font, char *msg, unsigned int len)
+{
+    char *cp;
+
+    if (font->acmsgs_len == 0)
+      font->acmsgs = (char *) malloc(len + 2);
+    else
+      font->acmsgs = (char *) realloc(font->acmsgs,
+                                      font->acmsgs_len + len + 2);
+
+    cp = font->acmsgs + font->acmsgs_len;
+    (void) memcpy(cp, msg, len);
+    cp += len;
+    *cp++ = '\n';
+    *cp = 0;
+    font->acmsgs_len += len + 1;
+}
+
+void
+_bdf_add_comment(bdf_font_t *font, char *comment, unsigned int len)
+{
+    char *cp;
+
+    if (font->comments_len == 0)
+      font->comments = (char *) malloc(len + 2);
+    else
+      font->comments = (char *) realloc(font->comments,
+                                        font->comments_len + len + 2);
+
+    cp = font->comments + font->comments_len;
+    (void) memcpy(cp, comment, len);
+    cp += len;
+    *cp++ = '\n';
+    *cp = 0;
+    font->comments_len += len + 1;
+}
+
+/*
+ * Set the spacing from the font name if it exists, or set it to the default
+ * specified in the options.
+ */
+static void
+_bdf_set_default_spacing(bdf_font_t *font, bdf_options_t *opts)
+{
+    unsigned int len;
+    char name[128];
+    _bdf_list_t list;
+
+    if (font == 0 || font->name == 0 || font->name[0] == 0)
+      return;
+
+    font->spacing = opts->font_spacing;
+
+    len = (unsigned int) (strlen(font->name) + 1);
+    (void) memcpy(name, font->name, len);
+    list.size = list.used = 0;
+    _bdf_split("-", name, len, &list);
+    if (list.used == 15) {
+        switch (list.field[11][0]) {
+          case 'C': case 'c': font->spacing = BDF_CHARCELL; break;
+          case 'M': case 'm': font->spacing = BDF_MONOWIDTH; break;
+          case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break;
+        }
+    }
+    if (list.size > 0)
+      free((char *) list.field);
+}
+
+/*
+ * Determine if the property is an atom or not.  If it is, then clean it up so
+ * the double quotes are removed if they exist.
+ */
+static int
+_bdf_is_atom(char *line, unsigned int linelen, char **name, char **value)
+{
+    int hold;
+    char *sp, *ep;
+    bdf_property_t *p;
+
+    *name = sp = ep = line;
+    while (*ep && *ep != ' ' && *ep != '\t')
+      ep++;
+
+    hold = -1;
+    if (*ep) {
+        hold = *ep;
+        *ep = 0;
+    }
+
+    p = bdf_get_property(sp);
+
+    /*
+     * Restore the character that was saved before any return can happen.
+     */
+    if (hold != -1)
+      *ep = hold;
+
+    /*
+     * If the propert exists and is not an atom, just return here.
+     */
+    if (p && p->format != BDF_ATOM)
+      return 0;
+
+    /*
+     * The property is an atom.  Trim all leading and trailing whitespace and
+     * double quotes for the atom value.
+     */
+    sp = ep;
+    ep = line + linelen;
+
+    /*
+     * Trim the leading whitespace if it exists.
+     */
+    *sp++ = 0;
+    while (*sp && (*sp == ' ' || *sp == '\t'))
+      sp++;
+
+    /*
+     * Trim the leading double quote if it exists.
+     */
+    if (*sp == '"')
+      sp++;
+    *value = sp;
+
+    /*
+     * Trim the trailing whitespace if it exists.
+     */
+    while (ep > sp && (*(ep - 1) == ' ' || *(ep - 1) == '\t'))
+      *--ep = 0;
+
+    /*
+     * Trim the trailing double quote if it exists.
+     */
+    if (ep > sp && *(ep - 1) == '"')
+      *--ep = 0;
+
+    return 1;
+}
+
+static void
+_bdf_add_property(bdf_font_t *font, char *name, char *value)
+{
+    unsigned int propid;
+    hashnode hn;
+    int len;
+    bdf_property_t *prop, *fp;
+
+    /*
+     * First, check to see if the property already exists in the font.
+     */
+    if ((hn = hash_lookup(name, (hashtable *) font->internal)) != 0) {
+        /*
+         * The property already exists in the font, so simply replace
+         * the value of the property with the current value.
+         */
+        fp = font->props + (unsigned int) hn->data;
+
+        switch (fp->format) {
+          case BDF_ATOM:
+            /*
+             * Delete the current atom if it exists.
+             */
+            if (fp->value.atom != 0)
+              free(fp->value.atom);
+
+            if (value == 0)
+              len = 1;
+            else
+              len = strlen(value) + 1;
+            if (len > 1) {
+                fp->value.atom = (char *) malloc(len);
+                (void) memcpy(fp->value.atom, value, len);
+            } else
+              fp->value.atom = 0;
+            break;
+          case BDF_INTEGER:
+            fp->value.int32 = _bdf_atol(value, 0, 10);
+            break;
+          case BDF_CARDINAL:
+            fp->value.card32 = _bdf_atoul(value, 0, 10);
+            break;
+        }
+        return;
+    }
+
+    /*
+     * See if this property type exists yet or not.  If not, create it.
+     */
+    hn = hash_lookup(name, &proptbl);
+    if (hn == 0) {
+        bdf_create_property(name, BDF_ATOM);
+        hn = hash_lookup(name, &proptbl);
+    }
+
+    /*
+     * Allocate another property if this is overflow.
+     */
+    if (font->props_used == font->props_size) {
+        if (font->props_size == 0)
+          font->props = (bdf_property_t *) malloc(sizeof(bdf_property_t));
+        else
+          font->props = (bdf_property_t *)
+              realloc((char *) font->props, sizeof(bdf_property_t) *
+                      (font->props_size + 1));
+        fp = font->props + font->props_size;
+        (void) memset((char *) fp, 0, sizeof(bdf_property_t));
+        font->props_size++;
+    }
+
+    propid = (unsigned int) hn->data;
+    if (propid >= _num_bdf_properties)
+      prop = user_props + (propid - _num_bdf_properties);
+    else
+      prop = _bdf_properties + propid;
+
+    fp = font->props + font->props_used;
+
+    fp->name = prop->name;
+    fp->format = prop->format;
+    fp->builtin = prop->builtin;
+
+    switch (prop->format) {
+      case BDF_ATOM:
+        if (value == 0)
+          len = 1;
+        else
+          len = strlen(value) + 1;
+        if (len > 1) {
+            fp->value.atom = (char *) malloc(len);
+            (void) memcpy(fp->value.atom, value, len);
+        } else
+          fp->value.atom = 0;
+        break;
+      case BDF_INTEGER:
+        fp->value.int32 = _bdf_atol(value, 0, 10);
+        break;
+      case BDF_CARDINAL:
+        fp->value.card32 = _bdf_atoul(value, 0, 10);
+        break;
+    }
+
+    /*
+     * If the property happens to be a comment, then it doesn't need
+     * to be added to the internal hash table.
+     */
+    if (memcmp(name, "COMMENT", 7) != 0)
+      /*
+       * Add the property to the font property table.
+       */
+      hash_insert(fp->name, (void *) font->props_used,
+                  (hashtable *) font->internal);
+
+    font->props_used++;
+
+    /*
+     * Some special cases need to be handled here.  The DEFAULT_CHAR property
+     * needs to be located if it exists in the property list, the FONT_ASCENT
+     * and FONT_DESCENT need to be assigned if they are present, and the
+     * SPACING property should override the default spacing.
+     */
+    if (memcmp(name, "DEFAULT_CHAR", 12) == 0)
+      font->default_glyph = fp->value.int32;
+    else if (memcmp(name, "FONT_ASCENT", 11) == 0)
+      font->font_ascent = fp->value.int32;
+    else if (memcmp(name, "FONT_DESCENT", 12) == 0)
+      font->font_descent = fp->value.int32;
+    else if (memcmp(name, "SPACING", 7) == 0) {
+        if (fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P')
+          font->spacing = BDF_PROPORTIONAL;
+        else if (fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M')
+          font->spacing = BDF_MONOWIDTH;
+        else if (fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C')
+          font->spacing = BDF_CHARCELL;
+    }
+}
+
+/*
+ * Actually parse the glyph info and bitmaps.
+ */
+static int
+_bdf_parse_glyphs(char *line, unsigned int linelen, unsigned int lineno,
+                  void *call_data, void *client_data)
+{
+    int c;
+    char *s;
+    unsigned char *bp;
+    unsigned int i, slen = 0, nibbles;
+    double ps, rx, dw, sw;
+    _bdf_line_func_t *next;
+    _bdf_parse_t *p;
+    bdf_glyph_t *glyph;
+    bdf_font_t *font;
+    char nbuf[128];
+
+    next = (_bdf_line_func_t *) call_data;
+    p = (_bdf_parse_t *) client_data;
+
+    font = p->font;
+
+    /*
+     * Check for a comment.
+     */
+    if (memcmp(line, "COMMENT", 7) == 0) {
+        linelen -= 7;
+        s = line + 7;
+        if (*s != 0) {
+            s++;
+            linelen--;
+        }
+        _bdf_add_comment(p->font, s, linelen);
+        return 0;
+    }
+
+    /*
+     * The very first thing expected is the number of glyphs.
+     */
+    if (!(p->flags & _BDF_GLYPHS)) {
+        if (memcmp(line, "CHARS", 5) != 0) {
+            sprintf(nbuf, ERRMSG1, lineno, "CHARS");
+            _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+            return BDF_MISSING_CHARS;
+        }
+        _bdf_split(" +", line, linelen, &p->list);
+        p->cnt = font->glyphs_size = _bdf_atoul(p->list.field[1], 0, 10);
+
+        /*
+         * Make sure the number of glyphs is non-zero.
+         */
+        if (p->cnt == 0)
+          font->glyphs_size = 64;
+
+        font->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) *
+                                              font->glyphs_size);
+        /*
+         * Make sure the glyph structures are initialized.
+         */
+        (void) memset((char *) font->glyphs, 0,
+                      sizeof(bdf_glyph_t) * font->glyphs_size);
+
+        /*
+         * Set up the callback to indicate the glyph loading is about to
+         * begin.
+         */
+        if (p->callback != 0) {
+            p->cb.reason = BDF_LOAD_START;
+            p->cb.total = p->cnt;
+            p->cb.current = 0;
+            (*p->callback)(&p->cb, p->client_data);
+        }
+        p->flags |= _BDF_GLYPHS;
+        return 0;
+    }
+
+    /*
+     * Check for the ENDFONT field.
+     */
+    if (memcmp(line, "ENDFONT", 7) == 0) {
+        /*
+         * Sort the glyphs by encoding.
+         */
+        qsort((char *) font->glyphs, font->glyphs_used, sizeof(bdf_glyph_t),
+              by_encoding);
+
+        p->flags &= ~_BDF_START;
+        return 0;
+    }
+
+    /*
+     * Check for the ENDCHAR field.
+     */
+    if (memcmp(line, "ENDCHAR", 7) == 0) {
+        /*
+         * Set up and call the callback if it was passed.
+         */
+        if (p->callback != 0) {
+            p->cb.reason = BDF_LOADING;
+            p->cb.total = font->glyphs_size;
+            p->cb.current = font->glyphs_used;
+            (*p->callback)(&p->cb, p->client_data);
+        }
+        p->glyph_enc = 0;
+        p->flags &= ~_BDF_GLYPH_BITS;
+        return 0;
+    }
+
+    /*
+     * Check to see if a glyph is being scanned but should be ignored
+     * because it is an unencoded glyph.
+     */
+    if ((p->flags & _BDF_GLYPH) &&
+        p->glyph_enc == -1 && p->opts->keep_unencoded == 0)
+      return 0;
+
+    /*
+     * Check for the STARTCHAR field.
+     */
+    if (memcmp(line, "STARTCHAR", 9) == 0) {
+        /*
+         * Set the character name in the parse info first until the
+         * encoding can be checked for an unencoded character.
+         */
+        if (p->glyph_name != 0)
+          free(p->glyph_name);
+        _bdf_split(" +", line, linelen, &p->list);
+        _bdf_shift(1, &p->list);
+        s = _bdf_join(' ', &slen, &p->list);
+        p->glyph_name = (char *) malloc(slen + 1);
+        (void) memcpy(p->glyph_name, s, slen + 1);
+        p->flags |= _BDF_GLYPH;
+        return 0;
+    }
+
+    /*
+     * Check for the ENCODING field.
+     */
+    if (memcmp(line, "ENCODING", 8) == 0) {
+        if (!(p->flags & _BDF_GLYPH)) {
+            /*
+             * Missing STARTCHAR field.
+             */
+            sprintf(nbuf, ERRMSG1, lineno, "STARTCHAR");
+            _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+            return BDF_MISSING_STARTCHAR;
+        }
+        _bdf_split(" +", line, linelen, &p->list);
+        p->glyph_enc = _bdf_atol(p->list.field[1], 0, 10);
+
+        /*
+         * Check to see if this encoding has already been encountered.  If it
+         * has then change it to unencoded so it gets added if indicated.
+         */
+        if (p->glyph_enc >= 0) {
+            if (_bdf_glyph_modified(p->have, p->glyph_enc)) {
+                /*
+                 * Add a message saying a glyph has been moved to the
+                 * unencoded area.
+                 */
+                sprintf(nbuf, ACMSG12, p->glyph_enc, p->glyph_name);
+                _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+                p->glyph_enc = -1;
+                font->modified = 1;
+            } else
+              _bdf_set_glyph_modified(p->have, p->glyph_enc);
+        }
+
+        if (p->glyph_enc >= 0) {
+            /*
+             * Make sure there are enough glyphs allocated in case the
+             * number of characters happen to be wrong.
+             */
+            if (font->glyphs_used == font->glyphs_size) {
+                font->glyphs = (bdf_glyph_t *)
+                    realloc((char *) font->glyphs,
+                            sizeof(bdf_glyph_t) * (font->glyphs_size + 64));
+                (void) memset((char *) (font->glyphs + font->glyphs_size),
+                              0, sizeof(bdf_glyph_t) << 6);
+                font->glyphs_size += 64;
+            }
+
+            glyph = font->glyphs + font->glyphs_used++;
+            glyph->name = p->glyph_name;
+            glyph->encoding = p->glyph_enc;
+
+            /*
+             * Reset the initial glyph info.
+             */
+            p->glyph_name = 0;
+        } else {
+            /*
+             * Unencoded glyph.  Check to see if it should be added or not.
+             */
+            if (p->opts->keep_unencoded != 0) {
+                /*
+                 * Allocate the next unencoded glyph.
+                 */
+                if (font->unencoded_used == font->unencoded_size) {
+                    if (font->unencoded_size == 0)
+                      font->unencoded = (bdf_glyph_t *)
+                          malloc(sizeof(bdf_glyph_t) << 2);
+                    else
+                      font->unencoded = (bdf_glyph_t *)
+                          realloc((char *) font->unencoded,
+                                  sizeof(bdf_glyph_t) *
+                                  (font->unencoded_size + 4));
+                    font->unencoded_size += 4;
+                }
+
+                glyph = font->unencoded + font->unencoded_used;
+                glyph->name = p->glyph_name;
+                glyph->encoding = font->unencoded_used++;
+            } else
+              /*
+               * Free up the glyph name if the unencoded shouldn't be
+               * kept.
+               */
+              free(p->glyph_name);
+
+            p->glyph_name = 0;
+        }
+
+        /*
+         * Clear the flags that might be added when width and height are
+         * checked for consistency.
+         */
+        p->flags &= ~(_BDF_GLYPH_WIDTH_CHECK|_BDF_GLYPH_HEIGHT_CHECK);
+
+        p->flags |= _BDF_ENCODING;
+        return 0;
+    }
+
+    /*
+     * Point at the glyph being constructed.
+     */
+    if (p->glyph_enc == -1)
+      glyph = font->unencoded + (font->unencoded_used - 1);
+    else
+      glyph = font->glyphs + (font->glyphs_used - 1);
+
+    /*
+     * Check to see if a bitmap is being constructed.
+     */
+    if (p->flags & _BDF_BITMAP) {
+        /*
+         * If there are more rows than are specified in the glyph metrics,
+         * ignore the remaining lines.
+         */
+        if (p->row >= glyph->bbx.height) {
+            if (!(p->flags & _BDF_GLYPH_HEIGHT_CHECK)) {
+                sprintf(nbuf, ACMSG13, glyph->encoding);
+                _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+                p->flags |= _BDF_GLYPH_HEIGHT_CHECK;
+                font->modified = 1;
+            }
+            return 0;
+        }
+
+        /*
+         * Only collect the number of nibbles indicated by the glyph metrics.
+         * If there are more columns, they are simply ignored.
+         */
+        nibbles = p->bpr << 1;
+        bp = glyph->bitmap + (p->row * p->bpr);
+        for (i = 0, *bp = 0; i < nibbles; i++) {
+            c = line[i];
+            *bp = (*bp << 4) + a2i[c];
+            if (i + 1 < nibbles && (i & 1))
+              *++bp = 0;
+        }
+
+        /*
+         * If any line has extra columns, indicate they have been removed.
+         */
+        if ((line[nibbles] == '0' || a2i[(int) line[nibbles]] != 0) &&
+            !(p->flags & _BDF_GLYPH_WIDTH_CHECK)) {
+            sprintf(nbuf, ACMSG14, glyph->encoding);
+            _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+            p->flags |= _BDF_GLYPH_WIDTH_CHECK;
+            font->modified = 1;
+        }
+
+        p->row++;
+        return 0;
+    }
+
+    /*
+     * Expect the SWIDTH (scalable width) field next.
+     */
+    if (memcmp(line, "SWIDTH", 6) == 0) {
+        if (!(p->flags & _BDF_ENCODING)) {
+            /*
+             * Missing ENCODING field.
+             */
+            sprintf(nbuf, ERRMSG1, lineno, "ENCODING");
+            _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+            return BDF_MISSING_ENCODING;
+        }
+        _bdf_split(" +", line, linelen, &p->list);
+        glyph->swidth = _bdf_atoul(p->list.field[1], 0, 10);
+        p->flags |= _BDF_SWIDTH;
+        return 0;
+    }
+
+    /*
+     * Expect the DWIDTH (scalable width) field next.
+     */
+    if (memcmp(line, "DWIDTH", 6) == 0) {
+        _bdf_split(" +", line, linelen, &p->list);
+        glyph->dwidth = _bdf_atoul(p->list.field[1], 0, 10);
+
+        if (!(p->flags & _BDF_SWIDTH)) {
+            /*
+             * Missing SWIDTH field.  Add an auto correction message and set
+             * the scalable width from the device width.
+             */
+            sprintf(nbuf, ACMSG9, lineno);
+            _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+            ps = (double) font->point_size;
+            rx = (double) font->resolution_x;
+            dw = (double) glyph->dwidth;
+            glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+        }
+
+        p->flags |= _BDF_DWIDTH;
+        return 0;
+    }
+
+    /*
+     * Expect the BBX field next.
+     */
+    if (memcmp(line, "BBX", 3) == 0) {
+        _bdf_split(" +", line, linelen, &p->list);
+        glyph->bbx.width = _bdf_atos(p->list.field[1], 0, 10);
+        glyph->bbx.height = _bdf_atos(p->list.field[2], 0, 10);
+        glyph->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10);
+        glyph->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10);
+
+        /*
+         * Generate the ascent and descent of the character.
+         */
+        glyph->bbx.ascent = glyph->bbx.height + glyph->bbx.y_offset;
+        glyph->bbx.descent = -glyph->bbx.y_offset;
+
+        /*
+         * Determine the overall font bounding box as the characters are
+         * loaded so corrections can be done later if indicated.
+         */
+        p->maxas = MAX(glyph->bbx.ascent, p->maxas);
+        p->maxds = MAX(glyph->bbx.descent, p->maxds);
+        p->rbearing = glyph->bbx.width + glyph->bbx.x_offset;
+        p->maxrb = MAX(p->rbearing, p->maxrb);
+        p->minlb = MIN(glyph->bbx.x_offset, p->minlb);
+        p->maxlb = MAX(glyph->bbx.x_offset, p->maxlb);
+
+        if (!(p->flags & _BDF_DWIDTH)) {
+            /*
+             * Missing DWIDTH field.  Add an auto correction message and set
+             * the device width to the glyph width.
+             */
+            sprintf(nbuf, ACMSG10, lineno);
+            _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+            glyph->dwidth = glyph->bbx.width;
+        }
+
+        /*
+         * If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH
+         * value if necessary.
+         */
+        if (p->opts->correct_metrics != 0) {
+            /*
+             * Determine the point size of the glyph.
+             */
+            ps = (double) font->point_size;
+            rx = (double) font->resolution_x;
+            dw = (double) glyph->dwidth;
+            sw = (unsigned short) ((dw * 72000.0) / (ps * rx));
+
+            if (sw != glyph->swidth) {
+                glyph->swidth = sw;
+                if (p->glyph_enc == -1)
+                  _bdf_set_glyph_modified(font->umod,
+                                          font->unencoded_used - 1);
+                else
+                  _bdf_set_glyph_modified(font->nmod, glyph->encoding);
+                p->flags |= _BDF_SWIDTH_ADJ;
+                font->modified = 1;
+            }
+        }
+        p->flags |= _BDF_BBX;
+        return 0;
+    }
+
+    /*
+     * And finally, gather up the bitmap.
+     */
+    if (memcmp(line, "BITMAP", 6) == 0) {
+        if (!(p->flags & _BDF_BBX)) {
+            /*
+             * Missing BBX field.
+             */
+            sprintf(nbuf, ERRMSG1, lineno, "BBX");
+            _bdf_add_acmsg(font, nbuf, strlen(nbuf));
+            return BDF_MISSING_BBX;
+        }
+        /*
+         * Allocate enough space for the bitmap.
+         */
+        p->bpr = ((glyph->bbx.width * p->font->bpp) + 7) >> 3;
+        glyph->bytes = p->bpr * glyph->bbx.height;
+        glyph->bitmap = (unsigned char *) malloc(glyph->bytes);
+        p->row = 0;
+        p->flags |= _BDF_BITMAP;
+        return 0;
+    }
+
+    return BDF_INVALID_LINE;
+}
+
+/*
+ * Load the font properties.
+ */
+static int
+_bdf_parse_properties(char *line, unsigned int linelen, unsigned int lineno,
+                      void *call_data, void *client_data)
+{
+    unsigned int vlen;
+    _bdf_line_func_t *next;
+    _bdf_parse_t *p;
+    char *name, *value, nbuf[128];
+
+    next = (_bdf_line_func_t *) call_data;
+    p = (_bdf_parse_t *) client_data;
+
+    /*
+     * Check for the end of the properties.
+     */
+    if (memcmp(line, "ENDPROPERTIES", 13) == 0) {
+        /*
+         * If the FONT_ASCENT or FONT_DESCENT properties have not been
+         * encountered yet, then make sure they are added as properties and
+         * make sure they are set from the font bounding box info.
+         *
+         * This is *always* done regardless of the options, because X11
+         * requires these two fields to compile fonts.
+         */
+        if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) {
+            p->font->font_ascent = p->font->bbx.ascent;
+            sprintf(nbuf, "%hd", p->font->bbx.ascent);
+            _bdf_add_property(p->font, "FONT_ASCENT", nbuf);
+            sprintf(nbuf, ACMSG1, p->font->bbx.ascent);
+            _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+            p->font->modified = 1;
+        }
+        if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) {
+            p->font->font_descent = p->font->bbx.descent;
+            sprintf(nbuf, "%hd", p->font->bbx.descent);
+            _bdf_add_property(p->font, "FONT_DESCENT", nbuf);
+            sprintf(nbuf, ACMSG2, p->font->bbx.descent);
+            _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+            p->font->modified = 1;
+        }
+        p->flags &= ~_BDF_PROPS;
+        *next = _bdf_parse_glyphs;
+        return 0;
+    }
+
+    /*
+     * Ignore the _XFREE86_GLYPH_RANGES and _XMBDFED_INFO properties.
+     */
+    if (memcmp(line, "_XFREE86_GLYPH_RANGES", 21) == 0 ||
+        memcmp(line, "_XMBDFED_INFO", 13) == 0)
+      return 0;
+
+    /*
+     * Handle COMMENT fields and properties in a special way to preserve
+     * the spacing.
+     */
+    if (memcmp(line, "COMMENT", 7) == 0) {
+        name = value = line;
+        value += 7;
+        if (*value)
+          *value++ = 0;
+        _bdf_add_property(p->font, name, value);
+    } else if (_bdf_is_atom(line, linelen, &name, &value))
+      _bdf_add_property(p->font, name, value);
+    else {
+        _bdf_split(" +", line, linelen, &p->list);
+        name = p->list.field[0];
+        _bdf_shift(1, &p->list);
+        value = _bdf_join(' ', &vlen, &p->list);
+        _bdf_add_property(p->font, name, value);
+    }
+
+    return 0;
+}
+
+/*
+ * Load the font header.
+ */
+static int
+_bdf_parse_start(char *line, unsigned int linelen, unsigned int lineno,
+                 void *call_data, void *client_data)
+{
+    unsigned int slen = 0;
+    _bdf_line_func_t *next;
+    _bdf_parse_t *p;
+    bdf_font_t *font;
+    char *s, nbuf[128];
+
+    next = (_bdf_line_func_t *) call_data;
+    p = (_bdf_parse_t *) client_data;
+
+    /*
+     * Check for a comment.  This is done to handle those fonts that have
+     * comments before the STARTFONT line for some reason.
+     */
+    if (memcmp(line, "COMMENT", 7) == 0) {
+        if (p->opts->keep_comments != 0 && p->font != 0) {
+            linelen -= 7;
+            s = line + 7;
+            if (*s != 0) {
+                s++;
+                linelen--;
+            }
+            _bdf_add_comment(p->font, s, linelen);
+        }
+        return 0;
+    }
+
+    if (!(p->flags & _BDF_START)) {
+        if (memcmp(line, "STARTFONT", 9) != 0)
+          /*
+           * No STARTFONT field is a good indication of a problem.
+           */
+          return BDF_MISSING_START;
+        p->flags = _BDF_START;
+        p->font = font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t));
+        p->font->internal = (void *) malloc(sizeof(hashtable));
+        hash_init((hashtable *) p->font->internal);
+        p->font->spacing = p->opts->font_spacing;
+        p->font->default_glyph = -1;
+        return 0;
+    }
+
+    /*
+     * Check for the start of the properties.
+     */
+    if (memcmp(line, "STARTPROPERTIES", 15) == 0) {
+        _bdf_split(" +", line, linelen, &p->list);
+        p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10);
+        p->font->props = (bdf_property_t *)
+            malloc(sizeof(bdf_property_t) * p->cnt);
+        p->flags |= _BDF_PROPS;
+        *next = _bdf_parse_properties;
+        return 0;
+    }
+
+    /*
+     * Check for the FONTBOUNDINGBOX field.
+     */
+    if (memcmp(line, "FONTBOUNDINGBOX", 15) == 0) {
+        if (!(p->flags & _BDF_SIZE)) {
+            /*
+             * Missing the SIZE field.
+             */
+            sprintf(nbuf, ERRMSG1, lineno, "SIZE");
+            _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+            return BDF_MISSING_SIZE;
+        }
+        _bdf_split(" +", line, linelen, &p->list);
+        p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10);
+        p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10);
+        p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10);
+        p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10);
+        p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset;
+        p->font->bbx.descent = -p->font->bbx.y_offset;
+        p->flags |= _BDF_FONT_BBX;
+        return 0;
+    }
+
+    /*
+     * The next thing to check for is the FONT field.
+     */
+    if (memcmp(line, "FONT", 4) == 0) {
+        _bdf_split(" +", line, linelen, &p->list);
+        _bdf_shift(1, &p->list);
+        s = _bdf_join(' ', &slen, &p->list);
+        p->font->name = (char *) malloc(slen + 1);
+        (void) memcpy(p->font->name, s, slen + 1);
+        /*
+         * If the font name is an XLFD name, set the spacing to the one in the
+         * font name.  If there is no spacing fall back on the default.
+         */
+        _bdf_set_default_spacing(p->font, p->opts);
+        p->flags |= _BDF_FONT_NAME;
+        return 0;
+    }
+
+    /*
+     * Check for the SIZE field.
+     */
+    if (memcmp(line, "SIZE", 4) == 0) {
+        if (!(p->flags & _BDF_FONT_NAME)) {
+            /*
+             * Missing the FONT field.
+             */
+            sprintf(nbuf, ERRMSG1, lineno, "FONT");
+            _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+            return BDF_MISSING_FONTNAME;
+        }
+        _bdf_split(" +", line, linelen, &p->list);
+        p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10);
+        p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10);
+        p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10);
+
+        /*
+         * Check for the bits per pixel field.
+         */
+        if (p->list.used == 5) {
+            p->font->bpp = _bdf_atos(p->list.field[4], 0, 10);
+            if (p->font->bpp > 1 && (p->font->bpp & 1)) {
+                /*
+                 * Move up to the next bits per pixel value if an odd number
+                 * is encountered.
+                 */
+                p->font->bpp++;
+                if (p->font->bpp <= 4) {
+                    sprintf(nbuf, ACMSG11, p->font->bpp);
+                    _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+                }
+            }
+            if (p->font->bpp > 4) {
+                sprintf(nbuf, ACMSG11, p->font->bpp);
+                _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+                p->font->bpp = 4;
+            }
+        } else
+          p->font->bpp = 1;
+
+        p->flags |= _BDF_SIZE;
+        return 0;
+    }
+
+    return BDF_INVALID_LINE;
+}
+
+/**************************************************************************
+ *
+ * API.
+ *
+ **************************************************************************/
+
+void
+bdf_setup(void)
+{
+    unsigned int i;
+    bdf_property_t *prop;
+
+    hash_init(&proptbl);
+    for (i = 0, prop = _bdf_properties; i < _num_bdf_properties; i++, prop++)
+      hash_insert(prop->name, (void *) i, &proptbl);
+}
+
+void
+bdf_cleanup(void)
+{
+    unsigned int i;
+    bdf_property_t *prop;
+
+    hash_free(&proptbl);
+
+    /*
+     * Free up the user defined properties.
+     */
+    for (prop = user_props, i = 0; i < nuser_props; i++, prop++) {
+        free(prop->name);
+        if (prop->format == BDF_ATOM && prop->value.atom != 0)
+          free(prop->value.atom);
+    }
+    if (nuser_props > 0)
+      free((char *) user_props);
+
+    _bdf_glyph_name_cleanup();
+}
+
+bdf_font_t *
+bdf_load_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback,
+              void *data)
+{
+    int n;
+    unsigned int lineno;
+    char msgbuf[128];
+    _bdf_parse_t p;
+
+    (void) memset((char *) &p, 0, sizeof(_bdf_parse_t));
+    p.opts = (opts != 0) ? opts : &_bdf_opts;
+    p.minlb = 32767;
+    p.callback = callback;
+    p.client_data = data;
+    n = _bdf_readlines(fileno(in), _bdf_parse_start, (void *) &p, &lineno);
+
+    if (p.font != 0) {
+        /*
+         * If the font is not proportional, set the fonts monowidth
+         * field to the width of the font bounding box.
+         */
+        if (p.font->spacing != BDF_PROPORTIONAL)
+          p.font->monowidth = p.font->bbx.width;
+
+        /*
+         * If the number of glyphs loaded is not that of the original count,
+         * indicate the difference.
+         */
+        if (p.cnt != p.font->glyphs_used + p.font->unencoded_used) {
+            sprintf(msgbuf, ACMSG15, p.cnt,
+                    p.font->glyphs_used + p.font->unencoded_used);
+            _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+            p.font->modified = 1;
+        }
+
+        /*
+         * Once the font has been loaded, adjust the overall font metrics if
+         * necessary.
+         */
+        if (p.opts->correct_metrics != 0 &&
+            (p.font->glyphs_used > 0 || p.font->unencoded_used > 0)) {
+            if (p.maxrb - p.minlb != p.font->bbx.width) {
+                sprintf(msgbuf, ACMSG3, p.font->bbx.width, p.maxrb - p.minlb);
+                _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+                p.font->bbx.width = p.maxrb - p.minlb;
+                p.font->modified = 1;
+            }
+            if (p.font->bbx.x_offset != p.minlb) {
+                sprintf(msgbuf, ACMSG4, p.font->bbx.x_offset, p.minlb);
+                _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+                p.font->bbx.x_offset = p.minlb;
+                p.font->modified = 1;
+            }
+            if (p.font->bbx.ascent != p.maxas) {
+                sprintf(msgbuf, ACMSG5, p.font->bbx.ascent, p.maxas);
+                _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+                p.font->bbx.ascent = p.maxas;
+                p.font->modified = 1;
+            }
+            if (p.font->bbx.descent != p.maxds) {
+                sprintf(msgbuf, ACMSG6, p.font->bbx.descent, p.maxds);
+                _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+                p.font->bbx.descent = p.maxds;
+                p.font->bbx.y_offset = -p.maxds;
+                p.font->modified = 1;
+            }
+            if (p.maxas + p.maxds != p.font->bbx.height) {
+                sprintf(msgbuf, ACMSG7, p.font->bbx.height, p.maxas + p.maxds);
+                _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+            }
+            p.font->bbx.height = p.maxas + p.maxds;
+
+            if (p.flags & _BDF_SWIDTH_ADJ)
+              _bdf_add_acmsg(p.font, ACMSG8, strlen(ACMSG8));
+        }
+    }
+
+    /*
+     * Last, if an error happened during loading, handle the messages.
+     */
+    if (n < 0 && callback != 0) {
+        /*
+         * An error was returned.  Alert the client.
+         */
+        p.cb.reason = BDF_ERROR;
+        p.cb.errlineno = lineno;
+        (*callback)(&p.cb, data);
+    } else if (p.flags & _BDF_START) {
+        if (p.font != 0) {
+            /*
+             * The ENDFONT field was never reached or did not exist.
+             */
+            if (!(p.flags & _BDF_GLYPHS))
+              /*
+               * Error happened while parsing header.
+               */
+              sprintf(msgbuf, ERRMSG2, lineno);
+            else
+              /*
+               * Error happened when parsing glyphs.
+               */
+              sprintf(msgbuf, ERRMSG3, lineno);
+
+            _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf));
+        }
+
+        if (callback != 0) {
+            p.cb.reason = BDF_ERROR;
+            p.cb.errlineno = lineno;
+            (*callback)(&p.cb, data);
+        }
+    } else if (callback != 0) {
+        /*
+         * This forces the progress bar to always finish.
+         */
+        p.cb.current = p.cb.total;
+        (*p.callback)(&p.cb, p.client_data);
+    }
+
+    /*
+     * Free up the list used during the parsing.
+     */
+    if (p.list.size > 0)
+      free((char *) p.list.field);
+
+    if (p.font != 0) {
+        /*
+         * Make sure the comments are NULL terminated if they exist.
+         */
+        if (p.font->comments_len > 0) {
+            p.font->comments = (char *) realloc(p.font->comments,
+                                                p.font->comments_len + 1);
+            p.font->comments[p.font->comments_len] = 0;
+        }
+
+        /*
+         * Make sure the auto-correct messages are NULL terminated if they
+         * exist.
+         */
+        if (p.font->acmsgs_len > 0) {
+            p.font->acmsgs = (char *) realloc(p.font->acmsgs,
+                                              p.font->acmsgs_len + 1);
+            p.font->acmsgs[p.font->acmsgs_len] = 0;
+        }
+    }
+
+    return p.font;
+}
+
+#ifdef HAVE_HBF
+
+static int
+_bdf_parse_hbf_header(char *line, unsigned int linelen, unsigned int lineno,
+                      void *call_data, void *client_data)
+{
+    unsigned int vlen = 0;
+    char *name, *value;
+    _bdf_parse_t *p;
+    _bdf_line_func_t *next;
+    char nbuf[24];
+
+    next = (_bdf_line_func_t *) call_data;
+    p = (_bdf_parse_t *) client_data;
+
+    /*
+     * Check for comments.
+     */
+    if (memcmp(line, "COMMENT", 7) == 0) {
+        if (p->opts->keep_comments != 0 && p->font != 0) {
+            name = line;
+            value = name + 7;
+            vlen = linelen - 7;
+            if (*value) {
+                *value++ = 0;
+                vlen--;
+            }
+            /*
+             * If the properties are being parsed, add the comment as a
+             * property.  Otherwise, simply add the comment in the normal
+             * fashion.
+             */
+            if (p->flags & _BDF_PROPS)
+              _bdf_add_property(p->font, name, value);
+            else
+              _bdf_add_comment(p->font, value, vlen);
+        }
+        return 0;
+    }
+
+    if (!(p->flags & _BDF_START)) {
+        if (memcmp(line, "HBF_START_FONT", 14) != 0)
+          return -1;
+        p->flags = _BDF_START;
+        p->font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t));
+        /*
+         * HBF fonts are always assumed to be 1 bit per pixel.
+         */
+        p->font->bpp = 1;
+        p->font->internal = (void *) malloc(sizeof(hashtable));
+        hash_init((hashtable *) p->font->internal);
+        p->font->hbf = 1;
+        p->font->spacing = p->opts->font_spacing;
+        p->font->default_glyph = -1;
+        return 0;
+    }
+
+    /*
+     * Check for the HBF_END_FONT field.
+     */
+    if (memcmp(line, "HBF_END_FONT", 12) == 0)
+      /*
+       * Need to perform some checks here to see whether some fields are
+       * missing or not.
+       */
+      return 0;
+
+    /*
+     * Check for HBF keywords which will be added as comments.  These should
+     * never occur in the properties list.  Assume they won't.
+     */
+    if (memcmp(line, "HBF_", 4) == 0) {
+        if (p->opts->keep_comments != 0)
+          _bdf_add_comment(p->font, line, linelen);
+        return 0;
+    }
+
+    if (!(p->flags & _BDF_PROPS)) {
+        /*
+         * Check for the start of the properties.
+         */
+        if (memcmp(line, "STARTPROPERTIES", 15) == 0) {
+            _bdf_split(" +", line, linelen, &p->list);
+            p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10);
+            p->font->props = (bdf_property_t *)
+                malloc(sizeof(bdf_property_t) * p->cnt);
+            p->flags |= _BDF_PROPS;
+            return 0;
+        }
+
+        /*
+         * Check for the CHARS field.
+         */
+        if (memcmp(line, "CHARS", 5) == 0) {
+            _bdf_split(" +", line, linelen, &p->list);
+            p->cnt = p->font->glyphs_size =
+                _bdf_atoul(p->list.field[1], 0, 10);
+            p->font->glyphs = (bdf_glyph_t *)
+                malloc(sizeof(bdf_glyph_t) * p->cnt);
+            return 0;
+        }
+
+        /*
+         * Check for the FONTBOUNDINGBOX field.
+         */
+        if (memcmp(line, "FONTBOUNDINGBOX", 15) == 0) {
+            if (!(p->flags & (_BDF_START|_BDF_FONT_NAME|_BDF_SIZE)))
+              return -1;
+            _bdf_split(" +", line, linelen, &p->list);
+            p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10);
+            p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10);
+            p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10);
+            p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10);
+            p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset;
+            p->font->bbx.descent = -p->font->bbx.y_offset;
+            p->flags |= _BDF_FONT_BBX;
+            return 0;
+        }
+
+        /*
+         * The next thing to check for is the FONT field.
+         */
+        if (memcmp(line, "FONT", 4) == 0) {
+            if (!(p->flags & _BDF_START))
+              return -1;
+            _bdf_split(" +", line, linelen, &p->list);
+            _bdf_shift(1, &p->list);
+            value = _bdf_join(' ', &vlen, &p->list);
+            p->font->name = (char *) malloc(vlen + 1);
+            (void) memcpy(p->font->name, value, vlen + 1);
+            /*
+             * If the font name is an XLFD name, set the spacing to the one in
+             * the font name.  If there is no spacing fall back on the
+             * default.
+             */
+            _bdf_set_default_spacing(p->font, p->opts);
+            p->flags |= _BDF_FONT_NAME;
+            return 0;
+        }
+
+        /*
+         * Check for the SIZE field.
+         */
+        if (memcmp(line, "SIZE", 4) == 0) {
+            if (!(p->flags & (_BDF_START|_BDF_FONT_NAME)))
+              return -1;
+            _bdf_split(" +", line, linelen, &p->list);
+            p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10);
+            p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10);
+            p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10);
+            p->flags |= _BDF_SIZE;
+            return 0;
+        }
+    } else {
+        /*
+         * Check for the end of the properties.
+         */
+        if (memcmp(line, "ENDPROPERTIES", 13) == 0) {
+            /*
+             * If the FONT_ASCENT or FONT_DESCENT properties have not been
+             * encountered yet, then make sure they are added as properties and
+             * make sure they are set from the font bounding box info.
+             *
+             * This is *always* done regardless of the options, because X11
+             * requires these two fields to compile fonts.
+             */
+            if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) {
+                p->font->font_ascent = p->font->bbx.ascent;
+                sprintf(nbuf, "%hd", p->font->bbx.ascent);
+                _bdf_add_property(p->font, "FONT_ASCENT", nbuf);
+                sprintf(nbuf, ACMSG1, p->font->bbx.ascent);
+                _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+                p->font->modified = 1;
+            }
+            if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) {
+                p->font->font_descent = p->font->bbx.descent;
+                sprintf(nbuf, "%hd", p->font->bbx.descent);
+                _bdf_add_property(p->font, "FONT_DESCENT", nbuf);
+                sprintf(nbuf, ACMSG2, p->font->bbx.descent);
+                _bdf_add_acmsg(p->font, nbuf, strlen(nbuf));
+                p->font->modified = 1;
+            }
+            p->flags &= ~_BDF_PROPS;
+            return 0;
+        }
+
+        /*
+         * Handle the next thing in the usual property fashion.
+         */
+        if (_bdf_is_atom(line, linelen, &name, &value))
+          _bdf_add_property(p->font, name, value);
+        else {
+            _bdf_split(" +", line, linelen, &p->list);
+            name = p->list.field[0];
+            _bdf_shift(1, &p->list);
+            value = _bdf_join(' ', &vlen, &p->list);
+            _bdf_add_property(p->font, name, value);
+        }
+        return 0;
+    }
+
+    /*
+     * Anything else is an error.
+     */
+    return -1;
+}
+
+#define CONST const
+
+static void
+_bdf_add_hbf_glyph(HBF *hbf, unsigned int code, void *callback_data)
+{
+    CONST unsigned char *bmap;
+    unsigned int n;
+    bdf_glyph_t *gp;
+    bdf_font_t *font;
+    _bdf_parse_t *p;
+    HBF_BBOX *fbbx;
+    double ps, rx, dw;
+    char nbuf[24];
+
+    /*
+     * Attempt to get the bitmap.
+     */
+    if ((bmap = hbfGetBitmap(hbf, code)) == 0)
+      /*
+       * Need some sort of error handling here.
+       */
+      return;
+
+    p = (_bdf_parse_t *) callback_data;
+
+    fbbx = hbfFontBBox(hbf);
+
+    font = p->font;
+
+    /*
+     * Check to make sure there is enough space to hold this glyph.  If not,
+     * allocate 10 more just in case.
+     */
+    if (font->glyphs_used == font->glyphs_size) {
+        if (font->glyphs_size == 0)
+          font->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * 16);
+        else
+          font->glyphs = (bdf_glyph_t *)
+              realloc((char *) font->glyphs,
+                      sizeof(bdf_glyph_t) * (font->glyphs_used + 16));
+        gp = font->glyphs + font->glyphs_size;
+        (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) * 16);
+        font->glyphs_size += 16;
+    }
+
+    gp = font->glyphs + font->glyphs_used++;
+
+    /*
+     * Set the glyph name.
+     */
+    sprintf(nbuf, "char%d", code);
+    n = (unsigned int) strlen(nbuf);
+    gp->name = (char *) malloc(n + 1);
+    (void) memcpy(gp->name, nbuf, n + 1);
+
+    /*
+     * Set encoding.
+     */
+    gp->encoding = (int) code;
+
+    /*
+     * Set the device width.
+     */
+    gp->dwidth = (unsigned short) fbbx->hbf_width;
+
+    /*
+     * Set the scalable width.
+     */
+    ps = (double) font->point_size;
+    rx = (double) font->resolution_x;
+    dw = (double) gp->dwidth;
+    gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+
+    /*
+     * Set the glyph bounding box.
+     */
+    gp->bbx.width = fbbx->hbf_width;
+    gp->bbx.height = fbbx->hbf_height;
+    gp->bbx.x_offset = fbbx->hbf_xDisplacement;
+    gp->bbx.y_offset = fbbx->hbf_yDisplacement;
+    gp->bbx.ascent = gp->bbx.height + gp->bbx.y_offset;
+    gp->bbx.descent = -gp->bbx.y_offset;
+
+    /*
+     * Add the bitmap by making a copy.  Assumes the font bbx is OK for
+     * determining the number of bytes needed for the glyph bitmap.
+     */
+    gp->bytes = ((gp->bbx.width + 7) >> 3) * gp->bbx.height;
+    gp->bitmap = (unsigned char *) malloc(gp->bytes);
+    (void) memcpy((char *) gp->bitmap, (char *) bmap, gp->bytes);
+
+    /*
+     * Call the callback if it was provided.
+     */
+    if (p->callback != 0) {
+        p->cb.reason = BDF_LOADING;
+        p->cb.total = font->glyphs_size;
+        p->cb.current = font->glyphs_used;
+        (*p->callback)(&p->cb, p->client_data);
+    }
+}
+
+bdf_font_t *
+bdf_load_hbf_font(char *filename, bdf_options_t *opts, bdf_callback_t callback,
+                  void *data)
+{
+    int n, diff;
+    unsigned int lineno;
+    FILE *in;
+    HBF *hbf;
+    bdf_property_t *pp;
+    char *name;
+    _bdf_parse_t p;
+
+    if ((hbf = hbfOpen(filename)) == 0)
+      return 0;
+
+    if ((in = fopen(hbfFileName(hbf), "r")) == 0) {
+        hbfClose(hbf);
+        return 0;
+    }
+
+    /*
+     * Parse the HBF header for properties and other things.
+     */
+    (void) memset((char *) &p, 0, sizeof(_bdf_parse_t));
+    p.opts = (opts != 0) ? opts : &_bdf_opts;
+    p.minlb = 32767;
+    p.callback = callback;
+    p.client_data = data;
+
+    n = _bdf_readlines(fileno(in), _bdf_parse_hbf_header, (void *) &p,
+                       &lineno);
+
+    fclose(in);
+
+    /*
+     * Determine what spacing the font has so the monowidth field can be set
+     * if necessary.
+     */
+    if ((pp = bdf_get_font_property(p.font, "SPACING")) != 0) {
+        switch (pp->value.atom[0]) {
+          case 'p': case 'P': p.font->spacing = BDF_PROPORTIONAL; break;
+          case 'm': case 'M': p.font->spacing = BDF_MONOWIDTH; break;
+          case 'c': case 'C': p.font->spacing = BDF_CHARCELL; break;
+        }
+    }
+
+    /*
+     * Set the monowidth field if necessary.
+     */
+    if (p.font->spacing != BDF_PROPORTIONAL)
+      p.font->monowidth = p.font->bbx.width;
+
+    /*
+     * Before loading the glyphs, check to see if any glyph structures have
+     * been added.  If not, check the HBF font for the number of characters.
+     * Dynamically increasing glyph storage causes memory fragmentation on
+     * some machines and crashes.  This takes care of the cases where the HBF
+     * file does not provide a "CHARS n" line.
+     */
+    if (p.font->glyphs_size < hbfChars(hbf)) {
+        if (p.font->glyphs_size == 0)
+          p.font->glyphs = (bdf_glyph_t *)
+              malloc(sizeof(bdf_glyph_t) * hbfChars(hbf));
+        else
+          p.font->glyphs = (bdf_glyph_t *)
+              realloc((char *) p.font->glyphs,
+                      sizeof(bdf_glyph_t) * hbfChars(hbf));
+        diff = hbfChars(hbf) - p.font->glyphs_size;
+        (void) memset((char *) (p.font->glyphs + p.font->glyphs_size), 0,
+                      diff);
+        p.font->glyphs_size = hbfChars(hbf);
+    }
+
+    /*
+     * Call the callback initially to set things up.
+     */
+    if (p.callback != 0) {
+        p.cb.reason = BDF_LOAD_START;
+        p.cb.total = p.font->glyphs_size;
+        p.cb.current = 0;
+        (*p.callback)(&p.cb, p.client_data);
+    }
+
+    /*
+     * Now load the glyphs.
+     */
+    hbfForEach(hbf, _bdf_add_hbf_glyph, (void *) &p);
+
+    /*
+     * Close the HBF font.
+     */
+    hbfClose(hbf);
+
+    /*
+     * Sort the glyphs by encoding.
+     */
+    qsort((char *) p.font->glyphs, p.font->glyphs_used, sizeof(bdf_glyph_t),
+          by_encoding);
+
+    /*
+     * After loading the HBF header, create an XLFD name.  If the XLFD name
+     * cannot be made then preserve the name found in the HBF file.
+     */
+    if ((name = bdf_make_xlfd_name(p.font, "HBF", "Unknown")) != 0) {
+        if (p.font->name != 0)
+          /*
+           * If a name already exists in the font, free it up.
+           */
+          free(p.font->name);
+
+        /*
+         * Replace the old name with the XLFD name.
+         */
+        p.font->name = name;
+    }
+
+    /*
+     * Mark the font as being modified and generate a message that says
+     * something about the font being converted from HBF format.
+     */
+    p.font->modified = 1;
+    _bdf_add_acmsg(p.font, "Font converted from HBF to BDF.", 31);
+
+    return p.font;
+}
+
+#endif /* HAVE_HBF */
+
+/*
+ * Crop the glyph bitmap to the minimum rectangle needed to hold the bits that
+ * are set.  Adjust the metrics based on the provided bounding box.
+ */
+void
+_bdf_crop_glyph(bdf_font_t *font, bdf_glyph_t *glyph)
+{
+    int byte;
+    unsigned short x, y, bpr, nbpr, col, colx, si, di;
+    unsigned short minx, maxx, miny, maxy;
+    unsigned int bytes;
+    unsigned char *bmap, *masks;
+    bdf_bbx_t nbbx;
+
+    if (glyph == 0)
+      return;
+
+    (void) memcpy((char *) &nbbx, (char *) &glyph->bbx, sizeof(bdf_bbx_t));
+
+    bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3;
+
+    maxx = maxy = 0;
+    minx = miny = 32767;
+
+    masks = 0;
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    for (y = 0; y < glyph->bbx.height; y++) {
+        for (col = x = 0; x < glyph->bbx.width; x++, col += font->bpp) {
+            si = (col & 7) / font->bpp;
+            if (glyph->bitmap[(y * bpr) + (col >> 3)] & masks[si]) {
+                minx = MIN(minx, x);
+                maxx = MAX(maxx, x);
+                miny = MIN(miny, y);
+                maxy = MAX(maxy, y);
+            }
+        }
+    }
+
+    /*
+     * Handle an empty bitmap as a special case.
+     */
+    if (minx == 32767) {
+        if (glyph->bytes > 0)
+          free((char *) glyph->bitmap);
+        glyph->bytes = 0;
+        (void) memset((char *) &glyph->bbx, 0, sizeof(bdf_bbx_t));
+        return;
+    }
+
+    /*
+     * Increment the max points so width and height calculations won't go
+     * wrong.
+     */
+    maxx++;
+    maxy++;
+
+    if (minx > 0)
+      nbbx.x_offset += minx;
+    if (maxx - minx != nbbx.width)
+      nbbx.width = maxx - minx;
+
+    if (miny > 0)
+      nbbx.ascent -= miny;
+    if (maxy - miny != nbbx.height)
+      nbbx.height = maxy - miny;
+    nbbx.descent = nbbx.height - nbbx.ascent;
+    nbbx.y_offset = -nbbx.descent;
+
+    nbpr = ((nbbx.width * font->bpp) + 7) >> 3;
+
+    /*
+     * If nothing changed, then the glyph is already contained in the
+     * minimum rectangle.
+     */
+    if (memcmp((char *) &nbbx, (char *) &glyph->bbx,
+               sizeof(bdf_bbx_t)) == 0 ||
+        (nbpr == bpr && nbbx.height == glyph->bbx.height))
+      return;
+
+    /*
+     * The metrics changed, so a new bitmap is needed.
+     */
+    bytes = nbpr * nbbx.height;
+    bmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) bmap, 0, bytes);
+
+    colx = minx * font->bpp;
+    for (y = miny; y < maxy; y++) {
+        for (col = x = minx; x < maxx; x++, col += font->bpp) {
+            si = (col & 7) / font->bpp;
+            byte = glyph->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                /*
+                 * Position the pixel in the byte if necessary.
+                 */
+                di = ((col - colx) & 7) / font->bpp;
+                if (di < si)
+                  byte <<= (si - di) * font->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * font->bpp;
+                bmap[((y - miny) * nbpr) + ((col - colx) >> 3)] |= byte;
+            }
+        }
+    }
+
+    if (glyph->bytes > 0)
+      free((char *) glyph->bitmap);
+    glyph->bytes = bytes;
+    glyph->bitmap = bmap;
+
+    (void) memcpy((char *) &glyph->bbx, (char *) &nbbx, sizeof(bdf_bbx_t));
+}
+
+/*
+ * Pad a character-cell font glyph to match the bounds specified in the
+ * provided bounding box.
+ */
+void
+_bdf_pad_cell(bdf_font_t *font, bdf_glyph_t *glyph, bdf_glyph_t *cell)
+{
+    bdf_bbx_t *bbx;
+    unsigned short si, di, sx, byte;
+    unsigned short x, y, dx, dy, bx, by, bpr, nbpr;
+    unsigned char *bmap, *masks;
+
+    masks = 0;
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    bbx = &font->bbx;
+
+    if (glyph->bbx.width == bbx->width && glyph->bbx.height == bbx->height) {
+        /*
+         * The glyph is already positioned in the cell.  Copy the bitmap
+         * and return.
+         */
+        (void) memcpy((char *) cell->bitmap, (char *) glyph->bitmap,
+                      cell->bytes);
+        return;
+    }
+
+    /*
+     * Determine the X and Y location of the baseline.
+     */
+    bx = MYABS(bbx->x_offset - glyph->bbx.x_offset);
+    by = (bbx->ascent + bbx->descent) + bbx->y_offset;
+
+    bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3;
+    nbpr = ((bbx->width * font->bpp) + 7) >> 3;
+
+    /*
+     * Set various cell values and clear the cell bitmap.
+     */
+    bmap = cell->bitmap;
+    (void) memset((char *) bmap, 0, cell->bytes);
+
+    for (dy = by - glyph->bbx.ascent, y = 0; y < glyph->bbx.height;
+         y++, dy++) {
+        for (dx = bx * font->bpp, sx = x = 0; x < glyph->bbx.width;
+             x++, dx += font->bpp, sx += font->bpp) {
+            si = (sx & 7) / font->bpp;
+            byte = glyph->bitmap[(y * bpr) + (sx >> 3)] & masks[si];
+            if (byte) {
+                di = (dx & 7) / font->bpp;
+                if (di < si)
+                  byte <<= (si - di) * font->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * font->bpp;
+                bmap[(dy * nbpr) + (dx >> 3)] |= byte;
+            }
+        }
+    }
+}
+
+static char *unix_eol = "\n";
+static char *dos_eol = "\r\n";
+static char *mac_eol = "\r";
+
+void
+bdf_save_font(FILE *out, bdf_font_t *font, bdf_options_t *opts,
+              bdf_callback_t callback, void *data)
+{
+    int len;
+    unsigned int i, j, bpr, pcnt;
+    double dw, ps, rx;
+    char *sp, *ep, *eol;
+    bdf_property_t *p;
+    bdf_glyph_t *c, *cp, cell;
+    bdf_callback_struct_t cb;
+
+    if (font == 0)
+      return;
+
+    eol = 0;
+    switch (opts->eol) {
+      case BDF_UNIX_EOL: eol = unix_eol; break;
+      case BDF_DOS_EOL: eol = dos_eol; break;
+      case BDF_MAC_EOL: eol = mac_eol; break;
+    }
+
+    /*
+     * If the font is a character cell font, allocate some space for the
+     * bitmap.
+     */
+    if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) {
+        bpr = ((font->bbx.width * font->bpp) + 7) >> 3;
+        cell.bytes = bpr * font->bbx.height;
+        cell.bitmap = (unsigned char *) malloc(cell.bytes);
+        (void) memcpy((char *) &cell.bbx, (char *) &font->bbx,
+                      sizeof(bdf_bbx_t));
+    }
+
+    /*
+     * Emit the header.
+     */
+    fprintf(out, "STARTFONT 2.1%s", eol);
+
+    /*
+     * Emit the comments.
+     */
+    if (font->comments_len > 0) {
+        for (sp = font->comments; *sp; sp++) {
+            ep = sp;
+            while (*ep && *ep != '\n')
+              ep++;
+            len = (int) (ep - sp);
+            fprintf(out, "COMMENT %.*s%s", len, sp, eol);
+            sp = ep;
+        }
+    }
+
+    /*
+     * Emit the font name.
+     */
+    fprintf(out, "FONT %s%s", font->name, eol);
+
+    /*
+     * Emit the size info.
+     */
+    if (font->bpp == 1)
+      fprintf(out, "SIZE %d %d %d%s", font->point_size,
+              font->resolution_x, font->resolution_y, eol);
+    else
+      fprintf(out, "SIZE %d %d %d %hd%s", font->point_size,
+              font->resolution_x, font->resolution_y, font->bpp, eol);
+
+    /*
+     * Emit the bounding box.
+     */
+    fprintf(out, "FONTBOUNDINGBOX %hd %hd %hd %hd%s",
+            font->bbx.width, font->bbx.height, font->bbx.x_offset,
+            font->bbx.y_offset, eol);
+
+    /*
+     * Emit the properties after counting how many are properties and
+     * how many are comments.
+     */
+    for (i = pcnt = 0, p = font->props; i < font->props_used; i++, p++) {
+        if (memcmp(p->name, "COMMENT", 7) != 0)
+          pcnt++;
+    }
+
+    fprintf(out, "STARTPROPERTIES %d%s", pcnt, eol);
+    for (i = 0, p = font->props; i < font->props_used; i++, p++) {
+        fprintf(out, "%s ", p->name);
+        if (p->format == BDF_ATOM) {
+            if (p->value.atom == 0)
+              fprintf(out, "\"\"%s", eol);
+            else
+              fprintf(out, "\"%s\"%s", p->value.atom, eol);
+        } else
+          fprintf(out, "%d%s", p->value.int32, eol);
+    }
+
+    fprintf(out, "ENDPROPERTIES%s", eol);
+
+    /*
+     * Emit the number of bitmaps in the font.
+     */
+    fprintf(out, "CHARS %d%s", font->unencoded_used + font->glyphs_used, eol);
+
+    /*
+     * Call the callback if it was passed to start the save.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_SAVE_START;
+        cb.total = font->unencoded_used + font->glyphs_used;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Emit the unencoded bitmaps.
+     */
+    for (i = 0, cp = font->unencoded; i < font->unencoded_used; i++, cp++) {
+        /*
+         * If the font has character-cell spacing and the option to pad the
+         * glyphs to the size of the font bbx is set, then pad the glyph.
+         * Otherwise, crop the glyph to the minimum rectangle needed to hold
+         * the bitmap.
+         */
+        if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) {
+            /*
+             * Point at the temporary glyph structure and copy the necessary
+             * glyph info into it.
+             */
+            c = &cell;
+            c->name = cp->name;
+            c->encoding = cp->encoding;
+            c->swidth = cp->swidth;
+            c->dwidth = cp->dwidth;
+            _bdf_pad_cell(font, cp, c);
+        } else {
+            c = cp;
+            _bdf_crop_glyph(font, c);
+        }
+
+        /*
+         * If the font has monowidth or character-cell spacing, then assign
+         * the font monowidth field to the device width and recalculate the
+         * scalable width.
+         */
+        if (font->spacing != BDF_PROPORTIONAL) {
+            c->dwidth = font->monowidth;
+            ps = (double) font->point_size;
+            rx = (double) font->resolution_x;
+            dw = (double) c->dwidth;
+            c->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+        }
+        if (c->name == 0)
+          fprintf(out, "STARTCHAR unencoded%d%sENCODING -1%s", i, eol, eol);
+        else
+          fprintf(out, "STARTCHAR %s%sENCODING -1%s", c->name, eol, eol);
+        fprintf(out, "SWIDTH %hd 0%sDWIDTH %hd 0%s",
+                c->swidth, eol, c->dwidth, eol);
+        fprintf(out, "BBX %hd %hd %hd %hd%s", c->bbx.width, c->bbx.height,
+                c->bbx.x_offset, c->bbx.y_offset, eol);
+        fprintf(out, "BITMAP%s", eol);
+        bpr = ((c->bbx.width * font->bpp) + 7) >> 3;
+        for (j = 0; bpr != 0 && j < c->bytes; j++) {
+            if (j && j % bpr == 0)
+              fprintf(out, eol);
+            fprintf(out, "%02X", c->bitmap[j]);
+        }
+        /*
+         * Handle empty bitmaps like this.
+         */
+        if (c->bbx.height > 0)
+          fprintf(out, eol);
+        fprintf(out, "ENDCHAR%s", eol);
+
+        /*
+         * Call the callback if supplied.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_SAVING;
+            cb.current++;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Emit the other bitmaps.
+     */
+    for (i = 0, cp = font->glyphs; i < font->glyphs_used; i++, cp++) {
+        /*
+         * If the font has character-cell spacing and the option to pad the
+         * glyphs to the size of the font bbx is set, then pad the glyph.
+         * Otherwise, crop the glyph to the minimum rectangle needed to hold
+         * the bitmap.
+         */
+        if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) {
+            /*
+             * Point at the temporary glyph structure and copy the necessary
+             * glyph info into it.
+             */
+            c = &cell;
+            c->name = cp->name;
+            c->encoding = cp->encoding;
+            c->swidth = cp->swidth;
+            c->dwidth = cp->dwidth;
+            _bdf_pad_cell(font, cp, c);
+        } else {
+            c = cp;
+            _bdf_crop_glyph(font, c);
+        }
+
+        /*
+         * If the font has monowidth or character-cell spacing, then assign
+         * the font monowidth field to the device width and recalculate the
+         * scalable width.
+         */
+        if (font->spacing != BDF_PROPORTIONAL) {
+            c->dwidth = font->monowidth;
+            ps = (double) font->point_size;
+            rx = (double) font->resolution_x;
+            dw = (double) c->dwidth;
+            c->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+        }
+        if (c->name == 0)
+          fprintf(out, "STARTCHAR char%d%sENCODING %d%s",
+                  c->encoding, eol, c->encoding, eol);
+        else
+          fprintf(out, "STARTCHAR %s%sENCODING %d%s",
+                  c->name, eol, c->encoding, eol);
+        fprintf(out, "SWIDTH %hd 0%sDWIDTH %hd 0%s",
+                c->swidth, eol, c->dwidth, eol);
+        fprintf(out, "BBX %hd %hd %hd %hd%s", c->bbx.width, c->bbx.height,
+                c->bbx.x_offset, c->bbx.y_offset, eol);
+        fprintf(out, "BITMAP%s", eol);
+        bpr = ((c->bbx.width * font->bpp) + 7) >> 3;
+        for (j = 0; bpr != 0 && j < c->bytes; j++) {
+            if (j && j % bpr == 0)
+              fprintf(out, eol);
+            fprintf(out, "%02X", c->bitmap[j]);
+        }
+        /*
+         * Handle empty bitmaps like this.
+         */
+        if (c->bbx.height > 0)
+          fprintf(out, eol);
+        fprintf(out, "ENDCHAR%s", eol);
+
+        /*
+         * Call the callback if supplied.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_SAVING;
+            cb.current++;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Emit the trailer.
+     */
+    fprintf(out, "ENDFONT%s", eol);
+
+    /*
+     * Always force a final call to the callback to make sure things
+     * get cleaned up.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_SAVING;
+        cb.current = cb.total;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * If the font is a character cell font, clean up the temporary glyph.
+     */
+    if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0)
+      free((char *) cell.bitmap);
+}
+
+/*
+ * Routine to write a single set of SBIT metrics.
+ */
+void
+bdf_save_sbit_metrics(FILE *out, bdf_font_t *font, bdf_options_t *opts,
+                      char *appname)
+{
+    char *eol;
+
+    eol = 0;
+    switch (opts->eol) {
+      case BDF_UNIX_EOL: eol = unix_eol; break;
+      case BDF_DOS_EOL: eol = dos_eol; break;
+      case BDF_MAC_EOL: eol = mac_eol; break;
+    }
+
+    /*
+     * Throw a simple header in.
+     */
+    if (appname)
+      fprintf(out, ";%s; SBIT metrics file generated by \"%s\".%s;%s%s", eol,
+              appname, eol, eol, eol);
+
+    /*
+     * Save PPEM.
+     */
+    fprintf(out, ";%s; Pixels Per Em.%s;%s", eol, eol, eol);
+    fprintf(out, "PPEM %d%s%s", font->point_size, eol, eol);
+
+    /*
+     * If the font is character cell or monowidth, set this boolean.
+     */
+    if (font->spacing != BDF_PROPORTIONAL) {
+        fprintf(out,
+                ";%s; Font is not proportional, so use mono advance.%s;%s",
+                eol, eol, eol);
+        fprintf(out, "FORCECONSTANTMETRICS TRUE%s%s", eol, eol);
+    } else {
+        fprintf(out,
+                ";%s; Font is proportional, so do not use mono advance.%s;%s",
+                eol, eol, eol);
+        fprintf(out, "FORCECONSTANTMETRICS FALSE%s%s", eol, eol);
+    }
+
+    /*
+     * Do the horizontal line metrics only.
+     */
+    fprintf(out, ";%s; Horizontal line metrics.%s;%s", eol, eol, eol);
+
+    fprintf(out, "H_ASCENDER %d%sH_DESCENDER %d%s", font->font_ascent, eol,
+            font->font_descent, eol);
+    fprintf(out, "H_WIDTHMAX %hd%s", font->bbx.width, eol);
+    fprintf(out, "H_MINORIGINSB %hd%sH_MINADVANCEBL %hd%s",
+            font->bbx.x_offset, eol,
+            font->bbx.width + font->bbx.x_offset, eol);
+    fprintf(out, "H_MAXBEFOREBL %hd%sH_MINAFTERBL %hd%s%s",
+            font->bbx.ascent, eol, font->bbx.y_offset, eol, eol);
+
+    /*
+     * Write the default caret info.
+     */
+    fprintf(out, ";%s; Caret slope and offset info.%s;%s", eol, eol, eol);
+    fprintf(out, "CARETSLOPENUMERATOR 1%sCARETSLOPEDENOMINATOR 0%s", eol, eol);
+    fprintf(out, "CARETOFFSET 0%s%s", eol, eol);
+
+    /*
+     * Write the bitmap options.
+     */
+    fprintf(out, ";%s; Bitmap options.%s;%s", eol, eol, eol);
+    fprintf(out, "DIRECTION H%sSTORAGE FAST%s%s", eol, eol, eol);
+
+    /*
+     * Scaled bitmaps not implemented yet.
+     */
+    fprintf(out, ";%s; Scaled bitmap info (Not Yet Implemented).%s;%s",
+            eol, eol, eol);
+}
+
+/*
+ * Special routine to dump the font in the Roman Czyborra's hex format.  It
+ * only dumps the encoded glyphs and assumes the bitmaps have the correct
+ * sizes.
+ */
+void
+bdf_export_hex(FILE *out, bdf_font_t *font, bdf_options_t *opts,
+               bdf_callback_t callback, void *data)
+{
+    int bpr, fbpr, j, k;
+    unsigned int i, ng;
+    bdf_glyph_t *gp, cell;
+    bdf_callback_struct_t cb;
+
+    if (font == 0 || out == 0)
+      return;
+
+    if (font->glyphs_used == 0)
+      return;
+
+    /*
+     * Call the callback if it was passed to start the export.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_EXPORT_START;
+        cb.total = font->glyphs_used;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    fbpr = ((font->bbx.width * font->bpp) + 7) >> 3;
+    bpr = (((font->bbx.width >> 1) * font->bpp) + 7) >> 3;
+    cell.bytes = fbpr * font->bbx.height;
+    cell.bitmap = (unsigned char *) malloc(cell.bytes);
+
+    for (i = 0, ng = font->glyphs_used, gp = font->glyphs; i < ng; i++, gp++) {
+        _bdf_pad_cell(font, gp, &cell);
+        fprintf(out, "%04X:", gp->encoding & 0xffff);
+        if (gp->bbx.width <= (font->bbx.width >> 1)) {
+            for (j = 0; j < cell.bytes; j += fbpr) {
+                for (k = 0; k < bpr; k++)
+                  fprintf(out, "%02X", cell.bitmap[j + k]);
+            }
+        } else {
+            for (j = 0; j < cell.bytes; j++)
+              fprintf(out, "%02X", cell.bitmap[j]);
+        }
+        if (cell.bytes > 0)
+          putc('\n', out);
+
+        /*
+         * Call the callback if supplied.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_EXPORTING;
+            cb.current++;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Clean up the cell.
+     */
+    free((char *) cell.bitmap);
+
+    /*
+     * Always call a final callback to make sure the client gets a chance to
+     * clean things up.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_EXPORTING;
+        cb.current = cb.total;
+        (*callback)(&cb, data);
+    }
+}
+
+void
+bdf_free_font(bdf_font_t *font)
+{
+    unsigned int i;
+    bdf_glyph_t *glyphs;
+
+    if (font == 0)
+        return;
+
+    if (font->name != 0)
+      free(font->name);
+
+    /*
+     * Free up the internal hash table of property names.
+     */
+    hash_free((hashtable *) font->internal);
+    free((char *) font->internal);
+
+    /*
+     * Free up the comment info.
+     */
+    if (font->comments_len > 0)
+      free(font->comments);
+
+    /*
+     * Free up the auto-correction messages.
+     */
+    if (font->acmsgs_len > 0)
+      free(font->acmsgs);
+
+    /*
+     * Free up the properties.
+     */
+    for (i = 0; i < font->props_size; i++) {
+        if (font->props[i].format == BDF_ATOM && font->props[i].value.atom)
+          free(font->props[i].value.atom);
+    }
+
+    if (font->props_size > 0 && font->props != 0)
+      free((char *) font->props);
+
+    /*
+     * Free up the character info.
+     */
+    for (i = 0, glyphs = font->glyphs; i < font->glyphs_used; i++, glyphs++) {
+        if (glyphs->name)
+          free(glyphs->name);
+        if (glyphs->bytes > 0 && glyphs->bitmap != 0)
+          free((char *) glyphs->bitmap);
+    }
+
+    for (i = 0, glyphs = font->unencoded; i < font->unencoded_used;
+         i++, glyphs++) {
+        if (glyphs->name)
+          free(glyphs->name);
+        if (glyphs->bytes > 0)
+          free((char *) glyphs->bitmap);
+        if (glyphs->unicode.map_size > 0)
+          free((char *) glyphs->unicode.map);
+    }
+
+    if (font->glyphs_size > 0)
+      free((char *) font->glyphs);
+
+    if (font->unencoded_size > 0)
+      free((char *) font->unencoded);
+
+    /*
+     * Free up the overflow storage if it was used.
+     */
+    for (i = 0, glyphs = font->overflow.glyphs; i < font->overflow.glyphs_used;
+         i++, glyphs++) {
+        if (glyphs->name != 0)
+          free(glyphs->name);
+        if (glyphs->bytes > 0)
+          free((char *) glyphs->bitmap);;
+        if (glyphs->unicode.map_size > 0)
+          free((char *) glyphs->unicode.map);
+    }
+    if (font->overflow.glyphs_size > 0)
+      free((char *) font->overflow.glyphs);
+
+    free((char *) font);
+}
+
+void
+bdf_create_property(char *name, int format)
+{
+    unsigned int n;
+    bdf_property_t *p;
+
+    /*
+     * First check to see if the property has
+     * already been added or not.  If it has, then
+     * simply ignore it.
+     */
+
+    if (hash_lookup(name, &proptbl))
+      return;
+
+    if (nuser_props == 0)
+      user_props = (bdf_property_t *) malloc(sizeof(bdf_property_t));
+    else
+      user_props = (bdf_property_t *) realloc((char *) user_props,
+                                              sizeof(bdf_property_t) *
+                                              (nuser_props + 1));
+
+    p = user_props + nuser_props;
+    (void) memset((char *) p, 0, sizeof(bdf_property_t));
+    n = (unsigned int) (strlen(name) + 1);
+    p->name = (char *) malloc(n);
+    (void) memcpy(p->name, name, n);
+    p->format = format;
+    p->builtin = 0;
+
+    n = _num_bdf_properties + nuser_props;
+    hash_insert(p->name, (void *) n, &proptbl);
+
+    nuser_props++;
+}
+
+bdf_property_t *
+bdf_get_property(char *name)
+{
+    hashnode hn;
+    unsigned int propid;
+
+    if (name == 0 || *name == 0)
+      return 0;
+
+    if ((hn = hash_lookup(name, &proptbl)) == 0)
+      return 0;
+
+    propid = (unsigned int) hn->data;
+    if (propid >= _num_bdf_properties)
+      return user_props + (propid - _num_bdf_properties);
+    return _bdf_properties + propid;
+}
+
+/*
+ * Routine to compare two property names.
+ */
+static int
+by_prop_name(const void *a, const void *b)
+{
+    bdf_property_t *p1, *p2;
+
+    p1 = (bdf_property_t *) a;
+    p2 = (bdf_property_t *) b;
+
+    return strcmp(p1->name, p2->name);
+}
+
+unsigned int
+bdf_property_list(bdf_property_t **props)
+{
+    unsigned int n;
+    bdf_property_t *p;
+
+    n = _num_bdf_properties + nuser_props;
+    if (props != 0 && n != 0) {
+        p = (bdf_property_t *) malloc(sizeof(bdf_property_t) * n);
+        (void) memcpy((char *) p, (char *) _bdf_properties,
+                      sizeof(bdf_property_t) * _num_bdf_properties);
+        (void) memcpy((char *) (p + _num_bdf_properties), (char *) user_props,
+                      sizeof(bdf_property_t) * nuser_props);
+        qsort((char *) p, n, sizeof(bdf_property_t), by_prop_name);
+        *props = p;
+    }
+    return n;
+}
+
+int
+bdf_replace_comments(bdf_font_t *font, char *comments,
+                     unsigned int comments_len)
+{
+    if (font == 0 || comments_len == 0)
+      return 0;
+
+    if (font->comments_len > 0)
+      free(font->comments);
+
+    font->comments = (char *) malloc(comments_len + 1);
+    (void) memcpy(font->comments, comments, comments_len);
+    font->comments[comments_len] = 0;
+    font->comments_len = comments_len;
+    font->modified = 1;
+    return 1;
+}
+
+unsigned int
+bdf_font_property_list(bdf_font_t *font, bdf_property_t **props)
+{
+    bdf_property_t *p;
+
+    if (font == 0 || font->props_used == 0)
+      return 0;
+
+    if (props != 0) {
+        p = (bdf_property_t *) malloc(sizeof(bdf_property_t) *
+                                      font->props_used);
+        (void) memcpy((char *) p, (char *) font->props,
+                      sizeof(bdf_property_t) * font->props_used);
+        qsort((char *) p, font->props_used, sizeof(bdf_property_t),
+              by_prop_name);
+        *props = p;
+    }
+
+    return font->props_used;
+}
+
+void
+bdf_add_font_property(bdf_font_t *font, bdf_property_t *property)
+{
+    int len;
+    unsigned int propid;
+    hashnode hn;
+    bdf_property_t *p, *ip;
+
+    if (property == 0 || property->name == 0 || property->name[0] == 0)
+      return;
+
+    /*
+     * If the font does not have a property hash table yet, make
+     * sure it is allocated.
+     */
+    if (font->internal == 0) {
+        font->internal = (void *) malloc(sizeof(hashtable));
+        hash_init((hashtable *) font->internal);
+    }
+
+    /*
+     * See if the property is in the general property table yet.
+     * If it isn't, then add it.
+     */
+    if ((hn = hash_lookup(property->name, &proptbl)) == 0)
+      bdf_create_property(property->name, property->format);
+    else {
+        /*
+         * If the property exists and is a user defined property, make sure
+         * its format is updated to match the property being added.
+         */
+        propid = (unsigned int) hn->data;
+        if (propid >= _num_bdf_properties) {
+            p = user_props + (propid - _num_bdf_properties);
+            if (p->format != property->format)
+              p->format = property->format;
+        }
+    }
+
+    /*
+     * If the font already has this property, then change the existing one.
+     */
+    hn = hash_lookup(property->name, (hashtable *) font->internal);
+    if (hn != 0) {
+        /*
+         * Changing an existing property value.
+         */
+        p = font->props + ((unsigned int) hn->data);
+
+        /*
+         * If the format changed, then free the atom value if the original
+         * format was an atom.
+         */
+        if (p->format == BDF_ATOM && property->format != BDF_ATOM &&
+            p->value.atom != 0)
+          free((char *) p->value.atom);
+        p->format = property->format;
+
+        switch (p->format) {
+          case BDF_ATOM:
+            /*
+             * If the property value is the same, then just return.
+             */
+            if (property->value.atom == p->value.atom ||
+                (property->value.atom && p->value.atom &&
+                 strcmp(property->value.atom, p->value.atom) == 0))
+              return;
+            if (property->value.atom == 0)
+              len = 1;
+            else
+              len = strlen(property->value.atom) + 1;
+            if (len > 1) {
+                p->value.atom = (char *) malloc(len);
+                (void) memcpy(p->value.atom, property->value.atom, len);
+            } else
+              p->value.atom = 0;
+            break;
+          case BDF_INTEGER:
+            /*
+             * If the property value is the same, then just return.
+             */
+            if (p->value.int32 == property->value.int32)
+              return;
+            p->value.int32 = property->value.int32;
+            break;
+          case BDF_CARDINAL:
+            /*
+             * If the property value is the same, then just return.
+             */
+            if (p->value.card32 == property->value.card32)
+              return;
+            p->value.card32 = property->value.card32;
+            break;
+        }
+    } else {
+        /*
+         * New property being added.
+         */
+
+        /*
+         * Get the internal table entry for a pointer to the
+         * name of the property.
+         */
+        hn = hash_lookup(property->name, &proptbl);
+        propid = (unsigned int) hn->data;
+        if (propid >= _num_bdf_properties)
+          ip = user_props + (propid - _num_bdf_properties);
+        else
+          ip = _bdf_properties + propid;
+
+        /*
+         * Add it to the property list first.
+         */
+        if (font->props_used == font->props_size) {
+            if (font->props_size == 0)
+              font->props = (bdf_property_t *) malloc(sizeof(bdf_property_t));
+            else
+              font->props = (bdf_property_t *)
+                realloc((char *) font->props, sizeof(bdf_property_t) *
+                        (font->props_size + 1));
+            font->props_size++;
+        }
+        p = font->props + font->props_used;
+
+        p->name = ip->name;
+        p->format = ip->format;
+        p->builtin = ip->builtin;
+
+        switch (p->format) {
+          case BDF_ATOM:
+            if (property->value.atom == 0)
+              len = 1;
+            else
+              len = strlen(property->value.atom) + 1;
+            if (len > 1) {
+                p->value.atom = (char *) malloc(len);
+                (void) memcpy(p->value.atom, property->value.atom, len);
+            } else
+              p->value.atom = 0;
+            break;
+          case BDF_INTEGER:
+            p->value.int32 = property->value.int32;
+            break;
+          case BDF_CARDINAL:
+            p->value.card32 = property->value.card32;
+            break;
+        }
+
+        /*
+         * Now insert it into the internal hash table.
+         */
+        hash_insert(p->name, (void *) font->props_used,
+                    (hashtable *) font->internal);
+        font->props_used++;
+    }
+
+    if (memcmp(property->name, "DEFAULT_CHAR", 12) == 0)
+      /*
+       * If the property just added is DEFAULT_CHAR, then make sure the
+       * default_glyph field is set.
+       */
+      font->default_glyph = p->value.card32;
+    else if (memcmp(property->name, "FONT_ASCENT", 11) == 0)
+      /*
+       * If the property just added is FONT_ASCENT, then adjust the
+       * font_ascent field.
+       */
+      font->font_ascent = p->value.int32;
+    else if (memcmp(property->name, "FONT_DESCENT", 12) == 0)
+      /*
+       * If the property just added is FONT_DESCENT, then adjust the
+       * font_descent field.
+       */
+      font->font_descent = p->value.int32;
+    else if (memcmp(property->name, "RESOLUTION_X", 12) == 0)
+      /*
+       * If the property just added is RESOLUTION_X, then adjust the
+       * resolution_x field.
+       */
+      font->resolution_x = p->value.card32;
+    else if (memcmp(property->name, "RESOLUTION_Y", 12) == 0)
+      /*
+       * If the property just added is RESOLUTION_Y, then adjust the
+       * resolution_y field.
+       */
+      font->resolution_y = p->value.card32;
+    else if (memcmp(property->name, "POINT_SIZE", 10) == 0)
+      /*
+       * If the property just added is POINT_SIZE, then adjust the
+       * point_size field.
+       */
+      font->point_size = p->value.int32 / 10;
+    else if (memcmp(property->name, "SPACING", 7) == 0) {
+        /*
+         * Make sure the font spacing is kept in synch if the property
+         * changes.  If the spacing changes from proportional to one
+         * of the others, force the monowidth to be set.
+         */
+        switch (p->value.atom[0]) {
+          case 'C': case 'c':
+            if (font->spacing == BDF_PROPORTIONAL)
+              font->monowidth = font->bbx.width + font->bbx.x_offset;
+            font->spacing = BDF_CHARCELL;
+            break;
+          case 'M': case 'm':
+            if (font->spacing == BDF_PROPORTIONAL)
+              font->monowidth = font->bbx.width + font->bbx.x_offset;
+            font->spacing = BDF_MONOWIDTH;
+            break;
+          case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break;
+        }
+    }
+
+    /*
+     * Make sure the font is marked as modified.
+     */
+    font->modified = 1;
+}
+
+void
+bdf_delete_font_property(bdf_font_t *font, char *name)
+{
+    hashnode hn;
+    unsigned int off;
+    bdf_property_t *p;
+
+    if (font == 0 || name == 0 || *name == 0 || font->props_used == 0)
+      return;
+
+    if ((hn = hash_lookup(name, (hashtable *) font->internal)) == 0)
+      return;
+
+    off = (unsigned int) hn->data;
+    p = font->props + off;
+
+    /*
+     * Delete the ATOM value if appropriate.
+     */
+    if (p->format == BDF_ATOM && p->value.atom != 0)
+      free(p->value.atom);
+
+    /*
+     * The property exists.  Two things needs to be done:
+     * 1. Remove the property from the hash table.
+     * 2. Remove the property from the font's list of properties.
+     */
+    hash_delete(name, (hashtable *) font->internal);
+
+    /*
+     * Locate its offset in the font property list.
+     */
+    if (off < font->props_used - 1)
+      /*
+       * We have to shift the property list down.
+       */
+      _bdf_memmove((char *) p, (char *) (p + 1),
+                   sizeof(bdf_property_t) * ((font->props_used - 1) - off));
+    font->props_used--;
+
+    /*
+     * If the font property happens to be DEFAULT_CHAR, then make sure the
+     * default_glyph field is reset.
+     */
+    if (strncmp(name, "DEFAULT_CHAR", 12) == 0)
+      font->default_glyph = -1;
+
+    /*
+     * Update the hash table with the correct indexes.
+     */
+    for (off = 0, p = font->props; off < font->props_used; off++, p++)
+      hash_insert(p->name, (void *) off, (hashtable *) font->internal);
+
+    /*
+     * Mark the font as being modified.
+     */
+    font->modified = 1;
+}
+
+bdf_property_t *
+bdf_get_font_property(bdf_font_t *font, char *name)
+{
+    hashnode hn;
+
+    if (font == 0 || font->props_size == 0 || name == 0 || *name == 0)
+      return 0;
+
+    hn = hash_lookup(name, (hashtable *) font->internal);
+    return (hn) ? (font->props + ((unsigned int) hn->data)) : 0;
+}
+
+typedef struct {
+    bdf_options_t *opts;
+    bdf_options_callback_t callback;
+    void *client_data;
+    _bdf_list_t list;
+} _bdf_opts_parse_t;
+
+static int
+_bdf_get_boolean(char *val)
+{
+    int ok;
+
+    ok = 0;
+    if (val == 0 || *val == 0)
+      return ok;
+
+    switch (val[0]) {
+      case '0': case 'F': case 'f': case 'N': case 'n': ok = 0; break;
+      case '1': case 'T': case 't': case 'Y': case 'y': ok = 1; break;
+    }
+    return ok;
+}
+
+static int
+_bdf_parse_options(char *line, unsigned int linelen, unsigned int lineno,
+                   void *call_data, void *client_data)
+{
+    _bdf_list_t *lp;
+    _bdf_opts_parse_t *p;
+    int bpp;
+
+    p = (_bdf_opts_parse_t *) client_data;
+    lp = &p->list;
+
+    /*
+     * Split the line into fields.
+     */
+    _bdf_split(" \t+", line, linelen, lp);
+
+    if (lp->field[0][0] == 'b' &&
+        memcmp(lp->field[0], "bits_per_pixel", 14) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: bits_per_pixel <1, 2, or 4>.\n",
+                    lineno);
+        } else {
+            bpp = _bdf_atol(lp->field[1], 0, 10);
+            if (!(bpp == 1 || bpp == 2 || bpp == 4)) {
+                fprintf(stderr,
+                        "bdf: warning: %d: invalid bits per pixel %d.\n",
+                        lineno, bpp);
+                fprintf(stderr,
+                        "bdf: warning: %d: bits_per_pixel <1, 2, or 4>.\n",
+                        lineno);
+            } else
+              p->opts->bits_per_pixel = bpp;
+        }
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'e' && memcmp(lp->field[0], "eol", 3) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: eol <eolname>.\n", lineno);
+        } else {
+            switch (lp->field[1][0]) {
+              case 'u': case 'U': p->opts->eol = BDF_UNIX_EOL; break;
+              case 'd': case 'D': p->opts->eol = BDF_DOS_EOL; break;
+              case 'm': case 'M': p->opts->eol = BDF_MAC_EOL; break;
+            }
+        }
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'c' &&
+        memcmp(lp->field[0], "correct_metrics", 15) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: correct_metrics <boolean>.\n", lineno);
+        } else
+          p->opts->correct_metrics = _bdf_get_boolean(lp->field[1]);
+
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'k' &&
+        memcmp(lp->field[0], "keep_unencoded", 14) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: keep_unencoded <boolean>.\n", lineno);
+        } else
+          p->opts->keep_unencoded = _bdf_get_boolean(lp->field[1]);
+
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'k' &&
+        memcmp(lp->field[0], "keep_comments", 13) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: keep_comments <boolean>.\n", lineno);
+        } else
+          p->opts->keep_comments = _bdf_get_boolean(lp->field[1]);
+
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'p' &&
+        memcmp(lp->field[0], "pad_character_cells", 19) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: pad_character_cells <boolean>.\n",
+                    lineno);
+        } else
+          p->opts->pad_cells = _bdf_get_boolean(lp->field[1]);
+
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'p' &&
+        memcmp(lp->field[0], "point_size", 10) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: point_size <integer>.\n", lineno);
+        } else
+          p->opts->point_size = _bdf_atol(lp->field[1], 0, 10);
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'h' &&
+        memcmp(lp->field[0], "horizontal_resolution", 21) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: horizontal_resolution <cardinal>.\n",
+                    lineno);
+        } else
+          p->opts->resolution_x = _bdf_atoul(lp->field[1], 0, 10);
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'v' &&
+        memcmp(lp->field[0], "vertical_resolution", 19) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: vertical_resolution <cardinal>.\n",
+                    lineno);
+        } else
+          p->opts->resolution_y = _bdf_atoul(lp->field[1], 0, 10);
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'f' &&
+        memcmp(lp->field[0], "font_spacing", 12) == 0) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: font_spacing <spacing name>.\n",
+                    lineno);
+        } else {
+            switch (lp->field[1][0]) {
+              case 'P': case 'p':
+                p->opts->font_spacing = BDF_PROPORTIONAL;
+                break;
+              case 'M': case 'm':
+                p->opts->font_spacing = BDF_MONOWIDTH;
+                break;
+              case 'C': case 'c':
+                p->opts->font_spacing = BDF_CHARCELL;
+                break;
+              default:
+                fprintf(stderr,
+                        "bdf: warning: %d: unknown font spacing '%s'.\n",
+                        lineno, lp->field[1]);
+            }
+        }
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'p' &&
+        memcmp(lp->field[0], "property", 8) == 0) {
+        if (lp->used < 3) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: property <name> <type>.\n",
+                    lineno);
+        } else {
+            switch (lp->field[2][0]) {
+              case 'A': case 'a':
+                bdf_create_property(lp->field[1], BDF_ATOM);
+                break;
+              case 'C': case 'c':
+                bdf_create_property(lp->field[1], BDF_CARDINAL);
+                break;
+              case 'I': case 'i':
+                bdf_create_property(lp->field[1], BDF_INTEGER);
+                break;
+              default:
+                fprintf(stderr,
+                        "bdf: warning: %d: unknown property type '%s'.\n",
+                        lineno, lp->field[2]);
+            }
+        }
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'h' &&
+        (memcmp(lp->field[0], "hint_truetype_glyphs", 20) == 0 ||
+         memcmp(lp->field[0], "hint_opentype_glyphs", 20) == 0)) {
+        if (lp->used < 2) {
+            fprintf(stderr,
+                    "bdf: warning: %d: incorrect number of fields %d.\n",
+                    lineno, lp->used);
+            fprintf(stderr,
+                    "bdf: warning: %d: hint_opentype_glyphs <boolean>.\n",
+                    lineno);
+        } else {
+#ifdef HAVE_FREETYPE
+            if (_bdf_get_boolean(lp->field[1]))
+              p->opts->otf_flags &= ~FT_LOAD_NO_HINTING;
+            else
+              p->opts->otf_flags |= FT_LOAD_NO_HINTING;
+#else
+            p->opts->otf_flags = 0;
+#endif /* HAVE_FREETYPE */
+        }
+
+        return 0;
+    }
+
+    if (lp->field[0][0] == 'g' &&
+        memcmp(lp->field[0], "generate_ranges", 15) == 0)
+      /*
+       * Simply ignore the glyph ranges entry in the config file.
+       */
+      return 0;
+
+    /*
+     * If the callback returns a non-zero value, the caller has handled the
+     * unknown option found in the file.
+     */
+    if (p->callback != 0 &&
+        (*p->callback)(p->opts, lp->field, lp->used, p->client_data) != 0)
+      return 0;
+
+    fprintf(stderr, "bdf: warning: %d: unknown configuration option '%s'.\n",
+            lineno, lp->field[0]);
+    return 0;
+}
+
+void
+bdf_load_options(FILE *in, bdf_options_t *opts,
+                 bdf_options_callback_t callback, void *client_data)
+{
+    unsigned int lineno;
+    _bdf_opts_parse_t p;
+
+    /*
+     * Don't bother loading the options if the file or options structure
+     * is NULL.
+     */
+    if (in == 0 || opts == 0)
+      return;
+
+    (void *) memset((char *) &p, 0, sizeof(_bdf_opts_parse_t));
+    p.opts = opts;
+    p.callback = callback;
+    p.client_data = client_data;
+    (void) _bdf_readlines(fileno(in), _bdf_parse_options, (void *) &p,
+                          &lineno);
+
+    /*
+     * Free up the list if there is any space allocated.
+     */
+    if (p.list.size > 0)
+      free((char *) p.list.field);
+}
+
+void
+bdf_save_options(FILE *out, bdf_options_t *opts)
+{
+    unsigned int i;
+
+    if (out == 0 || opts == 0)
+      return;
+
+    fprintf(out, "#\n# Metrics corrections.\n#\ncorrect_metrics ");
+    if (opts->correct_metrics)
+      fprintf(out, "true\n\n");
+    else
+      fprintf(out, "false\n\n");
+
+    fprintf(out, "#\n# Preserve unencoded glyphs.\n#\nkeep_unencoded ");
+    if (opts->keep_unencoded)
+      fprintf(out, "true\n\n");
+    else
+      fprintf(out, "false\n\n");
+
+    fprintf(out, "#\n# Preserve comments.\n#\nkeep_comments ");
+    if (opts->keep_comments)
+      fprintf(out, "true\n\n");
+    else
+      fprintf(out, "false\n\n");
+
+    fprintf(out, "#\n# Pad character cells.\n#\npad_character_cells ");
+    if (opts->pad_cells)
+      fprintf(out, "true\n\n");
+    else
+      fprintf(out, "false\n\n");
+
+    fprintf(out, "#\n# Font spacing.\n#\nfont_spacing ");
+    switch (opts->font_spacing) {
+      case BDF_PROPORTIONAL: fprintf(out, "proportional\n\n"); break;
+      case BDF_MONOWIDTH: fprintf(out, "monowidth\n\n"); break;
+      case BDF_CHARCELL: fprintf(out, "charactercell\n\n"); break;
+    }
+
+    fprintf(out, "#\n# Point size.\n#\npoint_size %d\n\n", opts->point_size);
+
+    fprintf(out,
+            "#\n# Horizontal resolution.\n#\nhorizontal_resolution %d\n\n",
+            opts->resolution_x);
+
+    fprintf(out,
+            "#\n# Vertical resolution.\n#\nvertical_resolution %d\n\n",
+            opts->resolution_x);
+
+    fprintf(out,
+            "#\n# Bits per pixel.\n#\nbits_per_pixel %d\n\n",
+            opts->bits_per_pixel);
+
+    fprintf(out, "#\n# Hint OpenType glyphs.\n#\nhint_opentype_glyphs ");
+#ifdef HAVE_FREETYPE
+    if (opts->otf_flags & FT_LOAD_NO_HINTING)
+      fprintf(out, "false\n\n");
+    else
+      fprintf(out, "true\n\n");
+#else
+    fprintf(out, "false\n\n");
+#endif /* HAVE_FREETYPE */
+
+    fprintf(out, "#\n# Set the EOL used when writing BDF fonts.\n#\neol ");
+    switch (opts->eol) {
+      case BDF_UNIX_EOL: fprintf(out, "unix\n\n"); break;
+      case BDF_DOS_EOL: fprintf(out, "dos\n\n"); break;
+      case BDF_MAC_EOL: fprintf(out, "mac\n\n"); break;
+    }
+
+    /*
+     * Write out the user defined properties if they exist.
+     */
+    if (nuser_props == 0)
+      return;
+
+    fprintf(out, "#\n# User defined properties.\n#\n");
+
+    for (i = 0; i < nuser_props; i++) {
+        fprintf(out, "property %s ", user_props[i].name);
+        switch (user_props[i].format) {
+          case BDF_ATOM: fprintf(out, "atom\n"); break;
+          case BDF_CARDINAL: fprintf(out, "cardinal\n"); break;
+          case BDF_INTEGER: fprintf(out, "integer\n"); break;
+        }
+    }
+}
+
+void
+bdf_default_options(bdf_options_t *opts)
+{
+    if (opts == 0)
+      return;
+
+    (void) memcpy((char *) opts, (char *) &_bdf_opts, sizeof(bdf_options_t));
+}
+
+bdf_font_t *
+bdf_new_font(char *name, int point_size, int resolution_x, int resolution_y,
+             int spacing, int bpp)
+{
+    int psize;
+    char sp[2];
+    bdf_font_t *font;
+    double dp, dr;
+    bdf_property_t prop;
+
+    font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t));
+    if (name != 0 && *name != 0) {
+        font->name = (char *) malloc(strlen(name) + 1);
+        (void) strcpy(font->name, name);
+    }
+
+    font->bpp = bpp;
+    font->point_size = point_size;
+    font->resolution_x = resolution_x;
+    font->resolution_y = resolution_y;
+
+    /*
+     * Determine the pixel size of the new font based on the
+     * point size and resolution.
+     */
+    dr = (double) resolution_y;
+    dp = (double) (point_size * 10);
+    psize = (int) (((dp * dr) / 722.7) + 0.5);
+
+    /*
+     * Make the default width about 1.5 smaller than the height.
+     */
+    font->bbx.height = psize;
+    font->bbx.width = ((double) psize) / 1.5;
+
+    /*
+     * Now determine the default ascent and descent assuming a
+     * the descent is about 1/4 the ascent.
+     */
+    font->bbx.descent = psize >> 2;
+    font->bbx.ascent = psize - font->bbx.descent;
+
+    font->bbx.y_offset = -font->bbx.descent;
+
+    /*
+     * Allocation the internal hash tables.
+     */
+    font->internal = (void *) malloc(sizeof(hashtable));
+    hash_init((hashtable *) font->internal);
+
+    font->default_glyph = -1;
+    font->spacing = spacing;
+
+    /*
+     * Add various useful properties.
+     */
+    prop.name = "POINT_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = font->point_size * 10;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "PIXEL_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = psize;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "RESOLUTION_X";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) font->resolution_x;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "RESOLUTION_Y";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) font->resolution_y;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int) font->bbx.ascent;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int) font->bbx.descent;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "AVERAGE_WIDTH";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = font->bbx.width * 10;
+    bdf_add_font_property(font, &prop);
+
+    sp[0] = 'P';
+    sp[1] = 0;
+    switch (spacing) {
+      case BDF_PROPORTIONAL: sp[0] = 'P'; break;
+      case BDF_MONOWIDTH: sp[0] = 'M'; break;
+      case BDF_CHARCELL: sp[0] = 'C'; break;
+    }
+    prop.name = "SPACING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = sp;
+    bdf_add_font_property(font, &prop);
+
+    /*
+     * Mark the font as unmodified.
+     */
+    font->modified = 0;
+
+    return font;
+}
+
+void
+bdf_set_default_metrics(bdf_font_t *font)
+{
+    int psize;
+    double dp, dr;
+    bdf_property_t prop;
+
+    /*
+     * Determine the pixel size of the new font based on the
+     * point size and resolution.
+     */
+    dr = (double) font->resolution_y;
+    dp = (double) (font->point_size * 10);
+    psize = (int) (((dp * dr) / 722.7) + 0.5);
+
+    /*
+     * Make the default width about 1.5 smaller than the height.
+     */
+    font->bbx.height = psize;
+    font->bbx.width = ((double) psize) / 1.5;
+
+    /*
+     * Now determine the default ascent and descent assuming a
+     * the descent is about 1/4 the ascent.
+     */
+    font->bbx.descent = psize >> 2;
+    font->bbx.ascent = psize - font->bbx.descent;
+
+    font->bbx.y_offset = -font->bbx.descent;
+
+    font->default_glyph = -1;
+
+    /*
+     * Add various useful properties.
+     */
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int) font->bbx.ascent;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int) font->bbx.descent;
+    bdf_add_font_property(font, &prop);
+
+    prop.name = "AVERAGE_WIDTH";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = font->bbx.width * 10;
+    bdf_add_font_property(font, &prop);
+}
+
+int
+bdf_glyph_modified(bdf_font_t *font, int which, int unencoded)
+{
+    if (font == 0 || which < 0)
+      return 0;
+
+    if (unencoded)
+      return _bdf_glyph_modified(font->umod, which);
+    else
+      return _bdf_glyph_modified(font->nmod, which);
+}
+
+void
+bdf_copy_glyphs(bdf_font_t *font, int start, int end,
+                bdf_glyphlist_t *glyphs, int unencoded)
+{
+    int tmp, i, nc;
+    bdf_glyph_t *cp, *dp;
+    short maxas, maxds, maxrb, minlb, maxlb, rb;
+
+    if (start > end) {
+        tmp = end;
+        end = start;
+        start = tmp;
+    }
+
+    glyphs->bpp = font->bpp;
+    glyphs->start = start;
+    glyphs->end = end;
+    glyphs->glyphs_used = 0;
+
+    tmp = (end - start) + 1;
+    if (tmp > glyphs->glyphs_size) {
+        if (glyphs->glyphs_size == 0)
+          glyphs->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * tmp);
+        else
+          glyphs->glyphs = (bdf_glyph_t *) realloc((char *) glyphs->glyphs,
+                                                   sizeof(bdf_glyph_t) * tmp);
+        cp = glyphs->glyphs + glyphs->glyphs_size;
+        (void) memset((char *) cp, 0,
+                      sizeof(bdf_glyph_t) * (tmp - glyphs->glyphs_size));
+        glyphs->glyphs_size = tmp;
+    }
+
+    /*
+     * Clear out bitmaps, names and any PSF Unicode mappings in the existing
+     * entries.
+     */
+    for (cp = glyphs->glyphs, i = 0; i < glyphs->glyphs_size; i++, cp++) {
+        if (cp->name != 0)
+          free(cp->name);
+        if (cp->bytes > 0)
+          free((char *) cp->bitmap);
+        if (cp->unicode.map_size > 0)
+          free((char *) cp->unicode.map);
+    }
+
+    /*
+     * Zero out everything.
+     */
+    (void) memset((char *) &glyphs->bbx, 0, sizeof(bdf_bbx_t));
+    (void) memset((char *) glyphs->glyphs, 0,
+                  sizeof(bdf_glyph_t) * glyphs->glyphs_size);
+
+    /*
+     * Initialize the bounds used to generate the overall bounding box for the
+     * set of glyphs being copied.
+     */
+    minlb = font->bbx.width;
+    maxlb = maxrb = maxas = maxds = 0;
+
+    /*
+     * Do the copy.
+     */
+    nc = (unencoded == 0) ? font->glyphs_used : font->unencoded_used;
+    cp = (unencoded == 0) ? font->glyphs : font->unencoded;
+    dp = glyphs->glyphs;
+
+    for (i = 0;
+         i < nc && ((unencoded && i <= end) || cp->encoding <= end);
+         i++, cp++) {
+        if ((unencoded && i >= start) || cp->encoding >= start) {
+            (void) memcpy((char *) dp, (char *) cp, sizeof(bdf_glyph_t));
+            if (cp->name != 0) {
+                dp->name = (char *) malloc(strlen(cp->name) + 1);
+                (void) strcpy(dp->name, cp->name);
+            }
+            if (cp->bytes > 0) {
+                dp->bytes = cp->bytes;
+                dp->bitmap = (unsigned char *) malloc(cp->bytes);
+                (void) memcpy((char *) dp->bitmap, (char *) cp->bitmap,
+                              cp->bytes);
+            }
+            if (cp->unicode.map_used > 0) {
+                dp->unicode.map_used = dp->unicode.map_size =
+                    cp->unicode.map_used;
+                dp->unicode.map =
+                    (unsigned char *) malloc(dp->unicode.map_used);
+                (void) memcpy((char *) dp->unicode.map,
+                              (char *) cp->unicode.map,
+                              dp->unicode.map_used);
+            }
+
+            /*
+             * Determine the overall metrics for the group of characters being
+             * copied.
+             */
+            maxas = MAX(cp->bbx.ascent, maxas);
+            maxds = MAX(cp->bbx.descent, maxds);
+            rb = cp->bbx.width + cp->bbx.x_offset;
+            maxrb = MAX(rb, maxrb);
+            minlb = MIN(cp->bbx.x_offset, minlb);
+            maxlb = MAX(cp->bbx.x_offset, maxlb);
+
+            glyphs->glyphs_used++;
+            dp++;
+        }
+    }
+
+    /*
+     * Set the overall metrics for this set of glyphs.
+     */
+    glyphs->bbx.width = maxrb - minlb;
+    glyphs->bbx.x_offset = minlb;
+
+    glyphs->bbx.height = maxas + maxds;
+    glyphs->bbx.ascent = maxas;
+    glyphs->bbx.descent = maxds;
+    glyphs->bbx.y_offset = -maxds;
+}
+
+bdf_glyph_t *
+_bdf_locate_glyph(bdf_font_t *font, int code, int unencoded)
+{
+    int l, r, m, nc;
+    bdf_glyph_t *gl;
+
+    if (code < 0 || font == 0)
+      return 0;
+
+    if ((unencoded && font->unencoded_used == 0) ||
+        font->glyphs_used == 0)
+      return 0;
+
+    if (unencoded) {
+        gl = font->unencoded;
+        nc = font->unencoded_used;
+    } else {
+        gl = font->glyphs;
+        nc = font->glyphs_used;
+    }
+    for (l = m = 0, r = nc - 1; l < r; ) {
+        m = (l + r) >> 1;
+        if (gl[m].encoding < code)
+          l = m + 1;
+        else if (gl[m].encoding > code)
+          r = m - 1;
+        else
+          break;
+    }
+
+    /*
+     * Go back until we hit the beginning of the glyphs or until
+     * we find the glyph with a code less than the specified code.
+     */
+    l = m;
+    while (m > 0 && gl[m].encoding > code)
+      m--;
+
+    /*
+     * Look forward if necessary.
+     */
+    m = l;
+    while (m < nc && gl[m].encoding < code)
+      m++;
+
+    return (m < nc) ? &gl[m] : &gl[nc - 1];
+}
+
+int
+bdf_delete_glyphs(bdf_font_t *font, int start, int end, int unencoded)
+{
+    int i, n, nc, cnt, mod;
+    bdf_glyph_t *cp, *sp, *ep;
+
+    mod = 0;
+
+    if (font == 0)
+      return mod;
+
+    if (start > end) {
+        cnt = end;
+        end = start;
+        start = cnt;
+    }
+
+    nc = (unencoded == 0) ? font->glyphs_used : font->unencoded_used;
+    cp = (unencoded == 0) ? font->glyphs : font->unencoded;
+    sp = ep = 0;
+
+    for (i = 0; i < nc && cp->encoding <= end; i++, cp++) {
+        if (cp->encoding >= start && sp == 0)
+          sp = cp;
+    }
+    ep = cp;
+    if (sp == 0)
+      sp = ep;
+
+    if (ep > sp) {
+        /*
+         * There are some glyphs to delete.
+         * 1. Free the name and bitmap fields of the glyphs being deleted.
+         * 2. Move the end range down if necessary.
+         * 3. Clear the glyphs on the end if a move was done.
+         */
+
+        /*
+         * Mark the font as being modified.
+         */
+        mod = font->modified = 1;
+
+        cnt = ep - sp;
+
+        for (cp = sp; cp < ep; cp++) {
+            /*
+             * Mark the glyphs being deleted as also being modified so the
+             * empty cells can be shown correctly by the client programs.
+             */
+            if (unencoded)
+              _bdf_set_glyph_modified(font->umod, cp->encoding);
+            else
+              _bdf_set_glyph_modified(font->nmod, cp->encoding);
+
+            if (cp->name != 0)
+              free(cp->name);
+            if (cp->bytes > 0)
+              free((char *) cp->bitmap);
+        }
+
+        cp = (unencoded == 0) ? font->glyphs : font->unencoded;
+
+        /*
+         * Check to see if there are some glyphs that need to
+         * be moved down.
+         */
+        if (ep - cp < nc) {
+            /*
+             * Shift the glyphs down.
+             */
+            n = nc - (ep - cp);
+            _bdf_memmove((char *) sp, (char *) ep, sizeof(bdf_glyph_t) * n);
+
+            /*
+             * Set the starting point for the clear.
+             */
+            ep = sp + n;
+        } else
+          /*
+           * Set the starting point for the clear.
+           */
+          ep = sp;
+
+        /*
+         * Clear the glyph space just moved.
+         */
+        n = nc - (ep - cp);
+        (void) memset((char *) ep, 0, sizeof(bdf_glyph_t) * n);
+
+        /*
+         * Adjust the number of glyphs used.
+         */
+        if (unencoded == 0)
+          font->glyphs_used -= cnt;
+        else
+          font->unencoded_used -= cnt;
+
+        /*
+         * If unencoded glyphs were deleted, re-encode all
+         * of them to cause a shift when everything is redrawn.
+         */
+        if (unencoded != 0) {
+            for (i = 0, cp = font->unencoded; i < font->unencoded_used;
+                 i++, cp++) {
+                if (_bdf_glyph_modified(font->umod, cp->encoding)) {
+                    _bdf_clear_glyph_modified(font->umod, cp->encoding);
+                    _bdf_set_glyph_modified(font->umod, i);
+                }
+                cp->encoding = i;
+            }
+        }
+    }
+    return mod;
+}
+
+/*
+ * These values are intended to give pixels mapped from 1bpp to nbpp the
+ * darkest available index, which is 1.
+ */
+static unsigned char twobpp_ones[] = {0x40, 0x10, 0x04, 0x01};
+static unsigned char fourbpp_ones[] = {0x10, 0x01};
+static unsigned char eightbpp_ones[] = {0x01};
+
+/*
+ * Routines for quick and dirty dithering.
+ */
+static void
+_bdf_one_to_n(bdf_glyphlist_t *gl, int n)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, sx, sy;
+    unsigned char *nbmap, *ones = 0;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    switch (n) {
+      case 1: ones = bdf_onebpp; break;
+      case 2: ones = twobpp_ones; break;
+      case 4: ones = fourbpp_ones; break;
+      case 8: ones = eightbpp_ones; break;
+    }
+
+    gl->bpp = n;
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        if (gp->bbx.width == 0 || gp->bbx.height == 0)
+          continue;
+        sbpr = (gp->bbx.width + 7) >> 3;
+        bpr = ((gp->bbx.width * n) + 7) >> 3;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += n) {
+                if (gp->bitmap[(sy * sbpr) + (sx >> 3)] & (0x80 >> (sx & 7)))
+                  nbmap[(sy * bpr) + (col >> 3)] |= ones[(col & 7) / n];
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+}
+
+static void
+_bdf_n_to_one(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = 0;
+    switch (gl->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        if (gp->bbx.width == 0 || gp->bbx.height == 0)
+          continue;
+        sbpr = ((gp->bbx.width * gl->bpp) + 7) >> 3;
+        bpr = (gp->bbx.width + 7) >> 3;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += gl->bpp) {
+                if (gp->bitmap[(sy * sbpr) + (col >> 3)] &
+                    masks[(col & 7) / gl->bpp])
+                  nbmap[(sy * bpr) + (sx >> 3)] |= (0x80 >> (sx & 7));
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+    gl->bpp = 1;
+}
+
+static void
+_bdf_two_to_four(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = bdf_twobpp;
+
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        if (gp->bbx.width == 0 || gp->bbx.height == 0)
+          continue;
+        sbpr = ((gp->bbx.width << 1) + 7) >> 3;
+        bpr = ((gp->bbx.width << 2) + 7) >> 3;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) {
+                si = (col & 7) >> 1;
+                byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    if (si < 3)
+                      byte >>= (3 - si) << 1;
+                    byte <<= 2;
+                    if ((sx & 1) == 0)
+                      byte <<= 4;
+                    nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte;
+                }
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+    gl->bpp = 4;
+}
+
+static void
+_bdf_four_to_two(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = bdf_fourbpp;
+
+    gl->bpp = 2;
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        sbpr = ((gp->bbx.width << 2) + 7) >> 3;
+        bpr = ((gp->bbx.width << 1) + 7) >> 3;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += 4) {
+                si = (col & 7) >> 2;
+                byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    /*
+                     * Shift the byte down to make an index.
+                     */
+                    if (si == 0)
+                      byte >>= 4;
+
+                    /*
+                     * Scale the index to two bits per pixel and shift it into
+                     * place if necessary.
+                     */
+                    byte >>= 2;
+                    /*
+                     * Any non-zero byte has to remain non-zero, because index
+                     * zero means no bits set.
+                     */
+                    if (byte == 0)
+                      byte = 1;
+
+                    si = ((sx << 1) & 7) >> 1;
+                    if (si < 3)
+                      byte <<= (3 - si) << 1;
+
+                    nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte;
+                }
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+}
+
+static void
+_bdf_two_to_eight(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = bdf_twobpp;
+
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        if (gp->bbx.width == 0 || gp->bbx.height == 0)
+          continue;
+        sbpr = ((gp->bbx.width << 1) + 7) >> 3;
+        bpr = gp->bbx.width;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) {
+                si = (col & 7) >> 1;
+                byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    /*
+                     * Shift the byte down to make an index.
+                     */
+                    if (si < 3)
+                      byte >>= (3 - si) * gl->bpp;
+
+                    /*
+                     * Scale the index to four bits per pixel and shift it into
+                     * place before adding it.
+                     */
+                    byte <<= 6;
+                    nbmap[(sy * bpr) + sx] = byte;
+                }
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+    gl->bpp = 8;
+}
+
+static void
+_bdf_eight_to_two(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = bdf_fourbpp;
+
+    gl->bpp = 2;
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        sbpr = gp->bbx.width;
+        bpr = ((gp->bbx.width << 1) + 7) >> 3;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (sx = 0; sx < gp->bbx.width; sx++) {
+                byte = gp->bitmap[(sy * sbpr) + sx];
+                if (byte) {
+                    byte >>= 6;
+                    if (byte == 0)
+                      byte = 1;
+
+                    si = ((sx << 1) & 7) >> 1;
+                    if (si < 3)
+                      byte <<= (3 - si) << 1;
+
+                    nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte;
+                }
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+}
+
+static void
+_bdf_four_to_eight(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = bdf_fourbpp;
+
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        if (gp->bbx.width == 0 || gp->bbx.height == 0)
+          continue;
+        sbpr = ((gp->bbx.width << 2) + 7) >> 3;
+        bpr = gp->bbx.width;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += 4) {
+                si = (col & 7) >> 2;
+                byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    if (si == 0)
+                      byte >>= 4;
+
+                    byte <<= 4;
+                    nbmap[(sy * bpr) + sx] = byte;
+                }
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+    gl->bpp = 8;
+}
+
+static void
+_bdf_eight_to_four(bdf_glyphlist_t *gl)
+{
+    int i;
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+    bdf_glyph_t *gp;
+
+    if (gl == 0 || gl->glyphs_used == 0)
+      return;
+
+    masks = bdf_twobpp;
+
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) {
+        if (gp->bbx.width == 0 || gp->bbx.height == 0)
+          continue;
+        sbpr = gp->bbx.width;
+        bpr = ((gp->bbx.width << 2) + 7) >> 3;
+        bytes = bpr * gp->bbx.height;
+        nbmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) nbmap, 0, bytes);
+
+        for (sy = 0; sy < gp->bbx.height; sy++) {
+            for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) {
+                byte = gp->bitmap[(sy * sbpr) + sx];
+                if (byte) {
+                    byte >>= 4;
+                    if (byte == 0)
+                      byte = 1;
+
+                    /*
+                     * Scale the index to four bits per pixel and shift it into
+                     * place before adding it.
+                     */
+                    si = (col & 7) >> 2;
+                    if (si == 0)
+                      byte <<= 4;
+                    nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte;
+                }
+            }
+        }
+        free((char *) gp->bitmap);
+        gp->bytes = bytes;
+        gp->bitmap = nbmap;
+    }
+    gl->bpp = 4;
+}
+
+/*
+ * This only works on glyphs that exist.
+ */
+int
+bdf_replace_mappings(bdf_font_t *font, int encoding, bdf_psf_unimap_t *map,
+                     int unencoded)
+{
+    bdf_glyph_t *gp;
+
+    if ((gp = _bdf_locate_glyph(font, encoding, unencoded)) == 0 ||
+        gp->encoding != encoding)
+      return 0;
+
+    if (map->map_size > gp->unicode.map_size) {
+        if (gp->unicode.map_size == 0)
+          gp->unicode.map = (unsigned char *)
+              malloc(sizeof(unsigned char) * map->map_size);
+        else
+          gp->unicode.map =(unsigned char *)
+              realloc((char *) gp->unicode.map,
+                      sizeof(unsigned char) * map->map_size);
+        gp->unicode.map_size = map->map_size;
+    }
+    gp->unicode.map_used = map->map_used;
+    (void) memcpy((char *) gp->unicode.map, (char *) map->map,
+                  sizeof(unsigned char) * map->map_used);
+
+    /*
+     * Mark the glyph as modified.
+     */
+    if (unencoded)
+      _bdf_set_glyph_modified(font->umod, gp->encoding);
+    else
+      _bdf_set_glyph_modified(font->nmod, gp->encoding);
+
+    font->modified = 1;
+
+    return 1;
+}
+
+int
+bdf_replace_glyphs(bdf_font_t *font, int start, bdf_glyphlist_t *glyphs,
+                   int unencoded)
+{
+    int resize, appending;
+    int i, n, ng, end, del, remaining, off[2];
+    bdf_glyph_t *sgp, *gp, *dgp;
+    short maxas, maxds, maxrb, minlb, maxlb, rb;
+    double ps, rx, dw;
+    bdf_bbx_t nbbx;
+
+    resize = 0;
+
+    if (font == 0)
+      return resize;
+
+    /*
+     * Dither the incoming bitmaps so they match the same bits per pixel as
+     * the font.
+     */
+    if (glyphs->bpp != font->bpp) {
+        if (glyphs->bpp == 1)
+          _bdf_one_to_n(glyphs, font->bpp);
+        else if (font->bpp == 1)
+          _bdf_n_to_one(glyphs);
+        else if (glyphs->bpp == 2) {
+            if (font->bpp == 4)
+              _bdf_two_to_four(glyphs);
+            else
+              _bdf_two_to_eight(glyphs);
+        } else if (glyphs->bpp == 4) {
+            if (font->bpp == 2)
+              _bdf_four_to_two(glyphs);
+            else
+              _bdf_four_to_eight(glyphs);
+        } else if (glyphs->bpp == 8) {
+            if (font->bpp == 2)
+              _bdf_eight_to_two(glyphs);
+            else
+              _bdf_eight_to_four(glyphs);
+        }
+    }
+
+    /*
+     * Set the point size and horizontal resolution so the scalable width can
+     * be determined.
+     */
+    ps = (double) font->point_size;
+    rx = (double) font->resolution_x;
+
+    /*
+     * Determine if a resize is needed.
+     */
+
+    /*
+     * Determine the bounding box for the font without the characters being
+     * replaced.
+     */
+    minlb = 32767;
+    maxlb = maxrb = maxas = maxds = 0;
+
+    /*
+     * Get the font bounds.
+     */
+    maxas = MAX(font->bbx.ascent, maxas);
+    maxds = MAX(font->bbx.descent, maxds);
+    rb = font->bbx.width + font->bbx.x_offset;
+    maxrb = MAX(rb, maxrb);
+    minlb = MIN(font->bbx.x_offset, minlb);
+    maxlb = MAX(font->bbx.x_offset, maxlb);
+
+    /*
+     * Get the bounds of the incoming glyphs.
+     */
+    maxas = MAX(glyphs->bbx.ascent, maxas);
+    maxds = MAX(glyphs->bbx.descent, maxds);
+    rb = glyphs->bbx.width + glyphs->bbx.x_offset;
+    maxrb = MAX(rb, maxrb);
+    minlb = MIN(glyphs->bbx.x_offset, minlb);
+    maxlb = MAX(glyphs->bbx.x_offset, maxlb);
+
+    /*
+     * Set up the new font bounding box, minus the characters that are being
+     * removed and with the new characters added.
+     */
+    nbbx.width = maxrb - minlb;
+    nbbx.x_offset = minlb;
+
+    nbbx.height = maxas + maxds;
+    nbbx.ascent = maxas;
+    nbbx.descent = maxds;
+    nbbx.y_offset = -maxds;
+
+    /*
+     * Now determine if the combination of the glyphs removed and the new
+     * glyphs cause the font bounding box to be changed.
+     */
+    resize = (nbbx.width > font->bbx.width ||
+              nbbx.height > font->bbx.height) ? 1 : 0;
+
+    /*
+     * Set the pointers to the glyphs.
+     */
+    ng = (unencoded == 0) ? font->glyphs_used : font->unencoded_used;
+    sgp = gp = (unencoded == 0) ? font->glyphs : font->unencoded;
+
+    /*
+     * Locate the closest glyph on or following `start'.
+     */
+    for (i = 0; i < ng && gp->encoding < start; i++, gp++) ;
+
+    appending = (i == ng);
+
+    /*
+     * Set the starting point for copying the incoming glyphs.
+     */
+    dgp = gp;
+
+    n = glyphs->end - glyphs->start;
+    end = start + n;
+
+    /*
+     * Delete all the glyphs between `start' and `end'.
+     */
+    for (del = 0, i = start; i <= end; i++) {
+        /*
+         * Mark the character as being modified.
+         */
+        if (ng > 0 && !appending && gp->encoding == i) {
+            if (unencoded == 0)
+              _bdf_set_glyph_modified(font->nmod, i);
+            else
+              _bdf_set_glyph_modified(font->umod, i);
+
+            if (gp->name != 0)
+              free(gp->name);
+            if (gp->bytes > 0)
+              free((char *) gp->bitmap);
+            if (gp->unicode.map_size > 0)
+              free((char *) gp->unicode.map);
+            del++;
+            gp++;
+        }
+    }
+
+    /*
+     * Determine how many glyphs remain following the last one deleted.
+     */
+    remaining = ng - (gp - sgp);
+
+    if (glyphs->glyphs_used == 0) {
+        /*
+         * If the glyph list is empty, then shift any remaining glyphs down
+         * to the destination.
+         */
+        _bdf_memmove((char *) dgp, (char *) gp,
+                     sizeof(bdf_glyph_t) * remaining);
+        if (unencoded == 0)
+          font->glyphs_used -= del;
+        else
+          font->unencoded_used -= del;
+    } else {
+        /*
+         * Insert the glyph list after making sure there is enough space to
+         * hold them.  Also adjust the encoding and scalable width values
+         * after copying the glyphs.
+         */
+        if (unencoded == 0) {
+            n = (font->glyphs_used - del) + glyphs->glyphs_used;
+            if (n > font->glyphs_size) {
+                off[0] = gp - sgp;
+                off[1] = dgp - sgp;
+                if (font->glyphs_size == 0)
+                  font->glyphs = sgp =
+                      (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * n);
+                else
+                  font->glyphs = sgp =
+                      (bdf_glyph_t *) realloc((char *) font->glyphs,
+                                              sizeof(bdf_glyph_t) * n);
+                gp = sgp + off[0];
+                dgp = sgp + off[1];
+                font->glyphs_size = n;
+            }
+
+            /*
+             * Calculate how many will need to be shifted.
+             */
+            if ((n = glyphs->glyphs_used - del) >= font->glyphs_used)
+              n = 0;
+        } else {
+            n = (font->unencoded_used - del) + glyphs->glyphs_used;
+            if (n > font->unencoded_size) {
+                off[0] = gp - sgp;
+                off[1] = dgp - sgp;
+                if (font->unencoded_size == 0)
+                  font->unencoded = sgp =
+                      (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * n);
+                else
+                  font->unencoded = sgp =
+                      (bdf_glyph_t *) realloc((char *) font->unencoded,
+                                              sizeof(bdf_glyph_t) * n);
+                gp = sgp + off[0];
+                dgp = sgp + off[1];
+                font->unencoded_size = n;
+            }
+
+            /*
+             * Calculate how many will need to be shifted.
+             */
+            if ((n = glyphs->glyphs_used - del) >= font->unencoded_used)
+              n = 0;
+        }
+
+        /*
+         * Shift any following glyphs up or down if needed.
+         */
+        if (n)
+          _bdf_memmove((char *) (gp + n), (char *) gp,
+                       sizeof(bdf_glyph_t) * remaining);
+
+        /*
+         * Copy the incoming glyphs, copy their names and bitmaps,
+         * set their encodings, and set their scalable widths.
+         */
+        (void) memcpy((char *) dgp, (char *) glyphs->glyphs,
+                      sizeof(bdf_glyph_t) * glyphs->glyphs_used);
+        for (i = 0; i < glyphs->glyphs_used; i++, dgp++) {
+            if (dgp->name != 0)
+              dgp->name = (char *) _bdf_strdup((unsigned char *) dgp->name,
+                                               strlen(dgp->name) + 1);
+
+            if (dgp->bytes > 0)
+              dgp->bitmap = _bdf_strdup(dgp->bitmap, dgp->bytes);
+
+            if (dgp->unicode.map_size > 0)
+              dgp->unicode.map = _bdf_strdup(dgp->unicode.map,
+                                             dgp->unicode.map_size);
+
+            dgp->encoding = start + (dgp->encoding - glyphs->start);
+
+            /*
+             * Mark the glyph as being modified in case it fills a cell that
+             * was empty before.
+             */
+            _bdf_set_glyph_modified(font->nmod, dgp->encoding);
+
+            dw = (double) dgp->dwidth;
+            dgp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+        }
+
+        /*
+         * Adjust the count of glyphs.
+         */
+        ng = (ng - del) + glyphs->glyphs_used;
+        if (unencoded == 0)
+          font->glyphs_used = ng;
+        else
+          font->unencoded_used = ng;
+    }
+
+    /*
+     * Last, if the replacement was done in the unencoded section,
+     * reencode all the glyphs so they show up properly.
+     */
+    if (unencoded != 0) {
+        for (i = 0; i < ng; i++, sgp++) {
+            if (_bdf_glyph_modified(font->umod, sgp->encoding)) {
+                _bdf_clear_glyph_modified(font->umod, sgp->encoding);
+                _bdf_set_glyph_modified(font->umod, i);
+            }
+            sgp->encoding = i;
+        }
+    }
+
+    if (resize)
+      (void) memcpy((char *) &font->bbx, (char *) &nbbx, sizeof(bdf_bbx_t));
+
+    font->modified = 1;
+
+    return resize;
+}
+
+int
+bdf_insert_glyphs(bdf_font_t *font, int start, bdf_glyphlist_t *glyphs)
+{
+    int resize;
+    unsigned int i, ng, n, which;
+    bdf_glyph_t *gp;
+
+    resize = 0;
+
+    if (font == 0)
+      return resize;
+
+    /*
+     * Dither the incoming bitmaps so they match the same bits per pixel as
+     * the font.
+     */
+    if (glyphs->bpp != font->bpp) {
+        if (glyphs->bpp == 1)
+          _bdf_one_to_n(glyphs, font->bpp);
+        else if (font->bpp == 1)
+          _bdf_n_to_one(glyphs);
+        else if (glyphs->bpp == 2)
+          _bdf_two_to_four(glyphs);
+        else
+          _bdf_four_to_two(glyphs);
+    }
+
+    /*
+     * Locate the starting glyph.
+     */
+    gp = font->glyphs;
+    ng = font->glyphs_used;
+    for (i = 0; i < ng && gp->encoding < start; i++, gp++) ;
+
+    /*
+     * If there are no glyphs at the starting point, then simply do a replace.
+     */
+    if (i == ng)
+      return bdf_replace_glyphs(font, start, glyphs, 0);
+
+    /*
+     * Go through the glyphs that would be shifted due to the insertion and
+     * determine if some of them will overflow the 0xffff boundary.
+     */
+    n = (glyphs->end - glyphs->start) + 1;
+    for (which = i; i < ng; i++, gp++) {
+        if (gp->encoding + n > 0xffff)
+          break;
+    }
+
+    if (i < ng) {
+        /*
+         * Some glyphs have to be moved to the unencoded area because they
+         * would overflow the 0xffff boundary if they were moved up.
+         */
+        bdf_copy_glyphs(font, gp->encoding, font->glyphs[ng - 1].encoding,
+                        &font->overflow, 0);
+        bdf_delete_glyphs(font,  gp->encoding, font->glyphs[ng - 1].encoding,
+                          0);
+        resize += bdf_replace_glyphs(font, font->unencoded_used,
+                                     &font->overflow, 1);
+    }
+
+    /*
+     * Go back to the insertion point and shift the remaining glyph encodings
+     * up by `n'.
+     */
+    for (gp = font->glyphs + which; which < font->glyphs_used; which++, gp++) {
+        /*
+         * Mark the new glyph locations as being modified.
+         */
+        gp->encoding += n;
+        _bdf_set_glyph_modified(font->nmod, gp->encoding);
+    }
+
+    /*
+     * Finally, mark the font as being modified and insert the new glyphs.
+     */
+    font->modified = 1;
+
+    return resize + bdf_replace_glyphs(font, start, glyphs, 0);
+}
+
+static void
+_bdf_combine_glyphs(bdf_font_t *font, bdf_glyph_t *f, bdf_glyph_t *g)
+{
+    unsigned short x, sx, sy, si, dx, dy, di, byte, dbpr, fbpr, gbpr;
+    short maxas, maxds, maxrb, minlb, maxlb, rb;
+    unsigned char *masks;
+    bdf_bbx_t nbbx;
+    bdf_glyph_t tmp;
+
+    /*
+     * Determine the max bounding box for the two glyphs.
+     */
+    minlb = 32767;
+    maxlb = maxrb = maxas = maxds = 0;
+
+    /*
+     * Get the font glyph bounds.
+     */
+    maxas = MAX(f->bbx.ascent, maxas);
+    maxds = MAX(f->bbx.descent, maxds);
+    rb = f->bbx.width + f->bbx.x_offset;
+    maxrb = MAX(rb, maxrb);
+    minlb = MIN(f->bbx.x_offset, minlb);
+    maxlb = MAX(f->bbx.x_offset, maxlb);
+
+    /*
+     * Get the bounds of the incoming glyph.
+     */
+    maxas = MAX(g->bbx.ascent, maxas);
+    maxds = MAX(g->bbx.descent, maxds);
+    rb = g->bbx.width + g->bbx.x_offset;
+    maxrb = MAX(rb, maxrb);
+    minlb = MIN(g->bbx.x_offset, minlb);
+    maxlb = MAX(g->bbx.x_offset, maxlb);
+
+    /*
+     * Set up the new glyph bounding box.
+     */
+    nbbx.width = maxrb - minlb;
+    nbbx.x_offset = minlb;
+    nbbx.height = maxas + maxds;
+    nbbx.ascent = maxas;
+    nbbx.descent = maxds;
+    nbbx.y_offset = -maxds;
+
+    masks = 0;
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    fbpr = ((f->bbx.width * font->bpp) + 7) >> 3;
+    gbpr = ((g->bbx.width * font->bpp) + 7) >> 3;
+    dbpr = ((nbbx.width * font->bpp) + 7) >> 3;
+
+    if (memcmp((char *) &nbbx, (char *) &f->bbx, sizeof(bdf_bbx_t)) == 0) {
+        /*
+         * The largest is the first, so merge the second in with it.
+         */
+        dy = f->bbx.ascent - g->bbx.ascent;
+        for (sy = 0; sy < g->bbx.height; sy++, dy++) {
+            for (x = sx = 0; x < g->bbx.width; x++, sx += font->bpp) {
+                si = (sx & 7) / font->bpp;
+                if ((byte = g->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si]))
+                  /*
+                   * No shifting of the byte is needed because the x offset
+                   * is the same for both glyphs.
+                   */
+                  f->bitmap[(dy * fbpr) + (sx >> 3)] |= byte;
+            }
+        }
+    } else if (memcmp((char *) &nbbx, (char *) &g->bbx,
+                      sizeof(bdf_bbx_t)) == 0) {
+        /*
+         * The largest is the incoming glyph, so merge into that one and swap
+         * it with the font glyph.
+         */
+        dy = g->bbx.ascent - f->bbx.ascent;
+        for (sy = 0; sy < f->bbx.height; sy++, dy++) {
+            for (x = sx = 0; x < f->bbx.width; x++, sx += font->bpp) {
+                si = (sx & 7) / font->bpp;
+                if ((byte = f->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si]))
+                  /*
+                   * No shifting of the byte is needed because the x offset
+                   * is the same for both glyphs.
+                   */
+                  g->bitmap[(dy * fbpr) + (sx >> 3)] |= byte;
+            }
+        }
+
+        /*
+         * Now swap the two glyphs while preserving the name and encoding of
+         * the first glyph.
+         */
+        tmp.swidth = g->swidth;
+        tmp.dwidth = g->dwidth;
+        tmp.bytes = g->bytes;
+        tmp.bitmap = g->bitmap;
+        (void) memcpy((char *) &tmp.bbx, (char *) &g->bbx, sizeof(bdf_bbx_t));
+
+        g->swidth = f->swidth;
+        g->dwidth = f->dwidth;
+        g->bytes = f->bytes;
+        g->bitmap = f->bitmap;
+        (void) memcpy((char *) &g->bbx, (char *) &f->bbx, sizeof(bdf_bbx_t));
+
+        f->swidth = tmp.swidth;
+        f->dwidth = tmp.dwidth;
+        f->bytes = tmp.bytes;
+        f->bitmap = tmp.bitmap;
+        (void) memcpy((char *) &f->bbx, (char *) &tmp.bbx, sizeof(bdf_bbx_t));
+    } else {
+        /*
+         * Need a new bitmap for the combination of the two.
+         */
+        tmp.bytes = nbbx.height * dbpr;
+        tmp.bitmap = (unsigned char *) malloc(tmp.bytes);
+        (void) memset((char *) tmp.bitmap, 0, tmp.bytes);
+
+        /*
+         * Merge the first glyph.
+         */
+        dy = nbbx.ascent - f->bbx.ascent;
+        for (sy = 0; sy < f->bbx.height; sy++, dy++) {
+            dx = MYABS(nbbx.x_offset - f->bbx.x_offset) * font->bpp;
+            for (x = sx = 0; x < f->bbx.width; x++,
+                     sx += font->bpp, dx += font->bpp) {
+                si = (sx & 7) / font->bpp;
+                if ((byte = f->bitmap[(sy * fbpr) + (sx >> 3)] & masks[si])) {
+                    di = (dx & 7) / font->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * font->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * font->bpp;
+                    tmp.bitmap[(dy * dbpr) + (dx >> 3)] |= byte;
+                }
+            }
+        }
+
+        /*
+         * Merge the second glyph.
+         */
+        dy = nbbx.ascent - g->bbx.ascent;
+        for (sy = 0; sy < g->bbx.height; sy++, dy++) {
+            dx = MYABS(nbbx.x_offset - g->bbx.x_offset) * font->bpp;
+            for (x = sx = 0; x < g->bbx.width; x++,
+                     sx += font->bpp, dx += font->bpp) {
+                si = (sx & 7) / font->bpp;
+                if ((byte = g->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) {
+                    di = (dx & 7) / font->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * font->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * font->bpp;
+                    tmp.bitmap[(dy * dbpr) + (dx >> 3)] |= byte;
+                }
+            }
+        }
+
+        /*
+         * Now clear the font glyph and copy the temp glyph to it.
+         */
+        if (f->bytes > 0)
+          free((char *) f->bitmap);
+        f->bytes = tmp.bytes;
+        f->bitmap = tmp.bitmap;
+        (void) memcpy((char *) &f->bbx, (char *) &nbbx, sizeof(bdf_bbx_t));
+
+        /*
+         * Set the device width.  Pay attention to whether the font is
+         * monowidth or character cell.
+         */
+        if (font->spacing != BDF_PROPORTIONAL)
+          f->dwidth = font->monowidth;
+        else
+          f->dwidth = MAX(f->dwidth, g->dwidth);
+    }
+}
+
+int
+bdf_merge_glyphs(bdf_font_t *font, int start, bdf_glyphlist_t *glyphs,
+                 int unencoded)
+{
+    int resize;
+    int i, n, ng, end, add, enc, off;
+    bdf_glyph_t *sgp, *gp, *dgp, *base;
+    short maxas, maxds, maxrb, minlb, maxlb, rb;
+    double ps, rx, dw;
+    bdf_bbx_t nbbx;
+
+    resize = 0;
+
+    if (font == 0)
+      return resize;
+
+    /*
+     * If the glyphs are being merged in the unencoded area, simply append
+     * them.  The unencoded area is simply storage.
+     */
+    if (unencoded)
+      return bdf_replace_glyphs(font, font->unencoded_used, glyphs, unencoded);
+
+    /*
+     * Dither the incoming bitmaps so they match the same bits per pixel as
+     * the font.
+     */
+    if (glyphs->bpp != font->bpp) {
+        if (glyphs->bpp == 1)
+          _bdf_one_to_n(glyphs, font->bpp);
+        else if (font->bpp == 1)
+          _bdf_n_to_one(glyphs);
+        else if (glyphs->bpp == 2)
+          _bdf_two_to_four(glyphs);
+        else
+          _bdf_four_to_two(glyphs);
+    }
+
+    /*
+     * Set the point size and horizontal resolution so the scalable width can
+     * be determined.
+     */
+    ps = (double) font->point_size;
+    rx = (double) font->resolution_x;
+
+    /*
+     * Determine if a resize is needed.
+     */
+
+    /*
+     * Determine the bounding box for the font without the characters being
+     * replaced.
+     */
+    minlb = 32767;
+    maxlb = maxrb = maxas = maxds = 0;
+
+    /*
+     * Get the font bounds.
+     */
+    maxas = MAX(font->bbx.ascent, maxas);
+    maxds = MAX(font->bbx.descent, maxds);
+    rb = font->bbx.width + font->bbx.x_offset;
+    maxrb = MAX(rb, maxrb);
+    minlb = MIN(font->bbx.x_offset, minlb);
+    maxlb = MAX(font->bbx.x_offset, maxlb);
+
+    /*
+     * Get the bounds of the incoming glyphs.
+     */
+    maxas = MAX(glyphs->bbx.ascent, maxas);
+    maxds = MAX(glyphs->bbx.descent, maxds);
+    rb = glyphs->bbx.width + glyphs->bbx.x_offset;
+    maxrb = MAX(rb, maxrb);
+    minlb = MIN(glyphs->bbx.x_offset, minlb);
+    maxlb = MAX(glyphs->bbx.x_offset, maxlb);
+
+    /*
+     * Set up the new font bounding box, minus the characters that are being
+     * removed and with the new characters added.
+     */
+    nbbx.width = maxrb - minlb;
+    nbbx.x_offset = minlb;
+
+    nbbx.height = maxas + maxds;
+    nbbx.ascent = maxas;
+    nbbx.descent = maxds;
+    nbbx.y_offset = -maxds;
+
+    /*
+     * Now determine if the combination of the glyphs removed and the new
+     * glyphs cause the font bounding box to be changed.
+     */
+    resize = (nbbx.width > font->bbx.width ||
+              nbbx.height > font->bbx.height) ? 1 : 0;
+
+    /*
+     * Set the pointers to the glyphs.
+     */
+    ng = (unencoded == 0) ? font->glyphs_used : font->unencoded_used;
+    gp = (unencoded == 0) ? font->glyphs : font->unencoded;
+
+    /*
+     * Locate the closest glyph on or following `start'.
+     */
+    for (i = 0; i < ng && gp->encoding < start; i++, gp++) ;
+
+    if (i == ng)
+      /*
+       * If the gylphs are being added off the end of the list, simply insert
+       * them so any overflows can be handled.
+       */
+      return bdf_insert_glyphs(font, start, glyphs);
+
+    /*
+     * Set the starting point for copying the incoming glyphs.
+     */
+    dgp = gp;
+
+    n = glyphs->end - glyphs->start;
+    end = start + n;
+
+    /*
+     * Count the number of glyphs that will be added and mark all the
+     * glyphs that will be modified.
+     */
+    for (sgp = glyphs->glyphs, add = 0, i = start; i <= end; i++) {
+        enc = (sgp->encoding - glyphs->start) + start;
+
+        /*
+         * Mark the glyph as being modified.
+         */
+        if (unencoded == 0)
+          _bdf_set_glyph_modified(font->nmod, enc);
+        else
+          _bdf_set_glyph_modified(font->umod, enc);
+
+        if (enc == gp->encoding)
+          sgp++;
+        else if (enc < gp->encoding) {
+            add++;
+            sgp++;
+        }
+
+        if (gp->encoding == i)
+          gp++;
+    }
+
+    if (add > 0) {
+        ng += add;
+
+        /*
+         * Need to make room for some glyphs that will be added.
+         */
+        if (unencoded) {
+            off = dgp - font->unencoded;
+            if (font->unencoded_used == 0)
+              font->unencoded =
+                  (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * ng);
+            else
+              font->unencoded =
+                  (bdf_glyph_t *) realloc((char *) font->unencoded,
+                                          sizeof(bdf_glyph_t) * ng);
+            dgp = font->unencoded + off;
+            font->unencoded_used = ng;
+        } else {
+            off = dgp - font->glyphs;
+            if (font->glyphs_used == 0)
+              font->glyphs =
+                  (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * ng);
+            else
+              font->glyphs =
+                  (bdf_glyph_t *) realloc((char *) font->glyphs,
+                                          sizeof(bdf_glyph_t) * ng);
+            dgp = font->glyphs + off;
+            font->glyphs_used = ng;
+        }
+    }
+
+    /*
+     * Now go through and do two things:
+     * 1. Insert new incoming glyphs.
+     * 2. Combine two glyphs at the same location.
+     */
+    base = (!unencoded) ? font->glyphs : font->unencoded;
+    for (gp = dgp, sgp = glyphs->glyphs, i = start; i <= end; i++) {
+        enc = (sgp->encoding - glyphs->start) + start;
+        if (enc < gp->encoding) {
+            /*
+             * Shift the glyphs up by one and add this one.
+             */
+            if (gp - base < ng)
+              _bdf_memmove((char *) (gp + 1), (char *) gp,
+                           sizeof(bdf_glyph_t) * (ng - (gp - base)));
+            (void) memcpy((char *) gp, (char *) sgp, sizeof(bdf_glyph_t));
+            gp->name = (char *) _bdf_strdup((unsigned char *) gp->name,
+                                            strlen(gp->name) + 1);
+            if (gp->bytes > 0)
+              gp->bitmap = _bdf_strdup(gp->bitmap, gp->bytes);
+            if (gp->unicode.map_size > 0)
+              gp->unicode.map = _bdf_strdup(gp->unicode.map,
+                                            gp->unicode.map_size);
+            gp->encoding = i;
+            sgp++;
+            dw = (double) gp->dwidth;
+            gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+        } else if (enc == gp->encoding) {
+            _bdf_combine_glyphs(font, gp, sgp);
+            dw = (double) gp->dwidth;
+            gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+            sgp++;
+        }
+        if (gp->encoding == i)
+          gp++;
+    }
+
+    if (resize)
+      (void) memcpy((char *) &font->bbx, (char *) &nbbx, sizeof(bdf_bbx_t));
+
+    font->modified = 1;
+
+    return resize;
+}
+
+void
+bdf_set_modified(bdf_font_t *font, int modified)
+{
+    if (font == 0 || font->modified == modified)
+      return;
+
+    if (modified == 0) {
+        /*
+         * Clear out the modified bitmaps.
+         */
+        (void) memset((char *) font->nmod, 0, sizeof(unsigned int) * 2048);
+        (void) memset((char *) font->umod, 0, sizeof(unsigned int) * 2048);
+    }
+    font->modified = modified;
+}
+
+/**************************************************************************
+ *
+ * XLFD font name functions.
+ *
+ **************************************************************************/
+
+static char *xlfdfields[] = {
+    "FOUNDRY",
+    "FAMILY_NAME",
+    "WEIGHT_NAME",
+    "SLANT",
+    "SETWIDTH_NAME",
+    "ADD_STYLE_NAME",
+    "PIXEL_SIZE",
+    "POINT_SIZE",
+    "RESOLUTION_X",
+    "RESOLUTION_Y",
+    "SPACING",
+    "AVERAGE_WIDTH",
+    "CHARSET_REGISTRY",
+    "CHARSET_ENCODING",
+};
+
+int
+bdf_is_xlfd_property(char *name)
+{
+    int i;
+
+    for (i = 0; i < 14; i++) {
+        if (strcmp(name, xlfdfields[i]) == 0)
+          return 1;
+    }
+    return 0;
+}
+
+int
+bdf_has_xlfd_name(bdf_font_t *font)
+{
+    unsigned int len;
+    char name[256];
+    _bdf_list_t list;
+
+    if (font == 0 || font->name == 0 || font->name[0] == 0)
+      return 0;
+
+    len = (unsigned int) (strlen(font->name) + 1);
+    (void) memcpy(name, font->name, len);
+    list.size = list.used = 0;
+    _bdf_split("-", name, len, &list);
+    if (list.size > 0)
+      free((char *) list.field);
+
+    return (list.used == 15);
+}
+
+char *
+bdf_make_xlfd_name(bdf_font_t *font, char *foundry, char *family)
+{
+    int len;
+    double dp, dr;
+    unsigned int i, width, used;
+    unsigned short awidth, pxsize;
+    bdf_property_t *pp;
+    bdf_glyph_t *gp;
+    char spacing, *name, *val, *np, nbuf[256];
+
+    if (font == 0 || bdf_has_xlfd_name(font))
+      return 0;
+
+    np = nbuf;
+
+    /*
+     * Add the FOUNDRY field.
+     */
+    if ((pp = bdf_get_font_property(font, "FOUNDRY")) != 0)
+      foundry = pp->value.atom;
+    sprintf(np, "-%s", foundry);
+    np += strlen(np);
+
+    /*
+     * Add the FAMILY_NAME field.
+     */
+    if ((pp = bdf_get_font_property(font, "FAMILY_NAME")) != 0)
+      family = pp->value.atom;
+    sprintf(np, "-%s", family);
+    np += strlen(np);
+
+    /*
+     * Add the WEIGHT_NAME field.
+     */
+    val = ((pp = bdf_get_font_property(font, "WEIGHT_NAME")) != 0) ?
+        pp->value.atom : "Medium";
+    if (val == 0)
+      val = "Medium";
+    sprintf(np, "-%s", val);
+    np += strlen(np);
+
+    /*
+     * Add the SLANT field.
+     */
+    val = ((pp = bdf_get_font_property(font, "SLANT")) != 0) ?
+        pp->value.atom : "R";
+    if (val == 0)
+      val = "R";
+    sprintf(np, "-%s", val);
+    np += strlen(np);
+
+    /*
+     * Add the SETWIDTH_NAME field.
+     */
+    val = ((pp = bdf_get_font_property(font, "SETWIDTH_NAME")) != 0) ?
+        pp->value.atom : "Normal";
+    if (val == 0)
+      val = "Normal";
+    sprintf(np, "-%s", val);
+    np += strlen(np);
+
+    /*
+     * Add the ADD_STYLE_NAME field.
+     */
+    val = ((pp = bdf_get_font_property(font, "ADD_STYLE_NAME")) != 0) ?
+        pp->value.atom : "";
+    if (val == 0)
+      val = "";
+    sprintf(np, "-%s", val);
+    np += strlen(np);
+
+    /*
+     * Add the PIXEL_SIZE field.
+     */
+    if ((pp = bdf_get_font_property(font, "PIXEL_SIZE")) != 0)
+      sprintf(np, "-%d", pp->value.int32);
+    else {
+        /*
+         * Determine the pixel size.
+         */
+        dp = (double) (font->point_size * 10);
+        dr = (double) font->resolution_y;
+        pxsize = (unsigned short) (((dp * dr) / 722.7) + 0.5);
+        sprintf(np, "-%hd", pxsize);
+    }
+    np += strlen(np);
+
+    /*
+     * Add the POINT_SIZE field.
+     */
+    if ((pp = bdf_get_font_property(font, "POINT_SIZE")) != 0)
+      sprintf(np, "-%d", pp->value.int32);
+    else
+      sprintf(np, "-%d", font->point_size * 10);
+    np += strlen(np);
+
+    /*
+     * Add the RESOLUTION_X field.
+     */
+    if ((pp = bdf_get_font_property(font, "RESOLUTION_X")) != 0)
+      sprintf(np, "-%d", pp->value.card32);
+    else
+      sprintf(np, "-%d", font->resolution_x);
+    np += strlen(np);
+
+    /*
+     * Add the RESOLUTION_Y field.
+     */
+    if ((pp = bdf_get_font_property(font, "RESOLUTION_Y")) != 0)
+      sprintf(np, "-%d", pp->value.card32);
+    else
+      sprintf(np, "-%d", font->resolution_y);
+    np += strlen(np);
+
+    /*
+     * Add the SPACING field.
+     */
+    if ((pp = bdf_get_font_property(font, "SPACING")) != 0)
+      spacing = pp->value.atom[0];
+    else {
+        spacing = 'P';
+        switch (font->spacing) {
+          case BDF_PROPORTIONAL: spacing = 'P'; break;
+          case BDF_MONOWIDTH: spacing = 'M'; break;
+          case BDF_CHARCELL: spacing = 'C'; break;
+        }
+    }
+    sprintf(np, "-%c", spacing);
+    np += strlen(np);
+
+    /*
+     * Add the AVERAGE_WIDTH field.
+     */
+    if ((pp = bdf_get_font_property(font, "AVERAGE_WIDTH")) != 0)
+      sprintf(np, "-%d", pp->value.int32);
+    else {
+        /*
+         * Determine the average width of all the glyphs in the font.
+         */
+        width = 0;
+        for (i = 0, gp = font->unencoded; i < font->unencoded_used; i++, gp++)
+          width += gp->dwidth;
+        for (i = 0, gp = font->glyphs; i < font->glyphs_used; i++, gp++)
+          width += gp->dwidth;
+
+        used = font->unencoded_used + font->glyphs_used;
+        if (used == 0)
+          awidth = font->bbx.width * 10;
+        else
+          awidth = (unsigned short) ((((float) width) /
+                                      ((float) used)) * 10.0);
+        sprintf(np, "-%hd", awidth);
+    }
+    np += strlen(np);
+
+    /*
+     * Add the CHARSET_REGISTRY field.
+     */
+    val = ((pp = bdf_get_font_property(font, "CHARSET_REGISTRY")) != 0) ?
+        pp->value.atom : "FontSpecific";
+    sprintf(np, "-%s", val);
+    np += strlen(np);
+
+    /*
+     * Add the CHARSET_ENCODING field.
+     */
+    val = ((pp = bdf_get_font_property(font, "CHARSET_ENCODING")) != 0) ?
+        pp->value.atom : "0";
+    sprintf(np, "-%s", val);
+    np += strlen(np);
+
+    len = (np - nbuf) + 1;
+    name = (char *) malloc(len);
+    (void) memcpy(name, nbuf, len);
+    return name;
+}
+
+void
+bdf_update_name_from_properties(bdf_font_t *font)
+{
+    unsigned int i;
+    bdf_property_t *p;
+    _bdf_list_t list;
+    char *np, name[128], nname[128];
+
+    if (font == 0 || bdf_has_xlfd_name(font) == 0)
+      return;
+
+    (void) memset((char *) &list, 0, sizeof(_bdf_list_t));
+
+    /*
+     * Split the name into fields and shift out the first empty field.
+     * This assumes that the font has a name.
+     */
+    i = (unsigned int) strlen(font->name);
+    (void) memcpy(name, font->name, i + 1);
+    _bdf_split("-", name, i, &list);
+    _bdf_shift(1, &list);
+
+    /*
+     * Initialize the pointer to the new name and add the '-' prefix.
+     */
+    np = nname;
+    *np++ = '-';
+    *np = 0;
+
+    for (i = 0; i < 14; i++) {
+        if ((p = bdf_get_font_property(font, xlfdfields[i])) != 0) {
+            /*
+             * The property exists, so add it to the new font name.
+             */
+            switch (p->format) {
+              case BDF_ATOM:
+                if (p->value.atom != 0)
+                  sprintf(np, "%s", p->value.atom);
+                break;
+              case BDF_CARDINAL:
+                sprintf(np, "%d", p->value.card32);
+                break;
+              case BDF_INTEGER:
+                sprintf(np, "%d", p->value.int32);
+                break;
+            }
+        } else
+          /*
+           * The property does not exist, so add the original value to the
+           * new font name.
+           */
+          sprintf(np, "%s", list.field[i]);
+        np += strlen(np);
+        if (i + 1 < 14) {
+            *np++ = '-';
+            *np = 0;
+        }
+    }
+
+    /*
+     * Replace the existing font name with the new one.
+     */
+    free(font->name);
+    i = (unsigned int) (strlen(nname) + 1);
+    font->name = (char *) malloc(i);
+    (void) memcpy(font->name, nname, i);
+
+    /*
+     * Free up the list.
+     */
+    if (list.size > 0)
+      free((char *) list.field);
+
+    font->modified = 1;
+}
+
+int
+bdf_update_properties_from_name(bdf_font_t *font)
+{
+    unsigned int i;
+    bdf_property_t *p, prop;
+    _bdf_list_t list;
+    char name[128];
+
+    if (font == 0 || font->name == 0 || bdf_has_xlfd_name(font) == 0)
+      return 0;
+
+    (void) memset((char *) &list, 0, sizeof(_bdf_list_t));
+
+    /*
+     * Split the name into fields and shift out the first empty field.
+     */
+    i = (unsigned int) strlen(font->name);
+    (void) memcpy(name, font->name, i + 1);
+    _bdf_split("-", name, i, &list);
+    _bdf_shift(1, &list);
+
+    for (i = 0; i < 14; i++) {
+        p = bdf_get_property(xlfdfields[i]);
+        prop.name = p->name;
+        prop.format = p->format;
+        switch (prop.format) {
+          case BDF_ATOM:
+            prop.value.atom = list.field[i];
+            break;
+          case BDF_CARDINAL:
+            prop.value.card32 = _bdf_atoul(list.field[i], 0, 10);
+            break;
+          case BDF_INTEGER:
+            prop.value.int32 = _bdf_atol(list.field[i], 0, 10);
+            break;
+        }
+        bdf_add_font_property(font, &prop);
+    }
+
+    /*
+     * Free up the list.
+     */
+    if (list.size > 0)
+      free((char *) list.field);
+
+    font->modified = 1;
+
+    return 1;
+}
+
+int
+bdf_update_average_width(bdf_font_t *font)
+{
+    int changed;
+    unsigned int i;
+    int oaw, awidth, used;
+    bdf_glyph_t *gp;
+    _bdf_list_t list;
+    bdf_property_t *pp, prop;
+    char *np, num[16], nbuf[128];
+
+    changed = 0;
+
+    used = font->unencoded_used + font->glyphs_used;
+    if (used == 0)
+      awidth = font->bbx.width * 10;
+    else {
+        for (i = 0, awidth = 0, gp = font->unencoded; i < font->unencoded_used;
+             i++, gp++)
+          awidth += gp->dwidth;
+        for (i = 0, gp = font->glyphs; i < font->glyphs_used; i++, gp++)
+          awidth += gp->dwidth;
+        awidth = (int) ((((double) awidth) / ((double) used)) * 10.0);
+    }
+
+    /*
+     * Check to see if it is different than the average width in the font
+     * name.
+     */
+    if (bdf_has_xlfd_name(font)) {
+        (void) memset((char *) &list, 0, sizeof(_bdf_list_t));
+        i = (unsigned int) strlen(font->name);
+        (void) memcpy(nbuf, font->name, i + 1);
+        _bdf_split("-", nbuf, i, &list);
+        oaw = _bdf_atol(list.field[12], 0, 10);
+        if (oaw != awidth) {
+            /*
+             * Construct a new font name with the new average width.
+             */
+            changed = 1;
+            sprintf(num, "%d", awidth);
+            used = strlen(num) - strlen(list.field[12]);
+            if (used > 0) {
+                /*
+                 * Resize the string used for the font name instead of
+                 * creating a new one.
+                 */
+                used += i;
+                font->name = (char *) realloc(font->name, used);
+            }
+
+            /*
+             * Copy the elements of the list back into the new font name.
+             */
+            np = font->name;
+            *np++ = '-';
+            for (i = 1; i < list.used; i++) {
+                if (i == 12)
+                  strcpy(np, num);
+                else
+                  strcpy(np, list.field[i]);
+                np += strlen(np);
+                if (i + 1 < list.used)
+                  *np++ = '-';
+            }
+        }
+
+        /*
+         * Clear up any space allocated for the list.
+         */
+        if (list.size > 0)
+          free((char *) list.field);
+    }
+
+    /*
+     * Now check for the AVERAGE_WIDTH property.
+     */
+    if ((pp = bdf_get_font_property(font, "AVERAGE_WIDTH")) != 0) {
+        if (pp->value.int32 != awidth) {
+            changed = 1;
+            pp->value.int32 = awidth;
+        }
+    } else {
+        /*
+         * Property doesn't exist yet, so add it.
+         */
+        changed = 1;
+        prop.name = "AVERAGE_WIDTH";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = awidth;
+        bdf_add_font_property(font, &prop);
+    }
+
+    if (changed)
+      font->modified = 1;
+
+    return changed;
+}
+
+/*
+ * Change the font bounding box and return a non-zero number if this causes
+ * the font to get larger or smaller.
+ */
+int
+bdf_set_font_bbx(bdf_font_t *font, bdf_metrics_t *metrics)
+{
+    int resize;
+
+    resize = 0;
+
+    if (font == 0 || metrics == 0)
+      return resize;
+
+    resize = (font->bbx.width != metrics->width ||
+              font->bbx.height != metrics->height) ? 1 : 0;
+
+    font->bbx.width = metrics->width;
+    font->bbx.height = metrics->height;
+    font->bbx.x_offset = metrics->x_offset;
+    font->bbx.y_offset = metrics->y_offset;
+    font->bbx.ascent = metrics->ascent;
+    font->bbx.descent = metrics->descent;
+
+    /*
+     * If the font is not proportional, then make sure the monowidth field is
+     * set to the font bounding box.
+     */
+    if (font->spacing != BDF_PROPORTIONAL)
+      font->monowidth = font->bbx.width;
+
+    return resize;
+}
+
+int
+bdf_translate_glyphs(bdf_font_t *font, short dx, short dy, int start,
+                     int end, bdf_callback_t callback, void *data,
+                     int unencoded)
+{
+    int resize, diff;
+    bdf_glyph_t *gp, *sp, *ep;
+    bdf_callback_struct_t cb;
+
+    if (font == 0 || (dx == 0 && dy == 0))
+      return 0;
+
+    if ((unencoded && font->unencoded_used == 0) || font->glyphs_used == 0)
+      return 0;
+
+    /*
+     * Call the progress initialization callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_TRANSLATE_START;
+        cb.total = (end - start) + 1;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Locate the first and last glyphs to be shifted.
+     */
+    sp = _bdf_locate_glyph(font, start, unencoded);
+    ep = _bdf_locate_glyph(font, end, unencoded);
+    for (resize = 0, gp = sp; sp <= ep; sp++) {
+        /*
+         * Call the callback if one was provided.
+         */
+        if (sp != gp && callback != 0) {
+            cb.reason = BDF_TRANSLATING;
+            cb.current = (sp->encoding - start) + 1;
+            (*callback)(&cb, data);
+        }
+
+        /*
+         * Apply the X translation.
+         */
+        if (dx != 0) {
+            sp->bbx.x_offset += dx;
+            diff = sp->bbx.x_offset - font->bbx.x_offset;
+            if (sp->bbx.x_offset < font->bbx.x_offset) {
+                font->bbx.x_offset = sp->bbx.x_offset;
+                font->bbx.width += MYABS(diff);
+                resize = 1;
+            } else if (sp->bbx.width + sp->bbx.x_offset >
+                       font->bbx.width + font->bbx.x_offset) {
+                font->bbx.width += MYABS(diff);
+                resize = 1;
+            }
+
+            /*
+             * Mark the glyph as modified appropriately.
+             */
+            if (unencoded)
+              _bdf_set_glyph_modified(font->umod, sp->encoding);
+            else
+              _bdf_set_glyph_modified(font->nmod, sp->encoding);
+        }
+
+        /*
+         * Apply the Y translation.
+         */
+        if (dy != 0) {
+            sp->bbx.y_offset += dy;
+            sp->bbx.descent = -sp->bbx.y_offset;
+            sp->bbx.ascent = sp->bbx.height - sp->bbx.descent;
+            diff = sp->bbx.y_offset - font->bbx.y_offset;
+            if (sp->bbx.y_offset < font->bbx.y_offset) {
+                font->bbx.y_offset = sp->bbx.y_offset;
+                font->bbx.descent = -font->bbx.y_offset;
+                font->bbx.height += MYABS(diff);
+                resize = 1;
+            } else if (sp->bbx.ascent > font->bbx.ascent) {
+                font->bbx.ascent += MYABS(diff);
+                font->bbx.height += MYABS(diff);
+                resize = 1;
+            }
+
+            /*
+             * Mark the glyph as modified appropriately.
+             */
+            if (unencoded)
+              _bdf_set_glyph_modified(font->umod, sp->encoding);
+            else
+              _bdf_set_glyph_modified(font->nmod, sp->encoding);
+        }
+    }
+
+    /*
+     * Call the callback one more time to make sure the client knows
+     * this is done.
+     */
+    if (callback != 0 && cb.current < cb.total) {
+        cb.reason = BDF_TRANSLATING;
+        cb.current = cb.total;
+        (*callback)(&cb, data);
+    }
+
+    if (resize)
+      font->modified = 1;
+
+    return resize;
+}
+
+static void
+_bdf_resize_rotation(bdf_font_t *font, int mul90, short degrees,
+                     bdf_glyph_t *glyph, bdf_bitmap_t *scratch,
+                     unsigned short *width, unsigned short *height)
+{
+    unsigned short w, h, wd, ht, bytes;
+    short cx, cy, x1, y1, x2, y2;
+    double dx1, dy1, dx2, dy2;
+
+    w = h = 0;
+
+    cx = glyph->bbx.width >> 1;
+    cy = glyph->bbx.height >> 1;
+
+    /*
+     * Rotate the lower left and upper right corners and check for a potential
+     * resize.
+     */
+    x1 = 0;
+    y1 = glyph->bbx.height;
+    x2 = glyph->bbx.width;
+    y2 = 0;
+
+    dx1 = (double) (x1 - cx);
+    dy1 = (double) (y1 - cy);
+    dx2 = (double) (x2 - cx);
+    dy2 = (double) (y2 - cx);
+
+    if (mul90) {
+        x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) -
+                           (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) +
+                           (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) -
+                           (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) +
+                           (dy2 * _bdf_cos_tbl[degrees]));
+    } else {
+        x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) -
+                               (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) +
+                               (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) -
+                               (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) +
+                               (dy2 * _bdf_cos_tbl[degrees]));
+    }
+
+    wd = MYABS(x2 - x1);
+    ht = MYABS(y2 - y1);
+
+    w = MAX(wd, w);
+    h = MAX(ht, h);
+
+    if (wd > font->bbx.width)
+      font->bbx.width += wd - font->bbx.width;
+    if (ht > font->bbx.height) {
+        font->bbx.ascent += ht - font->bbx.height;
+        font->bbx.height += ht - font->bbx.height;
+    }
+
+    /*
+     * Rotate the upper left and lower right corners and check for a potential
+     * resize.
+     */
+    x1 = 0;
+    y1 = 0;
+    x2 = glyph->bbx.width;
+    y2 = glyph->bbx.height;
+
+    dx1 = (double) (x1 - cx);
+    dy1 = (double) (y1 - cy);
+    dx2 = (double) (x2 - cx);
+    dy2 = (double) (y2 - cx);
+
+    if (mul90) {
+        x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) -
+                           (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) +
+                           (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) -
+                           (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) +
+                           (dy2 * _bdf_cos_tbl[degrees]));
+    } else {
+        x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) -
+                               (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) +
+                               (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) -
+                               (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) +
+                               (dy2 * _bdf_cos_tbl[degrees]));
+    }
+
+    wd = MYABS(x2 - x1);
+    ht = MYABS(y2 - y1);
+
+    w = MAX(wd, w);
+    h = MAX(ht, h);
+
+    if (wd > font->bbx.width)
+      font->bbx.width += wd - font->bbx.width;
+    if (ht > font->bbx.height) {
+        font->bbx.ascent += ht - font->bbx.height;
+        font->bbx.height += ht - font->bbx.height;
+    }
+
+    if (font->bbx.width > scratch->width ||
+        font->bbx.height > scratch->height) {
+        scratch->width = MAX(font->bbx.width, scratch->width);
+        scratch->height = MAX(font->bbx.height, scratch->height);
+        bytes = (((font->bbx.width * font->bpp) + 7) >> 3) * font->bbx.height;
+        if (scratch->bytes == 0)
+          scratch->bitmap = (unsigned char *) malloc(bytes);
+        else
+          scratch->bitmap = (unsigned char *)
+              realloc((char *) scratch->bitmap, bytes);
+        scratch->bytes = bytes;
+    }
+
+    /*
+     * Clear the bitmap.
+     */
+    (void) memset((char *) scratch->bitmap, 0, scratch->bytes);
+
+    /*
+     * Return the new glyph width and height.
+     */
+    *width = w;
+    *height = h;
+}
+
+int
+bdf_rotate_glyphs(bdf_font_t *font, short degrees, int start,
+                  int end, bdf_callback_t callback, void *data,
+                  int unencoded)
+{
+    int mul90, bpr, sbpr;
+    unsigned short wd, ht, si, di, byte, col;
+    short x, y, cx, cy, nx, ny, ox, oy, shiftx, shifty;
+    bdf_glyph_t *gp, *sp, *ep;
+    unsigned char *masks;
+    double dx, dy;
+    bdf_bitmap_t scratch;
+    bdf_callback_struct_t cb;
+
+    if (font == 0 ||
+        (unencoded && font->unencoded_used == 0) ||
+        font->glyphs_used == 0 ||
+        degrees == 0)
+      return 0;
+
+    while (degrees < 0)
+      degrees += 360;
+    while (degrees >= 360)
+      degrees -= 360;
+
+    mul90 = ((degrees % 90) == 0) ? 1 : 0;
+
+    masks = 0;
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Initialize the scratch bitmap.
+     */
+    (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t));
+
+    /*
+     * Call the progress initialization callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_ROTATE_START;
+        cb.total = (end - start) + 1;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    sp = _bdf_locate_glyph(font, start, unencoded);
+    ep = _bdf_locate_glyph(font, end, unencoded);
+    for (gp = sp; sp <= ep; sp++) {
+        /*
+         * Call the callback if one was provided.
+         */
+        if (sp != gp && callback != 0) {
+            cb.reason = BDF_ROTATING;
+            cb.current = (sp->encoding - start) + 1;
+            (*callback)(&cb, data);
+        }
+
+        /*
+         * Resize the bitmap, adjust the font bounding box, and get the new
+         * glyph width and height.
+         */
+        _bdf_resize_rotation(font, mul90, degrees, sp, &scratch, &wd, &ht);
+
+        cx = sp->bbx.width >> 1;
+        cy = sp->bbx.height >> 1;
+
+        shiftx = shifty = 0;
+        sbpr = ((wd * font->bpp) + 7) >> 3;
+        bpr = ((sp->bbx.width * font->bpp) + 7) >> 3;
+        for (y = 0; y < sp->bbx.height; y++) {
+            for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) {
+                si = (col & 7) / font->bpp;
+                byte = sp->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    dx = (double) (x - cx);
+                    dy = (double) (y - cy);
+                    if (mul90) {
+                        nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) -
+                                           (dy * _bdf_sin_tbl[degrees]));
+                        ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) +
+                                           (dy * _bdf_cos_tbl[degrees]));
+                    } else {
+                        nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) -
+                                               (dy * _bdf_sin_tbl[degrees]));
+                        ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) +
+                                               (dy * _bdf_cos_tbl[degrees]));
+                    }
+                    if (nx < 0) {
+                        shiftx = MIN(shiftx, nx);
+                        nx += wd;
+                    } else if (nx >= wd) {
+                        ox = (nx - wd) + 1;
+                        shiftx = MAX(shiftx, ox);
+                        nx -= wd;
+                    }
+                    if (ny < 0) {
+                        shifty = MIN(shifty, ny);
+                        ny += ht;
+                    } else if (ny >= ht) {
+                        oy = (ny - ht) + 1;
+                        shifty = MAX(shifty, oy);
+                        ny -= ht;
+                    }
+                    nx *= font->bpp;
+                    di = (nx & 7) / font->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * font->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * font->bpp;
+                    scratch.bitmap[(ny * sbpr) + (nx >> 3)] |= byte;
+                }
+            }
+        }
+        /*
+         * Resize the glyph bitmap if necessary.
+         */
+        if (wd != sp->bbx.width || ht != sp->bbx.height) {
+            sp->bbx.width = wd;
+            sp->bbx.height = ht;
+            sp->bbx.ascent = ht - sp->bbx.descent;
+            sp->bytes = (((wd * font->bpp) + 7) >> 3) * ht;
+            sp->bitmap = (unsigned char *)
+                realloc((char *) sp->bitmap, sp->bytes);
+        }
+        (void) memset((char *) sp->bitmap, 0, sp->bytes);
+
+        /*
+         * Copy the glyph from the scratch area to the glyph bitmap,
+         * adjusting for any shift values encountered.
+         */
+        bpr = ((sp->bbx.width * font->bpp) + 7) >> 3;
+        for (y = 0; y < sp->bbx.height; y++) {
+            for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) {
+                si = (col & 7) / font->bpp;
+                byte = scratch.bitmap[(y * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    nx = x - shiftx;
+                    ny = y - shifty;
+                    if (nx < 0)
+                      nx += sp->bbx.width;
+                    else if (nx >= sp->bbx.width)
+                      nx -= sp->bbx.width;
+                    if (ny < 0)
+                      ny += sp->bbx.height;
+                    else if (ny >= sp->bbx.height)
+                      ny -= sp->bbx.height;
+                    nx *= font->bpp;
+                    di = (nx & 7) / font->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * font->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * font->bpp;
+                    sp->bitmap[(ny * bpr) + (nx >> 3)] |= byte;
+                }
+            }
+        }
+        /*
+         * Mark the glyph as modified.
+         */
+        if (unencoded)
+          _bdf_set_glyph_modified(font->umod, sp->encoding);
+        else
+          _bdf_set_glyph_modified(font->nmod, sp->encoding);
+    }
+
+    /*
+     * Call the callback one more time to make sure the client knows
+     * this is done.
+     */
+    if (callback != 0 && cb.current < cb.total) {
+        cb.reason = BDF_TRANSLATING;
+        cb.current = cb.total;
+        (*callback)(&cb, data);
+    }
+
+    if (scratch.bytes > 0)
+      free((char *) scratch.bitmap);
+
+    /*
+     * Rotations always change things, so just return a value indicating this.
+     */
+    font->modified = 1;
+    return 1;
+}
+
+static void
+_bdf_resize_shear(bdf_font_t *font, int neg, short degrees,
+                  bdf_glyph_t *glyph, bdf_bitmap_t *scratch,
+                  unsigned short *width, unsigned short *height)
+{
+    unsigned short wd, w, bytes;
+    short x1, y1, x2, y2;
+
+    w = 0;
+    *height = glyph->bbx.height;
+
+    /*
+     * Shear the lower left and upper right corners and check for a potential
+     * resize.
+     */
+    x1 = 0;
+    y1 = glyph->bbx.height;
+    x2 = glyph->bbx.width;
+    y2 = 0;
+
+    if (neg) {
+        x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]);
+    } else {
+        x1 += (short) ((double) (glyph->bbx.height - y1) *
+                       _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) (glyph->bbx.height - y2) *
+                       _bdf_tan_tbl[degrees]);
+    }
+
+    wd = MYABS(x2 - x1);
+    w = MAX(w, wd);
+
+    if (wd > font->bbx.width)
+      font->bbx.width += wd - font->bbx.width;
+
+    /*
+     * Shear the upper left and lower right corners and check for a potential
+     * resize.
+     */
+    x1 = 0;
+    y1 = 0;
+    x2 = glyph->bbx.width;
+    y2 = glyph->bbx.height;
+
+    if (neg) {
+        x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]);
+    } else {
+        x1 += (short) ((double) (glyph->bbx.height - y1) *
+                       _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) (glyph->bbx.height - y2) *
+                       _bdf_tan_tbl[degrees]);
+    }
+
+    wd = MYABS(x2 - x1);
+    w = MAX(w, wd);
+
+    if (wd > font->bbx.width)
+      font->bbx.width += wd - font->bbx.width;
+
+    if (font->bbx.width > scratch->width ||
+        font->bbx.height > scratch->height) {
+        scratch->width = MAX(font->bbx.width, scratch->width);
+        scratch->height = MAX(font->bbx.height, scratch->height);
+        bytes = (((font->bbx.width * font->bpp) + 7) >> 3) * font->bbx.height;
+        if (scratch->bytes == 0)
+          scratch->bitmap = (unsigned char *) malloc(bytes);
+        else
+          scratch->bitmap = (unsigned char *)
+              realloc((char *) scratch->bitmap, bytes);
+        scratch->bytes = bytes;
+    }
+
+    /*
+     * Clear the bitmap.
+     */
+    (void) memset((char *) scratch->bitmap, 0, scratch->bytes);
+
+    /*
+     * Return the new glyph width.
+     */
+    *width = w;
+}
+
+int
+bdf_shear_glyphs(bdf_font_t *font, short degrees, int start,
+                 int end, bdf_callback_t callback, void *data,
+                 int unencoded)
+{
+    int neg, bpr, sbpr;
+    unsigned short wd, ht, si, di, byte, col;
+    short x, y, nx, shiftx, ox;
+    bdf_glyph_t *gp, *sp, *ep;
+    unsigned char *masks;
+    bdf_bitmap_t scratch;
+    bdf_callback_struct_t cb;
+
+    if (font == 0 || (unencoded && font->unencoded_used == 0) ||
+        font->glyphs_used == 0)
+      return 0;
+
+    if (degrees == 0 || degrees < -45 || degrees > 45)
+      return 0;
+
+    if ((neg = (degrees < 0)))
+      degrees = -degrees;
+
+    masks = 0;
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Initialize the scratch bitmap.
+     */
+    (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t));
+
+    /*
+     * Call the progress initialization callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_SHEAR_START;
+        cb.total = (end - start) + 1;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    sp = _bdf_locate_glyph(font, start, unencoded);
+    ep = _bdf_locate_glyph(font, end, unencoded);
+    for (gp = sp; sp <= ep; sp++) {
+        /*
+         * Call the callback if one was provided.
+         */
+        if (sp != gp && callback != 0) {
+            cb.reason = BDF_SHEARING;
+            cb.current = (sp->encoding - start) + 1;
+            (*callback)(&cb, data);
+        }
+
+        /*
+         * Resize the bitmap, adjust the font bounding box, and get the new
+         * glyph width and height.
+         */
+        _bdf_resize_shear(font, neg, degrees, sp, &scratch, &wd, &ht);
+
+        shiftx = 0;
+        sbpr = ((wd * font->bpp) + 7) >> 3;
+        bpr = ((sp->bbx.width * font->bpp) + 7) >> 3;
+        for (y = 0; y < sp->bbx.height; y++) {
+            for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) {
+                si = (col & 7) / font->bpp;
+                byte = sp->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    if (neg)
+                      nx = x + (short) ((double) y * _bdf_tan_tbl[degrees]);
+                    else
+                      nx = x + (short) ((double) (sp->bbx.height - y) *
+                                        _bdf_tan_tbl[degrees]);
+
+                    if (nx < 0) {
+                        shiftx = MIN(shiftx, nx);
+                        nx += wd;
+                    } else if (nx >= wd) {
+                        ox = (nx - wd) + 1;
+                        shiftx = MAX(shiftx, ox);
+                        nx -= wd;
+                    }
+                    nx *= font->bpp;
+                    di = (nx & 7) / font->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * font->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * font->bpp;
+                    scratch.bitmap[(y * sbpr) + (nx >> 3)] |= byte;
+                }
+            }
+        }
+        /*
+         * Resize the glyph bitmap if necessary.
+         */
+        if (wd != sp->bbx.width || ht != sp->bbx.height) {
+            sp->bbx.width = wd;
+            sp->bbx.height = ht;
+            sp->bbx.ascent = ht - sp->bbx.descent;
+            sp->bytes = (((wd * font->bpp) + 7) >> 3) * ht;
+            sp->bitmap = (unsigned char *)
+                realloc((char *) sp->bitmap, sp->bytes);
+        }
+        (void) memset((char *) sp->bitmap, 0, sp->bytes);
+
+        /*
+         * Copy the glyph from the scratch area to the glyph bitmap,
+         * adjusting for any shift values encountered.
+         */
+        bpr = ((sp->bbx.width * font->bpp) + 7) >> 3;
+        for (y = 0; y < sp->bbx.height; y++) {
+            for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) {
+                si = (col & 7) / font->bpp;
+                byte = scratch.bitmap[(y * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    nx = x - shiftx;
+                    if (nx < 0)
+                      nx += sp->bbx.width;
+                    else if (nx >= sp->bbx.width)
+                      nx -= sp->bbx.width;
+                    nx *= font->bpp;
+                    di = (nx & 7) / font->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * font->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * font->bpp;
+                    sp->bitmap[(y * bpr) + (nx >> 3)] |= byte;
+                }
+            }
+        }
+        /*
+         * Mark the glyph as modified.
+         */
+        if (unencoded)
+          _bdf_set_glyph_modified(font->umod, sp->encoding);
+        else
+          _bdf_set_glyph_modified(font->nmod, sp->encoding);
+    }
+
+    /*
+     * Call the callback one more time to make sure the client knows
+     * this is done.
+     */
+    if (callback != 0 && cb.current < cb.total) {
+        cb.reason = BDF_TRANSLATING;
+        cb.current = cb.total;
+        (*callback)(&cb, data);
+    }
+
+    if (scratch.bytes > 0)
+      free((char *) scratch.bitmap);
+
+    /*
+     * Rotations always change things, so just return a value indicating this.
+     */
+    font->modified = 1;
+    return 1;
+}
+
+static void
+_bdf_widen_by(bdf_font_t *f, bdf_glyph_t *g, bdf_bitmap_t *s, int n)
+{
+    int bytes, sbpr, dbpr, col;
+    short x, y, si, di;
+    unsigned char *bmap, *masks;
+
+    masks = 0;
+    switch (f->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    s->height = g->bbx.height;
+    s->width = g->bbx.width + n;
+
+    bytes = (((s->width * f->bpp) + 7) >> 3) * s->height;
+
+    if (s->bytes == 0)
+      s->bitmap = (unsigned char *) malloc(bytes);
+    else
+      s->bitmap = (unsigned char *)
+          realloc((char *) s->bitmap, bytes);
+    s->bytes = bytes;
+
+    (void) memset((char *) s->bitmap, 0, s->bytes);
+
+    /*
+     * Copy the glyph bitmap to the scratch area, and then swap the bitmaps.
+     */
+    sbpr = ((g->bbx.width * f->bpp) + 7) >> 3;
+    dbpr = ((s->width * f->bpp) + 7) >> 3;
+    for (y = 0; y < g->bbx.height; y++) {
+        for (col = x = 0; x < g->bbx.width; x++, col += f->bpp) {
+            si = (col & 7) / f->bpp;
+            bytes = g->bitmap[(y * sbpr) + (col >> 3)] & masks[si];
+            if (bytes) {
+                di = ((x * f->bpp) & 7) / f->bpp;
+                if (di < si)
+                  bytes <<= (si - di) * f->bpp;
+                else if (di > si)
+                  bytes >>= (di - si) * f->bpp;
+                s->bitmap[(y * dbpr) + (col >> 3)] |= bytes;
+            }
+        }
+    }
+    g->bbx.width = s->width;
+
+    /*
+     * Swap the bytes and bitmap fields from the scratch area and the glyph.
+     */
+    bytes = g->bytes;
+    g->bytes = s->bytes;
+    s->bytes = bytes;
+
+    bmap = g->bitmap;
+    g->bitmap = s->bitmap;
+    s->bitmap = bmap;
+}
+
+int
+bdf_embolden_glyphs(bdf_font_t *font, int start, int end,
+                    bdf_callback_t callback, void *data, int unencoded,
+                    int *resize)
+{
+    int mod, gmod, bpr;
+    short x, y;
+    unsigned short si, di, b1, b2, col;
+    unsigned char *masks;
+    bdf_glyph_t *gp, *sp, *ep;
+    bdf_bitmap_t scratch;
+    bdf_callback_struct_t cb;
+
+    if (font == 0 || (unencoded && font->unencoded_used == 0) ||
+        font->glyphs_used == 0)
+      return 0;
+
+    /*
+     * Initialize the scratch bitmap which may be needed.
+     */
+    (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t));
+
+    mod = 0;
+    gp = 0;
+
+    masks = 0;
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Call the progress initialization callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_EMBOLDEN_START;
+        cb.total = (end - start) + 1;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Initialize the resize flag for the caller.
+     */
+    *resize = 0;
+
+    sp = _bdf_locate_glyph(font, start, unencoded);
+    ep = _bdf_locate_glyph(font, end, unencoded);
+    for (; sp <= ep; sp++) {
+        /*
+         * Call the callback if one was provided.
+         */
+        if (sp != gp && callback != 0) {
+            cb.reason = BDF_EMBOLDENING;
+            cb.current = (sp->encoding - start) + 1;
+            (*callback)(&cb, data);
+        }
+
+        if (font->spacing == BDF_PROPORTIONAL ||
+            (font->spacing == BDF_MONOWIDTH &&
+             sp->bbx.width < font->bbx.width)) {
+            /*
+             * Only widen the glyph if it is within reason.
+             */
+            _bdf_widen_by(font, sp, &scratch, 1);
+
+            if (sp->bbx.width > font->bbx.width) {
+                /*
+                 * Bump the font width up by the difference.
+                 */
+                font->bbx.width += sp->bbx.width - font->bbx.width;
+                *resize = 1;
+            }
+        }
+
+        gmod = 0;
+        bpr = ((sp->bbx.width * font->bpp) + 7) >> 3;
+        for (y = 0; y < sp->bbx.height; y++) {
+            col = (sp->bbx.width - 1) * font->bpp;
+            for (x = sp->bbx.width - 1; x > 0; x--, col -= font->bpp) {
+                si = (col & 7) / font->bpp;
+                di = ((col - font->bpp) & 7) / font->bpp;
+                b1 = (x == sp->bbx.width) ? 0 :
+                    sp->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+                b2 = sp->bitmap[(y * bpr) + ((col - font->bpp) >> 3)] &
+                    masks[di];
+                if (!b1 && b2) {
+                    if (di < si)
+                      b2 >>= (si - di) * font->bpp;
+                    else if (di > si)
+                      b2 <<= (di - si) * font->bpp;
+                    sp->bitmap[(y * bpr) + (col >> 3)] |= b2;
+                    gmod = mod = 1;
+                }
+            }
+        }
+        /*
+         * Mark the glyph as modified.
+         */
+        if (gmod) {
+            if (unencoded)
+              _bdf_set_glyph_modified(font->umod, sp->encoding);
+            else
+              _bdf_set_glyph_modified(font->nmod, sp->encoding);
+        }
+    }
+
+    /*
+     * Call the callback one more time to make sure the client knows
+     * this is done.
+     */
+    if (callback != 0 && cb.current < cb.total) {
+        cb.reason = BDF_EMBOLDENING;
+        cb.current = cb.total;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Deallocate the scratch bitmap if necessary.
+     */
+    if (scratch.bytes > 0)
+      free((char *) scratch.bitmap);
+
+    font->modified = mod;
+
+    return mod;
+}
+
+static int _endian = 1;
+static char *little_endian = (char *) &_endian;
+
+int
+bdf_little_endian(void)
+{
+    return *little_endian;
+}
diff --git a/bdf.h b/bdf.h
new file mode 100644 (file)
index 0000000..3688df5
--- /dev/null
+++ b/bdf.h
@@ -0,0 +1,727 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_bdf
+#define _h_bdf
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef __digital__
+#include <unistd.h>
+#endif
+#include <string.h>
+
+#ifdef HAVE_XLIB
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif /* HAVE_XLIB */
+
+#ifdef HAVE_FREETYPE
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#endif /* HAVE_FREETYPE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**************************************************************************
+ *
+ * BDF font options macros and types.
+ *
+ **************************************************************************/
+
+#define BDF_UNIX_EOL 1           /* Save fonts with Unix LF.              */
+#define BDF_DOS_EOL  2           /* Save fonts with DOS CRLF.             */
+#define BDF_MAC_EOL  3           /* Save fonts with Mac CR.               */
+
+#define BDF_CORRECT_METRICS 0x01 /* Correct invalid metrics when loading. */
+#define BDF_KEEP_COMMENTS   0x02 /* Preserve the font comments.           */
+#define BDF_KEEP_UNENCODED  0x04 /* Keep the unencoded glyphs.            */
+#define BDF_PROPORTIONAL    0x08 /* Font has proportional spacing.        */
+#define BDF_MONOWIDTH       0x10 /* Font has mono width.                  */
+#define BDF_CHARCELL        0x20 /* Font has charcell spacing.            */
+
+#define BDF_ALL_SPACING (BDF_PROPORTIONAL|BDF_MONOWIDTH|BDF_CHARCELL)
+
+#define BDF_DEFAULT_LOAD_OPTIONS \
+    (BDF_CORRECT_METRICS|BDF_KEEP_COMMENTS|BDF_KEEP_UNENCODED|BDF_PROPORTIONAL)
+
+typedef struct {
+    int otf_flags;
+    int correct_metrics;
+    int keep_unencoded;
+    int keep_comments;
+    int pad_cells;
+    int font_spacing;
+    int point_size;
+    unsigned int resolution_x;
+    unsigned int resolution_y;
+    int bits_per_pixel;
+    int eol;
+    int psf_flags;
+    int cursor_font;
+} bdf_options_t;
+
+/*
+ * Callback function type for unknown configuration options.
+ */
+typedef int (*bdf_options_callback_t)(bdf_options_t *opts,
+                                      char **params,
+                                      unsigned int nparams,
+                                      void *client_data);
+
+/**************************************************************************
+ *
+ * BDF font property macros and types.
+ *
+ **************************************************************************/
+
+#define BDF_ATOM     1
+#define BDF_INTEGER  2
+#define BDF_CARDINAL 3
+
+/*
+ * This structure represents a particular property of a font.
+ * There are a set of defaults and each font has their own.
+ */
+typedef struct {
+    char *name;         /* Name of the property.                        */
+    int format;         /* Format of the property.                      */
+    int builtin;        /* A builtin property.                          */
+    union {
+        char *atom;
+        int int32;
+        unsigned int card32;
+    } value;            /* Value of the property.                       */
+} bdf_property_t;
+
+/**************************************************************************
+ *
+ * SBIT metrics specific structures.
+ *
+ **************************************************************************/
+
+/*
+ * Boolean flags for SBIT metrics files.
+ */
+#define BDF_SBIT_MONO_ADVANCE 0x0001
+#define BDF_SBIT_ADD_EBLC     0x0002
+#define BDF_SBIT_APPLE_COMPAT 0x0004
+
+/*
+ * Direction macros (inclusive, can be combined).
+ */
+#define BDF_SBIT_HORIZONTAL   0x0008
+#define BDF_SBIT_VERTICAL     0x0010
+
+/*
+ * Bitmap storage options (exclusive, cannot be combined).
+ */
+#define BDF_SBIT_STORE_SMALL  0x0020
+#define BDF_SBIT_STORE_FAST   0x0040
+
+typedef struct {
+    short cnum;         /* Caret slope numerator.                       */
+    short cdenom;       /* Caret slope denominator.                     */
+    short coff;         /* Caret offset.                                */
+    short sx;           /* Scaled version horizontal PPEM size.         */
+    short sy;           /* Scaled version vertical PPEM size (optional).*/
+    short flags;        /* Booleans and other non-numeric values.       */
+} bdf_sbit_t;
+
+/**************************************************************************
+ *
+ * BDF opaque undo information types.
+ *
+ **************************************************************************/
+
+typedef struct _bdf_undo_t *bdf_undo_t;
+
+/**************************************************************************
+ *
+ * PSF font flags and Unicode mapping tables.  Stored internally in UTF-8.
+ *
+ **************************************************************************/
+
+/*
+ * Flags used for exporting PSF fonts and their Unicode maps.
+ */
+#define BDF_PSF_FONT   0x01
+#define BDF_PSF_UNIMAP 0x02
+#define BDF_PSF_ALL (BDF_PSF_FONT|BDF_PSF_UNIMAP)
+
+typedef struct {
+    unsigned char *map;
+    unsigned int map_used;
+    unsigned int map_size;
+} bdf_psf_unimap_t;
+
+/**************************************************************************
+ *
+ * BDF font metric and glyph types.
+ *
+ **************************************************************************/
+
+/*
+ * A general bitmap type, mostly used when the glyph bitmap is being edited.
+ */
+typedef struct {
+    short x;
+    short y;
+    unsigned short width;
+    unsigned short height;
+    unsigned short bpp;
+    unsigned short pad;
+    unsigned char *bitmap;
+    unsigned int bytes;
+} bdf_bitmap_t;
+
+typedef struct {
+    int font_spacing;
+    unsigned short swidth;
+    unsigned short dwidth;
+    unsigned short width;
+    unsigned short height;
+    short x_offset;
+    short y_offset;
+    short ascent;
+    short descent;
+} bdf_metrics_t;
+
+typedef struct {
+    unsigned short width;
+    unsigned short height;
+    short x_offset;
+    short y_offset;
+    short ascent;
+    short descent;
+} bdf_bbx_t;
+
+typedef struct {
+    char *name;                 /* Glyph name.                          */
+    int encoding;              /* Glyph encoding.                      */
+    unsigned short swidth;      /* Scalable width.                      */
+    unsigned short dwidth;      /* Device width.                        */
+    bdf_bbx_t bbx;              /* Glyph bounding box.                  */
+    unsigned char *bitmap;      /* Glyph bitmap.                        */
+    unsigned short bytes;       /* Number of bytes used for the bitmap. */
+    bdf_psf_unimap_t unicode;   /* PSF Unicode mappings.                */
+} bdf_glyph_t;
+
+typedef struct {
+    unsigned short pad;         /* Pad to 4-byte boundary.              */
+    unsigned short bpp;         /* Bits per pixel.                      */
+    int start;                 /* Beginning encoding value of glyphs.  */
+    int end;                   /* Ending encoding value of glyphs.     */
+    bdf_glyph_t *glyphs;        /* Glyphs themselves.                   */
+    unsigned int glyphs_size;  /* Glyph structures allocated.          */
+    unsigned int glyphs_used;  /* Glyph structures used.               */
+    bdf_bbx_t bbx;              /* Overall bounding box of glyphs.      */
+} bdf_glyphlist_t;
+
+typedef struct {
+    char *name;                 /* Name of the font.                     */
+    bdf_bbx_t bbx;              /* Font bounding box.                    */
+
+    int point_size;            /* Point size of the font.               */
+    unsigned int resolution_x; /* Font horizontal resolution.           */
+    unsigned int resolution_y; /* Font vertical resolution.             */
+
+    int hbf;                    /* Font came from an HBF font.           */
+
+    int spacing;                /* Font spacing value.                   */
+
+    unsigned short monowidth;   /* Logical width for monowidth font.     */
+
+    int default_glyph;         /* Encoding of the default glyph.        */
+
+    int font_ascent;           /* Font ascent.                          */
+    int font_descent;          /* Font descent.                         */
+
+    int glyphs_size;           /* Glyph structures allocated.           */
+    int glyphs_used;           /* Glyph structures used.                */
+    bdf_glyph_t *glyphs;        /* Glyphs themselves.                    */
+
+    int unencoded_size;        /* Unencoded glyph structures allocated. */
+    int unencoded_used;        /* Unencoded glyph structures used.      */
+    bdf_glyph_t *unencoded;     /* Unencoded glyphs themselves.          */
+
+    unsigned int props_size;   /* Font properties allocated.            */
+    unsigned int props_used;   /* Font properties used.                 */
+    bdf_property_t *props;      /* Font properties themselves.           */
+
+    char *comments;             /* Font comments.                        */
+    unsigned int comments_len; /* Length of comment string.             */
+
+    char *acmsgs;               /* Auto-correction messages.             */
+    unsigned int acmsgs_len;   /* Length of auto-correction messages.   */
+
+    bdf_glyphlist_t overflow;   /* Storage used for glyph insertion.     */
+
+    void *internal;             /* Internal data for the font.           */
+
+    unsigned int nmod[2048];   /* Bitmap indicating modified glyphs.    */
+    unsigned int umod[2048];   /* Bitmap indicating modified unencoded. */
+
+    unsigned short modified;    /* Boolean indicating font modified.     */
+    unsigned short bpp;         /* Bits per pixel.                       */
+
+    bdf_sbit_t *sbits;          /* Associcated SBIT metrics.             */
+    unsigned int sbits_used;   /* Number of SBIT metrics entries.       */
+    unsigned int sbits_size;   /* Amount of entries allocated.          */
+
+    bdf_undo_t *undo_stack;     /* Record of undoable operations.        */
+    unsigned int undo_used;    /* Amount of undo stack used.            */
+    unsigned int undo_size;    /* Amount of undo stack allocated.       */
+
+    bdf_psf_unimap_t unicode;   /* PSF Unicode table.                    */
+} bdf_font_t;
+
+/**************************************************************************
+ *
+ * BDF glyph grid structures for editing glyph bitmaps.
+ *
+ **************************************************************************/
+
+typedef struct {
+    char *name;
+    int encoding;              /* The glyph encoding.                  */
+    unsigned short unencoded;   /* Whether the glyph was unencoded.     */
+    unsigned short bpp;         /* Bits per pixel.                      */
+    int spacing;                /* Font spacing.                        */
+    int resolution_x;          /* Horizontal resolution.               */
+    int resolution_y;          /* Vertical resolution.                 */
+    unsigned int point_size;   /* Font point size.                     */
+    unsigned short swidth;      /* Scalable width.                      */
+    unsigned short dwidth;      /* Device width.                        */
+    bdf_bbx_t font_bbx;         /* Font bounding box.                   */
+    bdf_bbx_t glyph_bbx;        /* Glyph bounding box.                  */
+    unsigned char *bitmap;      /* The grid bitmap.                     */
+    unsigned short bytes;       /* Number of bytes in the grid bitmap.  */
+    short grid_width;           /* Width of the grid.                   */
+    short grid_height;          /* Height of the grid.                  */
+    short base_x;               /* Baseline X coordinate.               */
+    short base_y;               /* Baseline Y coordinate.               */
+    short glyph_x;              /* Top-left X position of glyph.        */
+    short glyph_y;              /* Top-left Y position of glyph.        */
+    unsigned short modified;    /* Flag indicating if bitmap modified.  */
+    short cap_height;           /* Font CAP_HEIGHT if it exists.        */
+    short x_height;             /* Font X_HEIGHT if it exists.          */
+    bdf_bitmap_t sel;           /* Selected portion of the glyph bitmap.*/
+    bdf_psf_unimap_t unicode;   /* PSF Unicode mappings for this glyph. */
+} bdf_glyph_grid_t;
+
+/**************************************************************************
+ *
+ * Types for load/save callbacks.
+ *
+ **************************************************************************/
+
+/*
+ * Callback reasons.
+ */
+#define BDF_LOAD_START       1
+#define BDF_LOADING          2
+#define BDF_SAVE_START       3
+#define BDF_SAVING           4
+#define BDF_TRANSLATE_START  5
+#define BDF_TRANSLATING      6
+#define BDF_ROTATE_START     7
+#define BDF_ROTATING         8
+#define BDF_SHEAR_START      9
+#define BDF_SHEARING         10
+#define BDF_GLYPH_NAME_START 11
+#define BDF_GLYPH_NAME       12
+#define BDF_EXPORT_START     13
+#define BDF_EXPORTING        14
+#define BDF_EMBOLDEN_START   15
+#define BDF_EMBOLDENING      16
+#define BDF_WARNING          20
+#define BDF_ERROR            21
+
+/*
+ * Error codes.
+ */
+#define BDF_OK                 0
+#define BDF_MISSING_START     -1
+#define BDF_MISSING_FONTNAME  -2
+#define BDF_MISSING_SIZE      -3
+#define BDF_MISSING_FONTBBX   -4
+#define BDF_MISSING_CHARS     -5
+#define BDF_MISSING_STARTCHAR -6
+#define BDF_MISSING_ENCODING  -7
+#define BDF_MISSING_BBX       -8
+
+#define BDF_NOT_CONSOLE_FONT  -10
+#define BDF_NOT_MF_FONT       -11
+#define BDF_NOT_PSF_FONT      -12
+#define BDF_PSF_SHORT_TABLE   -13
+#define BDF_PSF_LONG_TABLE    -14
+#define BDF_PSF_CORRUPT_UTF8  -15
+#define BDF_PSF_BUFFER_OVRFL  -16
+#define BDF_PSF_UNSUPPORTED   -17
+#define BDF_BAD_RANGE         -98
+#define BDF_EMPTY_FONT        -99
+#define BDF_INVALID_LINE      -100
+
+typedef struct {
+    unsigned int reason;
+    unsigned int current;
+    unsigned int total;
+    unsigned int errlineno;
+} bdf_callback_struct_t;
+
+typedef void (*bdf_callback_t)(bdf_callback_struct_t *call_data,
+                               void *client_data);
+
+/**************************************************************************
+ *
+ * BDF font API.
+ *
+ **************************************************************************/
+
+/*
+ * Startup and shutdown functions.
+ */
+extern void bdf_setup(void);
+extern void bdf_cleanup(void);
+
+/*
+ * Configuration file loading and saving.
+ */
+extern void bdf_load_options(FILE *in, bdf_options_t *opts,
+                             bdf_options_callback_t callback,
+                             void *client_data);
+extern void bdf_save_options(FILE *out, bdf_options_t *opts);
+
+/*
+ * Font options functions.
+ */
+extern void bdf_default_options(bdf_options_t *opts);
+
+/*
+ * Font load, create, save and free functions.
+ */
+extern bdf_font_t *bdf_new_font(char *name, int point_size,
+                                int resolution_x, int resolution_y,
+                                int spacing, int bpp);
+extern bdf_font_t *bdf_load_font(FILE *in, bdf_options_t *opts,
+                                 bdf_callback_t callback, void *data);
+#ifdef HAVE_HBF
+extern bdf_font_t *bdf_load_hbf_font(char *filename, bdf_options_t *opts,
+                                     bdf_callback_t callback, void *data);
+#endif
+
+#ifdef HAVE_XLIB
+extern bdf_font_t *bdf_load_server_font(Display *d, XFontStruct *f,
+                                        char *name, bdf_options_t *opts,
+                                        bdf_callback_t callback,
+                                        void *data);
+#endif /* HAVE_XLIB */
+
+extern int bdf_load_console_font(FILE *in, bdf_options_t *opts,
+                                 bdf_callback_t callback, void *data,
+                                 bdf_font_t *fonts[3], int *nfonts);
+
+extern int bdf_load_mf_font(FILE *in, bdf_options_t *opts,
+                            bdf_callback_t callback, void *data,
+                            bdf_font_t **font);
+
+extern void bdf_save_font(FILE *out, bdf_font_t *font, bdf_options_t *opts,
+                          bdf_callback_t callback, void *data);
+
+extern void bdf_save_sbit_metrics(FILE *out, bdf_font_t *font,
+                                  bdf_options_t *opts, char *appname);
+
+extern void bdf_export_hex(FILE *out, bdf_font_t *font, bdf_options_t *opts,
+                           bdf_callback_t callback, void *data);
+
+extern int bdf_export_psf(FILE *out, bdf_font_t *font, bdf_options_t *opts,
+                          int start, int end);
+
+extern void bdf_free_font(bdf_font_t *font);
+
+#ifdef HAVE_FREETYPE
+
+/*
+ * OpenType related macros and functions.
+ */
+
+/*
+ * ID numbers of the strings that can appear in an OpenType font.
+ */
+#define BDFOTF_COPYRIGHT_STRING     0
+#define BDFOTF_FAMILY_STRING        1
+#define BDFOTF_SUBFAMILY_STRING     2
+#define BDFOTF_UNIQUEID_STRING      3
+#define BDFOTF_FULLNAME_STRING      4
+#define BDFOTF_VENDOR_STRING        5
+#define BDFOTF_POSTSCRIPT_STRING    6
+#define BDFOTF_TRADEMARK_STRING     7
+#define BDFOTF_FOUNDRY_STRING       8
+#define BDFOTF_DESIGNER_STRING      9
+#define BDFOTF_DESCRIPTION_STRING   10
+#define BDFOTF_VENDORURL_STRING     11
+#define BDFOTF_DESIGNERURL_STRING   12
+#define BDFOTF_LICENSE_STRING       13
+#define BDFOTF_LICENSEURL_STRING    14
+#define BDFOTF_RESERVED_STRING      15
+#define BDFOTF_PREFFAMILY_STRING    16
+#define BDFOTF_PREFSUBFAMILY_STRING 17
+#define BDFOTF_COMPATIBLEMAC_STRING 18
+#define BDFOTF_SAMPLETEXT_STRING    19
+#define BDFOTF_PSCIDFF_STRING       20
+
+extern char *bdfotf_platform_name(short pid);
+extern char *bdfotf_encoding_name(short pid, short eid);
+extern int bdfotf_get_english_string(FT_Face face, int nameID,
+                                     int dash_to_space, char *name);
+
+extern int bdfotf_load_font(FT_Face face, short pid, short eid,
+                            bdf_options_t *opts, bdf_callback_t callback,
+                            void *data, bdf_font_t **font);
+
+#endif /* HAVE_FREETYPE */
+
+/*
+ * FON/FNT related functions.
+ */
+
+/*
+ * String ID numbers for FON/FNT fonts.
+ */
+#define BDFFNT_COPYRIGHT 1
+#define BDFFNT_TYPEFACE  2
+
+/*
+ * Opaque font type.
+ */
+typedef struct _bdffnt_font_t *bdffnt_font_t;
+
+extern int bdffnt_open_font(char *path, bdffnt_font_t *font);
+extern void bdffnt_close_font(bdffnt_font_t font);
+extern int bdffnt_font_count(bdffnt_font_t font);
+extern int bdffnt_get_copyright(bdffnt_font_t font, unsigned int fontID,
+                                unsigned char *string);
+extern int bdffnt_get_facename(bdffnt_font_t font, unsigned int fontID,
+                               int for_xlfd, unsigned char *string);
+extern int bdffnt_char_count(bdffnt_font_t font, unsigned int fontID);
+extern int bdffnt_font_pointsize(bdffnt_font_t font, unsigned int fontID);
+extern int bdffnt_load_font(bdffnt_font_t font, unsigned int fontID,
+                            bdf_callback_t callback, void *data,
+                            bdf_font_t **out);
+
+/*
+ * PSF font section.
+ *
+ * In PSF fonts, a Unicode table on the end of the font may map a single
+ * glyph to several locations.  The BDFPSF_SOURCE_GLYPH marks the glyphs that
+ * are source glyphs and the BDFPSF_PSEUDO_GLYPH flag marks glyphs that are
+ * clones of a source glyph.
+ */
+#define BDFPSF_SOURCE_GLYPH 0x0001
+#define BDFPSF_PSEUDO_GLYPH 0x0002
+
+extern bdf_font_t *bdf_load_psf(FILE *in, unsigned char *magic,
+                                bdf_options_t *opts,
+                                bdf_callback_t callback, void *data,
+                                int *awidth);
+
+/*
+ * Font property functions.
+ */
+extern void bdf_create_property(char *name, int type);
+extern bdf_property_t *bdf_get_property(char *name);
+extern unsigned int bdf_property_list(bdf_property_t **props);
+
+extern void bdf_add_font_property(bdf_font_t *font, bdf_property_t *property);
+extern void bdf_delete_font_property(bdf_font_t *font, char *name);
+extern bdf_property_t *bdf_get_font_property(bdf_font_t *font, char *name);
+extern unsigned int bdf_font_property_list(bdf_font_t *font,
+                                            bdf_property_t **props);
+extern int bdf_is_xlfd_property(char *name);
+
+/*
+ * Font comment functions.
+ */
+extern int bdf_replace_comments(bdf_font_t *font, char *comments,
+                                unsigned int comments_len);
+
+/*
+ * Other miscellaneous functions.
+ */
+extern void bdf_set_default_metrics(bdf_font_t *font);
+
+/*
+ * Font glyph editing functions.
+ */
+extern int bdf_glyph_modified(bdf_font_t *font, int which, int unencoded);
+
+extern void bdf_copy_glyphs(bdf_font_t *font, int start, int end,
+                            bdf_glyphlist_t *glyphs, int unencoded);
+
+extern int bdf_delete_glyphs(bdf_font_t *font, int start, int end,
+                             int unencoded);
+
+extern int bdf_insert_glyphs(bdf_font_t *font, int start,
+                             bdf_glyphlist_t *glyphs);
+
+extern int bdf_replace_glyphs(bdf_font_t *font, int start,
+                              bdf_glyphlist_t *glyphs, int unencoded);
+
+extern int bdf_merge_glyphs(bdf_font_t *font, int start,
+                            bdf_glyphlist_t *glyphs, int unencoded);
+
+extern int bdf_replace_mappings(bdf_font_t *font, int encoding,
+                                bdf_psf_unimap_t *map, int unencoded);
+
+/**************************************************************************
+ *
+ * Other API functions.
+ *
+ **************************************************************************/
+
+extern int bdf_set_font_bbx(bdf_font_t *font, bdf_metrics_t *metrics);
+
+extern void bdf_set_modified(bdf_font_t *font, int modified);
+
+extern int bdf_has_xlfd_name(bdf_font_t *font);
+
+extern char *bdf_make_xlfd_name(bdf_font_t *font, char *foundry,
+                                char *family);
+
+extern void bdf_update_name_from_properties(bdf_font_t *font);
+
+extern int bdf_update_properties_from_name(bdf_font_t *font);
+
+extern int bdf_update_average_width(bdf_font_t *font);
+
+extern int bdf_set_unicode_glyph_names(FILE *in, bdf_font_t *font,
+                                       bdf_callback_t callback);
+
+extern int bdf_set_adobe_glyph_names(FILE *in, bdf_font_t *font,
+                                     bdf_callback_t callback);
+
+extern int bdf_set_glyph_code_names(int prefix, bdf_font_t *font,
+                                    bdf_callback_t callback);
+
+/*
+ * Routine to add Unicode mappings when editing PSF fonts.
+ */
+extern int bdf_psf_add_unicode_mapping(bdf_psf_unimap_t *u,
+                                       unsigned int *mapping,
+                                       unsigned int mapping_cnt);
+
+/**************************************************************************
+ *
+ * Glyph grid API.
+ *
+ **************************************************************************/
+
+/*
+ * Glyph grid allocation and deallocation functions.
+ */
+extern bdf_glyph_grid_t *bdf_make_glyph_grid(bdf_font_t *font,
+                                             int code,
+                                             int unencoded);
+extern void bdf_free_glyph_grid(bdf_glyph_grid_t *grid);
+
+/*
+ * Glyph grid information functions.
+ */
+extern void bdf_grid_image(bdf_glyph_grid_t *grid, bdf_bitmap_t *image);
+extern void bdf_grid_origin(bdf_glyph_grid_t *grid, short *x, short *y);
+extern bdf_glyph_t *bdf_grid_glyph(bdf_glyph_grid_t *grid);
+
+/*
+ * Glyph grid editing functions.
+ */
+extern int bdf_grid_enlarge(bdf_glyph_grid_t *grid, unsigned short width,
+                            unsigned short height);
+extern int bdf_grid_resize(bdf_glyph_grid_t *grid,
+                           bdf_metrics_t *metrics);
+extern int bdf_grid_crop(bdf_glyph_grid_t *grid, int grid_modified);
+
+extern int bdf_grid_set_pixel(bdf_glyph_grid_t *grid, short x, short y,
+                              int val);
+extern int bdf_grid_clear_pixel(bdf_glyph_grid_t *grid, short x, short y);
+extern int bdf_grid_invert_pixel(bdf_glyph_grid_t *grid,
+                                 short x, short y, int val);
+extern int bdf_grid_shift(bdf_glyph_grid_t *grid, short xcount,
+                          short ycount);
+extern int bdf_grid_flip(bdf_glyph_grid_t *grid, short dir);
+extern int bdf_grid_rotate(bdf_glyph_grid_t *grid, short degrees,
+                           int *resize);
+extern int bdf_grid_shear(bdf_glyph_grid_t *grid, short degrees,
+                          int *resize);
+extern int bdf_grid_embolden(bdf_glyph_grid_t *grid);
+
+/*
+ * Glyph grid selection functions.
+ */
+extern int bdf_has_selection(bdf_glyph_grid_t *grid, short *x, short *y,
+                             short *width, short *height);
+extern void bdf_set_selection(bdf_glyph_grid_t *grid, short x, short y,
+                              short width, short height);
+extern void bdf_lose_selection(bdf_glyph_grid_t *grid);
+extern void bdf_detach_selection(bdf_glyph_grid_t *grid);
+extern void bdf_attach_selection(bdf_glyph_grid_t *grid);
+extern void bdf_delete_selection(bdf_glyph_grid_t *grid);
+extern int bdf_in_selection(bdf_glyph_grid_t *grid, short x, short y,
+                            short *set);
+extern void bdf_add_selection(bdf_glyph_grid_t *grid, bdf_bitmap_t *sel);
+
+/*
+ * Glyph grid misc functions.
+ */
+extern int bdf_grid_color_at(bdf_glyph_grid_t *grid, short x, short y);
+
+/*
+ * Graphical transformation functions.
+ */
+extern int bdf_translate_glyphs(bdf_font_t *font, short dx, short dy,
+                                int start, int end,
+                                bdf_callback_t callback, void *data,
+                                int unencoded);
+
+extern int bdf_rotate_glyphs(bdf_font_t *font, short degrees,
+                             int start, int end,
+                             bdf_callback_t callback, void *data,
+                             int unencoded);
+
+extern int bdf_shear_glyphs(bdf_font_t *font, short degrees,
+                            int start, int end,
+                            bdf_callback_t callback, void *data,
+                            int unencoded);
+
+extern int bdf_embolden_glyphs(bdf_font_t *font, int start, int end,
+                               bdf_callback_t callback, void *data,
+                               int unencoded, int *resize);
+
+extern int bdf_little_endian(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _h_bdf */
diff --git a/bdfP.h b/bdfP.h
new file mode 100644 (file)
index 0000000..1e9284e
--- /dev/null
+++ b/bdfP.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_bdfP
+#define _h_bdfP
+
+#include "bdf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef MYABS
+#define MYABS(xx) ((xx) < 0 ? -(xx) : (xx))
+#endif
+
+/*
+ * Macros and structures used for undo operations in the font.
+ */
+#define _UNDO_REPLACE_GLYPHS 1
+#define _UNDO_INSERT_GLYPHS  2
+#define _UNDO_MERGE_GLYPHS   3
+
+/*
+ * This structure is for undo operations of replacing and merging glyphs
+ * in the font.
+ */
+typedef struct {
+    bdf_bbx_t b;
+    bdf_glyphlist_t g;
+} _bdf_undo1_t;
+
+/*
+ * This structure is for undo operations of inserting glyphs.
+ */
+typedef struct {
+    bdf_bbx_t b;
+    int start;
+    int end;
+} _bdf_undo2_t;
+
+/*
+ * This is the final undo structure used to store undo information with the
+ * font.
+ */
+typedef struct {
+    int type;
+    union {
+        _bdf_undo1_t one;
+        _bdf_undo2_t two;
+    } field;
+} _bdf_undo_t;
+
+/*
+ * Tables for rotation and shearing.
+ */
+extern double _bdf_cos_tbl[];
+extern double _bdf_sin_tbl[];
+extern double _bdf_tan_tbl[];
+
+/*
+ * PSF magic numbers.
+ */
+extern unsigned char _bdf_psf1magic[];
+extern unsigned char _bdf_psf2magic[];
+extern char _bdf_psfcombined[];
+
+/*
+ * Arrays of masks for test with different bits per pixel.
+ */
+extern unsigned char bdf_onebpp[];
+extern unsigned char bdf_twobpp[];
+extern unsigned char bdf_fourbpp[];
+extern unsigned char bdf_eightbpp[];
+
+/*
+ * Simple routine for determining the ceiling.
+ */
+extern short _bdf_ceiling(double v);
+
+extern unsigned char *_bdf_strdup(unsigned char *s, unsigned int len);
+extern void _bdf_memmove(char *dest, char *src, unsigned int bytes);
+
+extern short _bdf_atos(char *s, char **end, int base);
+extern int _bdf_atol(char *s, char **end, int base);
+extern unsigned int _bdf_atoul(char *s, char **end, int base);
+
+/*
+ * Function to locate the nearest glyph to a specified encoding.
+ */
+extern bdf_glyph_t *_bdf_locate_glyph(bdf_font_t *font, int encoding,
+                                      int unencoded);
+
+/*
+ * Macros to test/set the modified status of a glyph.
+ */
+#define _bdf_glyph_modified(map, e) ((map)[(e) >> 5] & (1 << ((e) & 31)))
+#define _bdf_set_glyph_modified(map, e) (map)[(e) >> 5] |= (1 << ((e) & 31))
+#define _bdf_clear_glyph_modified(map, e) (map)[(e) >> 5] &= ~(1 << ((e) & 31))
+
+/*
+ * Function to add a message to the font.
+ */
+extern void _bdf_add_acmsg(bdf_font_t *font, char *msg, unsigned int len);
+
+/*
+ * Function to add a comment to the font.
+ */
+extern void _bdf_add_comment(bdf_font_t *font, char *comment,
+                             unsigned int len);
+
+/*
+ * Function to do glyph name table cleanup when exiting.
+ */
+extern void _bdf_glyph_name_cleanup(void);
+
+/*
+ * Function to pad cells when saving glyphs.
+ */
+extern void _bdf_pad_cell(bdf_font_t *font, bdf_glyph_t *glyph,
+                          bdf_glyph_t *cell);
+
+/*
+ * Function to crop glyphs down to their minimum bitmap.
+ */
+extern void _bdf_crop_glyph(bdf_font_t *font, bdf_glyph_t *glyph);
+
+/*
+ * Routine to generate a string list from the PSF2 Unicode mapping format.
+ */
+extern char **_bdf_psf_unpack_mapping(bdf_psf_unimap_t *unimap, int *num_seq);
+
+/*
+ * Routine to convert a string list of mappings back to PSF2 format.
+ */
+extern int _bdf_psf_pack_mapping(char **list, int len, int encoding,
+                                 bdf_psf_unimap_t *map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _h_bdfP */
diff --git a/bdfcons.c b/bdfcons.c
new file mode 100644 (file)
index 0000000..0e39ee9
--- /dev/null
+++ b/bdfcons.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "bdfP.h"
+
+#undef MAX
+#undef MIN
+#define MAX(h,i) ((h) > (i) ? (h) : (i))
+#define MIN(l,o) ((l) < (o) ? (l) : (o))
+
+/*
+ * Header for Sun VF fonts.
+ */
+typedef struct {
+    unsigned short mag;
+    unsigned short total_bytes;
+    unsigned short max_width;
+    unsigned short max_height;
+    unsigned short pad;
+} vfhdr_t;
+
+/*
+ * Character metrics data for Sun VF fonts.
+ */
+typedef struct {
+    unsigned short offset;
+    unsigned short bytes;
+    char ascent;
+    char descent;
+    char lbearing;
+    char rbearing;
+    unsigned short dwidth;
+} vfmetrics_t;
+
+/**************************************************************************
+ *
+ * Support functions.
+ *
+ **************************************************************************/
+
+static bdf_font_t *
+_bdf_load_vfont(FILE *in, vfhdr_t *hdr, bdf_callback_t callback, void *data,
+                int *awidth)
+{
+    int first, ismono;
+    int i, pos;
+    bdf_font_t *fp;
+    bdf_glyph_t *gp;
+    bdf_callback_struct_t cb;
+    vfmetrics_t met, metrics[256];
+
+    /*
+     * Convert the header values to little endian if necessary.
+     */
+    if (bdf_little_endian()) {
+        hdr->total_bytes = ((hdr->total_bytes & 0xff) << 8) |
+            ((hdr->total_bytes >> 8) & 0xff);
+        hdr->max_width = ((hdr->max_width & 0xff) << 8) |
+            ((hdr->max_width >> 8) & 0xff);
+        hdr->max_height = ((hdr->max_height & 0xff) << 8) |
+            ((hdr->max_height >> 8) & 0xff);
+    }
+
+    /*
+     * The point size of the font will be the height, the resolution will
+     * default to 72dpi, and the spacing will default to proportional.
+     */
+    fp = bdf_new_font(0, (int) hdr->max_height, 72, 72, BDF_PROPORTIONAL, 1);
+
+    /*
+     * Force the bits per pixel to 1.
+     */
+    fp->bpp = 1;
+
+    /*
+     * Load the glyph metrics and set a marker to the beginning of the glyph
+     * bitmaps.
+     */
+    fread((char *) metrics, sizeof(vfmetrics_t), 256, in);
+    pos = ftell(in);
+
+    *awidth = 0;
+
+    /*
+     * Count the number of glyphs that actually exist and determine the font
+     * bounding box in the process.
+     */
+    (void) memset((char *) &met, 0, sizeof(vfmetrics_t));
+    met.lbearing = 127;
+    fp->glyphs_size = 0;
+    for (first = -1, ismono = 1, i = 0; i < 256; i++) {
+        if (metrics[i].bytes == 0)
+          continue;
+
+        if (first == -1)
+          first = i;
+
+        /*
+         * Start out by assuming the font is monowidth, but if any glyph
+         * encountered has metrics different than the first glyph defined,
+         * change that flag.  If the font is still flagged as monowidth when
+         * this loop is done, then change the font to a monowidth font.
+         */
+        if (i != first && ismono &&
+            (metrics[i].ascent != metrics[first].ascent ||
+             metrics[i].descent != metrics[first].descent ||
+             metrics[i].lbearing != metrics[first].lbearing ||
+             metrics[i].rbearing != metrics[first].rbearing))
+          ismono = 0;
+
+        /*
+         * If this is a little endian machine, convert the 16-bit values from
+         * big endian.
+         */
+        if (bdf_little_endian()) {
+            metrics[i].offset = ((metrics[i].offset & 0xff) << 8) |
+                ((metrics[i].offset >> 8) & 0xff);
+            metrics[i].bytes = ((metrics[i].bytes & 0xff) << 8) |
+                ((metrics[i].bytes >> 8) & 0xff);
+            metrics[i].dwidth = ((metrics[i].dwidth & 0xff) << 8) |
+                ((metrics[i].dwidth >> 8) & 0xff);
+        }
+
+        /*
+         * Update the value used for average width calculation.
+         */
+        *awidth = *awidth + (metrics[i].rbearing - metrics[i].lbearing);
+
+        /*
+         * Increment the count of characters.
+         */
+        fp->glyphs_size++;
+
+        /*
+         * Determine the font bounding box.
+         */
+        met.ascent = MAX(met.ascent, metrics[i].ascent);
+        met.descent = MAX(met.descent, metrics[i].descent);
+        met.lbearing = MIN(met.lbearing, metrics[i].lbearing);
+        met.rbearing = MAX(met.rbearing, metrics[i].rbearing);
+    }
+
+    /*
+     * Adjust the font bounding box accordingly.
+     */
+    fp->bbx.ascent = met.ascent;
+    fp->bbx.descent = met.descent;
+    fp->bbx.width = met.rbearing + met.lbearing;
+    fp->bbx.height = met.ascent + met.descent;
+    fp->bbx.x_offset = met.lbearing;
+    fp->bbx.y_offset = -met.descent;
+
+    /*
+     * If the font is still flagged as a monowidth font, change the font
+     * spacing.  The actual SPACING property will be adjusted once this
+     * routine returns.
+     */
+    if (ismono)
+      fp->spacing = BDF_MONOWIDTH;
+
+    /*
+     * Set up to load the glyphs.
+     */
+    fp->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * fp->glyphs_size);
+    (void) memset((char *) fp->glyphs, 0,
+                  sizeof(bdf_glyph_t) * fp->glyphs_size);
+
+    /*
+     * Set the callback up.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = fp->glyphs_size;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Get the glyphs.
+     */
+    for (i = 0; i < 256; i++) {
+        if (metrics[i].bytes == 0)
+          continue;
+
+        /*
+         * Put the file pointer back at the beginning of the bitmaps.
+         */
+        fseek(in, pos, 0L);
+
+        gp = fp->glyphs + fp->glyphs_used++;
+
+        gp->encoding = i;
+        gp->dwidth = metrics[i].dwidth;
+        gp->swidth = (unsigned short)
+            (((double) gp->dwidth) * 72000.0) /
+            ((double) fp->point_size * fp->resolution_x);
+
+        gp->bbx.ascent = metrics[i].ascent;
+        gp->bbx.descent = metrics[i].descent;
+        gp->bbx.width = metrics[i].rbearing + metrics[i].lbearing;
+        gp->bbx.height = metrics[i].ascent + metrics[i].descent;
+        gp->bbx.x_offset = metrics[i].lbearing;
+        gp->bbx.y_offset = -metrics[i].descent;
+        gp->bytes = metrics[i].bytes;
+        gp->bitmap = (unsigned char *) malloc(gp->bytes);
+
+        fseek(in, (int) metrics[i].offset, 1L);
+        fread((char *) gp->bitmap, gp->bytes, 1, in);
+
+        /*
+         * Call the callback if indicated.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.total = fp->glyphs_size;
+            cb.current = fp->glyphs_used;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Add a message indicating the font was converted.
+     */
+    _bdf_add_comment(fp, "Font converted from VF to BDF.", 30);
+    _bdf_add_acmsg(fp, "Font converted from VF to BDF.", 30);
+
+    /*
+     * Return the font.
+     */
+    return fp;
+}
+
+/*
+ * Load a simple binary font.
+ */
+static bdf_font_t *
+_bdf_load_simple(FILE *in, int height, bdf_callback_t callback, void *data,
+                 int type, int *awidth)
+{
+    int i;
+    unsigned short dwidth, swidth;
+    bdf_font_t *fp;
+    bdf_glyph_t *gp;
+    bdf_callback_struct_t cb;
+
+    /*
+     * The point size of the font will be the height, the resolution will
+     * default to 72dpi, and the spacing will default to character cell.
+     */
+    fp = bdf_new_font(0, (int) height, 72, 72, BDF_CHARCELL, 1);
+
+    /*
+     * Force the bits per pixel to be one.
+     */
+    fp->bpp = 1;
+
+    /*
+     * Make sure the width is always set to 8 no matter what.  This may
+     * change in the future, but not anytime soon.
+     */
+    *awidth = fp->bbx.width = 8;
+
+    /*
+     * Adjust the ascent and descent by hand for the 14pt and 8pt fonts.
+     */
+    if (height != 16) {
+        fp->bbx.ascent++;
+        fp->bbx.descent--;
+    }
+
+    /*
+     * Default the font ascent and descent to that of the bounding box.
+     */
+    fp->font_ascent = fp->bbx.ascent;
+    fp->font_descent = fp->bbx.descent;
+
+    /*
+     * Simple fonts will have at most 256 glyphs.
+     */
+    fp->glyphs_size = 256;
+    fp->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * fp->glyphs_size);
+    (void) memset((char *) fp->glyphs, 0,
+                  sizeof(bdf_glyph_t) * fp->glyphs_size);
+
+    /*
+     * Determine the default scalable and device width for each character.
+     */
+    dwidth = fp->bbx.width;
+    swidth = (unsigned short)
+        (((double) dwidth) * 72000.0) /
+        ((double) fp->point_size * fp->resolution_x);
+
+    /*
+     * Set up to call the callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = fp->glyphs_size;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Now load the glyphs, assigning a default encoding.
+     */
+    for (i = 0, gp = fp->glyphs; i < fp->glyphs_size; i++, gp++) {
+        gp->encoding = i;
+        gp->dwidth = dwidth;
+        gp->swidth = swidth;
+        (void) memcpy((char *) &gp->bbx, (char *) &fp->bbx, sizeof(bdf_bbx_t));
+
+        gp->bytes = height;
+        gp->bitmap = (unsigned char *) malloc(height);
+        fread((char *) gp->bitmap, height, 1, in);
+        fp->glyphs_used++;
+
+        /*
+         * Call the callback if indicated.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.total = fp->glyphs_size;
+            cb.current = fp->glyphs_used;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Add a message indicating the font was converted.
+     */
+    if (type == 1) {
+        _bdf_add_comment(fp, "Font converted from VGA/EGA to BDF.", 35);
+        _bdf_add_acmsg(fp, "Font converted from VGA/EGA to BDF.", 35);
+    } else if (type == 2) {
+        _bdf_add_comment(fp, "Fonts converted from CP to BDF.", 31);
+        _bdf_add_acmsg(fp, "Fonts converted from CP to BDF.", 31);
+    }
+
+    /*
+     * Return the new font.
+     */
+    return fp;
+}
+
+/*
+ * A structure to pass around in update callbacks.
+ */
+typedef struct {
+    unsigned int total;
+    unsigned int curr;
+    unsigned int lcurr;
+    bdf_callback_t cback;
+    void *data;
+} _bdf_update_rec_t;
+
+/*
+ * A routine to report the progress of loading a codepage font over all
+ * three fonts.
+ */
+static void
+_bdf_codepage_progress(bdf_callback_struct_t *cb, void *data)
+{
+    _bdf_update_rec_t *up;
+    bdf_callback_struct_t ncb;
+
+    up = (_bdf_update_rec_t *) data;
+
+    if (up->cback == 0)
+      return;
+
+    if (up->curr != 0 && cb->current == 0) {
+        up->lcurr = 0;
+        return;
+    }
+
+    up->curr += cb->current - up->lcurr;
+    up->lcurr = cb->current;
+
+    ncb.reason = cb->reason;
+    ncb.total = up->total;
+    ncb.current = up->curr;
+
+    if (up->cback != 0)
+      (*up->cback)(&ncb, up->data);
+}
+
+/*
+ * Load a codepage font which actually contains three fonts.  This makes
+ * use of the routine that loads the simple fonts.
+ */
+static int
+_bdf_load_codepage(FILE *in, bdf_callback_t callback, void *data,
+                   bdf_font_t *fonts[3], int awidth[3])
+{
+    _bdf_update_rec_t up;
+
+    /*
+     * Initialize an override callback structure.
+     */
+    up.cback = callback;
+    up.data = data;
+    up.total = 768;
+    up.curr = up.lcurr = 0;
+
+    /*
+     * Load the 16pt font.
+     */
+    if (fseek(in, 40, 0L))
+      return BDF_NOT_CONSOLE_FONT;
+
+    fonts[0] = _bdf_load_simple(in, 16, _bdf_codepage_progress, (void *) &up,
+                                0, &awidth[0]);
+
+    /*
+     * Load the 14pt font.
+     */
+    if (fseek(in, 4142, 0L)) {
+        if (fonts[0] != 0)
+          bdf_free_font(fonts[0]);
+        fonts[0] = 0;
+        return BDF_NOT_CONSOLE_FONT;
+    }
+    fonts[1] = _bdf_load_simple(in, 14, _bdf_codepage_progress, (void *) &up,
+                                0, &awidth[1]);
+
+    /*
+     * Load the 8pt font.
+     */
+    if (fseek(in, 7732, 0L)) {
+        if (fonts[0] != 0)
+          bdf_free_font(fonts[0]);
+        if (fonts[1] != 0)
+          bdf_free_font(fonts[1]);
+        fonts[0] = fonts[1] = 0;
+        return BDF_NOT_CONSOLE_FONT;
+    }
+    fonts[2] = _bdf_load_simple(in, 8, _bdf_codepage_progress, (void *) &up,
+                                2, &awidth[2]);
+
+    /*
+     * All the fonts loaded OK.
+     */
+    return BDF_OK;
+}
+
+/**************************************************************************
+ *
+ * API.
+ *
+ **************************************************************************/
+
+static unsigned char vfmagic[] = {0x01, 0x1e};
+
+int
+bdf_load_console_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback,
+                      void *data, bdf_font_t *fonts[3], int *nfonts)
+{
+    unsigned char hdr[4];
+    int res, awidth[3];
+    double dp, dr;
+    bdf_property_t prop;
+    vfhdr_t vhdr;
+    struct stat st;
+
+    (void) fstat(fileno(in), &st);
+
+    *nfonts = 1;
+    awidth[0] = awidth[1] = awidth[2] = 0;
+    (void) memset((char *) fonts, 0, sizeof(bdf_font_t *) * 3);
+
+    fread((char *) hdr, sizeof(unsigned char), 4, in);
+
+    if (memcmp((char *) hdr, _bdf_psfcombined, 4) == 0)
+      return BDF_PSF_UNSUPPORTED;
+
+    if (memcmp((char *) hdr, (char *) _bdf_psf1magic, 2) == 0 ||
+        memcmp((char *) hdr, (char *) _bdf_psf2magic, 4) == 0)
+      /*
+       * Have a PSF font that may contain a mapping table.
+       */
+      fonts[0] = bdf_load_psf(in, hdr, opts, callback, data, awidth);
+    else {
+        /*
+         * Reset to the beginning of the file.
+         */
+        fseek(in, 0, 0L);
+        if (memcmp((char *) hdr, (char *) vfmagic, 2) == 0) {
+            /*
+             * Have a Sun vfont.  Need to reload the header.
+             */
+            (void) fread((char *) &vhdr, sizeof(vfhdr_t), 1, in);
+            fonts[0] = _bdf_load_vfont(in, &vhdr, callback, data, awidth);
+        } else if (st.st_size == 9780) {
+            /*
+             * Have a CP font with three sizes.  Create all three fonts and
+             * return them.
+             */
+            *nfonts = 3;
+            if ((res = _bdf_load_codepage(in, callback, data, fonts, awidth)))
+              return res;
+        } else {
+            /*
+             * Have a plain font with 256 characters.  If the file size is not
+             * evenly divisible by 256, then the file is probably corrupt or
+             * is not a font.
+             */
+            if (st.st_size & 0xff)
+              return BDF_NOT_CONSOLE_FONT;
+
+            fonts[0] = _bdf_load_simple(in, st.st_size >> 8, callback, data,
+                                        1, awidth);
+        }
+    }
+
+    /*
+     * Add all the default properties.
+     */
+    for (res = 0; res < *nfonts; res++) {
+        prop.name = "POINT_SIZE";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = fonts[res]->point_size * 10;
+        bdf_add_font_property(fonts[res], &prop);
+
+        dr = (double) fonts[res]->resolution_y;
+        dp = (double) (fonts[res]->point_size * 10);
+        prop.name = "PIXEL_SIZE";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = (int) (((dp * dr) / 722.7) + 0.5);
+        bdf_add_font_property(fonts[res], &prop);
+
+        prop.name = "RESOLUTION_X";
+        prop.format = BDF_CARDINAL;
+        prop.value.card32 = (unsigned int) fonts[res]->resolution_x;
+        bdf_add_font_property(fonts[res], &prop);
+
+        prop.name = "RESOLUTION_Y";
+        prop.format = BDF_CARDINAL;
+        prop.value.card32 = (unsigned int) fonts[res]->resolution_y;
+        bdf_add_font_property(fonts[res], &prop);
+
+        prop.name = "FONT_ASCENT";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = (int) fonts[res]->bbx.ascent;
+        bdf_add_font_property(fonts[res], &prop);
+
+        prop.name = "FONT_DESCENT";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = (int) fonts[res]->bbx.descent;
+        bdf_add_font_property(fonts[res], &prop);
+
+        prop.name = "AVERAGE_WIDTH";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = (awidth[res] / fonts[res]->glyphs_used) * 10;
+        bdf_add_font_property(fonts[res], &prop);
+
+        prop.name = "SPACING";
+        prop.format = BDF_ATOM;
+        prop.value.atom = "P";
+        switch (fonts[res]->spacing) {
+          case BDF_PROPORTIONAL: prop.value.atom = "P"; break;
+          case BDF_MONOWIDTH: prop.value.atom = "M"; break;
+          case BDF_CHARCELL: prop.value.atom = "C"; break;
+        }
+        bdf_add_font_property(fonts[res], &prop);
+    }
+
+    return BDF_OK;
+}
diff --git a/bdffnt.c b/bdffnt.c
new file mode 100644 (file)
index 0000000..cfcab16
--- /dev/null
+++ b/bdffnt.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bdfP.h"
+
+/**************************************************************************
+ *
+ * Executable header and font structures.
+ *
+ **************************************************************************/
+
+typedef struct {
+    unsigned short id;
+    unsigned short count;
+    unsigned int reshandler;
+} res_typeinfo_t;
+
+typedef struct {
+    unsigned short offset;
+    unsigned short length;
+    unsigned short flags;
+    unsigned short id;
+    unsigned short handle;
+    unsigned short usage;
+} res_nameinfo_t;
+
+typedef struct {
+    unsigned short e_magic;
+    unsigned short e_cblp;
+    unsigned short e_cp;
+    unsigned short e_crlc;
+    unsigned short e_cparhdr;
+    unsigned short e_minalloc;
+    unsigned short e_maxalloc;
+    unsigned short e_ss;
+    unsigned short e_sp;
+    unsigned short e_csum;
+    unsigned short e_ip;
+    unsigned short e_cs;
+    unsigned short e_lfarlc;
+    unsigned short e_ovno;
+    unsigned short e_res[4];
+    unsigned short e_oemid;
+    unsigned short e_oeminfo;
+    unsigned short e_res2[10];
+    unsigned short e_lfanew;
+} dos_exe_t;
+
+typedef struct {
+    unsigned short ne_magic;
+    unsigned char  linker_version;
+    unsigned char  linker_revision;
+    unsigned short entry_tab_offset;
+    unsigned short entry_tab_length;
+    unsigned int  reserved1;
+    unsigned short format_flags;
+    unsigned short auto_data_seg;
+    unsigned short local_heap_length;
+    unsigned short stack_length;
+    unsigned short ip;
+    unsigned short cs;
+    unsigned short sp;
+    unsigned short ss;
+    unsigned short n_segment_tab;
+    unsigned short n_mod_ref_tab;
+    unsigned short nrname_tab_length;
+    unsigned short segment_tab_offset;
+    unsigned short resource_tab_offset;
+    unsigned short rname_tab_offset;
+    unsigned short moduleref_tab_offset;
+    unsigned short iname_tab_offset;
+    unsigned int  nrname_tab_offset;
+    unsigned short n_mov_entry_points;
+    unsigned short align_shift_count;
+    unsigned short n_resource_seg;
+    unsigned char  operating_system;
+    unsigned char  additional_flags;
+    unsigned short fastload_offset;
+    unsigned short fastload_length;
+    unsigned short reserved2;
+    unsigned short expect_version;
+} win_exe_t;
+
+typedef struct {
+    unsigned short dfVersion;
+    unsigned int  dfSize;
+    unsigned char  dfCopyright[60];
+    unsigned short dfType;
+    unsigned short dfPoints;
+    unsigned short dfVertRes;
+    unsigned short dfHorizRes;
+    unsigned short dfAscent;
+    unsigned short dfInternalLeading;
+    unsigned short dfExternalLeading;
+    unsigned char  dfItalic;
+    unsigned char  dfUnderline;
+    unsigned char  dfStrikeOut;
+    unsigned short dfWeight;
+    unsigned char  dfCharSet;
+    unsigned short dfPixWidth;
+    unsigned short dfPixHeight;
+    unsigned char  dfPitchAndFamily;
+    unsigned short dfAvgWidth;
+    unsigned short dfMaxWidth;
+    unsigned char  dfFirstChar;
+    unsigned char  dfLastChar;
+    unsigned char  dfDefaultChar;
+    unsigned char  dfBreakChar;
+    unsigned short dfWidthBytes;
+    unsigned int  dfDevice;
+    unsigned int  dfFace;
+    unsigned int  dfBitsPointer;
+    unsigned int  dfBitsOffset;
+    unsigned char  dfReserved;
+    unsigned int  dfFlags;
+    unsigned short dfAspace;
+    unsigned short dfBspace;
+    unsigned short dfCspace;
+    unsigned short dfColorPointer;
+    unsigned char  dfReserved1[4];
+#if 0
+    unsigned int  dfColorPointer;
+    unsigned char  dfReserved1[16];
+#endif
+} fntinfo_t;
+
+/*
+ * A structure used to load the font info data before transfering to the
+ * real font structure.
+ */
+typedef struct {
+    unsigned char dfVersion[2];
+    unsigned char dfSize[4];
+    unsigned char dfCopyright[60];
+    unsigned char dfType[2];
+    unsigned char dfPoints[2];
+    unsigned char dfVertRes[2];
+    unsigned char dfHorizRes[2];
+    unsigned char dfAscent[2];
+    unsigned char dfInternalLeading[2];
+    unsigned char dfExternalLeading[2];
+    unsigned char dfItalic[1];
+    unsigned char dfUnderline[1];
+    unsigned char dfStrikeOut[1];
+    unsigned char dfWeight[2];
+    unsigned char dfCharSet[1];
+    unsigned char dfPixWidth[2];
+    unsigned char dfPixHeight[2];
+    unsigned char dfPitchAndFamily[1];
+    unsigned char dfAvgWidth[2];
+    unsigned char dfMaxWidth[2];
+    unsigned char dfFirstChar[1];
+    unsigned char dfLastChar[1];
+    unsigned char dfDefaultChar[1];
+    unsigned char dfBreakChar[1];
+    unsigned char dfWidthBytes[2];
+    unsigned char dfDevice[4];
+    unsigned char dfFace[4];
+    unsigned char dfBitsPointer[4];
+    unsigned char dfBitsOffset[4];
+    unsigned char dfReserved[1];
+    unsigned char dfFlags[4];
+    unsigned char dfAspace[2];
+    unsigned char dfBspace[2];
+    unsigned char dfCspace[2];
+#if 0
+    unsigned char dfColorPointer[4];
+    unsigned char dfReserved1[16];
+#endif
+    unsigned char dfColorPointer[2];
+    unsigned char dfReserved1[4];
+} fishadow_t;
+
+typedef struct {
+    unsigned int width;
+    unsigned int offset;
+} chrinfo_t;
+
+/*
+ * Structure used for opening FON/FNT fonts.  Tracks the list of offsets to
+ * the font or fonts in the file.
+ */
+typedef struct _bdffnt_font_t {
+    FILE *in;
+    unsigned int *fonts;
+    unsigned int allocated;
+    unsigned int nfonts;
+    unsigned int first;
+
+    chrinfo_t *cinfo;
+    unsigned int cinfo_used;
+    unsigned int cinfo_size;
+
+    fntinfo_t info;
+} _bdffnt_font_t;
+
+/**************************************************************************
+ *
+ * Local macros and variables.
+ *
+ **************************************************************************/
+
+/*
+ * Executable signatures.
+ */
+#define DOS_SIG 0x5a4d
+#define WIN_SIG 0x454e
+
+/*
+ * Weight values.
+ */
+#define BDFFNT_WEIGHT_DONTCARE   0
+#define BDFFNT_WEIGHT_THIN       100
+#define BDFFNT_WEIGHT_EXTRALIGHT 200
+#define BDFFNT_WEIGHT_ULTRALIGHT 200
+#define BDFFNT_WEIGHT_LIGHT      300
+#define BDFFNT_WEIGHT_NORMAL     400
+#define BDFFNT_WEIGHT_REGULAR    400
+#define BDFFNT_WEIGHT_MEDIUM     500
+#define BDFFNT_WEIGHT_SEMIBOLD   600
+#define BDFFNT_WEIGHT_DEMIBOLD   600
+#define BDFFNT_WEIGHT_BOLD       700
+#define BDFFNT_WEIGHT_EXTRABOLD  800
+#define BDFFNT_WEIGHT_ULTRABOLD  800
+#define BDFFNT_WEIGHT_HEAVY      900
+#define BDFFNT_WEIGHT_BLACK      900
+
+/*
+ * Local structures to hold header info.
+ */
+static dos_exe_t dos;
+static win_exe_t win;
+
+/**************************************************************************
+ *
+ * Support functions.
+ *
+ **************************************************************************/
+
+static void
+_bdffnt_endian_shorts(unsigned short *sp, unsigned int n)
+{
+    for (; n > 0; n--, sp++)
+      *sp = ((*sp >> 8) & 0xff) |
+          (((*sp & 0xff) << 8) & 0xff00);
+}
+
+static void
+_bdffnt_endian_ints(unsigned int *lp, unsigned int n)
+{
+    for (; n > 0; n--, lp++)
+      *lp = (((*lp & 0xff) << 24) & 0xff000000) |
+          (((*lp >> 8) << 16) & 0xff0000) |
+          (((*lp >> 16) << 8) & 0xff00) |
+          ((*lp >> 24) & 0xff);
+}
+
+static unsigned short
+_bdffnt_get_short(unsigned char *field)
+{
+    int a = 0, b = 1;
+
+    return (field[a] & 0xff) | ((field[b] & 0xff) << 8);
+}
+
+static unsigned int
+_bdffnt_get_int(unsigned char *field)
+{
+    int a = 0, b = 1, c = 2, d = 3;
+
+    return (field[a] & 0xff) | ((field[b] & 0xff) << 8) |
+        ((field[c] & 0xff) << 16) | ((field[d] & 0xff) << 24);
+}
+
+/*
+ * This routine is called when the font header needs some fields adjusted for
+ * the endianess of the machine.
+ */
+static void
+_bdffnt_transfer_fntinfo(fntinfo_t *fi, fishadow_t *fis)
+{
+    fi->dfVersion = _bdffnt_get_short(fis->dfVersion);
+    (void) memcpy(fi->dfCopyright, fis->dfCopyright, 60);
+    fi->dfSize = _bdffnt_get_int(fis->dfSize);
+    fi->dfType = _bdffnt_get_short(fis->dfType);
+    fi->dfPoints = _bdffnt_get_short(fis->dfPoints);
+    fi->dfVertRes = _bdffnt_get_short(fis->dfVertRes);
+    fi->dfHorizRes = _bdffnt_get_short(fis->dfHorizRes);
+    fi->dfAscent = _bdffnt_get_short(fis->dfAscent);
+    fi->dfInternalLeading = _bdffnt_get_short(fis->dfInternalLeading);
+    fi->dfExternalLeading = _bdffnt_get_short(fis->dfExternalLeading);
+    fi->dfItalic = fis->dfItalic[0];
+    fi->dfUnderline = fis->dfUnderline[0];
+    fi->dfStrikeOut = fis->dfStrikeOut[0];
+    fi->dfWeight = _bdffnt_get_short(fis->dfWeight);
+    fi->dfCharSet = fis->dfCharSet[0];
+    fi->dfPixWidth = _bdffnt_get_short(fis->dfPixWidth);
+    fi->dfPixHeight = _bdffnt_get_short(fis->dfPixHeight);
+    fi->dfPitchAndFamily = fis->dfPitchAndFamily[0];
+    fi->dfAvgWidth = _bdffnt_get_short(fis->dfAvgWidth);
+    fi->dfMaxWidth = _bdffnt_get_short(fis->dfMaxWidth);
+    fi->dfFirstChar = fis->dfFirstChar[0];
+    fi->dfLastChar = fis->dfLastChar[0];
+    fi->dfDefaultChar = fis->dfDefaultChar[0];
+    fi->dfBreakChar = fis->dfBreakChar[0];
+    fi->dfWidthBytes = _bdffnt_get_short(fis->dfWidthBytes);
+    fi->dfDevice = _bdffnt_get_int(fis->dfDevice);
+    fi->dfFace = _bdffnt_get_int(fis->dfFace);
+    fi->dfBitsPointer = _bdffnt_get_int(fis->dfBitsPointer);
+    fi->dfBitsOffset = _bdffnt_get_int(fis->dfBitsOffset);
+    fi->dfReserved = fis->dfReserved[0];
+    fi->dfFlags = _bdffnt_get_int(fis->dfFlags);
+    fi->dfAspace = _bdffnt_get_short(fis->dfAspace);
+    fi->dfBspace = _bdffnt_get_short(fis->dfBspace);
+    fi->dfCspace = _bdffnt_get_short(fis->dfCspace);
+#if 0
+    fi->dfColorPointer = _bdffnt_get_int(fis->dfColorPointer);
+    (void) memcpy(fi->dfReserved1, fis->dfReserved1, 16);
+#endif
+    fi->dfColorPointer = _bdffnt_get_short(fis->dfColorPointer);
+    (void) memcpy(fi->dfReserved1, fis->dfReserved1, 4);
+}
+
+static char *
+_bdffnt_weight_name(unsigned short weight, int *len)
+{
+    char *name;
+
+    if (weight == 0) {
+        name = "Medium";
+        *len = 6;
+    } else if (weight <= BDFFNT_WEIGHT_THIN) {
+        name = "Thin";
+        *len = 4;
+    } else if (weight <= BDFFNT_WEIGHT_ULTRALIGHT) {
+        name = "UltraLight";
+        *len = 10;
+    } else if (weight <= BDFFNT_WEIGHT_LIGHT) {
+        name = "Light";
+        *len = 5;
+    } else if (weight <= BDFFNT_WEIGHT_MEDIUM) {
+        name = "Medium";
+        *len = 6;
+    } else if (weight <= BDFFNT_WEIGHT_DEMIBOLD) {
+        name = "DemiBold";
+        *len = 8;
+    } else if (weight <= BDFFNT_WEIGHT_BOLD) {
+        name = "Bold";
+        *len = 4;
+    } else if (weight <= BDFFNT_WEIGHT_ULTRABOLD) {
+        name = "UltraBold";
+        *len = 9;
+    } else {
+        name = "Black";
+        *len = 5;
+    }
+    return name;
+}
+
+static char *
+_bdffnt_cset_name(int cset, int *enc)
+{
+    *enc = 0;
+    switch (cset) {
+      case 0: *enc = 1; return "ISO8859";
+      case 1: return "WinDefault";
+      case 2: return "Symbol";
+      case 128: return "JISX0208.1983";
+      case 129: return "MSHangul";
+      case 134: return "GB2312.1980";
+      case 136: return "Big5";
+      case 161: *enc = 1; return "CP1253";
+      case 162: *enc = 1; return "CP1254";
+      case 177: *enc = 1; return "CP1255";
+      case 178: *enc = 1; return "CP1256";
+      case 186: *enc = 1; return "CP1257";
+      case 204: *enc = 1; return "CP1251";
+      case 238: *enc = 1; return "CP1250";
+      case 255: return "OEM";
+    }
+    return "Unknown";
+}
+
+/**************************************************************************
+ *
+ * API.
+ *
+ **************************************************************************/
+
+int
+bdffnt_open_font(char *path, bdffnt_font_t *fnt)
+{
+    unsigned short sshift, version;
+    int i;
+    unsigned int off;
+    FILE *in;
+    _bdffnt_font_t *f;
+    res_typeinfo_t rtype;
+    res_nameinfo_t ninfo;
+
+    if (path == 0 || *path == 0 || fnt == 0)
+      return 0;
+
+    if ((in = fopen(path, "r")) == 0)
+      return -1;
+
+    *fnt = 0;
+    f = (_bdffnt_font_t *) malloc(sizeof(_bdffnt_font_t));
+    (void) memset((char *) f, 0, sizeof(_bdffnt_font_t));
+
+    f->in = in;
+
+    if (fread((char *) &dos, 1, sizeof(dos_exe_t), in) != sizeof(dos_exe_t)) {
+        fclose(in);
+        free((char *) f);
+        return -1;
+    }
+
+    /*
+     * Endian everything if on a big-endian machine.
+     */
+    if (!bdf_little_endian())
+      _bdffnt_endian_shorts((unsigned short *) &dos,
+                            sizeof(dos_exe_t) / sizeof(unsigned short));
+
+    /*
+     * Check for exe signatures.
+     */
+    if (dos.e_magic == DOS_SIG) {
+        fseek(in, dos.e_lfanew, 0L);
+        if (fread((char *) &win, 1, sizeof(win_exe_t), in) !=
+            sizeof(win_exe_t)) {
+            fclose(in);
+            free((char *) f);
+            return -1;
+        }
+
+        /*
+         * Only endian the fields used.
+         */
+        if (!bdf_little_endian()) {
+            _bdffnt_endian_shorts(&win.ne_magic, 1);
+            _bdffnt_endian_shorts(&win.resource_tab_offset, 1);
+            _bdffnt_endian_shorts(&win.rname_tab_offset, 1);
+        }
+
+        /*
+         * This means the file is either NT 32-bit or something else.
+         */
+        if (win.ne_magic != WIN_SIG) {
+            fclose(in);
+            free((char *) f);
+            return -1;
+        }
+
+        /*
+         * Seek to the beginning of the resources.
+         */
+        off = dos.e_lfanew + win.resource_tab_offset;
+        fseek(in, off, 0L);
+        fread((char *) &sshift, 1, sizeof(unsigned short), in);
+        if (!bdf_little_endian())
+          _bdffnt_endian_shorts(&sshift, 1);
+
+        /*
+         * Search the resources for all the font resources.
+         */
+        if (fread((char *) &rtype, 1, sizeof(res_typeinfo_t), in) !=
+            sizeof(res_typeinfo_t)) {
+            fclose(in);
+            free((char *) f);
+            return -1;
+        }
+        while (rtype.id != 0) {
+            /*
+             * Change the endian order of the first two fields if necessary.
+             */
+            if (!bdf_little_endian())
+              _bdffnt_endian_shorts((unsigned short *) &rtype, 2);
+
+            if (rtype.id == 0x8008)
+              break;
+
+            /*
+             * Seek to the next resource entry and read it.
+             */
+            off = rtype.count * sizeof(res_nameinfo_t);
+            fseek(in, off, 1L);
+
+            if (fread((char *) &rtype, 1, sizeof(res_typeinfo_t), in) !=
+                sizeof(res_typeinfo_t)) {
+                fclose(in);
+                free((char *) f);
+                return -1;
+            }
+        }
+        if (rtype.id == 0x8008) {
+            /*
+             * Found a font resource, cycle through the entries.
+             */
+            for (i = 0; i < rtype.count; i++) {
+                if (fread((char *) &ninfo, 1, sizeof(res_nameinfo_t), in) !=
+                    sizeof(res_nameinfo_t)) {
+                    fclose(in);
+                    if (f->allocated > 0)
+                      free((char *) f->fonts);
+                    free((char *) f);
+                    return -1;
+                }
+
+                if (!bdf_little_endian())
+                  _bdffnt_endian_shorts((unsigned short *) &ninfo,
+                                        sizeof(res_nameinfo_t) >> 1);
+
+                /*
+                 * Check to make sure that the indicated offset is really a
+                 * valid font.
+                 */
+                off = ftell(in);
+                fseek(in, (ninfo.offset << sshift), 0L);
+                fread((char *) &version, sizeof(unsigned short), 1, in);
+                fseek(in, off, 0L);
+                if (!bdf_little_endian())
+                  _bdffnt_endian_shorts(&version, 1);
+                if (version != 0x200 && version != 0x300)
+                  continue;
+
+                if (f->nfonts == 0)
+                  f->first = ninfo.offset << sshift;
+                else {
+                    if (f->nfonts >= f->allocated) {
+                        if (f->allocated == 0)
+                          f->fonts = (unsigned int *)
+                              malloc(sizeof(unsigned int) << 3);
+                        else
+                          f->fonts = (unsigned int *)
+                              realloc((char *) f->fonts,
+                                      sizeof(unsigned int) *
+                                      (f->allocated + 8));
+                        f->allocated += 8;
+                    }
+                    f->fonts[0] = f->first;
+                    f->fonts[f->nfonts] = ninfo.offset << sshift;
+                }
+                f->nfonts++;
+            }
+        }
+    } else if (dos.e_magic == 0x200 || dos.e_magic == 0x300) {
+        /*
+         * Probably have a .FNT file.
+         */
+        f->first = ftell(in);
+        f->nfonts = 1;
+    } else
+      return -1;
+
+    if (f->nfonts == 0) {
+        /*
+         * If no fonts were loaded, free everything up.
+         */
+        free((char *) f);
+        return -1;
+    }
+    *fnt = f;
+    return 1;
+}
+
+void
+bdffnt_close_font(bdffnt_font_t font)
+{
+    if (font == 0)
+      return;
+
+    fclose(font->in);
+    if (font->cinfo_size > 0)
+      free((char *) font->cinfo);
+    if (font->allocated > 0)
+      free((char *) font->fonts);
+    free((char *) font);
+}
+
+int
+bdffnt_font_count(bdffnt_font_t font)
+{
+    return (font != 0) ? font->nfonts : 0;
+}
+
+int
+bdffnt_get_copyright(bdffnt_font_t font, unsigned int fontID,
+                     unsigned char *string)
+{
+    int off;
+    unsigned char *sp;
+    fishadow_t fi;
+
+    off = (font->nfonts == 1) ? font->first : font->fonts[fontID];
+    fseek(font->in, off, 0L);
+
+    if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) !=
+        sizeof(fishadow_t))
+      return -1;
+
+    for (sp = fi.dfCopyright; (*string = *sp); sp++, string++) ;
+    return sp - fi.dfCopyright;
+}
+
+int
+bdffnt_get_facename(bdffnt_font_t font, unsigned int fontID, int for_xlfd,
+                    unsigned char *string)
+{
+    int wlen, c;
+    int off;
+    unsigned char *sp, *wname;
+    fishadow_t fi;
+
+    if (font == 0 || fontID >= font->nfonts || string == 0)
+      return 0;
+
+    off = (font->nfonts == 1) ? font->first : font->fonts[fontID];
+    fseek(font->in, off, 0L);
+
+    if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) !=
+        sizeof(fishadow_t))
+      return -1;
+
+    _bdffnt_transfer_fntinfo(&font->info, &fi);
+
+    /*
+     * Seek to the location of the typeface name.
+     */
+    off = off + font->info.dfFace;
+    fseek(font->in, off, 0L);
+
+    /*
+     * Copy the typeface name into the parameter.
+     *
+     * stops when: -  == 0  -> end of string
+     *             -  <  0  -> end of file
+     */
+    sp = string;
+    while ((c = getc(font->in)) > 0) {
+        *sp = c;
+        if (for_xlfd && *sp == '-')
+          *sp = ' ';
+        sp++;
+    }
+    *sp = 0;
+
+    /*
+     * If the typeface name is not for an XLFD name, then append the style,
+     * weight and point size so the names will be informative.
+     */
+    if (!for_xlfd) {
+        *sp++ = ' ';
+        if (font->info.dfItalic & 1) {
+            (void) strcpy((char *) sp, "Italic ");
+            sp += 7;
+        }
+        wname = (unsigned char *) _bdffnt_weight_name(font->info.dfWeight,
+                                                      &wlen);
+        (void) strcpy((char *) sp, (char *) wname);
+        sp += wlen;
+        *sp++ = ' ';
+        sprintf((char *) sp, "%hdpt", font->info.dfPoints);
+        sp += strlen((char *) sp);
+    }
+
+    return sp - string;
+}
+
+int
+bdffnt_char_count(bdffnt_font_t font, unsigned int fontID)
+{
+    int off;
+    fishadow_t fi;
+
+    if (font == 0 || fontID >= font->nfonts)
+      return 0;
+
+    off = (font->nfonts == 1) ? font->first : font->fonts[fontID];
+    fseek(font->in, off, 0L);
+
+    if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) !=
+        sizeof(fishadow_t))
+      return -1;
+
+    _bdffnt_transfer_fntinfo(&font->info, &fi);
+
+    return (font->info.dfLastChar - font->info.dfFirstChar) + 1;
+}
+
+int
+bdffnt_font_pointsize(bdffnt_font_t font, unsigned int fontID)
+{
+    int off;
+    fishadow_t fi;
+
+    if (font == 0 || fontID >= font->nfonts)
+      return 0;
+
+    off = (font->nfonts == 1) ? font->first : font->fonts[fontID];
+    fseek(font->in, off, 0L);
+
+    if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) !=
+        sizeof(fishadow_t))
+      return -1;
+
+    _bdffnt_transfer_fntinfo(&font->info, &fi);
+
+    return font->info.dfPoints;
+}
+
+int
+bdffnt_load_font(bdffnt_font_t font, unsigned int fontID,
+                 bdf_callback_t callback, void *data, bdf_font_t **out)
+{
+    int x, y, i, nchars;
+    unsigned short tmp, bpr;
+    int off;
+    double swscale;
+    chrinfo_t *cp;
+    bdf_font_t *f;
+    bdf_glyph_t *gp;
+    char name[256];
+    bdf_property_t prop;
+    bdf_callback_struct_t cb;
+    fishadow_t fi;
+
+    if (font == 0 || fontID >= font->nfonts || out == 0)
+      return 0;
+
+    off = (font->nfonts == 1) ? font->first : font->fonts[fontID];
+    fseek(font->in, off, 0L);
+
+    if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) !=
+        sizeof(fishadow_t))
+      return -1;
+
+    _bdffnt_transfer_fntinfo(&font->info, &fi);
+
+    /*
+     * This cheap hack needed to get to the character info because the FNT
+     * docs don't mention that for Win 2.0 fonts, the header was a different
+     * size.  This may be the case for a version 3.* as well, but I have no
+     * version 3.* fonts to test with.
+     */
+    fseek(font->in, off + 118, 0L);
+
+    /*
+     * Determine how many character info records there are and make sure
+     * enough space is allocated in the font structure.
+     */
+    nchars = (font->info.dfLastChar - font->info.dfFirstChar) + 1;
+
+    if (font->cinfo_size < nchars) {
+        if (font->cinfo_size == 0)
+          font->cinfo = (chrinfo_t *) malloc(sizeof(chrinfo_t) * nchars);
+        else
+          font->cinfo = (chrinfo_t *) realloc((char *) font->cinfo,
+                                              sizeof(chrinfo_t) * nchars);
+        font->cinfo_size = nchars;
+    }
+    cp = font->cinfo;
+    for (i = 0, font->cinfo_used = 0; i < nchars; i++, cp++) {
+        fread((char *) &tmp, sizeof(unsigned short), 1, font->in);
+        if (!bdf_little_endian())
+          _bdffnt_endian_shorts(&tmp, 1);
+        cp->width = tmp;
+        if (font->info.dfVersion == 0x300) {
+            fread((char *) &cp->offset, sizeof(unsigned int), 1, font->in);
+            if (!bdf_little_endian())
+              _bdffnt_endian_ints(&cp->offset, 1);
+        } else {
+            fread((char *) &tmp, sizeof(unsigned short), 1, font->in);
+            if (!bdf_little_endian())
+              _bdffnt_endian_shorts(&tmp, 1);
+            cp->offset = tmp;
+        }
+    }
+
+    /*
+     * Create the font.
+     */
+    f = (bdf_font_t *) malloc(sizeof(bdf_font_t));
+    (void) memset((char *) f, 0, sizeof(bdf_font_t));
+
+    /*
+     * Set some defaults.
+     */
+    f->bpp = 1;
+    f->default_glyph = font->info.dfDefaultChar + font->info.dfFirstChar;
+    f->spacing = (font->info.dfFlags & 1) ? BDF_CHARCELL : BDF_PROPORTIONAL;
+    f->glyphs_size = nchars;
+    f->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * nchars);
+    (void) memset((char *) f->glyphs, 0, sizeof(bdf_glyph_t) * nchars);
+    f->point_size = font->info.dfPoints;
+    f->resolution_x = font->info.dfHorizRes;
+    f->resolution_y = font->info.dfVertRes;
+    f->font_ascent = font->info.dfAscent;
+    f->font_descent = f->font_ascent - font->info.dfPixHeight;
+
+    /*
+     * Set the font bounding box.
+     */
+    f->bbx.width = font->info.dfMaxWidth;
+    f->bbx.height = font->info.dfPixHeight;
+    f->bbx.ascent = font->info.dfAscent;
+    f->bbx.descent = f->bbx.height - f->bbx.ascent;
+    f->bbx.y_offset = -f->bbx.descent;
+    f->bbx.x_offset = 0;
+
+    if (f->spacing == BDF_CHARCELL)
+      f->monowidth = f->bbx.width;
+
+    /*
+     * Determine the SWIDTH scale factor.
+     */
+    swscale = ((double) f->resolution_y) * ((double) f->point_size);
+
+    /*
+     * Call the initial callback if one was provided.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = nchars;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Start collecting glyphs.
+     */
+    for (i = 0, cp = font->cinfo, gp = f->glyphs; i < nchars;
+         i++, cp++, gp++) {
+        /*
+         * Set the glyph encoding.
+         */
+        gp->encoding = font->info.dfFirstChar + i;
+
+        /*
+         * Set the glyph bounding box.
+         */
+        gp->bbx.width = gp->dwidth = cp->width;
+        gp->bbx.height = font->info.dfPixHeight;
+        gp->bbx.ascent = font->info.dfAscent;
+        gp->bbx.descent = gp->bbx.height - gp->bbx.ascent;
+        gp->bbx.y_offset = -gp->bbx.descent;
+        gp->bbx.x_offset = 0;
+        gp->swidth = (unsigned short)
+            (((double) gp->dwidth) * 72000.0) / swscale;
+
+        /*
+         * Allocate the glyph bitmap.
+         */
+        bpr = (cp->width + 7) >> 3;
+        gp->bytes = bpr * font->info.dfPixHeight;
+        gp->bitmap = (unsigned char *) malloc(gp->bytes);
+
+        /*
+         * Seek to the bitmap and read the bytes.
+         */
+        fseek(font->in, off + cp->offset, 0L);
+        if (bpr == 1)
+          fread((char *) gp->bitmap, gp->bytes, 1, font->in);
+        else {
+            /*
+             * Typical MS wierdness.  This awkward section is just to get the
+             * bytes in the right place.
+             */
+            for (x = 0; x < bpr; x++) {
+                for (y = 0; y < gp->bbx.height; y++)
+                  gp->bitmap[(y * bpr) + x] = getc(font->in);
+            }
+        }
+
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.current = i;
+            cb.total = nchars;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Call the callback one more time to make sure the client knows the
+     * load is done.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOADING;
+        cb.current = nchars;
+        cb.total = nchars;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Set the number of glyphs used.
+     */
+    f->glyphs_used = gp - f->glyphs;
+
+    /*
+     * Add all the properties.
+     */
+    prop.name = "FOUNDRY";
+    prop.format = BDF_ATOM;
+    prop.value.atom = "Windows";
+    bdf_add_font_property(f, &prop);
+
+    i = bdffnt_get_facename(font, fontID, 1, (unsigned char *) name);
+    prop.name = "FAMILY_NAME";
+    prop.format = BDF_ATOM;
+    prop.value.atom = name;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "WEIGHT_NAME";
+    prop.format = BDF_ATOM;
+    prop.value.atom = _bdffnt_weight_name(font->info.dfWeight, &i);
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "SLANT";
+    prop.format = BDF_ATOM;
+    if (font->info.dfItalic & 1)
+      prop.value.atom = "I";
+    else
+      prop.value.atom = "R";
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "SETWIDTH_NAME";
+    prop.format = BDF_ATOM;
+    prop.value.atom = "Normal";
+    bdf_add_font_property(f, &prop);
+
+    if (font->info.dfPitchAndFamily & 0xf0) {
+        prop.name = "ADDSTYLE_NAME";
+        prop.format = BDF_ATOM;
+        switch (font->info.dfPitchAndFamily & 0xf0) {
+          case 0x20: prop.value.atom = "Swiss"; break;
+          case 0x30: prop.value.atom = "Modern"; break;
+          case 0x40: prop.value.atom = "Script"; break;
+          case 0x50: prop.value.atom = "Decorative"; break;
+          default: prop.value.atom = 0;
+        }
+        if (prop.value.atom != 0)
+          bdf_add_font_property(f, &prop);
+    }
+
+    prop.name = "PIXEL_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int)
+        ((((double) (f->point_size * 10) *
+           (double) f->resolution_y) / 722.7) + 0.5);
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "POINT_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->point_size * 10;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_X";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) f->resolution_x;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_Y";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) f->resolution_y;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->font_ascent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->font_descent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "AVERAGE_WIDTH";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = font->info.dfAvgWidth * 10;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "SPACING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = "P";
+    switch (f->spacing) {
+      case BDF_PROPORTIONAL: prop.value.atom = "P"; break;
+      case BDF_MONOWIDTH: prop.value.atom = "M"; break;
+      case BDF_CHARCELL: prop.value.atom = "C"; break;
+    }
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "CHARSET_REGISTRY";
+    prop.format = BDF_ATOM;
+    prop.value.atom = _bdffnt_cset_name(font->info.dfCharSet, &i);
+    bdf_add_font_property(f, &prop);
+
+    sprintf(name, "%d", i);
+    prop.name = "CHARSET_ENCODING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = name;
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Generate the XLFD name.
+     */
+    f->name = bdf_make_xlfd_name(f, 0, 0);
+
+    /*
+     * Add messages indicating the font was converted.
+     */
+    _bdf_add_comment(f, "Font converted from FNT/FON to BDF.", 35);
+    _bdf_add_acmsg(f, "Font converted from FNT/FON to BDF.", 35);
+
+    /*
+     * Mark the font as being modified.
+     */
+    f->modified = 1;
+
+    *out = f;
+
+    return 0;
+}
diff --git a/bdfgname.c b/bdfgname.c
new file mode 100644 (file)
index 0000000..f038843
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bdfP.h"
+
+typedef struct {
+    int code;
+    int start;
+    int end;
+    int pad;
+} _bdf_adobe_name_t;
+
+static _bdf_adobe_name_t *adobe_names;
+static unsigned int adobe_names_size;
+static unsigned int adobe_names_used;
+
+/*
+ * Provide a maximum length for glyph names just to make things clearer.
+ */
+#define MAX_GLYPH_NAME_LEN 127
+
+static int
+bdf_getline(FILE *in, char *buf, int limit)
+{
+    int c, i;
+
+    c = EOF;
+
+    for (i = 0; i < limit - 1; i++) {
+        if ((c = getc(in)) == EOF || (c == '\n' || c == '\r'))
+          break;
+        buf[i] = c;
+    }
+    buf[i] = 0;
+
+    /*
+     * Discard the rest of the line which did not fit into the buffer.
+     */
+    while (c != EOF && c != '\n' && c != '\r')
+      c = getc(in);
+
+    if (c == '\r') {
+        /*
+         * Check for a trailing newline.
+         */
+        c = getc(in);
+        if (c != '\n')
+          ungetc(c, in);
+    }
+
+    return i;
+}
+
+static int
+_bdf_find_name(int code, char *name, FILE *in)
+{
+    int c, i, pos;
+    char *sp, buf[256];
+
+    while (!feof(in)) {
+        pos = ftell(in);
+        (void) bdf_getline(in, buf, 256);
+        while (!feof(in) && (buf[0] == 0 || buf[0] == '#')) {
+            buf[0] = 0;
+            pos = ftell(in);
+            (void) bdf_getline(in, buf, 256);
+        }
+
+        if (buf[0] == 0)
+          return -1;
+
+        c = _bdf_atol(buf, 0, 16);
+
+        if (c > code) {
+            /*
+             * Restore the last position read in case the code is not in the
+             * file and the current code is greater than the expected code.
+             */
+            fseek(in, pos, 0L);
+            return -1;
+        }
+
+        if (c == code) {
+            for (sp = buf; *sp != ';'; sp++) ;
+            sp++;
+            for (i = 0; *sp != ';' && i < MAX_GLYPH_NAME_LEN; sp++, i++)
+              name[i] = *sp;
+            name[i] = 0;
+            return i;
+        }
+    }
+    return -1;
+}
+
+static int
+by_encoding(const void *a, const void *b)
+{
+    _bdf_adobe_name_t *c1, *c2;
+
+    c1 = (_bdf_adobe_name_t *) a;
+    c2 = (_bdf_adobe_name_t *) b;
+    if (c1->code < c2->code)
+      return -1;
+    else if (c1->code > c2->code)
+      return 1;
+    return 0;
+}
+
+static void
+_bdf_load_adobe_names(FILE *in)
+{
+    int c, pos;
+    char *sp, buf[256];
+
+    /*
+     * Go back to the beginning of the file to look for the code because the
+     * codes are not in order in the current Adobe Glyph Name list file.
+     */
+    fseek(in, 0, 0);
+
+    while (!feof(in)) {
+        pos = ftell(in);
+        (void) bdf_getline(in, buf, 256);
+        while (!feof(in) && (buf[0] == 0 || buf[0] == '#')) {
+            buf[0] = 0;
+            pos = ftell(in);
+            (void) bdf_getline(in, buf, 256);
+        }
+
+        if (adobe_names_used == adobe_names_size) {
+            if (adobe_names_size == 0)
+              adobe_names = (_bdf_adobe_name_t *)
+                  malloc(sizeof(_bdf_adobe_name_t) << 9);
+            else
+              adobe_names = (_bdf_adobe_name_t *)
+                  realloc((char *) adobe_names,
+                          sizeof(_bdf_adobe_name_t) *
+                          (adobe_names_size + 512));
+            (void) memset((char *) (adobe_names + adobe_names_size), 0,
+                          sizeof(_bdf_adobe_name_t) << 9);
+            adobe_names_size += 512;
+        }
+
+        adobe_names[adobe_names_used].start = pos;
+        for (sp = buf; *sp != ';'; sp++) ;
+        adobe_names[adobe_names_used].end = pos + (sp - buf);
+        sp++;
+
+        c = _bdf_atol(sp, 0, 16);
+
+        /*
+         * Ignore the Adobe-specific names in the Private Use Area.
+         */
+        if (c < 0xe000 || c > 0xf8ff)
+          adobe_names[adobe_names_used++].code = c;
+    }
+
+    /*
+     * Sort the results by code.
+     */
+    qsort((char *) adobe_names, adobe_names_used, sizeof(_bdf_adobe_name_t),
+          by_encoding);
+}
+
+static int
+_bdf_find_adobe_name(int code, char *name, FILE *in)
+{
+    int len;
+    int l, r, m;
+
+    if (code < 0x20 || (code >= 0x7f && code <= 0x9f) ||
+        code == 0xfffe || code == 0xffff) {
+        sprintf(name, "char%u", code);
+        return (int) strlen(name);
+    }
+
+    if (code >= 0xe000 && code <= 0xf8ff) {
+        sprintf(name, "uni%04X", code & 0xffff);
+        return (int) strlen(name);
+    }
+
+    if (adobe_names_size == 0)
+      _bdf_load_adobe_names(in);
+
+    l = 0;
+    r = adobe_names_used - 1;
+    while (l <= r) {
+        m = (l + r) >> 1;
+        if (adobe_names[m].code < code)
+          l = m + 1;
+        else if (adobe_names[m].code > code)
+          r = m - 1;
+        else {
+            fseek(in, adobe_names[m].start, 0);
+            len = adobe_names[m].end - adobe_names[m].start;
+            if (len > MAX_GLYPH_NAME_LEN)
+              len = MAX_GLYPH_NAME_LEN;
+            len = (int) fread(name, sizeof(char), len, in);
+            name[len] = 0;
+            return len;
+        }
+    }
+
+    sprintf(name, "uni%04X", code & 0xffff);
+    return (int) strlen(name);
+}
+
+static int
+_bdf_set_glyph_names(FILE *in, bdf_font_t *font, bdf_callback_t callback,
+                     int adobe)
+{
+    int changed;
+    int i, size, len;
+    bdf_glyph_t *gp;
+    bdf_callback_struct_t cb;
+    char name[MAX_GLYPH_NAME_LEN + 1];
+
+    if (callback != 0) {
+        cb.reason = BDF_GLYPH_NAME_START;
+        cb.current = 0;
+        cb.total = font->glyphs_used;
+        (*callback)(&cb, 0);
+    }
+    for (changed = 0, i = 0, gp = font->glyphs; i < font->glyphs_used;
+         i++, gp++) {
+        size = (adobe) ?
+            _bdf_find_adobe_name(gp->encoding, name, in) :
+            _bdf_find_name(gp->encoding, name, in);
+        if (size < 0)
+          continue;
+
+        len = (gp->name) ? strlen(gp->name) : 0;
+        if (len == 0) {
+          gp->name = (char *) _bdf_strdup((unsigned char *) name, size + 1);
+          changed = 1;
+        } else if (size != len || strcmp(gp->name, name) != 0) {
+            /*
+             * Simply resize existing storage so lots of memory allocations
+             * are not needed.
+             */
+            if (size > len)
+              gp->name = (char *) realloc(gp->name, size + 1);
+            (void) strcpy(gp->name, name);
+            changed = 1;
+        }
+
+        if (callback != 0) {
+            cb.reason = BDF_GLYPH_NAME;
+            cb.current = i;
+            (*callback)(&cb, 0);
+        }
+    }
+
+    if (callback != 0) {
+        cb.reason = BDF_GLYPH_NAME;
+        cb.current = cb.total;
+        (*callback)(&cb, 0);
+    }
+
+    return changed;
+}
+
+int
+bdf_set_unicode_glyph_names(FILE *in, bdf_font_t *font,
+                            bdf_callback_t callback)
+{
+    return _bdf_set_glyph_names(in, font, callback, 0);
+}
+
+int
+bdf_set_adobe_glyph_names(FILE *in, bdf_font_t *font, bdf_callback_t callback)
+{
+    return _bdf_set_glyph_names(in, font, callback, 1);
+}
+
+int
+bdf_set_glyph_code_names(int prefix, bdf_font_t *font, bdf_callback_t callback)
+{
+    int changed;
+    int i, size, len;
+    bdf_glyph_t *gp;
+    bdf_callback_struct_t cb;
+    char name[128];
+
+    if (callback != 0) {
+        cb.reason = BDF_GLYPH_NAME_START;
+        cb.current = 0;
+        cb.total = font->glyphs_used;
+        (*callback)(&cb, 0);
+    }
+    for (changed = 0, i = 0, gp = font->glyphs; i < font->glyphs_used;
+         i++, gp++) {
+        switch (prefix) {
+          case 'u': sprintf(name, "uni%04X", gp->encoding & 0xffff); break;
+          case 'x': sprintf(name, "0x%04X", gp->encoding & 0xffff); break;
+          case '+': sprintf(name, "U+%04X", gp->encoding & 0xffff); break;
+          case '\\': sprintf(name, "\\u%04X", gp->encoding & 0xffff); break;
+        }
+        size = 6;
+
+        len = (gp->name) ? strlen(gp->name) : 0;
+        if (len == 0) {
+          gp->name = (char *) _bdf_strdup((unsigned char *) name, size + 1);
+          changed = 1;
+        } else if (size != len || strcmp(gp->name, name) != 0) {
+            /*
+             * Simply resize existing storage so lots of memory allocations
+             * are not needed.
+             */
+            if (size > len)
+              gp->name = (char *) realloc(gp->name, size + 1);
+            (void) strcpy(gp->name, name);
+            changed = 1;
+        }
+
+        if (callback != 0) {
+            cb.reason = BDF_GLYPH_NAME;
+            cb.current = i;
+            (*callback)(&cb, 0);
+        }
+    }
+
+    if (callback != 0) {
+        cb.reason = BDF_GLYPH_NAME;
+        cb.current = cb.total;
+        (*callback)(&cb, 0);
+    }
+
+    return changed;
+}
+
+void
+_bdf_glyph_name_cleanup(void)
+{
+    if (adobe_names_size > 0)
+      free((char *) adobe_names);
+    adobe_names_size = adobe_names_used = 0;
+}
diff --git a/bdfgrab.c b/bdfgrab.c
new file mode 100644 (file)
index 0000000..b5548e5
--- /dev/null
+++ b/bdfgrab.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This file will only be compiled if the HAVE_XLIB macro is defined.
+ */
+#ifdef HAVE_XLIB
+
+/*
+ * Code to get BDF fonts from the X server.  Reimplementation of the famous
+ * "getbdf" program by the equally famous der Mouse :-)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+#if 0
+#include <X11/Xmu/Error.h>
+#endif
+#include "bdfP.h"
+
+/*
+ * Routine to compare two glyphs by encoding so they can be sorted.
+ */
+static int
+by_encoding(const void *a, const void *b)
+{
+    bdf_glyph_t *c1, *c2;
+
+    c1 = (bdf_glyph_t *) a;
+    c2 = (bdf_glyph_t *) b;
+    if (c1->encoding < c2->encoding)
+      return -1;
+    else if (c1->encoding > c2->encoding)
+      return 1;
+    return 0;
+}
+
+static void
+_bdf_get_glyphs(Display *d, XFontStruct *f, bdf_font_t *font,
+                bdf_callback_t callback, void *data)
+{
+    unsigned int off, b1, b2, black, x, y, bpr;
+    GC cleargc, drawgc;
+    Pixmap canvas;
+    XImage *image;
+    XCharStruct *cp;
+    bdf_glyph_t *gp;
+    XChar2b ch;
+    bdf_callback_struct_t cb;
+    char name[16];
+    XGCValues gcv;
+
+    black = BlackPixel(d, DefaultScreen(d));
+
+    /*
+     * Create the Pixmap which will be used to draw the glyphs.
+     */
+    canvas = XCreatePixmap(d, XRootWindow(d, DefaultScreen(d)),
+                           font->bbx.width, font->bbx.height, 1);
+
+    /*
+     * Create the graphics contexts for drawing.
+     */
+    gcv.function = GXcopy;
+    gcv.foreground = WhitePixel(d, DefaultScreen(d));
+    cleargc = XCreateGC(d, canvas, GCFunction|GCForeground, &gcv);
+
+    gcv.background = gcv.foreground;
+    gcv.foreground = black;
+    gcv.font = f->fid;
+    drawgc = XCreateGC(d, canvas,
+                       GCFunction|GCForeground|GCBackground|GCFont, &gcv);
+
+    /*
+     * Do it.
+     */
+    for (b1 = f->min_byte1; b1 <= f->max_byte1; b1++) {
+
+        off = (b1 - f->min_byte1) *
+            (f->max_char_or_byte2 + 1 - f->min_char_or_byte2);
+
+        for (b2 = f->min_char_or_byte2; b2 <= f->max_char_or_byte2;
+             b2++, off++) {
+
+            /*
+             * Point at the glyph metrics.
+             */
+            cp = (f->per_char != 0) ? f->per_char + off : &f->min_bounds;
+
+            if (cp->lbearing || cp->rbearing || cp->width ||
+                cp->ascent || cp->descent) {
+                /*
+                 * Make sure there is enough glyph storage to handle
+                 * the glyphs.
+                 */
+                if (font->glyphs_used == font->glyphs_size) {
+                    if (font->glyphs_size == 0)
+                      font->glyphs = (bdf_glyph_t *)
+                          malloc(sizeof(bdf_glyph_t) * 16);
+                    else
+                      font->glyphs = (bdf_glyph_t *)
+                          realloc((char *) font->glyphs,
+                                  sizeof(bdf_glyph_t) *
+                                  (font->glyphs_size + 16));
+                    font->glyphs_size += 16;
+                }
+
+                /*
+                 * Point at the next glyph structure.
+                 */
+                gp = font->glyphs + font->glyphs_used++;
+
+                /*
+                 * Determine the glyph encoding and set the metrics.
+                 */
+                gp->encoding = (b1 << 8) | b2;
+                gp->dwidth = cp->width;
+                gp->swidth = (unsigned short) (cp->width * 72000.0
+                  / (font->point_size * font->resolution_x));
+                gp->bbx.width = cp->rbearing - cp->lbearing;
+                gp->bbx.x_offset = cp->lbearing;
+                gp->bbx.ascent = cp->ascent;
+                gp->bbx.descent = cp->descent;
+                gp->bbx.y_offset = -cp->descent;
+                gp->bbx.height = cp->ascent + cp->descent;
+
+                /*
+                 * Create a glyph name.
+                 */
+                sprintf(name, "char%d", gp->encoding);
+                gp->name = (char *) malloc(strlen(name) + 1);
+                (void) strcpy(gp->name, name);
+
+                /*
+                 * Determine the number of bytes that will be needed for this
+                 * glyph.
+                 */
+                bpr = (gp->bbx.width + 7) >> 3;
+                gp->bytes = bpr * gp->bbx.height;
+                gp->bitmap = (unsigned char *) malloc(gp->bytes);
+                (void) memset((char *) gp->bitmap, 0, gp->bytes);
+
+                /*
+                 * Clear the PSF Unicode mappings.
+                 */
+                gp->unicode.map_size = gp->unicode.map_used = 0;
+
+                /*
+                 * Clear the canvas.
+                 */
+                XFillRectangle(d, canvas, cleargc, 0, 0,
+                               font->bbx.width, font->bbx.height);
+
+                /*
+                 * Render the glyph.
+                 */
+                ch.byte1 = (b1 == 0) ? (b2 >> 8) : b1;
+                ch.byte2 = b2;
+                XDrawString16(d, canvas, drawgc, -cp->lbearing, cp->ascent,
+                              &ch, 1);
+                image = XGetImage(d, canvas, 0, 0,
+                                  font->bbx.width, font->bbx.height, 1L,
+                                  XYPixmap);
+                for (y = 0; y < gp->bbx.height; y++) {
+                    for (x = 0; x < gp->bbx.width; x++) {
+                        if (XGetPixel(image, x, y) == black)
+                          gp->bitmap[(y * bpr) + (x >> 3)] |=
+                              (0x80 >> (x & 7));
+                    }
+                }
+                XDestroyImage(image);
+
+                /*
+                 * Call the update callback if necessary.
+                 */
+                if (callback != 0) {
+                    cb.reason = BDF_LOADING;
+                    cb.total = font->glyphs_size;
+                    cb.current = font->glyphs_used;
+                    (*callback)(&cb, data);
+                }
+            }
+        }
+    }
+
+    /*
+     * Delete the Pixmap and the GCs.
+     */
+    XFreePixmap(d, canvas);
+    XFreeGC(d, cleargc);
+    XFreeGC(d, drawgc);
+
+    /*
+     * Make sure the glyphs are sorted by encoding.
+     */
+    qsort((char *) font->glyphs, font->glyphs_used, sizeof(bdf_glyph_t),
+          by_encoding);
+}
+
+static int
+error_handler(Display *d, XErrorEvent *event)
+{
+
+    if (event->request_code != X_GetAtomName)
+      fprintf(stderr, "X Server Error\n");
+#if 0
+        XmuPrintDefaultErrorMessage(d, event, stderr);
+#endif
+    return 0;
+}
+
+bdf_font_t *
+bdf_load_server_font(Display *d, XFontStruct *f, char *name,
+                     bdf_options_t *opts, bdf_callback_t callback, void *data)
+{
+    unsigned int i, len, b1, b2;
+    bdf_font_t *font;
+    XFontProp *xfp;
+    XCharStruct *cp;
+    bdf_property_t *pp, prop;
+    bdf_callback_struct_t cb;
+    int (*old_error_handler)();
+
+    if (f == 0)
+      return 0;
+
+    font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t));
+
+    font->bpp = 1;
+
+    /*
+     * Set up the font bounding box.
+     */
+    font->bbx.width = f->max_bounds.rbearing - f->min_bounds.lbearing;
+    font->bbx.x_offset = f->min_bounds.lbearing;
+    font->bbx.height = f->max_bounds.ascent + f->max_bounds.descent;
+    font->bbx.ascent = f->max_bounds.ascent;
+    font->bbx.descent = f->max_bounds.descent;
+    font->bbx.y_offset = -font->bbx.descent;
+
+    /*
+     * If the font happens to be a character cell or monowidth, make sure that
+     * value is taken from the max bounds.
+     */
+    font->monowidth = f->max_bounds.width;
+
+    font->default_glyph = (int) f->default_char;
+
+    /*
+     * Now load the font properties.
+     */
+    old_error_handler = XSetErrorHandler(error_handler);
+    for (i = 0, xfp = f->properties; i < f->n_properties; i++, xfp++) {
+        if (xfp->name == XA_FONT)
+          /*
+           * Set the font name but don't add it to the list in the font.
+           */
+          font->name = XGetAtomName(d, (Atom) xfp->card32);
+        else {
+            /*
+             * Add the property to the font.
+             */
+            prop.name = XGetAtomName(d, xfp->name);
+            if (prop.name) {
+                if ((pp = bdf_get_property(prop.name)) == 0) {
+                    /*
+                     * The property does not exist, so create it with type Atom.
+                     */
+                    bdf_create_property(prop.name, BDF_ATOM);
+                    pp = bdf_get_property(prop.name);
+                }
+                prop.format = pp->format;
+                switch (prop.format) {
+                  case BDF_ATOM:
+                    prop.value.atom = XGetAtomName(d, (Atom) xfp->card32);
+                    break;
+                  case BDF_CARDINAL:
+                    prop.value.card32 = xfp->card32;
+                    break;
+                  case BDF_INTEGER:
+                    prop.value.int32 = (int) xfp->card32;
+                    break;
+                }
+                /*
+                 * Ignore the _XMBDFED_INFO property.
+                 */
+                if (strcmp(prop.name, "_XMBDFED_INFO") != 0)
+                  bdf_add_font_property(font, &prop);
+            }
+
+            /*
+             * Free up the Atom names returned by X.
+             */
+            XFree(prop.name);
+            if (prop.format == BDF_ATOM)
+              XFree(prop.value.atom);
+        }
+    }
+    XSetErrorHandler(old_error_handler);
+
+    /*
+     * Now go through and initialize the various fields needed for the font.
+     */
+
+    /*
+     * If the font name was not set when the properties were loaded,
+     * set it to the name that was passed.
+     */
+    if (font->name == 0) {
+        len = (unsigned int) strlen(name);
+        font->name = (char *) malloc(len + 1);
+        (void) memcpy(font->name, name, len + 1);
+    }
+
+    /*
+     * If the font default glyph is non-zero, make sure the DEFAULT_CHAR
+     * property is updated appropriately.  Otherwise, make sure the
+     * DEFAULT_CHAR property is deleted from the font.
+     */
+    if (font->default_glyph > 0) {
+        prop.name = "DEFAULT_CHAR";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = font->default_glyph;
+        bdf_add_font_property(font, &prop);
+    } else if (bdf_get_font_property(font, "DEFAULT_CHAR") != 0)
+      bdf_delete_font_property(font, "DEFAULT_CHAR");
+
+    /*
+     * Check the point size.
+     */
+    if ((pp = bdf_get_font_property(font, "POINT_SIZE")) != 0)
+      font->point_size = (pp->value.card32 / 10);
+    else
+      font->point_size = 12;
+
+    /*
+     * Check for the deprecated "RESOLUTION" property first in case it exists
+     * and "RESOLUTION_X" and "RESOLUTION_Y" do not.
+     */
+    if ((pp = bdf_get_font_property(font, "RESOLUTION")) != 0)
+      font->resolution_x = font->resolution_y = pp->value.int32;
+
+    if ((pp = bdf_get_font_property(font, "RESOLUTION_X")) != 0)
+      font->resolution_x = pp->value.int32;
+    if ((pp = bdf_get_font_property(font, "RESOLUTION_Y")) != 0)
+      font->resolution_y = pp->value.int32;
+
+    /*
+     * If the horizontal or vertical resolutions have not been set, then
+     * define them to be the resolution of the display.
+     */
+    if (font->resolution_x == 0)
+      font->resolution_x =
+          (int) (((((double) DisplayWidth(d, DefaultScreen(d))) * 25.4) /
+                   ((double) DisplayWidthMM(d, DefaultScreen(d)))) + 0.5);
+    if (font->resolution_y == 0)
+      font->resolution_y =
+          (int) (((((double) DisplayHeight(d, DefaultScreen(d))) * 25.4) /
+                   ((double) DisplayHeightMM(d, DefaultScreen(d)))) + 0.5);
+
+    /*
+     * Check the font ascent and descent.
+     */
+    if ((pp = bdf_get_font_property(font, "FONT_ASCENT")) != 0)
+      font->font_ascent = pp->value.int32;
+    else {
+        /*
+         * Add the FONT_ASCENT property.
+         */
+        prop.name = "FONT_ASCENT";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = font->bbx.ascent;
+        bdf_add_font_property(font, &prop);
+        font->font_ascent = font->bbx.ascent;
+    }
+
+    if ((pp = bdf_get_font_property(font, "FONT_DESCENT")) != 0)
+      font->font_descent = pp->value.int32;
+    else {
+        /*
+         * Add the FONT_DESCENT property.
+         */
+        prop.name = "FONT_DESCENT";
+        prop.format = BDF_INTEGER;
+        prop.value.int32 = font->bbx.descent;
+        bdf_add_font_property(font, &prop);
+        font->font_descent = font->bbx.descent;
+    }
+
+    /*
+     * Get the font spacing.
+     */
+    font->spacing = BDF_PROPORTIONAL;
+    if ((pp = bdf_get_font_property(font, "SPACING")) != 0) {
+        switch (pp->value.atom[0]) {
+          case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break;
+          case 'M': case 'm': font->spacing = BDF_MONOWIDTH; break;
+          case 'C': case 'c': font->spacing = BDF_CHARCELL; break;
+        }
+    }
+
+    /*
+     * Now determine the number of glyphs.
+     */
+    if (f->per_char != 0) {
+        for (b1 = f->min_byte1; b1 <= f->max_byte1; b1++) {
+            len = (b1 - f->min_byte1) *
+                (f->max_char_or_byte2 + 1 - f->min_char_or_byte2);
+            for (b2 = f->min_char_or_byte2; b2 <= f->max_char_or_byte2;
+                 b2++, len++) {
+                cp = f->per_char + len;
+                /*
+                 * If any of the metrics values are non-zero, then count this
+                 * as a glyph.
+                 */
+                if (cp->lbearing || cp->rbearing || cp->width ||
+                    cp->ascent || cp->descent)
+                  font->glyphs_size++;
+            }
+        }
+    } else
+      font->glyphs_size = (f->max_byte1 + 1 - f->min_byte1) *
+          (f->max_char_or_byte2 + 1 - f->min_char_or_byte2);
+
+    /*
+     * Call the callback if it was provided.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.total = font->glyphs_size;
+        cb.current = 0;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Allocate enough glyph storage for the specified number of glyphs.
+     */
+    font->glyphs = (bdf_glyph_t *)
+        malloc(sizeof(bdf_glyph_t) * font->glyphs_size);
+
+    /*
+     * Actually load the glyphs.
+     */
+    _bdf_get_glyphs(d, f, font, callback, data);
+
+    /*
+     * Add a message to the font to indicate it was loaded
+     * from the server.
+     */
+    _bdf_add_comment(font, "Font grabbed from the X server.", 31);
+    _bdf_add_acmsg(font, "Font grabbed from the X server.", 31);
+
+    /*
+     * Mark the font as being modified.
+     */
+    font->modified = 1;
+
+    return font;
+}
+
+#endif /* HAVE_XLIB */
diff --git a/bdfgrid.c b/bdfgrid.c
new file mode 100644 (file)
index 0000000..2362b55
--- /dev/null
+++ b/bdfgrid.c
@@ -0,0 +1,3402 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bdfP.h"
+
+#ifndef MYABS
+#define MYABS(n) ((n) < 0 ? -(n) : (n))
+#endif
+
+#undef MAX
+#define MAX(h, i) ((h) > (i) ? (h) : (i))
+
+#undef MIN
+#define MIN(l, o) ((l) < (o) ? (l) : (o))
+
+double _bdf_cos_tbl[360] = {
+    0.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195,
+    0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627,
+    0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305,
+    0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505,
+    0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620,
+    0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152,
+    0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710,
+    0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998,
+    0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815,
+    0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038,
+    0.500000, 0.484810, 0.469472, 0.453990, 0.438371, 0.422618,
+    0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568,
+    0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951,
+    0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869,
+    0.104528, 0.087156, 0.069756, 0.052336, 0.034899, 0.017452,
+    0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156,
+    -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809,
+    -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372,
+    -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731,
+    -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810,
+    -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576,
+    -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059,
+    -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354,
+    -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798636,
+    -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167,
+    -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308,
+    -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519,
+    -0.951057, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370,
+    -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546,
+    -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848,
+    -1.000000, -0.999848, -0.999391, -0.998630, -0.997564, -0.996195,
+    -0.994522, -0.992546, -0.990268, -0.987688, -0.984808, -0.981627,
+    -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305,
+    -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505,
+    -0.913545, -0.906308, -0.898794, -0.891007, -0.882948, -0.874620,
+    -0.866025, -0.857167, -0.848048, -0.838671, -0.829038, -0.819152,
+    -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710,
+    -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998,
+    -0.669131, -0.656059, -0.642788, -0.629320, -0.615661, -0.601815,
+    -0.587785, -0.573576, -0.559193, -0.544639, -0.529919, -0.515038,
+    -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618,
+    -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568,
+    -0.309017, -0.292372, -0.275637, -0.258819, -0.241922, -0.224951,
+    -0.207912, -0.190809, -0.173648, -0.156434, -0.139173, -0.121869,
+    -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452,
+    -0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156,
+    0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809,
+    0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372,
+    0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731,
+    0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810,
+    0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576,
+    0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059,
+    0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354,
+    0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636,
+    0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167,
+    0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308,
+    0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519,
+    0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370,
+    0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546,
+    0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848,
+};
+
+double _bdf_sin_tbl[360] = {
+    0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156,
+    0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809,
+    0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372,
+    0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731,
+    0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810,
+    0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576,
+    0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059,
+    0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354,
+    0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636,
+    0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167,
+    0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308,
+    0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519,
+    0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370,
+    0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546,
+    0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848,
+    1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195,
+    0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627,
+    0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305,
+    0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505,
+    0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620,
+    0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152,
+    0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710,
+    0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998,
+    0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815,
+    0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038,
+    0.500000, 0.484810, 0.469472, 0.453990, 0.438371, 0.422618,
+    0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568,
+    0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951,
+    0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869,
+    0.104528, 0.087156, 0.069756, 0.052336, 0.034899, 0.017452,
+    0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156,
+    -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809,
+    -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372,
+    -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731,
+    -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810,
+    -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576,
+    -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059,
+    -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354,
+    -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798636,
+    -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167,
+    -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308,
+    -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519,
+    -0.951057, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370,
+    -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546,
+    -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848,
+    -1.000000, -0.999848, -0.999391, -0.998630, -0.997564, -0.996195,
+    -0.994522, -0.992546, -0.990268, -0.987688, -0.984808, -0.981627,
+    -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305,
+    -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505,
+    -0.913545, -0.906308, -0.898794, -0.891007, -0.882948, -0.874620,
+    -0.866025, -0.857167, -0.848048, -0.838671, -0.829038, -0.819152,
+    -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710,
+    -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998,
+    -0.669131, -0.656059, -0.642788, -0.629320, -0.615661, -0.601815,
+    -0.587785, -0.573576, -0.559193, -0.544639, -0.529919, -0.515038,
+    -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618,
+    -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568,
+    -0.309017, -0.292372, -0.275637, -0.258819, -0.241922, -0.224951,
+    -0.207912, -0.190809, -0.173648, -0.156434, -0.139173, -0.121869,
+    -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452,
+};
+
+double _bdf_tan_tbl[90] = {
+    0.000000, 0.017455, 0.034921, 0.052408, 0.069927, 0.087489,
+    0.105104, 0.122785, 0.140541, 0.158384, 0.176327, 0.194380,
+    0.212557, 0.230868, 0.249328, 0.267949, 0.286745, 0.305731,
+    0.324920, 0.344328, 0.363970, 0.383864, 0.404026, 0.424475,
+    0.445229, 0.466308, 0.487733, 0.509525, 0.531709, 0.554309,
+    0.577350, 0.600861, 0.624869, 0.649408, 0.674509, 0.700208,
+    0.726543, 0.753554, 0.781286, 0.809784, 0.839100, 0.869287,
+    0.900404, 0.932515, 0.965689, 1.000000, 1.035530, 1.072369,
+    1.110613, 1.150368, 1.191754, 1.234897, 1.279942, 1.327045,
+    1.376382, 1.428148, 1.482561, 1.539865, 1.600335, 1.664279,
+    1.732051, 1.804048, 1.880726, 1.962611, 2.050304, 2.144507,
+    2.246037, 2.355852, 2.475087, 2.605089, 2.747477, 2.904211,
+    3.077684, 3.270853, 3.487414, 3.732051, 4.010781, 4.331476,
+    4.704630, 5.144554, 5.671282, 6.313752, 7.115370, 8.144346,
+    9.514364, 11.430052, 14.300666, 19.081137, 28.636253, 57.289962,
+};
+
+/*
+ * Determine the actual ink bounds.
+ */
+static int
+_bdf_grid_ink_bounds(bdf_glyph_grid_t *grid, short *x, short *y,
+                     short *width, short *height)
+{
+    short bx, by, bwd, bht, minx, maxx, miny, maxy, dx, dy;
+    unsigned short bpr, ink, sel, col;
+    unsigned char *bmap, *masks;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    if (grid->sel.width != 0 && grid->sel.height != 0) {
+        sel = 1;
+        bx = by = 0;
+        bwd = grid->sel.width;
+        bht = grid->sel.height;
+        bmap = grid->sel.bitmap;
+    } else {
+        sel = 0;
+        bx = grid->glyph_x;
+        by = grid->glyph_y;
+        bwd = grid->glyph_bbx.width;
+        bht = grid->glyph_bbx.height;
+        bmap = grid->bitmap;
+    }
+    maxx = maxy = 0;
+    minx = bx + bwd;
+    miny = by + bht;
+
+    bpr = ((bwd * grid->bpp) + 7) >> 3;
+    ink = 0;
+
+    bwd += bx;
+    bht += by;
+    for (dy = by; dy < bht; dy++) {
+        for (col = bx * grid->bpp, dx = bx; dx < bwd; dx++, col += grid->bpp) {
+            if (bmap[(dy * bpr) + (col >> 3)] & masks[(col & 7) / grid->bpp]) {
+                ink = 1;
+                minx = MIN(minx, dx);
+                miny = MIN(miny, dy);
+                maxx = MAX(maxx, dx);
+                maxy = MAX(maxy, dy);
+            }
+        }
+    }
+
+    *x = minx + ((sel) ? grid->sel.x : 0);
+    *y = miny + ((sel) ? grid->sel.y : 0);
+    if (ink == 0)
+      *width = *height = 0;
+    else {
+        *width = (maxx - minx) + 1;
+        *height = (maxy - miny) + 1;
+    }
+    return ink;
+}
+
+/**************************************************************************
+ *
+ * Glyph grid create and destroy functions.
+ *
+ **************************************************************************/
+
+/*
+ * Make a glyph grid with the glyph bitmap set in the bitmap.
+ */
+bdf_glyph_grid_t *
+bdf_make_glyph_grid(bdf_font_t *font, int code, int unencoded)
+{
+    unsigned short si, di, col, colx, byte;
+    short ht, as, ds, gsize, bpr, x, y, nx, ny;
+    long l, r, m;
+    bdf_glyph_grid_t *gr;
+    bdf_glyph_t *gl, *glp;
+    bdf_property_t *p;
+    unsigned char *masks;
+    char name[24];
+
+#if 0
+    if (font == 0)
+      return 0;
+#endif
+
+    /*
+     * Allocate the grid and initialize it.
+     */
+    gr = (bdf_glyph_grid_t *) malloc(sizeof(bdf_glyph_grid_t));
+    (void) memset((char *) gr, 0, sizeof(bdf_glyph_grid_t));
+
+    /*
+     * Set the encoding and the unencoded flag.
+     */
+    gr->bpp = (font) ? font->bpp : 1;
+    gr->encoding = code;
+    gr->unencoded = unencoded;
+
+    /*
+     * Set the glyph grid spacing.
+     */
+    gr->spacing = (font) ? font->spacing : BDF_CHARCELL;
+
+    /*
+     * Set the point size and resolutions.
+     */
+    if (font) {
+        gr->point_size = font->point_size;
+        gr->resolution_x = font->resolution_x;
+        gr->resolution_y = font->resolution_y;
+    } else {
+        gr->point_size = 12;
+        gr->resolution_x = gr->resolution_y = 100;
+    }
+
+    /*
+     * Set the CAP_HEIGHT and X_HEIGHT if they exist in the font.
+     */
+    if (font) {
+        if ((p = bdf_get_font_property(font, "CAP_HEIGHT")) != 0)
+          gr->cap_height = (short) p->value.int32;
+        if ((p = bdf_get_font_property(font, "X_HEIGHT")) != 0)
+          gr->x_height = (short) p->value.int32;
+    }
+
+    masks = 0;
+    switch (gr->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Copy the font bounding box into the grid.
+     */
+    if (font)
+      (void) memcpy((char *) &gr->font_bbx, (char *) &font->bbx,
+                    sizeof(bdf_bbx_t));
+    else {
+        gr->font_bbx.height = 17;
+        gr->font_bbx.width = 8;
+        gr->font_bbx.descent = 8;
+        gr->font_bbx.ascent = 9;
+        gr->font_bbx.y_offset = -8;
+    }
+
+
+    if (font) {
+        if (unencoded) {
+            gl = font->unencoded;
+            r = font->unencoded_used;
+        } else {
+            gl = font->glyphs;
+            r = font->glyphs_used;
+        }
+    } else {
+        gl = 0;
+        r = 0;
+    }
+
+    /*
+     * Locate the specified glyph using a simple binary search.
+     */
+    glp = 0;
+    if (r > 0) {
+        for (l = 0; r >= l; ) {
+            m = (l + r) >> 1;
+            glp = gl + m;
+            if (glp->encoding == code)
+              break;
+            if (glp->encoding > code)
+              r = m - 1;
+            else if (glp->encoding < code)
+              l = m + 1;
+            glp = 0;
+        }
+    }
+
+    ht = gr->font_bbx.height;
+    as = gr->font_bbx.ascent;
+    ds = gr->font_bbx.descent;
+
+    /*
+     * 1. Determine width and height needed from the largest of the
+     *    width or height.
+     */
+    gr->grid_width = gr->grid_height =
+        MAX(gr->font_bbx.width, gr->font_bbx.height);
+
+    /*
+     * 2. Make sure the grid is at least a square of the largest of the width
+     *    or height of the glyph itself to allow room for transformations.
+     */
+    if (glp != 0) {
+        /*
+         * Set the glyph name and other metrics.
+         */
+        if (glp->name) {
+            gr->name = (char *) malloc(strlen(glp->name) + 1);
+            (void) memcpy(gr->name, glp->name, strlen(glp->name) + 1);
+        } else {
+            sprintf(name, "char%d", code);
+            gr->name = (char *) malloc(strlen(name) + 1);
+            (void) memcpy(gr->name, name, strlen(name) + 1);
+        }
+        gr->dwidth = glp->dwidth;
+
+        /*
+         * Copy the glyph bounding box into the grid.
+         */
+        (void) memcpy((char *) &gr->glyph_bbx, (char *) &glp->bbx,
+                      sizeof(bdf_bbx_t));
+
+        if (glp->bbx.height < glp->bbx.ascent + glp->bbx.descent)
+          gsize = glp->bbx.ascent + glp->bbx.descent;
+        else
+          gsize = glp->bbx.height;
+
+        /*
+         * Figure the maximum of the glyph width and height.
+         */
+        gsize = MAX(gr->glyph_bbx.width, gsize);
+
+        /*
+         * If either the grid width or grid height is less than the
+         * grid size just determined, then adjust them to the new grid size.
+         */
+        gr->grid_width = MAX(gr->grid_width, gsize);
+        gr->grid_height = MAX(gr->grid_height, gsize);
+    } else {
+        /*
+         * The glyph doesn't exist, so make up a name for it.
+         */
+        if (unencoded)
+          sprintf(name, "unencoded%d", code);
+        else
+          sprintf(name, "char%d", code);
+        gr->name = (char *) malloc(strlen(name) + 1);
+        (void) memcpy(gr->name, name, strlen(name) + 1);
+    }
+
+    /*
+     * If the font has character-cell or mono spacing, make sure the grid
+     * device width is set to the width stored in the font.
+     */
+    if (gr->spacing != BDF_PROPORTIONAL)
+      gr->dwidth = (font) ? font->monowidth : 8;
+
+    /*
+     * Determine the vertical origin based on the font bounding box.
+     */
+    if (ht >= as + ds)
+      gr->base_y = (((gr->grid_height >> 1) - (ht >> 1)) + ht) - ds;
+    else
+      gr->base_y = ((gr->grid_height >> 1) - ((as + ds) >> 1)) + as;
+
+    /*
+     * The final adjust is to check to see if the glyph positioned relative to
+     * the baseline would cause the grid to change size.  This sometimes
+     * happens in fonts that have incorrect metrics.
+     */
+    if (gr->base_y + gr->glyph_bbx.descent > gr->grid_height) {
+        gsize = gr->base_y + gr->glyph_bbx.descent;
+        gr->grid_width = MAX(gsize, gr->grid_width);
+        gr->grid_height = MAX(gsize, gr->grid_height);
+    }
+
+    /*
+     * Determine the horizontal origin based on the font bounding box and
+     * centered within the grid.
+     */
+    gr->base_x = (gr->grid_width >> 1) - (gr->font_bbx.width >> 1);
+    if (gr->font_bbx.x_offset < 0)
+      gr->base_x += MYABS(gr->font_bbx.x_offset);
+
+    /*
+     * Allocate double the storage needed for the grid bitmap.  The extra
+     * storage will be used for transformations.
+     */
+    gr->bytes = ((((gr->grid_width * gr->bpp) + 7) >> 3) *
+                 gr->grid_height) << 1;
+    gr->bitmap = (unsigned char *) malloc(gr->bytes);
+    (void) memset((char *) gr->bitmap, 0, gr->bytes);
+
+    /*
+     * Initialize the top-left coordinates of the glyph to the baseline
+     * coordinates.
+     */
+    gr->glyph_x = gr->base_x;
+    gr->glyph_y = gr->base_y;
+
+    /*
+     * If the glyph was not found, simply return the empty grid.
+     */
+    if (glp == 0)
+      return gr;
+
+    /*
+     * Determine the top-left coordinates of the glyph with respect to the
+     * baseline coordinates.
+     */
+    gr->glyph_x = nx = gr->base_x + gr->glyph_bbx.x_offset; 
+    gr->glyph_y = ny = gr->base_y - gr->glyph_bbx.ascent;
+
+    /*
+     * Now copy the glyph bitmap to the appropriate location in the grid.
+     */
+    bpr = ((gr->glyph_bbx.width * gr->bpp) + 7) >> 3;
+    gsize = ((gr->grid_width * gr->bpp) + 7) >> 3;
+    for (y = 0; y < gr->glyph_bbx.height; y++, ny++) {
+        for (colx = nx * gr->bpp, col = x = 0; x < gr->glyph_bbx.width;
+             x++, col += gr->bpp, colx += gr->bpp) {
+            si = (col & 7) / gr->bpp;
+            byte = glp->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                di = (colx & 7) / gr->bpp;
+                if (di < si)
+                  byte <<= (si - di) * gr->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * gr->bpp;
+                gr->bitmap[(ny * gsize) + (colx >> 3)] |= byte;
+            }
+        }
+    }
+
+    /*
+     * Always crop the glyph to the ink bounds before editing.
+     */
+    bdf_grid_crop(gr, 0);
+
+    /*
+     * Copy any Unicode mappings that might be present for this glyph, even if
+     * it is a proportional font.  It might be changed to a character cell or
+     * monowidth font later.
+     */
+    gr->unicode.map_size = glp->unicode.map_size;
+    gr->unicode.map_used = glp->unicode.map_used;
+    gr->unicode.map = (unsigned char *)
+        malloc(sizeof(unsigned char) * gr->unicode.map_size);
+    (void) memcpy((char *) gr->unicode.map, (char *) glp->unicode.map,
+                  sizeof(unsigned char) * gr->unicode.map_used);
+
+    /*
+     * Return the grid.
+     */
+    return gr;
+}
+
+void
+bdf_free_glyph_grid(bdf_glyph_grid_t *grid)
+{
+    if (grid == 0)
+      return;
+
+    if (grid->name != 0)
+      free(grid->name);
+    if (grid->bytes > 0)
+      free((char *) grid->bitmap);
+    if (grid->sel.bytes > 0)
+      free((char *) grid->sel.bitmap);
+    if (grid->unicode.map_size > 0)
+      free((char *) grid->unicode.map);
+    free((char *) grid);
+}
+
+/**************************************************************************
+ *
+ * Glyph grid resize functions.
+ *
+ **************************************************************************/
+
+/*
+ * Enlarge the grid without affecting the font or glyph metrics.
+ */
+int
+bdf_grid_enlarge(bdf_glyph_grid_t *grid, unsigned short width,
+                 unsigned short height)
+{
+    unsigned short si, di, col, colx, byte;
+    short ht, wd, as, ds, x, y, nx, ny;
+    unsigned short gwd, ght, bytes, obpr, nbpr, gsize;
+    unsigned char *bitmap, *masks;
+
+    if (grid == 0 || (width < grid->grid_width && height < grid->grid_height))
+      return 0;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    ht = height;
+    as = grid->font_bbx.ascent;
+    ds = grid->font_bbx.descent;
+
+    gwd = MAX(width, grid->grid_width);
+    ght = MAX(height, grid->grid_height);
+    gsize = MAX(gwd, ght);
+
+    nbpr = ((gsize * grid->bpp) + 7) >> 3;
+    bytes = (nbpr * ght) << 1;
+    bitmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) bitmap, 0, bytes);
+
+    /*
+     * Determine the new baseline.
+     */
+    if (ht >= as + ds)
+      grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds;
+    else
+      grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as;
+
+    grid->base_x = (gwd >> 1) - (grid->font_bbx.width >> 1);
+    if (grid->font_bbx.x_offset < 0)
+      grid->base_x += MYABS(grid->font_bbx.x_offset);
+
+    nx = grid->base_x + grid->glyph_bbx.x_offset;
+    ny = grid->base_y - grid->glyph_bbx.ascent;
+
+    /*
+     * Now copy the bitmap into the new storage base on the new metrics
+     * values.
+     */
+    obpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    wd = grid->glyph_x + grid->glyph_bbx.width;
+    ht = grid->glyph_y + grid->glyph_bbx.height;
+    for (y = grid->glyph_y; y < ht; y++, ny++) {
+        col = grid->glyph_x * grid->bpp;
+        colx = nx * grid->bpp;
+        for (x = grid->glyph_x; x < wd;
+             x++, col += grid->bpp, colx += grid->bpp) {
+            si = (col & 7) / grid->bpp;
+            byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                di = (colx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                bitmap[(ny * nbpr) + (colx >> 3)] |= byte;
+            }
+        }
+    }
+
+    /*
+     * Adjust the glyph coordinates.
+     */
+    grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset;
+    grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent;
+
+    /*
+     * Get rid of the old grid bitmap and replace it with the new one.
+     */
+    free((char *) grid->bitmap);
+    grid->bytes = bytes;
+    grid->bitmap = bitmap;
+
+    /*
+     * Update the new grid width and height.
+     */
+    grid->grid_width = grid->grid_height = gsize;
+
+    /*
+     * Always mark the grid as being modified on a resize.
+     */
+    grid->modified = 1;
+
+    return 1;
+}
+
+/*
+ * Change the font bounding box values and resize the grid bitmap if
+ * necessary.
+ */
+int
+bdf_grid_resize(bdf_glyph_grid_t *grid, bdf_metrics_t *metrics)
+{
+    int changed;
+    unsigned short si, di, col, colx, byte;
+    short ht, wd, as, ds, x, y, nx, ny;
+    unsigned short gwd, ght, bytes, obpr, nbpr, gsize;
+    unsigned char *bitmap, *masks;
+
+    changed = 0;
+
+    if (grid == 0 || metrics == 0)
+      return changed;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Create new grid bitmaps in preparation for the various metrics changing.
+     */
+    if (metrics->width > grid->grid_width ||
+        metrics->height > grid->grid_height) {
+        changed = 1;
+
+        ht = metrics->height;
+        as = metrics->ascent;
+        ds = metrics->descent;
+
+        gwd = MAX(metrics->width, grid->grid_width);
+        ght = MAX(metrics->height, grid->grid_height);
+
+        /*
+         * Get the larger of the two dimensions.
+         */
+        gsize = MAX(gwd, ght);
+
+        nbpr = ((gsize * grid->bpp) + 7) >> 3;
+        bytes = (nbpr * gsize) << 1;
+        bitmap = (unsigned char *) malloc(bytes);
+        (void) memset((char *) bitmap, 0, bytes);
+
+        /*
+         * Determine the new baseline.
+         */
+        if (ht >= as + ds)
+          grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds;
+        else
+          grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as;
+
+        grid->base_x = (gwd >> 1) - (metrics->width >> 1);
+        if (metrics->x_offset < 0)
+          grid->base_x += MYABS(metrics->x_offset);
+
+        nx = grid->base_x + grid->glyph_bbx.x_offset;
+        ny = grid->base_y - grid->glyph_bbx.ascent;
+
+        /*
+         * Now copy the bitmap into the new storage base on the new metrics
+         * values.
+         */
+        obpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+        wd = grid->glyph_x + grid->glyph_bbx.width;
+        ht = grid->glyph_y + grid->glyph_bbx.height;
+        for (y = grid->glyph_y; y < ht; y++, ny++) {
+            col = grid->glyph_x * grid->bpp;
+            colx = nx * grid->bpp;
+            for (x = grid->glyph_x; x < wd;
+                 x++, col += grid->bpp, colx += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    di = (colx & 7) / grid->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * grid->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * grid->bpp;
+                    bitmap[(ny * nbpr) + (colx >> 3)] |= byte;
+                }
+            }
+        }
+
+        /*
+         * Adjust the glyph coordinates.
+         */
+        grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset;
+        grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent;
+
+        /*
+         * Get rid of the old grid bitmap and replace it with the new one.
+         */
+        free((char *) grid->bitmap);
+        grid->bytes = bytes;
+        grid->bitmap = bitmap;
+
+        /*
+         * Update the new grid width and height.
+         */
+        grid->grid_width = grid->grid_height = gsize;
+
+        /*
+         * Copy the metrics info into the font bounding box.
+         */
+        grid->font_bbx.width = metrics->width;
+        grid->font_bbx.x_offset = metrics->x_offset;
+        grid->font_bbx.height = metrics->height;
+        grid->font_bbx.ascent = metrics->ascent;
+        grid->font_bbx.descent = metrics->descent;
+        grid->font_bbx.y_offset = metrics->y_offset;
+    } else {
+        /*
+         * The grid does not need to resized, but the baseline must
+         * be recalculated and the bitmap copied again.
+         */
+        bytes = grid->bytes >> 1;
+        bitmap = grid->bitmap + bytes;
+        (void) memset((char *) bitmap, 0, bytes);
+
+        ht = metrics->height;
+        as = metrics->ascent;
+        ds = metrics->descent;
+
+        gwd = grid->grid_width;
+        ght = grid->grid_height;
+
+        /*
+         * Determine the new baseline.
+         */
+        if (ht >= as + ds)
+          grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds;
+        else
+          grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as;
+
+        grid->base_x = (gwd >> 1) - (metrics->width >> 1);
+        if (metrics->x_offset < 0)
+          grid->base_x += MYABS(metrics->x_offset);
+
+        nx = grid->base_x + grid->glyph_bbx.x_offset;
+        ny = grid->base_y - grid->glyph_bbx.ascent;
+
+        wd = grid->glyph_x + grid->glyph_bbx.width;
+        ht = grid->glyph_y + grid->glyph_bbx.height;
+
+        obpr = nbpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+        for (y = grid->glyph_y; y < ht; y++, ny++) {
+            col = grid->glyph_x * grid->bpp;
+            colx = nx * grid->bpp;
+            for (x = grid->glyph_x; x < wd;
+                 x++, col += grid->bpp, colx += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    di = (colx & 7) / grid->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * grid->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * grid->bpp;
+                    bitmap[(ny * nbpr) + (colx >> 3)] |= byte;
+                }
+            }
+        }
+
+        /*
+         * Copy the adjusted bitmap back into the main area.
+         */
+        (void) memcpy((char *) grid->bitmap, (char *) bitmap, bytes);
+
+        /*
+         * Adjust the glyph coordinates.
+         */
+        grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset;
+        grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent;
+
+        /*
+         * Copy the metrics info into the font bounding box.
+         */
+        grid->font_bbx.width = metrics->width;
+        grid->font_bbx.x_offset = metrics->x_offset;
+        grid->font_bbx.height = metrics->height;
+        grid->font_bbx.ascent = metrics->ascent;
+        grid->font_bbx.descent = metrics->descent;
+        grid->font_bbx.y_offset = metrics->y_offset;
+    }
+
+    /*
+     * If the font is not proportional, make sure the device width is adjusted
+     * to meet the new font bounding box.
+     */
+    if (changed && grid->spacing != BDF_PROPORTIONAL)
+      grid->dwidth = grid->font_bbx.width;
+
+    /*
+     * Always mark the grid as being modified on a resize.
+     */
+    grid->modified = 1;
+
+    return changed;
+}
+
+int
+bdf_grid_crop(bdf_glyph_grid_t *grid, int grid_modified)
+{
+    int cropped;
+    short x, y, delta, maxx, minx, maxy, miny, col;
+    unsigned short bpr;
+    unsigned char *masks;
+
+    cropped = 0;
+    if (grid == 0)
+      return cropped;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+
+    maxx = maxy = -1;
+    minx = miny = grid->grid_width;
+    for (y = 0; y < grid->grid_height; y++) {
+        for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) {
+            if (grid->bitmap[(y * bpr) + (col >> 3)] &
+                masks[(col & 7) / grid->bpp]) {
+                minx = MIN(minx, x);
+                maxx = MAX(maxx, x);
+                miny = MIN(miny, y);
+                maxy = MAX(maxy, y);
+            }
+        }
+    }
+
+    /*
+     * Handle an empty bitmap as a special case.
+     */
+    if (maxx == -1) {
+        /*
+         * If the glyph bounding box indicated something was there originally,
+         * then indicate that it was cropped.
+         */
+        if (grid->glyph_bbx.width != 0 || grid->glyph_bbx.height != 0)
+          cropped = 1;
+        (void) memset((char *) &grid->glyph_bbx, 0, sizeof(bdf_bbx_t));
+        grid->glyph_x = grid->base_x;
+        grid->glyph_y = grid->base_y;
+        if (cropped && grid_modified)
+          grid->modified = 1;
+        return cropped;
+    }
+
+    /*
+     * Increment the max points so width and height calculations won't go
+     * wrong.
+     */
+    maxx++;
+    maxy++;
+
+    if (minx != grid->glyph_x) {
+        cropped = 1;
+        delta = minx - grid->glyph_x;
+        grid->glyph_x += delta;
+        grid->glyph_bbx.x_offset += delta;
+    }
+    if (maxx - minx != grid->glyph_bbx.width) {
+        cropped = 1;
+        delta = (maxx - minx) - grid->glyph_bbx.width;
+        grid->glyph_bbx.width += delta;
+    }
+
+    if (miny != grid->glyph_y) {
+        cropped = 1;
+        delta = miny - grid->glyph_y;
+        grid->glyph_y += delta;
+        grid->glyph_bbx.y_offset =
+            grid->base_y - (grid->glyph_y + (maxy - miny));
+    }
+    if (maxy - miny != grid->glyph_bbx.height) {
+        cropped = 1;
+        delta = (maxy - miny) - grid->glyph_bbx.height;
+        grid->glyph_bbx.height += delta;
+        grid->glyph_bbx.y_offset =
+            grid->base_y - (grid->glyph_y + (maxy - miny));
+        grid->glyph_bbx.ascent =
+            grid->glyph_bbx.height + grid->glyph_bbx.y_offset;
+        grid->glyph_bbx.descent = -grid->glyph_bbx.y_offset;
+    }
+
+    /*
+     * Indicate that the grid was modified if the glyph had to be cropped.
+     */
+    if (cropped && grid_modified)
+      grid->modified = 1;
+
+    return cropped;
+}
+
+/**************************************************************************
+ *
+ * Glyph grid pixel functions.
+ *
+ **************************************************************************/
+
+int
+bdf_grid_set_pixel(bdf_glyph_grid_t *grid, short x, short y, int val)
+{
+    unsigned short si, di, dx;
+    int set, bpr, delta;
+    unsigned char *masks;
+
+    set = 0;
+
+    if (grid == 0 || x < 0 || x >= grid->grid_width ||
+        y < 0 || y >= grid->grid_height)
+      return set;
+
+    si = 0;
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; si = 7; break;
+      case 2: masks = bdf_twobpp; si = 3; break;
+      case 4: masks = bdf_fourbpp; si = 1; break;
+      case 8: masks = bdf_eightbpp; si = 0; break;
+    }
+
+    /*
+     * Remove any unused bits from the value.
+     */
+    val &= masks[si];
+
+    dx = x * grid->bpp;
+    di = (dx & 7) / grid->bpp;
+
+    /*
+     * Shift up the value to the appropriate place if necessary.
+     */
+    if (di < si)
+      val <<= (si - di) * grid->bpp;
+
+    /*
+     * Determine the bytes-per-row.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+
+    /*
+     * If the pixel is already set, simply return with an indication that
+     * nothing changed.
+     */
+    if ((grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]) == val)
+      return set;
+
+    /*
+     * Set the bit.
+     */
+    set = 1;
+
+    /*
+     * Clear the bits that will take the new value.
+     */
+    grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di];
+    grid->bitmap[(y * bpr) + (dx >> 3)] |= val;
+
+    /*
+     * Adjust the glyph bounding box.
+     */
+    if (x < grid->glyph_x) {
+        delta = grid->glyph_x - x;
+        grid->glyph_bbx.width += delta;
+        grid->glyph_bbx.x_offset -= delta;
+        if (grid->spacing == BDF_PROPORTIONAL)
+          grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset;
+        grid->glyph_x -= delta;
+    } else if (x >= grid->glyph_x + grid->glyph_bbx.width) {
+        delta = x - (grid->glyph_x + grid->glyph_bbx.width) + 1;
+        grid->glyph_bbx.width += delta;
+        if (grid->spacing == BDF_PROPORTIONAL)
+          grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset;
+    }
+    if (y < grid->glyph_y) {
+        delta = grid->glyph_y - y;
+        grid->glyph_bbx.ascent += delta;
+        grid->glyph_bbx.height += delta;
+        grid->glyph_y -= delta;
+    } else if (y >= grid->glyph_y + grid->glyph_bbx.height) {
+        delta = y - (grid->glyph_y + grid->glyph_bbx.height) + 1;
+        grid->glyph_bbx.descent += delta;
+        grid->glyph_bbx.height += delta;
+        grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent;
+    }
+
+    /*
+     * Indicate that the glyph was modified.
+     */
+    grid->modified = 1;
+
+    return set;
+}
+
+int
+bdf_grid_clear_pixel(bdf_glyph_grid_t *grid, short x, short y)
+{
+    int cleared, bpr;
+    short delta, maxx, minx, maxy, miny, wd, ht;
+    unsigned short di, dx;
+    unsigned char *masks;
+
+    cleared = 0;
+
+    if (grid == 0 || x < 0 || x >= grid->grid_width ||
+        y < 0 || y >= grid->grid_height)
+      return cleared;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Determine the bytes-per-row.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+
+    dx = x * grid->bpp;
+    di = (dx & 7) / grid->bpp;
+
+    /*
+     * If the bit is already clear, simply return with an indication that
+     * nothing changed.
+     */
+    if (!(grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]))
+      return cleared;
+
+    /*
+     * Clear the bit.
+     */
+    cleared = 1;
+    grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di];
+
+    /*
+     * Determine the new min and max values.
+     */
+    maxx = maxy = 0;
+    minx = miny = 32767;
+
+    wd = grid->glyph_x + grid->glyph_bbx.width;
+    ht = grid->glyph_y + grid->glyph_bbx.height;
+
+    for (y = grid->glyph_y; y < ht; y++) {
+        dx = grid->glyph_x * grid->bpp;
+        for (x = grid->glyph_x; x < wd; x++, dx += grid->bpp) {
+            di = (dx & 7) / grid->bpp;
+            if (grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]) {
+                minx = MIN(minx, x);
+                maxx = MAX(maxx, x);
+                miny = MIN(miny, y);
+                maxy = MAX(maxy, y);
+            }
+        }
+    }
+
+    /*
+     * If this call clears the last bit in the image, set the glyph origin
+     * to the base and return.
+     */
+    if (maxx == 0) {
+        grid->glyph_x = grid->base_x;
+        grid->glyph_y = grid->base_y;
+        if (grid->spacing == BDF_PROPORTIONAL)
+          grid->dwidth = 0;
+        (void) memset((char *) &grid->glyph_bbx, 0, sizeof(grid->glyph_bbx));
+        grid->modified = 1;
+        return cleared;
+    }
+
+    /*
+     * Figure out the left and right bearing changes.
+     */
+    if (minx > grid->glyph_x) {
+        delta = minx - grid->glyph_x;
+        grid->glyph_bbx.width -= delta;
+        grid->glyph_bbx.x_offset += delta;
+        if (grid->spacing == BDF_PROPORTIONAL)
+          grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset;
+        grid->glyph_x += delta;
+    } else if (maxx < wd - 1) {
+        delta = (wd - 1) - maxx;
+        grid->glyph_bbx.width -= delta;
+        if (grid->spacing == BDF_PROPORTIONAL)
+          grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset;
+    }
+
+    if (miny > grid->glyph_y) {
+        delta = miny - grid->glyph_y;
+        grid->glyph_bbx.ascent -= delta;
+        grid->glyph_bbx.height -= delta;
+        grid->glyph_y += delta;
+    } else if (maxy < ht - 1) {
+        delta = (ht - 1) - maxy;
+        grid->glyph_bbx.descent -= delta;
+        grid->glyph_bbx.height -= delta;
+        grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent;
+    }
+
+    /*
+     * Indicate that the glyph was modified.
+     */
+    grid->modified = 1;
+
+    return cleared;
+}
+
+int
+bdf_grid_invert_pixel(bdf_glyph_grid_t *grid, short x, short y, int val)
+{
+    short bpr, di;
+    unsigned char *masks;
+
+    if (grid == 0 || x < 0 || x >= grid->grid_width ||
+        y < 0 || y >= grid->grid_height)
+      return 0;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Determine the bytes-per-row and mask index.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    di = ((x * grid->bpp) & 7) / grid->bpp;
+
+    /*
+     * If the bit is set, then clear it, otherwise, set it.
+     */
+    if (grid->bitmap[(y * bpr) + ((x * grid->bpp) >> 3)] & masks[di])
+      return bdf_grid_clear_pixel(grid, x, y);
+    else
+      return bdf_grid_set_pixel(grid, x, y, val);
+}
+
+/**************************************************************************
+ *
+ * Glyph grid bitmap transformation functions.
+ *
+ **************************************************************************/
+
+short
+_bdf_ceiling(double v)
+{
+    short val, neg;
+
+    val = neg = 0;
+    if (v < 0) {
+        neg = 1;
+        while (v < -1.0) {
+            val++;
+            v += 1.0;
+        }
+    } else if (v > 0) {
+        while (v > 1.0) {
+            val++;
+            v -= 1.0;
+        }
+        if (v > 0.0)
+          val++;
+    }
+    return (!neg) ? val : -val;
+}
+
+static int
+_bdf_rotate_selection(bdf_glyph_grid_t *grid, int mul90, short degrees)
+{
+    int rotated, byte;
+    short wd, ht, nx, ny, cx, cy, x, y, col;
+    short ox, oy, shiftx, shifty, si, di;
+    double dx, dy;
+    unsigned short bytes, bpr;
+    unsigned char *scratch, *masks;
+
+    rotated = 0;
+
+    /*
+     * Check to see if the number of rotations would have no affect by
+     * checking if the count is a multiple of 4 (mod 4 == 0).
+     */
+    if (grid == 0 || degrees == 0)
+      return rotated;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    bytes = grid->sel.bytes >> 1;
+    scratch = grid->sel.bitmap + bytes;
+    (void) memset((char *) scratch, 0, bytes);
+
+    cx = grid->sel.width >> 1;
+    cy = grid->sel.height >> 1;
+
+    wd = ht = MAX(grid->sel.width, grid->sel.height);
+    cx = cy = wd >> 1;
+
+    bpr = ((wd * grid->bpp) + 7) >> 3;
+
+    for (shiftx = shifty = y = 0; y < ht; y++) {
+        for (col = x = 0; x < wd; x++, col += grid->bpp) {
+            dx = (double) (x - cx);
+            dy = (double) (y - cy);
+            if (mul90) {
+                nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) -
+                                   (dy * _bdf_sin_tbl[degrees]));
+                ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) +
+                                   (dy * _bdf_cos_tbl[degrees]));
+            } else {
+                nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) -
+                                       (dy * _bdf_sin_tbl[degrees]));
+                ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) +
+                                       (dy * _bdf_cos_tbl[degrees]));
+            }
+
+            /*
+             * Wrap the coordinates around the edges if necessary.
+             */
+            if (nx < 0) {
+                shiftx = MIN(shiftx, nx);
+                nx += wd;
+            } else if (nx >= wd) {
+                ox = (nx - wd) + 1;
+                shiftx = MAX(shiftx, ox);
+                nx -= wd;
+            }
+            if (ny < 0) {
+                shifty = MIN(shifty, ny);
+                ny += ht;
+            } else if (ny >= ht) {
+                oy = (ny - ht) + 1;
+                shifty = MAX(shifty, oy);
+                ny -= ht;
+            }
+
+            si = (col & 7) / grid->bpp;
+            byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                rotated = 1;
+                nx *= grid->bpp;
+                di = (nx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                scratch[(ny * bpr) + (nx >> 3)] |= byte;
+            }
+        }
+    }
+
+    if (rotated) {
+        /*
+         * If a shift is required, then shift the scratch area back into
+         * the main bitmap.
+         */
+        if (shiftx || shifty) {
+            (void) memset((char *) grid->sel.bitmap, 0, bytes);
+            for (y = 0; y < ht; y++) {
+                for (col = x = 0; x < wd; x++, col += grid->bpp) {
+                    si = (col & 7) / grid->bpp;
+                    byte = scratch[(y * bpr) + (col >> 3)] & masks[si];
+                    if (byte) {
+                        nx = x - shiftx;
+                        ny = y - shifty;
+
+                        if (nx < 0)
+                          nx += wd;
+                        else if (nx >= wd)
+                          nx -= wd;
+                        if (ny < 0)
+                          ny += ht;
+                        else if (ny >= ht)
+                          ny -= ht;
+
+                        nx *= grid->bpp;
+                        di = (nx & 7) / grid->bpp;
+                        if (di < si)
+                          byte <<= (si - di) * grid->bpp;
+                        else if (di > si)
+                          byte >>= (di - si) * grid->bpp;
+                        grid->sel.bitmap[(ny * bpr) + (nx >> 3)] |= byte;
+                    }
+                }
+            }
+        } else
+          /*
+           * Copy the scratch buffer back to the main buffer.
+           */
+          (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes);
+
+        /*
+         * Determine the new selection width and height.
+         */
+        ox = oy = 0;
+        nx = ny = 16384;
+        for (y = 0; y < ht; y++) {
+            for (col = x = 0; x < wd; x++, col += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                if (grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]) {
+                    ox = MAX(ox, x);
+                    nx = MIN(nx, x);
+                    oy = MAX(oy, y);
+                    ny = MIN(ny, y);
+                }
+            }
+        }
+
+        /*
+         * Recalculate the center corrdinates so the selection will be
+         * positioned nicely once it is shifted to the upper left corner.
+         */
+        cx = grid->sel.width >> 1;
+        cy = grid->sel.height >> 1;
+
+        /*
+         * Set the new width and height.
+         */
+        grid->sel.width = (ox - nx) + 1;
+        grid->sel.height = (oy - ny) + 1;
+
+        /*
+         * Shift again to force the selection to the upper left corner.
+         */
+        if (nx || ny) {
+            (void) memset((char *) scratch, 0, bytes);
+            for (y = 0; y < ht; y++) {
+                for (col = x = 0; x < wd; x++, col += grid->bpp) {
+                    si = (col & 7) / grid->bpp;
+                    byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] &
+                        masks[si];
+                    if (byte) {
+                        oy = y - ny;
+                        ox = (x - nx) * grid->bpp;
+                        di = (ox & 7) / grid->bpp;
+                        if (di < si)
+                          byte <<= (si - di) * grid->bpp;
+                        else if (di > si)
+                          byte >>= (di - si) * grid->bpp;
+                        scratch[(oy * bpr) + (ox >> 3)] |= byte;
+                    }
+                }
+            }
+            (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes);
+        }
+
+        /*
+         * Determine the new top left coordinates from the center coordinates.
+         */
+        grid->sel.x = (grid->sel.x + cx) - (grid->sel.width >> 1);
+        grid->sel.y = (grid->sel.y + cy) - (grid->sel.height >> 1);
+
+        /*
+         * If the rotation caused the selection rectangle to overlap the edges
+         * of the grid, shift it so it is completely visible again.
+         */
+        if (grid->sel.x + grid->sel.width > grid->grid_width)
+          grid->sel.x -= (grid->sel.x + grid->sel.width) - grid->grid_width;
+        if (grid->sel.y + grid->sel.height > grid->grid_height)
+          grid->sel.y -= (grid->sel.y + grid->sel.height) - grid->grid_height;
+
+        /*
+         * Mark the grid as being modified.
+         */
+        grid->modified = 1;
+    }
+
+    return rotated;
+}
+
+static void
+_bdf_rotate_resize(bdf_glyph_grid_t *grid, int mul90, short degrees,
+                  int *resize)
+{
+    unsigned short wd, ht;
+    short cx, cy, x1, y1, x2, y2;
+    double dx1, dy1, dx2, dy2;
+    bdf_metrics_t metrics;
+
+    *resize = 0;
+    (void) memset((char *) &metrics, 0, sizeof(bdf_metrics_t));
+
+    metrics.x_offset = grid->font_bbx.x_offset;
+    metrics.width = grid->font_bbx.width;
+    metrics.ascent = grid->font_bbx.ascent;
+    metrics.descent = grid->font_bbx.descent;
+    metrics.height = grid->font_bbx.height;
+    metrics.y_offset = grid->font_bbx.y_offset;
+
+    cx = grid->glyph_x + (grid->glyph_bbx.width >> 1);
+    cy = grid->glyph_y + (grid->glyph_bbx.height >> 1);
+
+    /*
+     * Rotate the lower left and upper right corners and check for a potential
+     * resize.
+     */
+    x1 = grid->glyph_x;
+    y1 = grid->glyph_y + grid->glyph_bbx.height;
+    x2 = grid->glyph_x + grid->glyph_bbx.width;
+    y2 = grid->glyph_y;
+
+    dx1 = (double) (x1 - cx);
+    dy1 = (double) (y1 - cy);
+    dx2 = (double) (x2 - cx);
+    dy2 = (double) (y2 - cx);
+
+    if (mul90) {
+        x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) -
+                           (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) +
+                           (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) -
+                           (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) +
+                           (dy2 * _bdf_cos_tbl[degrees]));
+    } else {
+        x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) -
+                               (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) +
+                               (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) -
+                               (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) +
+                               (dy2 * _bdf_cos_tbl[degrees]));
+    }
+
+    wd = MYABS(x2 - x1);
+    ht = MYABS(y2 - y1);
+    if (wd > metrics.width) {
+        metrics.width += wd - grid->font_bbx.width;
+        *resize = 1;
+    }
+    if (ht > metrics.height) {
+        metrics.ascent += ht - grid->font_bbx.height;
+        metrics.height += ht - grid->font_bbx.height;
+        *resize = 1;
+    }
+
+    /*
+     * Rotate the upper left and lower right corners and check for a potential
+     * resize.
+     */
+    x1 = grid->glyph_x;
+    y1 = grid->glyph_y;
+    x2 = grid->glyph_x + grid->glyph_bbx.width;
+    y2 = grid->glyph_y + grid->glyph_bbx.height;
+
+    dx1 = (double) (x1 - cx);
+    dy1 = (double) (y1 - cy);
+    dx2 = (double) (x2 - cx);
+    dy2 = (double) (y2 - cx);
+
+    if (mul90) {
+        x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) -
+                           (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) +
+                           (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) -
+                           (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) +
+                           (dy2 * _bdf_cos_tbl[degrees]));
+    } else {
+        x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) -
+                               (dy1 * _bdf_sin_tbl[degrees]));
+        y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) +
+                               (dy1 * _bdf_cos_tbl[degrees]));
+        x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) -
+                               (dy2 * _bdf_sin_tbl[degrees]));
+        y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) +
+                               (dy2 * _bdf_cos_tbl[degrees]));
+    }
+
+    wd = MYABS(x2 - x1);
+    ht = MYABS(y2 - y1);
+    if (wd > metrics.width) {
+        metrics.width += wd - grid->font_bbx.width;
+        *resize = 1;
+    }
+    if (ht > metrics.height) {
+        metrics.ascent += ht - grid->font_bbx.height;
+        metrics.height += ht - grid->font_bbx.height;
+        *resize = 1;
+    }
+
+    if (*resize)
+      (void) bdf_grid_resize(grid, &metrics);
+}
+
+static void
+_bdf_shear_resize(bdf_glyph_grid_t *grid, short degrees, int neg, int *resize)
+{
+    unsigned short wd;
+    short x1, y1, x2, y2;
+    bdf_metrics_t metrics;
+
+    *resize = 0;
+    (void) memset((char *) &metrics, 0, sizeof(bdf_metrics_t));
+
+    metrics.x_offset = grid->font_bbx.x_offset;
+    metrics.width = grid->font_bbx.width;
+    metrics.ascent = grid->font_bbx.ascent;
+    metrics.descent = grid->font_bbx.descent;
+    metrics.height = grid->font_bbx.height;
+    metrics.y_offset = grid->font_bbx.y_offset;
+
+    /*
+     * Shear the lower left and upper right corners and check for a potential
+     * resize.
+     */
+    x1 = 0;
+    y1 = grid->glyph_bbx.height;
+    x2 = grid->glyph_bbx.width;
+    y2 = 0;
+
+    if (neg) {
+        x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]);
+    } else {
+        x1 += (short) ((double) (grid->glyph_bbx.height - y1) *
+                       _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) (grid->glyph_bbx.height - y2) *
+                       _bdf_tan_tbl[degrees]);
+    }
+
+    wd = MYABS(x2 - x1);
+    if (wd > metrics.width) {
+        metrics.width += wd - grid->font_bbx.width;
+        *resize = 1;
+    }
+
+    /*
+     * Shear the upper left and lower right corners and check for a potential
+     * resize.
+     */
+    x1 = 0;
+    y1 = 0;
+    x2 = grid->glyph_bbx.width;
+    y2 = grid->glyph_bbx.height;
+
+    if (neg) {
+        x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]);
+    } else {
+        x1 += (short) ((double) (grid->glyph_bbx.height - y1) *
+                       _bdf_tan_tbl[degrees]);
+        x2 += (short) ((double) (grid->glyph_bbx.height - y2) *
+                       _bdf_tan_tbl[degrees]);
+    }
+
+    wd = MYABS(x2 - x1);
+    if (wd > metrics.width) {
+        metrics.width += wd - grid->font_bbx.width;
+        *resize = 1;
+    }
+
+    if (*resize)
+      (void) bdf_grid_resize(grid, &metrics);
+}
+
+/*
+ * Rotate the bitmap in the grid by some number of degrees.
+ */
+int
+bdf_grid_rotate(bdf_glyph_grid_t *grid, short degrees, int *resize)
+{
+    int rotated, mul90;
+    short nx, ny, cx, cy, x, y, wd, ht;
+    short ox, oy, gx, gy, shiftx, shifty;
+    unsigned short si, di, col, byte;
+    double dx, dy;
+    unsigned short bytes, bpr;
+    unsigned char *scratch, *masks;
+
+    rotated = 0;
+
+    /*
+     * Make sure the number of degrees is between 0 and 359 and adjusted to a
+     * positive number of degrees if necessary.
+     */
+    while (degrees < 0)
+      degrees += 360;
+    while (degrees >= 360)
+      degrees -= 360;
+
+    if (grid == 0 || degrees == 0 ||
+        (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0))
+      return rotated;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    mul90 = ((degrees % 90) == 0) ? 1 : 0;
+
+    /*
+     * Force the grid to resize if the rotation requires it.
+     */
+    _bdf_rotate_resize(grid, mul90, degrees, resize);
+
+    if (grid->sel.width != 0 && grid->sel.height != 0)
+      return _bdf_rotate_selection(grid, mul90, degrees);
+
+    /*
+     * Halve the byte count in the grid for later use.
+     */
+    bytes = grid->bytes >> 1;
+
+    /*
+     * Point at the scratch buffer area and initialize it.
+     */
+    scratch = grid->bitmap + bytes;
+    (void) memset((char *) scratch, 0, bytes);
+
+    /*
+     * Determine the bytes per row.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+
+    /*
+     * Determine the center coordinates of the glyph bitmap rectangle.
+     */
+    cx = grid->glyph_x + (grid->glyph_bbx.width >> 1);
+    cy = grid->glyph_y + (grid->glyph_bbx.height >> 1);
+
+    /*
+     * Only run over the rectangle containing the glyph itself.
+     */
+    gx = grid->glyph_x;
+    gy = grid->glyph_y;
+
+    wd = gx + grid->glyph_bbx.width;
+    ht = gy + grid->glyph_bbx.height;
+
+    /*
+     * Initialize the adjustment counts used if the bitmap
+     * wraps around the edge.
+     */
+    shiftx = shifty = 0;
+
+    for (y = gy; y < ht; y++) {
+        col = gx * grid->bpp;
+        for (x = gx; x < wd; x++, col += grid->bpp) {
+
+            /*
+             * Rotate the point.
+             */
+            dx = (double) (x - cx);
+            dy = (double) (y - cy);
+            if (mul90) {
+                nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) -
+                                   (dy * _bdf_sin_tbl[degrees]));
+                ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) +
+                                   (dy * _bdf_cos_tbl[degrees]));
+            } else {
+                nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) -
+                                       (dy * _bdf_sin_tbl[degrees]));
+                ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) +
+                                       (dy * _bdf_cos_tbl[degrees]));
+            }
+
+            /*
+             * Wrap the coordinates around the edges if necessary.
+             */
+            if (nx < 0) {
+                shiftx = MIN(shiftx, nx);
+                nx += grid->grid_width;
+            } else if (nx >= grid->grid_width) {
+                ox = (nx - grid->grid_width) + 1;
+                shiftx = MAX(shiftx, ox);
+                nx -= grid->grid_width;
+            }
+            if (ny < 0) {
+                shifty = MIN(shifty, ny);
+                ny += grid->grid_height;
+            } else if (ny >= grid->grid_height) {
+                oy = (ny - grid->grid_height) + 1;
+                shifty = MAX(shifty, oy);
+                ny -= grid->grid_height;
+            }
+
+            si = (col & 7) / grid->bpp;
+            byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                rotated = 1;
+                nx *= grid->bpp;
+                di = (nx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                scratch[(ny * bpr) + (nx >> 3)] |= byte;
+            }
+        }
+    }
+
+    if (rotated) {
+        /*
+         * If a shift is required, then shift the scratch area back into
+         * the main bitmap.
+         */
+        if (shiftx || shifty) {
+            (void) memset((char *) grid->bitmap, 0, bytes);
+            for (y = 0; y < grid->grid_height; y++) {
+                for (col = x = 0; x < grid->grid_width;
+                     x++, col += grid->bpp) {
+                    si = (col & 7) / grid->bpp;
+                    byte = scratch[(y * bpr) + (col >> 3)] & masks[si];
+                    if (byte) {
+                        nx = x - shiftx;
+                        ny = y - shifty;
+
+                        if (nx < 0)
+                          nx += grid->grid_width;
+                        else if (nx >= grid->grid_width)
+                          nx -= grid->grid_width;
+                        if (ny < 0)
+                          ny += grid->grid_height;
+                        else if (ny >= grid->grid_height)
+                          ny -= grid->grid_height;
+
+                        nx *= grid->bpp;
+                        di = (nx & 7) / grid->bpp;
+                        if (di < si)
+                          byte <<= (si - di) * grid->bpp;
+                        else if (di > si)
+                          byte >>= (di - si) * grid->bpp;
+                        grid->bitmap[(ny * bpr) + (nx >> 3)] |= byte;
+                    }
+                }
+            }
+        } else
+          /*
+           * Copy the scratch buffer back to the main buffer.
+           */
+          (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes);
+
+        /*
+         * Determine the new glyph bounding box and the top left coordinates.
+         */
+        ox = oy = 0;
+        nx = ny = 16384;
+        for (y = 0; y < grid->grid_height; y++) {
+            for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                if (grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]) {
+                    nx = MIN(nx, x);
+                    ox = MAX(ox, x);
+                    ny = MIN(ny, y);
+                    oy = MAX(oy, y);
+                }
+            }
+        }
+
+        /*
+         * Set the new top left corrdinates.
+         */
+        grid->glyph_x = nx;
+        grid->glyph_y = ny;
+
+        /*
+         * Set the new glyph bounding box.
+         */
+        grid->glyph_bbx.width = (ox - nx) + 1;
+        grid->glyph_bbx.x_offset = nx - grid->base_x;
+        grid->glyph_bbx.height = (oy - ny) + 1;
+        grid->glyph_bbx.ascent = grid->base_y - ny;
+        grid->glyph_bbx.descent = grid->glyph_bbx.height -
+            grid->glyph_bbx.ascent;
+        grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent;
+
+        /*
+         * Mark the grid as being modified.
+         */
+        grid->modified = 1;
+    }
+
+    return rotated;
+}
+
+int
+bdf_grid_shear(bdf_glyph_grid_t *grid, short degrees, int *resize)
+{
+    int sheared, neg;
+    short cx, cy, wd, ht, gx, gy, x, y;
+    short nx, ox, ny, oy, shiftx, shifty;
+    unsigned short bytes, bpr, si, di, col, byte;
+    unsigned char *scratch, *masks;
+
+    sheared = 0;
+
+    if (degrees == 0 || degrees < -45 || degrees > 45 || grid == 0 ||
+        (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0))
+      return sheared;
+
+    if ((neg = (degrees < 0)))
+      degrees = -degrees;
+
+    /*
+     * Check to see if the grid needs to be resized to hold the sheared glyph.
+     */
+    _bdf_shear_resize(grid, degrees, neg, resize);
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Halve the byte count in the grid for later use.
+     */
+    bytes = grid->bytes >> 1;
+
+    /*
+     * Point at the scratch buffer area and initialize it.
+     */
+    scratch = grid->bitmap + bytes;
+    (void) memset((char *) scratch, 0, bytes);
+
+    /*
+     * Determine the bytes per row.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+
+    /*
+     * Determine the center coordinates of the glyph bitmap rectangle.
+     */
+    gx = grid->glyph_x;
+    gy = grid->glyph_y;
+
+    cx = gx + (grid->glyph_bbx.width >> 1);
+    cy = gy + (grid->glyph_bbx.height >> 1);
+
+    wd = gx + grid->glyph_bbx.width;
+    ht = gy + grid->glyph_bbx.height;
+
+    shiftx = shifty = 0;
+    for (y = gy; y < ht; y++) {
+        col = gx * grid->bpp;
+        for (x = gx; x < wd; x++, col += grid->bpp) {
+            ny = y;
+            if (neg)
+              nx = x + (short) ((double) y * _bdf_tan_tbl[degrees]);
+            else
+              nx = x + (short) ((double) (gy + (ht - y)) *
+                                _bdf_tan_tbl[degrees]);
+
+            if (nx < 0) {
+                shiftx = MIN(shiftx, nx);
+                nx += grid->grid_width;
+            } else if (nx >= grid->grid_width) {
+                ox = (nx - grid->grid_width) + 1;
+                shiftx = MAX(shiftx, ox);
+                nx -= grid->grid_width;
+            }
+            if (ny < 0) {
+                shifty = MIN(shifty, ny);
+                ny += grid->grid_height;
+            } else if (ny >= grid->grid_height) {
+                oy = (ny - grid->grid_height) + 1;
+                shifty = MAX(shifty, oy);
+                ny -= grid->grid_height;
+            }
+
+            si = (col & 7) / grid->bpp;
+            byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                sheared = 1;
+                nx *= grid->bpp;
+                di = (nx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                scratch[(y * bpr) + (nx >> 3)] |= byte;
+            }
+        }
+    }
+
+    if (sheared) {
+        /*
+         * If a shift is required, then shift the scratch area back into
+         * the main bitmap.
+         */
+        if (shiftx || shifty) {
+            (void) memset((char *) grid->bitmap, 0, bytes);
+            for (y = 0; y < grid->grid_height; y++) {
+                for (col = x = 0; x < grid->grid_width;
+                     x++, col += grid->bpp) {
+                    si = (col & 7) / grid->bpp;
+                    byte = scratch[(y * bpr) + (col >> 3)] & masks[si];
+                    if (byte) {
+                        nx = x - shiftx;
+                        ny = y - shifty;
+
+                        if (nx < 0)
+                          nx += grid->grid_width;
+                        else if (nx >= grid->grid_width)
+                          nx -= grid->grid_width;
+                        if (ny < 0)
+                          ny += grid->grid_height;
+                        else if (ny >= grid->grid_height)
+                          ny -= grid->grid_height;
+
+                        nx *= grid->bpp;
+                        di = (nx & 7) / grid->bpp;
+                        if (di < si)
+                          byte <<= (si - di) * grid->bpp;
+                        else if (di > si)
+                          byte >>= (di - si) * grid->bpp;
+                        grid->bitmap[(ny * bpr) + (nx >> 3)] |= byte;
+                    }
+                }
+            }
+        } else
+          /*
+           * Copy the scratch buffer back to the main buffer.
+           */
+          (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes);
+
+        ox = oy = 0;
+        nx = ny = 16384;
+        for (y = 0; y < grid->grid_height; y++) {
+            for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                if (grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]) {
+                    ox = MAX(ox, x);
+                    nx = MIN(nx, x);
+                    oy = MAX(oy, y);
+                    ny = MIN(ny, y);
+                }
+            }
+        }
+
+        /*
+         * Set the new top left corrdinates.
+         */
+        grid->glyph_x = nx;
+        grid->glyph_y = ny;
+
+        /*
+         * Set the new glyph bounding box.
+         */
+        grid->glyph_bbx.width = (ox - nx) + 1;
+        grid->glyph_bbx.x_offset = nx - grid->base_x;
+        grid->glyph_bbx.height = (oy - ny) + 1;
+        grid->glyph_bbx.ascent = grid->base_y - ny;
+        grid->glyph_bbx.descent = grid->glyph_bbx.height -
+            grid->glyph_bbx.ascent;
+        grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent;
+
+        /*
+         * Mark the grid as being modified.
+         */
+        grid->modified = 1;
+    }
+                 
+    return sheared;
+}
+
+int
+bdf_grid_embolden(bdf_glyph_grid_t *grid)
+{
+    int done;
+    short wd, ht, gx, gy, x, y;
+    unsigned short b1, b2, bpr, si, di, col;
+    unsigned char *masks;
+
+    done = 0;
+
+    if (grid == 0 ||
+        (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0))
+      return done;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    /*
+     * Determine the bytes per row.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+
+    gx = grid->glyph_x;
+    gy = grid->glyph_y;
+
+    wd = gx + grid->glyph_bbx.width;
+    ht = gy + grid->glyph_bbx.height;
+
+    if (grid->spacing == BDF_PROPORTIONAL ||
+        (grid->spacing == BDF_MONOWIDTH &&
+         grid->glyph_bbx.width < grid->font_bbx.width))
+      /*
+       * Only allow horizontal expansion in the cases that make sense.
+       */
+      wd++;
+
+    for (y = gy; y < ht; y++) {
+        col = (wd - 1) * grid->bpp;
+        for (x = wd - 1; x > gx; x--, col -= grid->bpp) {
+            si = (col & 7) / grid->bpp;
+            di = ((col - grid->bpp) & 7) / grid->bpp;
+            b1 = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            b2 = grid->bitmap[(y * bpr) + ((col - grid->bpp) >> 3)] &
+                masks[di];
+            if (!b1 && b2) {
+                if (di < si)
+                  b2 >>= (si - di) * grid->bpp;
+                else if (di > si)
+                  b2 <<= (di - si) * grid->bpp;
+                grid->bitmap[(y * bpr) + (col >> 3)] |= b2;
+                /*
+                 * Mark the grid as being modified.
+                 */
+                done = grid->modified = 1;
+            }
+        }
+    }
+
+    /*
+     * Adjust the glyph width so it will be reflected when the glyph is stored
+     * back in the font.
+     */
+    grid->glyph_bbx.width = wd - gx;
+
+    return done;
+}
+
+/**************************************************************************
+ *
+ * Glyph grid selection functions.
+ *
+ **************************************************************************/
+
+int
+bdf_has_selection(bdf_glyph_grid_t *grid, short *x, short *y,
+                  short *width, short *height)
+{
+    if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0))
+      return 0;
+
+    if (x != 0)
+      *x = grid->sel.x;
+    if (y != 0)
+      *y = grid->sel.y;
+    if (width != 0)
+      *width = grid->sel.width;
+    if (height != 0)
+      *height = grid->sel.height;
+
+    return 1;
+}
+
+/*
+ * Select a rectangle on the grid.
+ */
+void
+bdf_set_selection(bdf_glyph_grid_t *grid, short x, short y,
+                  short width, short height)
+{
+    short nx, ny, wd, ht, ssize, dx, dy, col;
+    unsigned short bytes, bpr, sbpr, si, di, byte;
+    unsigned char *masks;
+
+    if (grid == 0)
+      return;
+
+    /*
+     * Make sure the specified rectangle is within reasonable bounds.
+     */
+    if (x < 0 || x >= grid->grid_width)
+      x = 0;
+    if (y < 0 || y >= grid->grid_height)
+      y = 0;
+
+    if (x + width > grid->grid_width)
+      width = (x + width) - grid->grid_width;
+    if (y + height > grid->grid_height)
+      height = (y + height) - grid->grid_height;
+
+    grid->sel.x = x;
+    grid->sel.y = y;
+    grid->sel.width = width;
+    grid->sel.height = height;
+
+    /*
+     * Allocate enough space to represent a square the size of the largest
+     * of the width and height of the selection.  This allows rotation and
+     * flipping of the selected bitmap.
+     */
+    ssize = MAX(width, height);
+
+    bytes = ((((ssize * grid->bpp) + 7) >> 3) * ssize) << 1;
+
+    /*
+     * If the selection is being removed (width and height are 0), then simply
+     * return.
+     */
+    if (bytes == 0)
+      return;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    if (bytes > grid->sel.bytes) {
+        if (grid->sel.bytes == 0)
+          grid->sel.bitmap = (unsigned char *) malloc(bytes);
+        else
+          grid->sel.bitmap = (unsigned char *)
+              realloc((char *) grid->sel.bitmap, bytes);
+        grid->sel.bytes = bytes;
+    } else
+      bytes = grid->sel.bytes;
+
+    /*
+     * Initialize the selection bitmap and copy the selected bits to it.
+     */
+    (void) memset((char *) grid->sel.bitmap, 0, bytes);
+
+    wd = x + width;
+    ht = y + height;
+
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3;
+
+    for (ny = 0, dy = y; dy < ht; dy++, ny++) {
+        col = x * grid->bpp;
+        for (nx = 0, dx = x; dx < wd;
+             dx++, nx += grid->bpp, col += grid->bpp) {
+            si = (col & 7) / grid->bpp;
+            byte = grid->bitmap[(dy * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                di = (nx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                grid->sel.bitmap[(ny * sbpr) + (nx >> 3)] |= byte;
+            }
+        }
+    }
+}
+
+/*
+ * Detach a selection in preparation for moving it.  What it does is clear the
+ * bits set in the selection from the main grid.  Again, this is only used for
+ * move operations.
+ */
+void
+bdf_detach_selection(bdf_glyph_grid_t *grid)
+{
+    short sx, sy, x, y, wd, ht, dx;
+    unsigned short bpr, sbpr, si, di, byte;
+    unsigned char *masks;
+
+    if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0))
+      return;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3;
+
+    wd = grid->sel.x + grid->sel.width;
+    ht = grid->sel.y + grid->sel.height;
+
+    for (sy = 0, y = grid->sel.y; y < ht; y++, sy++) {
+        for (sx = 0, x = grid->sel.x; x < wd; x++, sx += grid->bpp) {
+            si = (sx & 7) / grid->bpp;
+            byte = grid->sel.bitmap[(sy * sbpr) + (sx >> 3)] & masks[si];
+            if (byte) {
+                dx = x * grid->bpp;
+                di = (dx & 7) / grid->bpp;
+                grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di];
+            }
+        }
+    }
+
+    /*
+     * Crop the new image to determine the new bounds with the selection.
+     */
+    (void) bdf_grid_crop(grid, 1);
+}
+
+void
+bdf_attach_selection(bdf_glyph_grid_t *grid)
+{
+    short sx, sy, x, y, wd, ht;
+    unsigned short bpr, sbpr, dx, di, si, byte;
+    unsigned char *masks;
+
+    if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0))
+      return;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3;
+
+    wd = grid->sel.x + grid->sel.width;
+    ht = grid->sel.y + grid->sel.height;
+
+    for (sy = 0, y = grid->sel.y; y < ht; y++, sy++) {
+        for (sx = 0, x = grid->sel.x; x < wd; x++, sx += grid->bpp) {
+            si = (sx & 7) / grid->bpp;
+            byte = grid->sel.bitmap[(sy * sbpr) + (sx >> 3)] & masks[si];
+            if (byte) {
+                dx = x * grid->bpp;
+                di = (dx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                grid->bitmap[(y * bpr) + (dx >> 3)] |= byte;
+            }
+        }
+    }
+
+    /*
+     * Crop the new image to determine the new bounds with the selection.
+     */
+    (void) bdf_grid_crop(grid, 1);
+}
+
+/*
+ * Indicate the selection no longer exists by setting the width and height to
+ * 0.
+ */
+void
+bdf_lose_selection(bdf_glyph_grid_t *grid)
+{
+    if (grid == 0)
+      return;
+    grid->sel.width = grid->sel.height = 0;
+}
+
+/*
+ * Delete the selection by first detaching it which will erase the rectangle
+ * on the grid and then losing the selection.
+ */
+void
+bdf_delete_selection(bdf_glyph_grid_t *grid)
+{
+    bdf_detach_selection(grid);
+    bdf_lose_selection(grid);
+}
+
+/*
+ * Check to see if a coordinate pair is in the selected region.
+ */
+int
+bdf_in_selection(bdf_glyph_grid_t *grid, short x, short y, short *set)
+{
+    short wd, ht;
+    unsigned short bpr, si, di, byte;
+    unsigned char *masks;
+
+    if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0))
+      return 0;
+
+    di = 0;
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    bpr = ((grid->sel.width * grid->bpp) + 7) >> 3;
+
+    wd = grid->sel.x + grid->sel.width;
+    ht = grid->sel.y + grid->sel.height;
+
+    if ((x >= grid->sel.x && x < wd) && (y >= grid->sel.y && y < ht)) {
+        if (set) {
+            /*
+             * Adjust the byte back to an index value.
+             */
+            x *= grid->bpp;
+            si = (x & 7) / grid->bpp;
+            byte = grid->sel.bitmap[(y * bpr) + (x >> 3)] & masks[si];
+            if (di > si)
+              byte >>= (di - si) * grid->bpp;
+            *set = byte;
+        }
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+bdf_grid_shift(bdf_glyph_grid_t *grid, short xcount, short ycount)
+{
+    int sel, delta;
+    short xdir, ydir, x, y, wd, ht, dx, dy, nx, ny;
+    unsigned short bytes, bpr, si, di, byte, col;
+    unsigned char *scratch, *masks;
+
+    if (grid == 0)
+      return 0;
+
+    xdir = ydir = 1;
+    if (xcount < 0) {
+        xdir = -1;
+        xcount = -xcount;
+    }
+
+    if (ycount < 0) {
+        ydir = -1;
+        ycount = -ycount;
+    }
+
+    /*
+     * Adjust the shift counts if they are larger than they should be.
+     */
+    if (xcount > grid->grid_width)
+      xcount -= grid->grid_width;
+    if (ycount > grid->grid_height)
+      ycount -= grid->grid_height;
+
+    /*
+     * Adjust the counts to limit the shift to the boundaries of the grid.
+     */
+    if (grid->sel.width != 0 && grid->sel.height != 0) {
+        /*
+         * The selection is being shifted.
+         */
+        x = grid->sel.x;
+        y = grid->sel.y;
+        wd = grid->sel.width;
+        ht = grid->sel.height;
+        sel = 1;
+    } else {
+        x = grid->glyph_x;
+        y = grid->glyph_y;
+        wd = grid->glyph_bbx.width;
+        ht = grid->glyph_bbx.height;
+        sel = 0;
+    }
+
+    /*
+     * If the width and height are 0, then simply return, because there
+     * is nothing to shift.
+     */
+    if (wd == 0 && ht == 0)
+      return 0;
+
+    if (xdir == 1 && x + wd + xcount > grid->grid_width)
+      xcount = grid->grid_width - (x + wd);
+    else if (xdir == -1 && xcount > x)
+      xcount = x;
+
+    if (ydir == 1 && y + ht + ycount > grid->grid_height)
+      ycount = grid->grid_height - (y + ht);
+    else if (ydir == -1 && ycount > y)
+      ycount = y;
+
+    if (xcount == 0 && ycount == 0)
+      return 0;
+
+    /*
+     * If the selection is the one being shifted, adjust the X and Y
+     * coordinates and adjust the glyph metrics.
+     */
+    if (sel) {
+        /*
+         * Determine the actual ink bounds of the selection so the
+         * glyph metrics can be adjusted if necessary.
+         */
+        if (_bdf_grid_ink_bounds(grid, &x, &y, &wd, &ht)) {
+            /*
+             * Have to adjust the glyph metrics.
+             */
+            x += xdir * xcount;
+            y += ydir * ycount;
+            if (x < grid->glyph_x) {
+                delta = grid->glyph_x - x;
+                grid->glyph_bbx.width += delta;
+                grid->glyph_bbx.x_offset -= delta;
+                if (grid->spacing == BDF_PROPORTIONAL)
+                  grid->dwidth += delta;
+                grid->glyph_x -= delta;
+            } else if (x >= grid->glyph_x + grid->glyph_bbx.width) {
+                delta = x - (grid->glyph_x + grid->glyph_bbx.width);
+                grid->glyph_bbx.width += delta;
+                if (grid->spacing == BDF_PROPORTIONAL)
+                  grid->dwidth += delta;
+            }
+
+            if (y < grid->glyph_y) {
+                delta = grid->glyph_y - y;
+                grid->glyph_bbx.height += delta;
+                grid->glyph_bbx.ascent += delta;
+                grid->glyph_y -= delta;
+            } else if (y + ht >= grid->glyph_y + grid->glyph_bbx.height) {
+                delta = (y + ht) - (grid->glyph_y + grid->glyph_bbx.height);
+                grid->glyph_bbx.height += delta;
+                grid->glyph_bbx.y_offset -= delta;
+                grid->glyph_bbx.descent += delta;
+            }
+
+            grid->modified = 1;
+        }
+
+        /*
+         * Adjust the top-left coordinate of the selection rectangle.
+         */
+        grid->sel.x += xdir * xcount;
+        grid->sel.y += ydir * ycount;
+
+        return 1;
+    }
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    /*
+     * The glyph itself is being shifted.
+     */
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    bytes = grid->bytes >> 1;
+    scratch = grid->bitmap + bytes;
+    (void) memset((char *) scratch, 0, bytes);
+
+    /*
+     * Shift just the glyph rectangle to keep things fast.
+     */
+    wd += x;
+    ht += y;
+    for (dy = y; dy < ht; dy++) {
+        col = x * grid->bpp;
+        for (dx = x; dx < wd; dx++, col += grid->bpp) {
+            si = (col & 7) / grid->bpp;
+            byte = grid->bitmap[(dy * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                nx = dx + (xdir * xcount);
+                ny = dy + (ydir * ycount);
+                nx *= grid->bpp;
+                di = (nx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                scratch[(ny * bpr) + (nx >> 3)] |= byte;
+            }
+        }
+    }
+
+    /*
+     * Copy the scratch buffer back to the main buffer.
+     */
+    (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes);
+
+    /*
+     * Adjust the top-left coordinate of the glyph rectangle.
+     */
+    grid->glyph_x += xdir * xcount;
+    grid->glyph_y += ydir * ycount;
+
+    /*
+     * Adjust the glyph offsets relative to the baseline coordinates.
+     */
+    grid->glyph_bbx.x_offset = grid->glyph_x - grid->base_x;
+    grid->glyph_bbx.y_offset = grid->base_y -
+        (grid->glyph_y + grid->glyph_bbx.height);
+
+    /*
+     * Adjust the glyph ascent and descent.
+     */
+    grid->glyph_bbx.ascent = grid->base_y - grid->glyph_y;
+    grid->glyph_bbx.descent = (grid->glyph_y + grid->glyph_bbx.height) -
+        grid->base_y;
+
+    /*
+     * Mark the grid as being modified.
+     */
+    grid->modified = 1;
+
+    return 1;
+}
+
+
+int
+bdf_grid_flip(bdf_glyph_grid_t *grid, short dir)
+{
+    int flipped, sel, delta;
+    short dx, dy, x, y, nx, ny, wd, ht;
+    unsigned short bytes, bpr, si, di, col, colx, byte;
+    unsigned char *bmap, *scratch, *masks;
+
+    flipped = 0;
+
+    if (grid == 0)
+      return flipped;
+
+    if (grid->sel.width != 0 && grid->sel.height != 0) {
+        sel = 1;
+        x = y = 0;
+        wd = grid->sel.width;
+        ht = grid->sel.height;
+        bpr = ((wd * grid->bpp) + 7) >> 3;
+        bytes = grid->sel.bytes >> 1;
+        bmap = grid->sel.bitmap;
+    } else {
+        sel = 0;
+        x = grid->glyph_x;
+        y = grid->glyph_y;
+        wd = grid->glyph_bbx.width;
+        ht = grid->glyph_bbx.height;
+        bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+        bytes = grid->bytes >> 1;
+        bmap = grid->bitmap;
+    }
+
+    /*
+     * If the width or height is 0, don't do anything.
+     */
+    if (wd == 0|| ht == 0)
+      return flipped;
+
+    nx = 0;
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    /*
+     * Set and initialize the scratch area.
+     */
+    scratch = bmap + bytes;
+    (void) memset((char *) scratch, 0, bytes);
+
+    wd += x;
+    ht += y;
+
+    if (dir < 0) {
+        /*
+         * Flip horizontally.
+         */
+        for (dy = y; dy < ht; dy++) {
+            col = x * grid->bpp;
+            for (nx = wd - 1, dx = x; dx < wd; dx++, nx--, col += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                byte = bmap[(dy * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    flipped = 1;
+                    colx = nx * grid->bpp;
+                    di = (colx & 7) / grid->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * grid->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * grid->bpp;
+                    scratch[(dy * bpr) + (colx >> 3)] |= byte;
+                }
+            }
+        }
+        if (flipped) {
+            if (sel)
+              grid->sel.x += nx + 1;
+            else {
+                grid->glyph_x = nx + 1;
+                grid->glyph_bbx.x_offset = grid->glyph_x - grid->base_x;
+            }
+        }
+    } else {
+        /*
+         * Flip vertically.
+         */
+        for (ny = ht - 1, dy = y; dy < ht; dy++, ny--) {
+            col = x * grid->bpp;
+            for (dx = x; dx < wd; dx++, col += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                byte = bmap[(dy * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    flipped = 1;
+                    scratch[(ny * bpr) + (col >> 3)] |= byte;
+                }
+            }
+        }
+        if (flipped) {
+            if (sel)
+              grid->sel.y += ny + 1;
+            else {
+                grid->glyph_y = ny + 1;
+                grid->glyph_bbx.y_offset = grid->base_y -
+                    (grid->glyph_y + grid->glyph_bbx.height);
+                grid->glyph_bbx.ascent = grid->base_y - grid->glyph_y;
+                grid->glyph_bbx.descent =
+                    (grid->glyph_y + grid->glyph_bbx.height) - grid->base_y;
+            }
+        }
+    }
+
+    if (flipped) {
+        /*
+         * Copy the scratch area back to the working area.
+         */
+        if (sel)
+          (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes);
+        else
+          (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes);
+
+        if (sel) {
+            /*
+             * Check to see if flipping the selection caused the glyph metrics
+             * to change.
+             */
+            if (_bdf_grid_ink_bounds(grid, &x, &y, &wd, &ht)) {
+                if (x < grid->glyph_x) {
+                    delta = grid->glyph_x - x;
+                    grid->glyph_bbx.width += delta;
+                    grid->glyph_bbx.x_offset -= delta;
+                    grid->glyph_x -= delta;
+                    if (grid->spacing == BDF_PROPORTIONAL)
+                      grid->dwidth += delta;
+                } else if (x >= grid->glyph_x + grid->glyph_bbx.width) {
+                    delta = x - (grid->glyph_x + grid->glyph_bbx.width);
+                    grid->glyph_bbx.width += delta;
+                    if (grid->spacing == BDF_PROPORTIONAL)
+                      grid->dwidth += delta;
+                }
+
+                if (y < grid->glyph_y) {
+                    delta = grid->glyph_y - y;
+                    grid->glyph_bbx.height += delta;
+                    grid->glyph_bbx.ascent += delta;
+                    grid->glyph_y -= delta;
+                } else if (y >= grid->glyph_y + grid->glyph_bbx.height) {
+                    delta = y - (grid->glyph_y + grid->glyph_bbx.height);
+                    grid->glyph_bbx.height += delta;
+                    grid->glyph_bbx.y_offset -= delta;
+                    grid->glyph_bbx.descent += delta;
+                }
+            }
+        }
+
+        /*
+         * Mark the grid as being modified.
+         */
+        grid->modified = 1;
+    }
+
+    return flipped;
+}
+
+void
+bdf_grid_origin(bdf_glyph_grid_t *grid, short *x, short *y)
+{
+    if (grid == 0)
+      return;
+
+    *x = grid->base_x;
+    *y = grid->base_y;
+}
+
+bdf_glyph_t *
+bdf_grid_glyph(bdf_glyph_grid_t *grid)
+{
+    int len;
+    short x, y, nx, ny, wd, ht, gx, gy;
+    unsigned short bpr, nbpr, si, di, col, byte;
+    bdf_glyph_t *glyph;
+    unsigned char *masks;
+    double ps, dw, rx;
+
+    if (grid == 0)
+      return 0;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    /*
+     * Create the new glyph.
+     */
+    glyph = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t));
+    (void) memset((char *) glyph, 0, sizeof(bdf_glyph_t));
+
+    gx = grid->glyph_x;
+    gy = grid->glyph_y;
+
+    /*
+     * Copy the bounding box.
+     */
+    (void) memcpy((char *) &glyph->bbx, (char *) &grid->glyph_bbx,
+                  sizeof(bdf_bbx_t));
+
+    /*
+     * If the font has character-cell spacing, then make sure the bitmap is
+     * cropped to fit within the bounds of the font bbx.
+     */
+    if (grid->spacing == BDF_CHARCELL) {
+        if (gx < grid->base_x) {
+            glyph->bbx.x_offset = 0;
+            glyph->bbx.width -= grid->base_x - gx;
+            gx += grid->base_x - gx;
+        }
+        if (glyph->bbx.width > grid->font_bbx.width)
+          glyph->bbx.width -= glyph->bbx.width - grid->font_bbx.width;
+    }
+
+    /*
+     * Set up its bitmap.
+     */
+    nbpr = ((glyph->bbx.width * grid->bpp) + 7) >> 3;
+    glyph->bytes = nbpr * glyph->bbx.height;
+    glyph->bitmap = (unsigned char *) malloc(glyph->bytes);
+    (void) memset((char *) glyph->bitmap, 0, glyph->bytes);
+
+    /*
+     * Set the other values.
+     */
+    if (grid->name != 0) {
+        len = strlen(grid->name) + 1;
+        glyph->name = (char *) malloc(len);
+        (void) memcpy(glyph->name, grid->name, len);
+    }
+    glyph->encoding = grid->encoding;
+    glyph->dwidth = grid->dwidth;
+
+    /*
+     * Reset the glyph SWIDTH value.
+     */
+    ps = (double) grid->point_size;
+    rx = (double) grid->resolution_x;
+    dw = (double) grid->dwidth;
+    glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx));
+
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    wd = gx + glyph->bbx.width;
+    ht = gy + glyph->bbx.height;
+
+    /*
+     * Copy the bitmap from the grid into the glyph.
+     */
+    for (ny = 0, y = gy; y < ht; y++, ny++) {
+        col = gx * grid->bpp;
+        for (nx = 0, x = gx; x < wd; x++, nx += grid->bpp, col += grid->bpp) {
+            si = (col & 7) / grid->bpp;
+            byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                di = (nx & 7) / grid->bpp;
+                if (di < si)
+                  byte <<= (si - di) * grid->bpp;
+                else if (di > si)
+                  byte >>= (di - si) * grid->bpp;
+                glyph->bitmap[(ny * nbpr) + (nx >> 3)] |= byte;
+            }
+        }
+    }
+
+    /*
+     * Return the new glyph.
+     */
+    return glyph;
+}
+
+/*
+ * Create a bitmap with the glyph image as well as the selection.
+ */
+void
+bdf_grid_image(bdf_glyph_grid_t *grid, bdf_bitmap_t *image)
+{
+    short x, y, ix, iy;
+    unsigned short bpr, ibpr, si, di, col, colx, byte;
+    unsigned char *masks;
+
+    if (grid == 0 || image == 0)
+      return;
+
+    masks = 0;
+    switch (grid->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    image->bpp = grid->bpp;
+    image->x = image->y = 0;
+    image->width = grid->grid_width;
+    image->height = grid->grid_height;
+    image->bytes = grid->bytes >> 1;
+    image->bitmap = (unsigned char *) malloc(image->bytes);
+    (void) memcpy((char *) image->bitmap, (char *) grid->bitmap, image->bytes);
+
+    /*
+     * Add the selection to the bitmap if it exists.
+     */
+    if (grid->sel.width != 0 && grid->sel.height != 0) {
+        ibpr = ((image->width * grid->bpp) + 7) >> 3;
+        bpr = ((grid->sel.width * grid->bpp) + 7) >> 3;
+        for (iy = grid->sel.y, y = 0; y < grid->sel.height; y++, iy++) {
+            for (ix = grid->sel.x, col = x = 0; x < grid->sel.width;
+                 x++, ix++, col += grid->bpp) {
+                si = (col & 7) / grid->bpp;
+                byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si];
+                if (byte) {
+                    colx = ix * grid->bpp;
+                    di = (colx & 7) / grid->bpp;
+                    if (di < si)
+                      byte <<= (si - di) * grid->bpp;
+                    else if (di > si)
+                      byte >>= (di - si) * grid->bpp;
+                    image->bitmap[(iy * ibpr) + (colx >> 3)] |= byte;
+                }
+            }
+        }
+    }
+}
+
+/*
+ * These values are intended to give pixels mapped from 1bpp to nbpp the
+ * darkest available index, which is 1.
+ */
+static unsigned char twobpp_ones[] = {0x40, 0x10, 0x04, 0x01};
+static unsigned char fourbpp_ones[] = {0x10, 0x01};
+static unsigned char eightbpp_ones[] = {0x01};
+
+/*
+ * Routines for quick and dirty dithering.
+ */
+static void
+_bdf_one_to_n(bdf_bitmap_t *bmap, int n)
+{
+    unsigned short bpr, sbpr, bytes, col, sx, sy;
+    unsigned char *nbmap, *ones = 0;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    switch (n) {
+      case 1: ones = bdf_onebpp; break;
+      case 2: ones = twobpp_ones; break;
+      case 4: ones = fourbpp_ones; break;
+      case 8: ones = eightbpp_ones; break;
+    }
+
+    sbpr = (bmap->width + 7) >> 3;
+    bpr = ((bmap->width * n) + 7) >> 3;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += n) {
+            if (bmap->bitmap[(sy * sbpr) + (sx >> 3)] & (0x80 >> (sx & 7)))
+              nbmap[(sy * bpr) + (col >> 3)] |= ones[(col & 7) / n];
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = n;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_n_to_one(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, col, sx, sy;
+    unsigned char *nbmap, *masks;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    masks = 0;
+    switch (bmap->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    sbpr = ((bmap->width * bmap->bpp) + 7) >> 3;
+    bpr = (bmap->width + 7) >> 3;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += bmap->bpp) {
+            if (bmap->bitmap[(sy * sbpr) + (col >> 3)] &
+                masks[(col & 7) / bmap->bpp])
+              nbmap[(sy * bpr) + (sx >> 3)] |= (0x80 >> (sx & 7));
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 1;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_two_to_four(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    masks = bdf_twobpp;
+
+    sbpr = ((bmap->width << 1) + 7) >> 3;
+    bpr = ((bmap->width << 2) + 7) >> 3;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += 2) {
+            si = (col & 7) >> 1;
+            byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                /*
+                 * Shift the byte down to leave the index in the lowest 2
+                 * bits.
+                 */
+                if (si < 3)
+                  byte >>= (3 - si) << 1;
+
+                /*
+                 * Break 16 into 4 groups of 4 and map the 2bpp index to one
+                 * of those 4.
+                 */
+                bytes <<= 2;
+                if ((sx & 1) == 0)
+                  byte <<= 4;
+                nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte;
+            }
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 4;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_four_to_two(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    masks = bdf_fourbpp;
+
+    sbpr = ((bmap->width << 2) + 7) >> 3;
+    bpr = ((bmap->width << 1) + 7) >> 3;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += 4) {
+            si = (col & 7) >> 2;
+            byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                /*
+                 * Shift the byte down to make an index.
+                 */
+                if (si == 0)
+                  byte >>= 4;
+
+                /*
+                 * Scale the index to two bits per pixel and shift it into
+                 * place if necessary.
+                 */
+                byte >>= 2;
+                if (byte == 0)
+                  byte = 1;
+
+                si = ((sx << 1) & 7) >> 1;
+                if (si < 3)
+                  byte <<= (3 - si) << 1;
+
+                nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte;
+            }
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 2;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_two_to_eight(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    masks = bdf_twobpp;
+
+    sbpr = ((bmap->width << 1) + 7) >> 3;
+    bpr = bmap->width;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += 2) {
+            si = (col & 7) >> 1;
+            byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                /*
+                 * Shift the byte down to leave the index in the lowest 2
+                 * bits.
+                 */
+                if (si < 3)
+                  byte >>= (3 - si) << 1;
+
+                /*
+                 * Break 256 into 4 groups of 64 and map the 2bpp index to one
+                 * of those 4.
+                 */
+                byte <<= 6;
+                nbmap[(sy * bpr) + sx] = byte;
+            }
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 8;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_eight_to_two(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, si, byte, sx, sy;
+    unsigned char *nbmap;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    sbpr = bmap->width;
+    bpr = ((bmap->width << 1) + 7) >> 3;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (sx = 0; sx < bmap->width; sx++) {
+            byte = bmap->bitmap[(sy * sbpr) + sx];
+            if (byte) {
+                /*
+                 * Scale the index to two bits per pixel and shift it into
+                 * place if necessary.
+                 */
+                byte >>= 6;
+                if (byte == 0)
+                  byte = 1;
+
+                si = ((sx << 1) & 7) >> 1;
+                if (si < 3)
+                  byte <<= (3 - si) << 1;
+
+                nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte;
+            }
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 2;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_four_to_eight(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap, *masks;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    masks = bdf_fourbpp;
+
+    sbpr = ((bmap->width << 2) + 7) >> 3;
+    bpr = bmap->width;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += 4) {
+            si = (col & 7) >> 2;
+            byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                /*
+                 * Shift the byte down to make an index.
+                 */
+                if (si == 0)
+                  byte >>= 4;
+
+                /*
+                 * Multiply by 16 to get the 8bpp index.
+                 */
+                byte <<= 4;
+
+                nbmap[(sy * bpr) + sx] = byte;
+            }
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 8;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+static void
+_bdf_eight_to_four(bdf_bitmap_t *bmap)
+{
+    unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy;
+    unsigned char *nbmap;
+
+    if (bmap == 0 || bmap->width == 0 || bmap->height == 0)
+      return;
+
+    sbpr = bmap->width;
+    bpr = ((bmap->width << 2) + 7) >> 3;
+    bytes = bpr * bmap->height;
+    nbmap = (unsigned char *) malloc(bytes);
+    (void) memset((char *) nbmap, 0, bytes);
+
+    for (sy = 0; sy < bmap->height; sy++) {
+        for (col = sx = 0; sx < bmap->width; sx++, col += 4) {
+            byte = bmap->bitmap[(sy * sbpr) + sx];
+            if (byte) {
+                /*
+                 * Divide the index by 16 to determine which 4bpp index
+                 * it will be.
+                 */
+                byte >>= 4;
+                if (byte == 0)
+                  byte = 1;
+
+                /*
+                 * Shift the bits up by 4 if the index is even.
+                 */
+                si = (col & 7) >> 2;
+                if (si == 0)
+                  byte <<= 4;
+
+                nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte;
+            }
+        }
+    }
+    free((char *) bmap->bitmap);
+    bmap->bpp = 4;
+    bmap->bytes = bytes;
+    bmap->bitmap = nbmap;
+}
+
+/*
+ * Add a bitmap to a grid as a selection.
+ */
+void
+bdf_add_selection(bdf_glyph_grid_t *grid, bdf_bitmap_t *sel)
+{
+    unsigned short bytes, bpr;
+
+    if (grid == 0 || sel == 0 || sel->width == 0 || sel->height == 0 ||
+        sel->bytes == 0)
+      return;
+
+    if (sel->bpp != grid->bpp) {
+        /*
+         * Dither the incoming bitmap to match the same bits per pixel as the
+         * grid it is being added to.
+         */
+        if (sel->bpp == 1)
+          _bdf_one_to_n(sel, grid->bpp);
+        else if (grid->bpp == 1)
+          _bdf_n_to_one(sel);
+        else if (sel->bpp == 2) {
+            if (grid->bpp == 4)
+              _bdf_two_to_four(sel);
+            else
+              _bdf_two_to_eight(sel);
+        } else if (sel->bpp == 4) {
+            if (grid->bpp == 2)
+              _bdf_four_to_two(sel);
+            else
+              _bdf_four_to_eight(sel);
+        } else if (sel->bpp == 8) {
+            if (grid->bpp == 2)
+              _bdf_eight_to_two(sel);
+            else
+              _bdf_eight_to_four(sel);
+        }
+    }
+
+    /*
+     * If the bitmap is too big then trim the right and/or the bottom to fit
+     * in the grid.
+     */
+    if (sel->width > grid->grid_width)
+      sel->width = grid->grid_width;
+    if (sel->height > grid->grid_height)
+      sel->height = grid->grid_height;
+
+    /*
+     * If the positioning puts the selection bitmap off one of the edges,
+     * adjust it so it is completely on the grid.
+     */
+    if (sel->x + sel->width > grid->grid_width)
+      sel->x -= (sel->x + sel->width) - grid->grid_width;
+    if (sel->y + sel->height > grid->grid_height)
+      sel->y -= (sel->y + sel->height) - grid->grid_height;
+
+    bpr = ((sel->width * grid->bpp) + 7) >> 3;
+    bytes = (bpr * sel->height) << 1;
+
+    /*
+     * Resize the storage for the selection bitmap if necessary.
+     */
+    if (bytes > grid->sel.bytes) {
+        if (grid->sel.bytes == 0)
+          grid->sel.bitmap = (unsigned char *) malloc(bytes);
+        else
+          grid->sel.bitmap = (unsigned char *)
+              realloc((char *) grid->sel.bitmap, bytes);
+        grid->sel.bytes = bytes;
+    }
+
+    /*
+     * Copy the width and height values.
+     */
+    grid->sel.x = sel->x;
+    grid->sel.y = sel->y;
+    grid->sel.width = sel->width;
+    grid->sel.height = sel->height;
+
+    /*
+     * Copy the incoming bitmap to the new selection bitmap.
+     */
+    (void) memcpy((char *) grid->sel.bitmap, (char *) sel->bitmap,
+                  bytes >> 1);
+
+    /*
+     * Crop the image to adjust the glyph bounding box.
+     */
+    (void) bdf_grid_crop(grid, 1);
+}
+
+int
+bdf_grid_color_at(bdf_glyph_grid_t *grid, short x, short y)
+{
+    unsigned short bpr, si, di, byte;
+    unsigned char *masks = 0;
+
+    if (grid->bpp == 1)
+      return -1;
+
+    di = 0;
+    switch (grid->bpp) {
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    x *= grid->bpp;
+
+    bpr = ((grid->grid_width * grid->bpp) + 7) >> 3;
+    si = (x & 7) / grid->bpp;
+
+    byte = grid->bitmap[(y * bpr) + (x >> 3)] & masks[si];
+    if (di > si)
+      byte >>= (di - si) * grid->bpp;
+    return (int) byte;
+}
diff --git a/bdfotf.c b/bdfotf.c
new file mode 100644 (file)
index 0000000..8725db1
--- /dev/null
+++ b/bdfotf.c
@@ -0,0 +1,746 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Only compile this if the FreeType library is available.
+ */
+#ifdef HAVE_FREETYPE
+
+#include "bdfP.h"
+#include FT_GLYPH_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_TABLES_H
+
+#undef MAX
+#define MAX(h, i) ((h) > (i) ? (h) : (i))
+
+#undef MIN
+#define MIN(l, o) ((l) < (o) ? (l) : (o))
+
+/**************************************************************************
+ *
+ * Local variables.
+ *
+ **************************************************************************/
+
+static char *platform_names[] = {
+    "Apple Unicode", "Macintosh", "ISO", "Microsoft", "Unknown"
+};
+static int nplatform_names =
+sizeof(platform_names) / sizeof(platform_names[0]);
+
+/*
+ * Mac encoding names used when creating the BDF XLFD font name and when
+ * selecting an encoding from a font.
+ */
+static char *mac_encodings[] = {
+    "MacRoman",    "MacJapanese",   "MacChinese",   "MacKorean",
+    "MacArabic",   "MacHebrew",     "MacGreek",     "MacRussian",
+    "MacRSymbol",  "MacDevanagari", "MacGurmukhi",  "MacGujarati",
+    "MacOriya",    "MacBengali",    "MacTamil",     "MacTelugu",
+    "MacKannada",  "MacMalayalam",  "MacSinhalese", "MacBurmese",
+    "MacKhmer",    "MacThai",       "MacLaotian",   "MacGeorgian",
+    "MacArmenian", "MacMaldivian",  "MacTibetan",   "MacMongolian",
+    "MacGeez",     "MacSlavic",     "MacVietnamese","MacSindhi",
+    "MacUninterp"
+};
+static int nmac_encodings = sizeof(mac_encodings) / sizeof(mac_encodings[0]);
+
+/*
+ * ISO encoding names used when creating the BDF XLFD font name and when
+ * selecting an encoding from a font.
+ */
+static char *iso_encodings[] = {
+    "ASCII", "ISO10646", "ISO8859-1"
+};
+static int niso_encodings = sizeof(iso_encodings) / sizeof(iso_encodings[0]);
+
+/*
+ * Microsoft encoding names used when creating the BDF XLFD font name and
+ * when selecting an encoding from a font.
+ */
+static char *ms_encodings[] = {
+    "Symbol", "ISO10646", "ShiftJIS", "GB2312.1980", "Big5",
+    "KSC5601.1987", "KSC5601.1992"
+};
+static int nms_encodings = sizeof(ms_encodings) / sizeof(ms_encodings[0]);
+
+/**************************************************************************
+ *
+ * API.
+ *
+ **************************************************************************/
+
+/*
+ * Routine to get the platform name from the platform ID.
+ */
+char *
+bdfotf_platform_name(short pid)
+{
+    return (pid < nplatform_names) ?
+        platform_names[pid] : platform_names[nplatform_names - 1];
+}
+
+/*
+ * Routine to get the encoding name from the platform and encoding IDs.
+ */
+char *
+bdfotf_encoding_name(short pid, short eid)
+{
+    int nnames;
+    char **names;
+
+    switch (pid) {
+      case 0: return "ISO10646";
+      case 1:
+        nnames = nmac_encodings;
+        names = mac_encodings;
+        break;
+      case 2:
+        nnames = niso_encodings;
+        names = iso_encodings;
+        break;
+      case 3:
+        nnames = nms_encodings;
+        names = ms_encodings;
+        break;
+      default: return "Unknown";
+    }
+
+    return (eid < nnames) ? names[eid] : "Unknown";
+}
+
+/*
+ * A generic routine to get a name from the TT name table.  This routine
+ * always looks for English language names and checks three possibilities:
+ * 1. English names with the MS Unicode encoding ID.
+ * 2. English names with the MS unknown encoding ID.
+ * 3. English names with the Apple Unicode encoding ID.
+ *
+ * The particular name ID mut be provided (e.g. nameID = 0 for copyright
+ * string, nameID = 6 for Postscript name, nameID = 1 for typeface name.
+ *
+ * If the `dash_to_space' flag is set, all dashes (-) in the name will be
+ * replaced with spaces.
+ *
+ * Returns the number of bytes added.
+ */
+int
+bdfotf_get_english_string(FT_Face face, int nameID, int dash_to_space,
+                          char *name)
+{
+    int j, encid;
+    FT_UInt i, nrec;
+    FT_SfntName sfntName;
+    unsigned char *s;
+    unsigned short slen;
+
+    nrec = FT_Get_Sfnt_Name_Count(face);
+
+    for (encid = 1, j = 0; j < 2; j++, encid--) {
+        /*
+         * Locate one of the MS English font names.
+         */
+        for (i = 0; i < nrec; i++) {
+           FT_Get_Sfnt_Name(face, i, &sfntName);
+           if (sfntName.platform_id == 3 &&
+               sfntName.encoding_id == encid &&
+               sfntName.name_id == nameID &&
+               (sfntName.language_id == 0x0409 ||
+                sfntName.language_id == 0x0809 ||
+                sfntName.language_id == 0x0c09 ||
+                sfntName.language_id == 0x1009 ||
+                sfntName.language_id == 0x1409 ||
+                sfntName.language_id == 0x1809)) {
+               s = sfntName.string;
+               slen = sfntName.string_len;
+               break;
+           }
+        }
+
+        if (i < nrec) {
+            /*
+             * Found one of the MS English font names.  The name is by
+             * definition encoded in Unicode, so copy every second byte into
+             * the `name' parameter, assuming there is enough space.
+             */
+            for (i = 1; i < slen; i += 2) {
+                if (dash_to_space)
+                  *name++ = (s[i] != '-') ? s[i] : ' ';
+                else if (s[i] == '\r' || s[i] == '\n') {
+                    if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n')
+                      i += 2;
+                    *name++ = ' ';
+                    *name++ = ' ';
+                } else
+                  *name++ = s[i];
+            }
+            *name = 0;
+            return (slen >> 1);
+        }
+    }
+
+    /*
+     * No MS English name found, attempt to find an Apple Unicode English
+     * name.
+     */
+    for (i = 0; i < nrec; i++) {
+        FT_Get_Sfnt_Name(face, i, &sfntName);
+        if (sfntName.platform_id == 0 && sfntName.language_id == 0 &&
+            sfntName.name_id == nameID) {
+            s = sfntName.string;
+            slen = sfntName.string_len;
+            break;
+        }
+    }
+
+    if (i < nrec) {
+        /*
+         * Found the Apple Unicode English name.  The name is by definition
+         * encoded in Unicode, so copy every second byte into the `name'
+         * parameter, assuming there is enough space.
+         */
+        for (i = 1; i < slen; i += 2) {
+            if (dash_to_space)
+              *name++ = (s[i] != '-') ? s[i] : ' ';
+            else if (s[i] == '\r' || s[i] == '\n') {
+                if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n')
+                  i += 2;
+                *name++ = ' ';
+                *name++ = ' ';
+            } else
+              *name++ = s[i];
+        }
+        *name = 0;
+        return (slen >> 1);
+    }
+
+    return 0;
+}
+
+static int
+_bdfotf_generate(FT_Face face, int nocmap, bdf_options_t *opts,
+                 bdf_callback_t callback, void *data, bdf_font_t *fp)
+{
+    int ismono;
+    int awidth, code, idx;
+    short maxrb, maxlb, minlb, y, x;
+    short x_off, y_off, maxas, maxds;
+    int upm, bpr, wd, ht, sx, ex, sy, ey;
+    unsigned char *bmap;
+    bdf_glyph_t *gp;
+    double swscale;
+    bdf_callback_struct_t cb;
+    bdf_property_t prop;
+
+    FT_Size_Metrics imetrics;
+    TT_HoriHeader *horizontal = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
+
+    /*
+     * Set the instance resolution and point size.
+     */
+    FT_Set_Char_Size(face, 0, opts->point_size * 64,
+                     opts->resolution_x, opts->resolution_y);
+    imetrics = face->size->metrics;
+
+    /*
+     * Set up the initialization callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = face->num_glyphs;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Get the units per em value.
+     */
+    upm = face->units_per_EM;
+
+    ismono = 1;
+    wd = 0xffff;
+    awidth = 0;
+    maxrb = maxlb = maxas = maxds = 0;
+    minlb = 32767;
+
+    /*
+     * Calculate the SWIDTH scaling factor.
+     */
+    swscale = ((double) opts->resolution_y) * ((double) opts->point_size);
+
+    for (code = fp->glyphs_used = 0; code < 0xffff; code++) {
+        if (nocmap) {
+            /*
+             * No cmap is being used, so do each index in turn.
+             */
+            if (code >= face->num_glyphs)
+              break;
+            idx = code;
+        } else
+          idx = FT_Get_Char_Index(face, code);
+
+        if (idx <= 0 ||
+            FT_Load_Glyph(face, idx, opts->otf_flags))
+          continue;
+
+        if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))
+          continue;
+
+        /*
+         * Increase the amount of storage by 128 every time.
+         */
+        if (fp->glyphs_used == fp->glyphs_size) {
+            fp->glyphs = (bdf_glyph_t *)
+                realloc((char *) fp->glyphs,
+                        sizeof(bdf_glyph_t) * (fp->glyphs_size + 128));
+            gp = fp->glyphs + fp->glyphs_size;
+            (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) << 7);
+            fp->glyphs_size += 128;
+        }
+
+        /*
+         * Point at the next glyph.
+         */
+        gp = fp->glyphs + fp->glyphs_used++;
+        gp->encoding = code;
+        gp->dwidth = face->glyph->metrics.horiAdvance >> 6;
+        gp->swidth = (unsigned short)
+            (((double) gp->dwidth) * 72000.0) / swscale;
+
+        /*
+         * Determine the actual bounding box of the glyph bitmap.  Do not
+         * forget that the glyph is rendered upside down!
+         */
+        sx = sy = 0xffff;
+        ex = ey = 0;
+        bmap = face->glyph->bitmap.buffer;
+        for (y = 0; y <  face->glyph->bitmap.rows; y++) {
+            for (x = 0; x < face->glyph->bitmap.width; x++) {
+                if (bmap[x >> 3] & (0x80 >> (x & 7))) {
+                    if (x < sx) sx = x;
+                    if (x > ex) ex = x;
+                    if (y < sy) sy = y;
+                    if (y > ey) ey = y;
+                }
+            }
+            bmap += face->glyph->bitmap.pitch;
+        }
+
+        /*
+         * If the glyph is actually an empty bitmap, set the size to 0 all
+         * around.
+         */
+        if (sx == 0xffff && sy == 0xffff && ex == 0 && ey == 0)
+          sx = ex = sy = ey = 0;
+        else {
+            /*
+             * Adjust the end points.
+             */
+            ex++;
+            ey++;
+        }
+
+        /*
+         * Test to see if the font is going to be monowidth or not by
+         * comparing the current glyph width against the last one.
+         */
+        if (ismono && (ex - sx) + 1 != wd)
+          ismono = 0;
+
+        /*
+         * Set the initial metrics.
+         */
+        wd = ex - sx;
+        ht = ey - sy;
+        x_off = sx + face->glyph->bitmap_left;
+        y_off = sy + face->glyph->bitmap_top - face->glyph->bitmap.rows;
+
+        /*
+         * Adjust the overall bounding box.
+         */
+        maxas = MAX(maxas, ht + y_off);
+        maxds = MAX(maxds, -y_off);
+        maxrb = MAX(maxrb, wd + x_off);
+        minlb = MIN(minlb, x_off);
+        maxlb = MAX(maxlb, x_off);
+
+        /*
+         * Accumulate the average width value.
+         */
+        awidth += wd;
+
+        /*
+         * Set the glyph metrics.
+         */
+        gp->bbx.width = wd;
+        gp->bbx.height = ht;
+        gp->bbx.x_offset = x_off;
+        gp->bbx.y_offset = y_off;
+        gp->bbx.ascent = ht + y_off;
+        gp->bbx.descent = -y_off;
+
+        /*
+         * Allocate the bitmap for the glyph.
+         */
+        bpr = (wd + 7) >> 3;
+        gp->bytes = bpr * ht;
+        gp->bitmap = (unsigned char *) malloc(gp->bytes);
+        (void) memset((char *) gp->bitmap, 0, gp->bytes);
+
+        /*
+         * Shift the bits into the glyph bitmap.
+         */
+        bmap = face->glyph->bitmap.buffer + sy * face->glyph->bitmap.pitch;
+        for (y = 0; y < ey - sy; y++) {
+            for (x = 0; x < ex - sx; x++) {
+                if (bmap[(x+sx) >> 3] & (0x80 >> ((x+sx) & 7)))
+                  gp->bitmap[(y * bpr) + (x >> 3)] |= (0x80 >> (x & 7));
+            }
+            bmap += face->glyph->bitmap.pitch;
+        }
+
+        /*
+         * Call the callback if it was provided.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.current = fp->glyphs_used;
+            cb.total = face->num_glyphs;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Calculate the font average width.
+     */
+    awidth =
+        (int) ((((double) awidth / (double) fp->glyphs_used) + 0.5) * 10.0);
+
+    /*
+     * Set the font bounding box.
+     */
+    fp->bbx.width = maxrb - minlb;
+    fp->bbx.height = maxas + maxds;
+    fp->bbx.x_offset = minlb;
+    fp->bbx.y_offset = -maxds;
+    fp->bbx.ascent = maxas;
+    fp->bbx.descent = maxds;
+
+    /*
+     * Set the font ascent and descent.
+     */
+    fp->font_ascent =
+        (horizontal->Ascender * imetrics.y_ppem) / upm;
+    fp->font_descent =
+        -((horizontal->Descender * imetrics.y_ppem) / upm);
+
+    /*
+     * Determine if the font is monowidth.
+     */
+    if (ismono) {
+        fp->spacing = BDF_MONOWIDTH;
+        fp->monowidth = fp->bbx.width;
+    }
+
+    /*
+     * Add the properties needed for the XLFD name.
+     */
+    prop.name = "POINT_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = fp->point_size * 10;
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "PIXEL_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int)
+        ((((double) (fp->point_size * 10) *
+           (double) fp->resolution_y) / 722.7) + 0.5);
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "RESOLUTION_X";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) fp->resolution_x;
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "RESOLUTION_Y";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) fp->resolution_y;
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = fp->font_ascent;
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = fp->font_descent;
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "AVERAGE_WIDTH";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = awidth;
+    bdf_add_font_property(fp, &prop);
+
+    prop.name = "SPACING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = "P";
+    switch (fp->spacing) {
+      case BDF_PROPORTIONAL: prop.value.atom = "P"; break;
+      case BDF_MONOWIDTH: prop.value.atom = "M"; break;
+      case BDF_CHARCELL: prop.value.atom = "C"; break;
+    }
+    bdf_add_font_property(fp, &prop);
+
+    return 1;
+}
+
+int
+bdfotf_load_font(FT_Face face, short pid, short eid, bdf_options_t *opts,
+                 bdf_callback_t callback, void *data, bdf_font_t **font)
+{
+    int i, nocmap, res, slen;
+    bdf_font_t *fp;
+    char *np, str[256];
+    bdf_property_t prop;
+    bdf_callback_struct_t cb;
+    TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+
+    /*
+     * First get the requested cmap from the font.
+     */
+    for (nocmap = i = 0; i < face->num_charmaps; i++) {
+        if (face->charmaps[i]->platform_id == pid &&
+            face->charmaps[i]->encoding_id == eid)
+          break;
+    }
+
+    /*
+     * If the requested cmap was not found, attempt to fall back on the
+     * Microsoft Unicode cmap.
+     */
+    if (i == face->num_charmaps) {
+        for (i = 0; i < face->num_charmaps; i++) {
+            if (face->charmaps[i]->platform_id == 3 &&
+                face->charmaps[i]->encoding_id == 1)
+              break;
+        }
+        if (i == face->num_charmaps) {
+            /*
+             * No cmap was found.
+             */
+            nocmap = 1;
+            pid = eid = -1;
+        } else {
+            /*
+             * Found the Microsoft Unicode cmap.
+             */
+            pid = 3;
+            eid = 1;
+            FT_Set_Charmap(face, face->charmaps[i]);
+        }
+    } else
+      FT_Set_Charmap(face, face->charmaps[i]);
+
+    /*
+     * Create the font.
+     */
+    *font = fp = (bdf_font_t *) malloc(sizeof(bdf_font_t));
+    (void) memset((char *) fp, 0, sizeof(bdf_font_t));
+
+    /*
+     * Do some initializations by defaulting to proportional spacing and
+     * allocate at least the reported number of glyphs so reallocations will
+     * be minimal.
+     */
+    fp->bpp = 1;
+    fp->default_glyph = -1;
+    fp->spacing = BDF_PROPORTIONAL;
+    fp->glyphs_size = face->num_glyphs;
+    fp->glyphs = (bdf_glyph_t *)
+        malloc(sizeof(bdf_glyph_t) * face->num_glyphs);
+    (void) memset((char *) fp->glyphs, 0,
+                  sizeof(bdf_glyph_t) * face->num_glyphs);
+
+    /*
+     * Set the metrics.
+     */
+    fp->point_size = opts->point_size;
+    fp->resolution_x = opts->resolution_x;
+    fp->resolution_y = opts->resolution_y;
+
+    /*
+     * Actually generate the font.
+     */
+    res = _bdfotf_generate(face, nocmap, opts, callback, data, fp);
+
+    /*
+     * If the number of glyphs loaded is less than the reported number of
+     * glyphs, force a callback if one was provided.
+     */
+    if (callback != 0 && fp->glyphs_used < face->num_glyphs) {
+        cb.reason = BDF_LOADING;
+        cb.total = cb.current = face->num_glyphs;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * If the font did not load successfully, free up the font.
+     */
+    if (!res) {
+        bdf_free_font(fp);
+        *font = 0;
+    } else {
+        /*
+         * Add other sundry properties so the XLFD name can be generated.
+         */
+        prop.name = "FOUNDRY";
+        prop.format = BDF_ATOM;
+        prop.value.atom = "FreeType";
+        bdf_add_font_property(fp, &prop);
+
+        /*
+         * Get the typeface name.
+         */
+        slen = bdfotf_get_english_string(face, BDFOTF_FAMILY_STRING, 1, str);
+        prop.name = "FAMILY_NAME";
+        prop.format = BDF_ATOM;
+        if (slen > 0)
+          prop.value.atom = str;
+        else
+          prop.value.atom = "Unknown";
+        bdf_add_font_property(fp, &prop);
+
+        /*
+         * Add the CHARSET_REGISTRY and CHARSET_ENCODING properties.
+         */
+        np = bdfotf_encoding_name(pid, eid);
+        if (strcmp(np, "ISO8859-1") == 0) {
+            prop.name = "CHARSET_REGISTRY";
+            prop.format = BDF_ATOM;
+            prop.value.atom = "ISO8859";
+            bdf_add_font_property(fp, &prop);
+            prop.name = "CHARSET_ENCODING";
+            prop.format = BDF_ATOM;
+            prop.value.atom = "1";
+            bdf_add_font_property(fp, &prop);
+        } else if (strcmp(np, "ISO10646") == 0) {
+            prop.name = "CHARSET_REGISTRY";
+            prop.format = BDF_ATOM;
+            prop.value.atom = np;
+            bdf_add_font_property(fp, &prop);
+            prop.name = "CHARSET_ENCODING";
+            prop.format = BDF_ATOM;
+            prop.value.atom = "1";
+            bdf_add_font_property(fp, &prop);
+        } else {
+            prop.name = "CHARSET_REGISTRY";
+            prop.format = BDF_ATOM;
+            prop.value.atom = np;
+            bdf_add_font_property(fp, &prop);
+            prop.name = "CHARSET_ENCODING";
+            prop.format = BDF_ATOM;
+            prop.value.atom = "0";
+            bdf_add_font_property(fp, &prop);
+        }
+
+        /*
+         * Determine the weight name.
+         */
+        prop.name = "WEIGHT_NAME";
+        prop.format = BDF_ATOM;
+        slen = bdfotf_get_english_string(face, BDFOTF_SUBFAMILY_STRING,
+                                         1, str);
+        if (strcmp(str, "Regular") == 0)
+          prop.value.atom = "Medium";
+        else if (os2->fsSelection & 0x20)
+          prop.value.atom = "Bold";
+        else if (slen > 0)
+          prop.value.atom = str;
+        else
+          prop.value.atom = "Medium";
+        bdf_add_font_property(fp, &prop);
+
+        /*
+         * Determine the slant name.
+         */
+        prop.name = "SLANT";
+        prop.format = BDF_ATOM;
+        if (os2->fsSelection & 0x01)
+          prop.value.atom = "I";
+        else
+          prop.value.atom = "R";
+        bdf_add_font_property(fp, &prop);
+
+        /*
+         * Add the default SETWIDTH_NAME.
+         */
+        prop.name = "SETWIDTH_NAME";
+        prop.format = BDF_ATOM;
+        prop.value.atom = "Normal";
+        bdf_add_font_property(fp, &prop);
+
+        /*
+         * Create the XLFD font name for the font.
+         */
+        fp->name = bdf_make_xlfd_name(fp, 0, 0);
+
+        /*
+         * Add the COPYRIGHT notice.
+         */
+        slen = bdfotf_get_english_string(face, BDFOTF_COPYRIGHT_STRING,
+                                         0, str);
+        if (slen > 0) {
+            prop.name = "COPYRIGHT";
+            prop.format = BDF_ATOM;
+            prop.value.atom = str;
+            bdf_add_font_property(fp, &prop);
+        }
+
+        /*
+         * Add the special _TTF_PSNAME atom with the font Postscript name.
+         */
+        slen = bdfotf_get_english_string(face, BDFOTF_POSTSCRIPT_STRING,
+                                         0, str);
+        if (slen > 0) {
+            prop.name = "_TTF_PSNAME";
+            prop.format = BDF_ATOM;
+            prop.value.atom = str;
+            bdf_add_font_property(fp, &prop);
+        }
+
+        /*
+         * Add a message indicating the font was converted.
+         */
+        _bdf_add_comment(fp, "Font converted from OTF to BDF.", 31);
+        _bdf_add_acmsg(fp, "Font converted from OTF to BDF.", 31);
+
+        /*
+         * Finally, mark the font as being modified.
+         */
+        fp->modified = 1;
+    }
+
+    return res;
+}
+
+#endif /* HAVE_FREETYPE */
diff --git a/bdfpkgf.c b/bdfpkgf.c
new file mode 100644 (file)
index 0000000..902d879
--- /dev/null
+++ b/bdfpkgf.c
@@ -0,0 +1,1495 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Routines to import GF and PK format bitmap font files.
+ *
+ * GF is the "generic font" file format for bitmap fonts; GF files are
+ * typically produced by Metafont.
+ *
+ * PK is the "packed file" font file format which is the de facto standard
+ * bitmap font format in the TeX world. It contains most of the information
+ * that's in a GF file, but much more compactly. PK fonts are typically
+ * generated from GF fonts by gftopk(1), or by a converter like gsftopk(1).
+ *
+ * Documentation for these file formats can be found in the literate programs
+ * GFtoPK, PKtoGF, GFtoDVI and GFtype which are included in a typical TeX
+ * distribution's source.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef BDF_NO_X11
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif /* !BDF_NO_X11 */
+
+#include "bdfP.h"
+
+#undef MAX
+#define MAX(h, i) ((h) > (i) ? (h) : (i))
+
+#undef MIN
+#define MIN(l, o) ((l) < (o) ? (l) : (o))
+
+/*
+ * Symbolic names for the opcode bytes in GF files.
+ */
+#define GF_paint1       64 /* 0x40 */
+#define GF_paint2       65 /* 0x41 */
+#define GF_paint3       66 /* 0x42 */
+#define GF_boc          67 /* 0x43 */
+#define GF_boc1                 68 /* 0x44 */
+#define GF_eoc          69 /* 0x45 */
+#define GF_skip0        70 /* 0x46 */
+#define GF_skip1        71 /* 0x47 */
+#define GF_skip2        72 /* 0x48 */
+#define GF_skip3        73 /* 0x49 */
+#define GF_newrow_0     74 /* 0x4A */
+#define GF_newrow_164  238 /* 0xEE */
+#define GF_xxx1                239 /* 0xEF */
+#define GF_xxx2                240 /* 0xF0 */
+#define GF_xxx3                241 /* 0xF1 */
+#define GF_xxx4                242 /* 0xF2 */
+#define GF_yyy         243 /* 0xF3 */
+#define GF_no_op       244 /* 0xF4 */
+#define GF_char_loc    245 /* 0xF5 */
+#define GF_char_loc0   246 /* 0xF6 */
+#define GF_pre         247 /* 0xF7 */
+#define GF_post                248 /* 0xF8 */
+#define GF_post_post   249 /* 0xF9 */
+
+/*
+ * Symbolic names for the opcode bytes in PK files.
+ */
+
+#define PK_id           89 /* 0x59 */
+#define PK_xxx1                240 /* 0xF0 */
+#define PK_xxx4                243 /* 0xF3 */
+#define PK_yyy         244 /* 0xF4 */
+#define PK_post                245 /* 0xF5 */
+#define PK_reserved1   248 /* 0xF8 */
+#define PK_pre         247 /* 0xF7 */
+
+/*
+ * Structure used to track the state for various things when reading a font.
+ */
+typedef struct {
+    int top;
+    int mask;
+    int c;
+} _bdf_mf_state_t;
+
+/*
+ * Routine to compare two glyphs by encoding so they can be sorted.
+ */
+static int
+by_encoding(const void *a, const void *b)
+{
+    bdf_glyph_t *c1, *c2;
+
+    c1 = (bdf_glyph_t *) a;
+    c2 = (bdf_glyph_t *) b;
+    if (c1->encoding < c2->encoding)
+      return -1;
+    else if (c1->encoding > c2->encoding)
+      return 1;
+    return 0;
+}
+
+/*
+ * Routines for scanning numbers from GF and PK files.
+ */
+static int
+_bdf_mf_get16(FILE *in)
+{
+    return (getc(in) << 8) | (getc(in) & 0xff);
+}
+
+static int
+_bdf_mf_get32(FILE *in)
+{
+    int hi = _bdf_mf_get16(in);
+
+    if (hi > 32767)
+      hi -= 65536;
+    return (hi << 16) + _bdf_mf_get16(in);
+}
+
+static void
+printscaled(int s, unsigned char *buf)
+{
+    int delta;
+
+    *buf++ = ' ';
+    *buf++ = '(';
+    if (s < 0) {
+        *buf++ = '-';
+        s = -s;
+    }
+    sprintf((char *) buf, "%d", s >> 16);
+    buf += strlen((char *) buf);
+    s = 10 * ( s & 65535 ) + 5;
+    if (s != 5) {
+        delta = 10;
+        *buf++ = '.';
+        do {
+            if (delta > 65536)
+              s = s + 32768 - (delta >> 1);
+            *buf++ = 0x30 + (s >> 16);
+            s = 10 * (s & 65535);
+            delta *= 10;
+        } while (s > delta);
+    }
+    sprintf((char *) buf, " scaled)");
+} 
+
+/*
+ * Routine to scan the PK specials and add them as comments if necessary.
+ */
+static int
+_bdf_pk_specials(FILE *in, bdf_font_t *font, bdf_options_t *opts,
+                 unsigned char *glyphname)
+{
+    int c;
+    int i, n, num;
+    unsigned int comment_size;
+    unsigned char *comment, bytes[4];
+
+    /*
+     * Initialize the variable that keeps track of the storage allocated
+     * for the comments encountered.
+     */
+    comment = 0;
+    comment_size = 0;
+
+    while ((c = getc(in)) >= PK_xxx1 && c != GF_char_loc) {
+        /*
+         * Anything between PK_reserved1 and 0xff are bad values.  PK_pre is
+         * the font preamble which is not expected here.
+         */
+        if (c == PK_pre || (c >= PK_post && c <= 0xff))
+          return -2;
+
+        /*
+        * Anything between PK_xxx1 and PK_xxx4 are string specials which will
+        * be added with the comments if comments are being kept.
+         */
+        if (c >= PK_xxx1 && c <= PK_xxx4) {
+            /*
+             * Determine the number of bytes that need to be read to determine
+             * the length of the string special.
+             */
+            n = (c - PK_xxx1) + 1;
+            fread((char *) bytes, n, 1, in);
+            for (i = 0, num = 0; i < n; i++)
+              num = (num << 8) | bytes[i];
+
+            if (opts->keep_comments) {
+                /*
+                 * Make sure there is enough space for the string.
+                 */
+                if (comment_size < num + 1) {
+                    if (comment_size == 0)
+                      comment = (unsigned char *) malloc(num + 1);
+                    else
+                      comment = (unsigned char *)
+                          realloc((char *) comment, num + 1);
+                    comment_size = num + 1;
+                }
+                /*
+                 * Read the comment and add it to the font.
+                 */
+                fread((char *) comment, num, 1, in);
+                comment[num] = 0;
+                if (!strncmp((char *) comment, "title ", 6))
+                  /*
+                   * The comment is the glyph's name/title; save it so it can
+                   * be associated with the forthcoming glyph, rather than
+                   * with the font as a whole.
+                   */
+                  strcpy((char *) glyphname, (char *) comment + 6);
+                else
+                  /*
+                   * A regular comment.
+                   */
+                  _bdf_add_comment(font, (char *) comment, num);
+            } else
+              /*
+               * Skip the string special.
+               */
+              fseek(in, num, 1L);
+        }
+
+        /*
+         * PK_yyy is a numeric special.  Add the number as a comment if
+         * specified.
+         */
+        if (c == PK_yyy) {
+            num = _bdf_mf_get32(in);
+            if (opts->keep_comments) {
+                if (comment_size < 64) {
+                    if (comment_size == 0)
+                      comment = (unsigned char *) malloc(64);
+                    else
+                      comment = (unsigned char *)
+                          realloc((char *) comment, 64);
+                    comment_size = 64;
+                }
+                sprintf((char *) comment, "%d", num);
+                printscaled(num, comment + strlen((char *) comment));
+                _bdf_add_comment(font, (char *) comment,
+                                 strlen((char *) comment));
+            }
+        }
+    }
+
+    /*
+     * Free up the comment buffer if it was allocated.
+     */
+    if (comment_size > 0)
+      free((char *) comment);
+
+    /*
+     * Return the byte that caused the loop to terminate.  This will be the
+     * postamble marker GF_post or the start of the glyphs.
+     */
+    return c;
+}
+
+/*
+ * Awkward little routine to collect packed bits from a PK file.
+ */
+static int
+_bdf_pk_getbit(FILE *in, _bdf_mf_state_t *state)
+{
+    state->mask >>= 1;
+    if (state->mask == 0) {
+        state->c = getc(in);
+        state->mask = 0x80;
+    }
+    return (state->c & state->mask);
+}
+
+/*
+ * Another awkward little routine to get 4 bits at a time from a PK file.
+ */
+static int
+_bdf_pk_getnybble(FILE *in, _bdf_mf_state_t *state)
+{
+    int r;
+
+    if (state->top == 0) {
+        state->c = getc(in);
+        state->top = 2;
+    }
+    r = (state->c >> ((state->top - 1) << 2)) & 0x0f;
+    state->top--;
+    return r;
+}
+
+/*
+ * Yet another awkward routine to read a packed number and a repeat count from
+ * a PK file.
+ */
+static int
+_bdf_pk_getpacked(FILE *in, int *rcount, int dyn, _bdf_mf_state_t *state)
+{
+    int i, j;
+
+    if ((i = _bdf_pk_getnybble(in, state)) == 0) {
+        do {
+            j = _bdf_pk_getnybble(in, state);
+            i++;
+        } while (!j);
+        for (;i > 0; i--)
+          j = (j << 4) + _bdf_pk_getnybble(in, state);
+        return j - 15 + ((13 - dyn) << 4) + dyn;
+    }
+
+    if (i <= dyn)
+      return i;
+
+    if (i < 14)
+      return ((i - dyn - 1) << 4) + dyn + 1 + _bdf_pk_getnybble(in, state);
+
+    *rcount = (i == 14) ? _bdf_pk_getpacked(in, rcount, dyn, state) : 1;
+    return _bdf_pk_getpacked(in, rcount, dyn, state);
+}
+
+static int
+_bdf_load_pk_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback,
+                  void *data, bdf_font_t **font)
+{
+    int n, res, set, x, y, bpr, rcnt, ismono;
+    int num, plen, pend, row_size, awidth;
+    short rb, maxrb, minlb, maxlb;
+    double denom, dw;
+    bdf_font_t *f;
+    bdf_glyph_t *gp, g;
+    bdf_property_t prop;
+    bdf_callback_struct_t cb;
+    _bdf_mf_state_t state;
+    struct stat st;
+    unsigned char *row, bytes[256], glyphname[256];
+
+    row = 0;
+    glyphname[0] = 0;
+
+    /*
+     * Create a font to work with.
+     */
+    *font = f = (bdf_font_t *) malloc(sizeof(bdf_font_t));
+    (void) memset((char *) f, 0, sizeof(bdf_font_t));
+
+    /*
+     * Set some defaults and allocate an initial amount of space.  Make the
+     * initial assumption that the font is monowidth but determine if it
+     * should be proportional when the glyphs are loaded.  Allocate space for
+     * at least 128 glyphs before loading the font.
+     */
+    f->bpp = 1;
+    f->spacing = BDF_MONOWIDTH;
+    f->glyphs_size = 128;
+    f->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) << 7);
+    (void) memset((char *) f->glyphs, 0, sizeof(bdf_glyph_t) << 7);
+
+    /*
+     * Initialize the horizontal and vertical offsets of the font to some
+     * large number.
+     */
+    f->bbx.x_offset = f->bbx.y_offset = 32767;
+
+    /*
+     * Initialize things.
+     */
+    ismono = 1;
+    row_size = 0;
+    awidth = 0;
+    rb = maxrb = maxlb = 0;
+    minlb = 32767;
+    (void) memset((char *) &g, 0, sizeof(bdf_glyph_t));
+
+    /*
+     * Load the initial comment.
+     */
+    fread((char *) bytes, 1, 1, in);
+    n = bytes[0];
+    fread(bytes, n, 1, in);
+    bytes[n] = 0;
+
+    /*
+     * Add the comment to the font if indicated.
+     */
+    if (opts->keep_comments)
+      _bdf_add_comment(f, (char *) bytes, (unsigned int) n);
+
+    /*
+     * Get the point size and scale it down 
+     */
+    f->point_size = (int) (((float) _bdf_mf_get32(in)) /
+                            ((float) (1 << 20)));
+
+    /*
+     * Skip the checksum.
+     */
+    fread(bytes, 4, 1, in);
+
+    /*
+     * Get the horizontal resolution.
+     */
+    f->resolution_x = (int)
+        (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5);
+
+    /*
+     * Get the vertical resolution.
+     */
+    f->resolution_y = (int)
+        (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5);
+
+    /*
+     * Determine the denominator used for scalable width calculations.
+     */
+    denom = ((double) f->point_size) * ((double) f->resolution_x);
+
+    /*
+     * Get the font info so we can set up the callback.  The callback will
+     * update after every glyph is loaded and is based on file size instead of
+     * number of glyphs.  This allows the font to be read all at once instead
+     * of twice.
+     */
+    (void) fstat(fileno(in), &st);
+
+    /*
+     * Set the callback up.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = st.st_size;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Load the glyphs.
+     */
+    gp = f->glyphs;
+    while ((res = _bdf_pk_specials(in, f, opts, glyphname)) != PK_post &&
+           res > 0) {
+       /* Set the glyph's name, if we've seen it */
+        if (strlen((char *) glyphname)) {
+            g.name = malloc(strlen((char *) glyphname)+1);
+           strcpy((char *) g.name, (char *) glyphname);
+       }
+        if ((res & 7) == 7) {
+            /*
+             * Long glyph info.
+             */
+
+            /*
+             * Get the packet length and glyph encoding.
+             */
+            plen = _bdf_mf_get32(in);
+            g.encoding = _bdf_mf_get32(in);
+
+            pend = plen + ftell(in);
+
+            /*
+             * Get the glyph metrics.
+             */
+
+            /*
+             * Ignore the TFM width.
+             */
+            (void) _bdf_mf_get32(in);
+
+            /*
+             * Get the device width (DWIDTH) from the horizontal escapement of
+             * the glyph and calculate the scalable width (SWIDTH) from it.
+             */
+            num = _bdf_mf_get32(in);
+            g.dwidth = num >> 16;
+            dw = (double) g.dwidth;
+            g.swidth = (unsigned short) ((dw * 72000.0) / denom);
+
+            /*
+             * Ignore the vertical escapement.
+             */
+            (void) _bdf_mf_get32(in);
+
+            /*
+             * Collect the remaining glyph metrics info and calculate the
+             * glyph ascent and descent from the height and vertical offset.
+             */
+            g.bbx.width = (unsigned short) _bdf_mf_get32(in);
+            g.bbx.height = (unsigned short) _bdf_mf_get32(in);
+            g.bbx.x_offset = (short) _bdf_mf_get32(in);
+            g.bbx.y_offset = (short) _bdf_mf_get32(in);
+        } else if (res & 0x04) {
+            /*
+             * Extended glyph info.
+             */
+            if (res & 0x08)
+              plen = (((res & 0x07) - 4) << 16) + _bdf_mf_get16(in);
+            else
+              plen = (((res & 0x0f) - 4) << 16) + _bdf_mf_get16(in);
+
+            /*
+             * Load the encoding byte and the first byte of the TFM width.
+             */
+            fread(bytes, 2, 1, in);
+            g.encoding = (int) bytes[0];
+
+            pend = plen + ftell(in) - 1;
+
+            /*
+             * Get the glyph metrics.
+             */
+
+            /*
+             * Ignore the last two bytes of the TFM width.
+             */
+            (void) _bdf_mf_get16(in);
+
+            /*
+             * Get the device width (DWIDTH) from the horizontal escapement of
+             * the glyph and calculate the scalable width (SWIDTH) from it.
+             */
+            g.dwidth = (unsigned short) _bdf_mf_get16(in);
+            dw = (double) g.dwidth;
+            g.swidth = (unsigned short) ((dw * 72000.0) / denom);
+
+            /*
+             * Collect the remaining glyph metrics info and calculate the
+             * glyph ascent and descent from the height and vertical offset.
+             */
+            g.bbx.width = (unsigned short) _bdf_mf_get16(in);
+            g.bbx.height = (unsigned short) _bdf_mf_get16(in);
+            if ((num = _bdf_mf_get16(in)) > 32767)
+              g.bbx.x_offset = num - 65536;
+            else
+              g.bbx.x_offset = (short) num;
+            if ((num = _bdf_mf_get16(in)) > 32767)
+              g.bbx.y_offset = num - 65536;
+            else
+              g.bbx.y_offset = (short) num;
+        } else {
+            /*
+             * Short glyph info.  Read the next 10 bytes so they can be used
+             * as part of the glyph info calculations.
+             */
+            fread(bytes, 10, 1, in);
+
+            num = 0;
+            if (res & 0x08)
+              plen = ((res & 0x07) << 8) + bytes[num++];
+            else
+              plen = ((res & 0x0f) << 8) + bytes[num++];
+
+            g.encoding = (int) bytes[num++];
+
+            pend = plen + ftell(in) - 8;
+
+            /*
+             * Skip the TFM width.
+             */
+            num += 3;
+
+            /*
+             * Get the device width (DWIDTH) from the horizontal escapement of
+             * the glyph and calculate the scalable width (SWIDTH) from it.
+             */
+            g.dwidth = (unsigned short) bytes[num++];
+            dw = (double) g.dwidth;
+            g.swidth = (unsigned short) ((dw * 72000.0) / denom);
+
+            /*
+             * Collect the remaining glyph metrics info and calculate the
+             * glyph ascent and descent from the height and vertical offset.
+             */
+            g.bbx.width = (unsigned short) bytes[num++];
+            g.bbx.height = (unsigned short) bytes[num++];
+            /*
+             * The hoff value we're now to interpret gives the horizontal
+             * offset of the reference point compared to the character's
+             * pixels.  i.e. a value of -2 means the pixels start at +2; see
+             * the discussion of the example character raster in the pktogf
+             * web.
+             */
+            g.bbx.x_offset = (short) ((bytes[num] & 0x80) ?
+                                      256 - bytes[num] : bytes[num]);
+            num++;
+            g.bbx.y_offset = (short) ((bytes[num] & 0x80) ?
+                                      bytes[num] - 256 : bytes[num]);
+            num++;
+        }
+
+        /*
+         * Adjust the vertical metrics of the glyph.
+         */
+        g.bbx.y_offset = (g.bbx.y_offset + 1) - g.bbx.height;
+        g.bbx.ascent = g.bbx.height + g.bbx.y_offset;
+        g.bbx.descent = -g.bbx.y_offset;
+
+        /*
+         * Check to see if the font needs to be marked as proportional.
+         */
+        if (f->glyphs_used > 0 && ismono &&
+            (f->glyphs[0].bbx.width != g.bbx.width ||
+             f->glyphs[0].bbx.height != g.bbx.height ||
+             f->glyphs[0].bbx.x_offset != g.bbx.x_offset ||
+             f->glyphs[0].bbx.y_offset != g.bbx.y_offset ||
+             f->glyphs[0].bbx.ascent != g.bbx.ascent ||
+             f->glyphs[0].bbx.descent != g.bbx.descent)) {
+            ismono = 0;
+            f->spacing = BDF_PROPORTIONAL;
+        }
+
+        /*
+         * Now load the packed bits or the run length encoded image.
+         */
+        bpr = (g.bbx.width + 7) >> 3;
+        g.bytes = bpr * g.bbx.height;
+        g.bitmap = (unsigned char *) malloc(g.bytes);
+        (void) memset((char *) g.bitmap, 0, g.bytes);
+
+        /*
+         * Reset the state values.
+         */
+        state.top = state.mask = state.c = 0;
+        if ((res & 0xf0) == 0xe0) {
+            /*
+             * Packed bit format.
+             */
+            if ((g.bbx.width & 7) == 0)
+              /*
+               * The bits are on a boundary that is a multiple of 8, so the
+               * bitmap can be read all at once.
+               */
+              fread(g.bitmap, g.bbx.height * bpr, 1, in);
+            else {
+                /*
+                 * The width is not a multiple of 8, so the bitmap should be
+                 * read one bit at a time.
+                 */
+                for (y = 0; y < g.bbx.height; y++) {
+                    for (x = 0; x < g.bbx.width; x++) {
+                        if (_bdf_pk_getbit(in, &state))
+                          g.bitmap[(y * bpr) + (x >> 3)] |= (0x80 >> (x & 7));
+                    }
+                }
+            }
+        } else {
+            /*
+             * Get the run length encoded image.
+             */
+
+            /*
+             * The glyph image is going to be run length encoded, so allocate
+             * a row to collect bits into.
+             */
+            if (row_size < bpr) {
+                if (row_size == 0)
+                  row = (unsigned char *) malloc(bpr);
+                else
+                  row = (unsigned char *) realloc((char *) row, bpr);
+                row_size = bpr;
+            }
+            /*
+             * Initialize the row buffer so we don't get any extra bits in the
+             * image by accident.
+             */
+            (void) memset((char *) row, 0, row_size);
+
+            /*
+             * Determine if the run length encoding starts with bits that are
+             * to be set or cleared.
+             */
+            set = res & 0x08;
+            for (rcnt = x = y = 0; y < g.bbx.height;) {
+                /*
+                 * Get the next number and a repeat count.
+                 */
+                n = _bdf_pk_getpacked(in, &rcnt, (res >> 4) & 0x0f, &state);
+                while (n > 0) {
+                    for (; n > 0 && x < g.bbx.width; x++, n--) {
+                        if (set)
+                          row[x >> 3] |= (0x80 >> (x & 7));
+                    }
+                    if (x == g.bbx.width) {
+                        /*
+                         * Copy the row into the actual glyph bitmap as many
+                         * times as called for by the repeat count, reset x to
+                         * 0 so a new row can be started, and initialize the
+                         * row buffer again.
+                         */
+                        for (x = 0; rcnt >= 0; rcnt--, y++)
+                          (void) memcpy((char *) (g.bitmap + (y * bpr)),
+                                        (char *) row, bpr);
+                        (void) memset((char *) row, 0, bpr);
+                        rcnt = 0;
+                    }
+                }
+                /*
+                 * Invert the flag that indicates whether bits need
+                 * to be set or not.
+                 */
+                set = !set;
+            }
+        }
+
+        /*
+         * Adjust the font bounding box.
+         */
+        f->bbx.ascent = MAX(g.bbx.ascent, f->bbx.ascent);
+        f->bbx.descent = MAX(g.bbx.descent, f->bbx.descent);
+
+        rb = g.bbx.width + g.bbx.x_offset;
+        maxrb = MAX(rb, maxrb);
+        minlb = MIN(g.bbx.x_offset, minlb);
+        maxlb = MAX(g.bbx.x_offset, maxlb);
+
+        /*
+         * Increase the average width count to be used later.
+         */
+        awidth += g.bbx.width;
+
+        if ((g.encoding < 0 || g.encoding > 65535) && opts->keep_unencoded) {
+            /*
+             * If the glyph is unencoded (encoding field < 0 or > 65535) and
+             * the unencoded glyphs should be kept, then add the glyph to the
+             * unencoded list of the font.
+             */
+            if (f->unencoded_used == f->unencoded_size) {
+                if (f->unencoded_size == 0)
+                  f->unencoded = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t));
+                else
+                  f->unencoded = (bdf_glyph_t *)
+                      realloc((char *) f->unencoded,
+                              sizeof(bdf_glyph_t) * (f->unencoded_size + 1));
+                f->unencoded_size++;
+            }
+            (void) memcpy((char *) (f->unencoded + f->unencoded_used),
+                          (char *) &g, sizeof(bdf_glyph_t));
+            f->unencoded_used++;
+        } else if (g.encoding >= 0 && g.encoding <= 65535) {
+            /*
+             * Add the glyph to the encoded list.
+             */
+            if (f->glyphs_used == f->glyphs_size) {
+                /*
+                 * Expand by 128 glyphs at a time.
+                 */
+                if (f->glyphs_used == 0)
+                  f->glyphs = (bdf_glyph_t *)
+                      malloc(sizeof(bdf_glyph_t) << 7);
+                else
+                  f->glyphs = (bdf_glyph_t *)
+                      realloc((char *) f->glyphs,
+                              sizeof(bdf_glyph_t) * (f->glyphs_used + 128));
+                gp = f->glyphs + f->glyphs_used;
+                (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) << 7);
+                f->glyphs_size += 128;
+            }
+            gp = f->glyphs + f->glyphs_used++;
+            (void) memcpy((char *) gp, (char *) &g, sizeof(bdf_glyph_t));;
+        } else {
+            /*
+             * Free up the memory allocated for the temporary glyph so it
+             * doesn't leak.
+             */
+            if (g.bytes > 0)
+              free((char *) g.bitmap);
+        }
+
+        /*
+         * Make sure the temporary glyph is reinitialized.
+         */
+        (void) memset((char *) &g, 0, sizeof(bdf_glyph_t));
+
+        /*
+         * Call the callback if indicated.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.total = st.st_size;
+            cb.current = ftell(in);
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Sort all the glyphs by encoding.
+     */
+    qsort((char *) f->glyphs, f->glyphs_used, sizeof(bdf_glyph_t),
+          by_encoding);
+
+    /*
+     * Adjust the font bounding box from the values collected.
+     */
+    f->bbx.width = maxrb - minlb;
+    f->bbx.height = f->bbx.ascent + f->bbx.descent;
+    f->bbx.x_offset = minlb;
+    f->bbx.y_offset = -f->bbx.descent;
+
+    /*
+     * Set the default character as being undefined.
+     */
+    f->default_glyph = -1;
+
+    /*
+     * Set the font ascent and descent.
+     */
+    f->font_ascent = f->bbx.ascent;
+    f->font_descent = f->bbx.descent;
+
+    /*
+     * Now add the properties to the font.
+     */
+    prop.name = "POINT_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->point_size * 10;
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Calculate and add the pixel size.
+     */
+    denom = (double) f->resolution_y;
+    dw = (double) (f->point_size * 10);
+    prop.name = "PIXEL_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int) (((denom * dw) / 722.7) + 0.5);
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_X";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) f->resolution_x;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_Y";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) f->resolution_y;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->font_ascent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->font_descent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "AVERAGE_WIDTH";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (awidth / (f->unencoded_used + f->glyphs_used)) * 10;
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Default all PK fonts to proportional spacing.
+     */
+    prop.name = "SPACING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = "P";
+    switch (f->spacing) {
+      case BDF_PROPORTIONAL: prop.value.atom = "P"; break;
+      case BDF_MONOWIDTH: prop.value.atom = "M"; break;
+      case BDF_CHARCELL: prop.value.atom = "C"; break;
+    }
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Free up the row buffer if it was allocated.
+     */
+    if (row_size > 0)
+      free((char *) row);
+
+    /*
+     * Call the callback one last time if necessary.
+     */
+    if (callback != 0 && cb.current != cb.total) {
+        cb.reason = BDF_LOADING;
+        cb.total = cb.current = st.st_size;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Add a message indicating the font was converted.
+     */
+    _bdf_add_comment(f, "Font converted from PK to BDF.", 30);
+    _bdf_add_acmsg(f, "Font converted from PK to BDF.", 30);
+
+    return BDF_OK;
+}
+
+static int
+_bdf_gf_specials(FILE *in, bdf_font_t *font, bdf_options_t *opts,
+                 unsigned char glyphname[])
+{
+    int c;
+    int i, n, num;
+    unsigned int comment_size;
+    unsigned char *comment, bytes[4];
+
+    /*
+     * Initialize the variable that keeps track of the storage allocated
+     * for the comments encountered.
+     */
+    comment = 0;
+    comment_size = 0;
+
+    while ((c = getc(in)) >= GF_xxx1) {
+        /*
+         * GF_xxx1 .. GF_xxx4 are string specials which will be added with the
+         * comments if comments are being kept.
+         */
+        if (c >= GF_xxx1 && c <= GF_xxx4) {
+            /*
+             * Determine the number of bytes that need to be read to determine
+             * the length of the string special.
+             */
+            n = (c - GF_xxx1) + 1;
+            fread((char *) bytes, n, 1, in);
+            for (i = 0, num = 0; i < n; i++)
+              num = (num << 8) | bytes[i];
+
+            if (opts->keep_comments) {
+                /*
+                 * Make sure there is enough space for the string.
+                 */
+                if (comment_size < num + 1) {
+                    if (comment_size == 0)
+                      comment = (unsigned char *) malloc(num + 1);
+                    else
+                      comment = (unsigned char *)
+                          realloc((char *) comment, num + 1);
+                    comment_size = num + 1;
+                }
+                /*
+                 * Read the comment and add it to the font.
+                 */
+                fread((char *) comment, num, 1, in);
+                comment[num] = 0;
+                if (!strncmp((char *) comment, "title ", 6))
+                  /*
+                   * The comment is the glyph's name/title; save it so
+                   * it can be associated with the forthcoming glyph,
+                   * rather than with the font as a whole.
+                   */
+                  strcpy((char *) glyphname, (char *) comment + 6);
+                else
+                  /*
+                   * A regular comment
+                   */
+                  _bdf_add_comment(font, (char *) comment, num);
+            } else
+              /*
+               * Skip the string special.
+               */
+              fseek(in, num, 1L);
+            c = GF_no_op;
+        }
+
+        /*
+         * GF_yyy is a numeric special.  Add the number as a comment if
+         * specified.
+         */
+        if (c == GF_yyy) {
+            num = _bdf_mf_get32(in);
+            if (opts->keep_comments) {
+                if (comment_size < 64) {
+                    if (comment_size == 0)
+                      comment = (unsigned char *) malloc(64);
+                    else
+                      comment = (unsigned char *)
+                          realloc((char *) comment, 64);
+                    comment_size = 64;
+                }
+                sprintf((char *) comment, "%d", num);
+                printscaled(num, comment + strlen((char *) comment));
+                _bdf_add_comment(font, (char *) comment,
+                                 strlen((char *) comment));
+            }
+            c = GF_no_op;
+        }
+        if (c != GF_no_op)
+          break;
+    }
+
+    /*
+     * Free up the comment buffer if it was allocated.
+     */
+    if (comment_size > 0)
+      free((char *) comment);
+
+    /*
+     * Return the byte that caused the loop to terminate.
+     */
+    return c;
+}
+
+static int
+_bdf_load_gf_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback,
+                  void *data, bdf_font_t **font)
+{
+    int n, res, set, x, y, bpr, ismono;
+    int awidth, num;
+    short rb, maxrb, minlb, maxlb;
+    double denom, dw;
+    bdf_font_t *f;
+    bdf_glyph_t *gp, g;
+    bdf_property_t prop;
+    bdf_callback_struct_t cb;
+    struct stat st;
+    unsigned char bytes[256], glyphname[256];
+
+    glyphname[0] = 0;
+
+    /*
+     * Create a font to work with.
+     */
+    *font = f = (bdf_font_t *) malloc(sizeof(bdf_font_t));
+    (void) memset((char *) f, 0, sizeof(bdf_font_t));
+
+    /*
+     * Set some defaults and allocate an initial amount of space.  Make the
+     * initial assumption that the font is monowidth but determine if it
+     * should be proportional when the glyphs are loaded.  Allocate space for
+     * at least 128 glyphs before loading the font.
+     */
+    f->bpp = 1;
+    f->spacing = BDF_MONOWIDTH;
+    f->glyphs_size = 128;
+    f->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) << 7);
+    (void) memset((char *) f->glyphs, 0, sizeof(bdf_glyph_t) << 7);
+
+    /*
+     * Initialize the horizontal and vertical offsets of the font to some
+     * large number.
+     */
+    f->bbx.x_offset = f->bbx.y_offset = 32767;
+
+    /*
+     * Initialize things.
+     */
+    ismono = 1;
+    awidth = 0;
+    rb = maxrb = maxlb = 0;
+    minlb = 32767;
+    (void) memset((char *) &g, 0, sizeof(bdf_glyph_t));
+
+    /*
+     * Load the initial comment.
+     */
+    fread((char *) bytes, 1, 1, in);
+    n = bytes[0];
+    fread(bytes, n, 1, in);
+    bytes[n] = 0;
+
+    /*
+     * Add the comment to the font if indicated.
+     */
+    if (opts->keep_comments)
+      _bdf_add_comment(f, (char *) bytes, (unsigned int) n);
+
+    /*
+     * Get the font info so we can set up the callback.  The callback will
+     * update after every glyph is loaded and is based on file size instead of
+     * number of glyphs.  This allows the font to be read all at once instead
+     * of twice.
+     */
+    (void) fstat(fileno(in), &st);
+
+    /*
+     * Set the callback up.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = st.st_size;
+        (*callback)(&cb, data);
+    }
+
+    while ((res = _bdf_gf_specials(in, f, opts, glyphname)) != GF_post) {
+       /* Set the glyph's name, if we've seen it */
+        if (((res == GF_boc) || (res == GF_boc1)) &&
+            strlen((char *) glyphname)) {
+            g.name = malloc(strlen((char *) glyphname)+1);
+           strcpy(g.name, (char *) glyphname);
+       }
+
+        if (res == GF_boc) {
+           /* 32-bit character code */
+            if ((g.encoding = _bdf_mf_get32(in) & 0xff) < 0)
+              g.encoding += 256;
+           /* Skip navigation pointer that's not relevant for us */
+            (void) _bdf_mf_get32(in);
+           /* 4 times 32-bit loose bounding box parameters */
+            g.bbx.x_offset = -((short) _bdf_mf_get32(in));
+            g.bbx.width =
+                (unsigned short) (_bdf_mf_get32(in) + g.bbx.x_offset);
+            g.bbx.y_offset = (short) _bdf_mf_get32(in);
+            g.bbx.height =
+                (unsigned short) (_bdf_mf_get32(in) - g.bbx.y_offset);
+            g.bbx.height++;
+            g.bbx.ascent = g.bbx.height + g.bbx.y_offset;
+            g.bbx.descent = -g.bbx.y_offset;
+        } else if (res == GF_boc1) {
+            g.encoding = getc(in);
+            g.bbx.width = getc(in);
+            g.bbx.x_offset = getc(in) - g.bbx.width;
+            g.bbx.height = getc(in);
+            g.bbx.y_offset = getc(in) - g.bbx.height;
+            g.bbx.height++;
+            g.bbx.width++;
+            g.bbx.ascent = g.bbx.height + g.bbx.y_offset;
+            g.bbx.descent = -g.bbx.y_offset;
+        }
+
+        /*
+         * Check to see if the font needs to be marked as proportional.
+         */
+        if (f->glyphs_used > 0 && ismono &&
+            (f->glyphs[0].bbx.width != g.bbx.width ||
+             f->glyphs[0].bbx.height != g.bbx.height ||
+             f->glyphs[0].bbx.x_offset != g.bbx.x_offset ||
+             f->glyphs[0].bbx.y_offset != g.bbx.y_offset ||
+             f->glyphs[0].bbx.ascent != g.bbx.ascent ||
+             f->glyphs[0].bbx.descent != g.bbx.descent)) {
+            ismono = 0;
+            f->spacing = BDF_PROPORTIONAL;
+        }
+
+        bpr = (g.bbx.width + 7) >> 3;
+        g.bytes = g.bbx.height * bpr;
+        g.bitmap = (unsigned char *) malloc(g.bytes);
+        (void) memset((char *) g.bitmap, 0, g.bytes);
+
+        /*
+         * Get the glyph.
+         */
+        set = x = y = 0;
+        while ((res = getc(in)) < GF_xxx1) {
+            if (res == GF_eoc)
+              break;
+            if (res < GF_paint3) {
+                switch (res) {
+                  case GF_paint1:
+                    res = getc(in);
+                    break;
+                  case GF_paint2:
+                    res = _bdf_mf_get16(in);
+                    break;
+                  case GF_paint3:
+                    res = (_bdf_mf_get16(in) << 8) | (getc(in) & 0xff);
+                    break;
+                }
+                for (; res > 0; x++, res--) {
+                    if (set)
+                      g.bitmap[(y * bpr) + (x >> 3)] |= (0x80 >> (x & 7));
+                }
+                set = !set;
+            } else if (GF_skip0 <= res && res <= GF_skip3) {
+                switch (res) {
+                  case GF_skip1:
+                    res = getc(in);
+                    break;
+                  case GF_skip2:
+                    res = _bdf_mf_get16(in);
+                    break;
+                  case GF_skip3:
+                    res = (_bdf_mf_get16(in) << 8) | (getc(in) & 0xff);
+                    break;
+                  default:
+                    res = 0;
+                }
+                x = 0;
+                y += res;
+                set = 0;
+            } else if (GF_newrow_0 <= res && res <= GF_newrow_164) {
+                y++;
+                x = res - GF_newrow_0;
+                set = 1;
+            }
+        }
+
+        /*
+         * Adjust the font bounding box.
+         */
+        f->bbx.ascent = MAX(g.bbx.ascent, f->bbx.ascent);
+        f->bbx.descent = MAX(g.bbx.descent, f->bbx.descent);
+
+        rb = g.bbx.width + g.bbx.x_offset;
+        maxrb = MAX(rb, maxrb);
+        minlb = MIN(g.bbx.x_offset, minlb);
+        maxlb = MAX(g.bbx.x_offset, maxlb);
+
+        /*
+         * Increase the average width count to be used later.
+         */
+        awidth += g.bbx.width;
+
+        if ((g.encoding < 0 || g.encoding > 65535) && opts->keep_unencoded) {
+            /*
+             * If the glyph is unencoded (encoding field < 0 or > 65535) and
+             * the unencoded glyphs should be kept, then add the glyph to the
+             * unencoded list of the font.
+             */
+            if (f->unencoded_used == f->unencoded_size) {
+                if (f->unencoded_size == 0)
+                  f->unencoded = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t));
+                else
+                  f->unencoded = (bdf_glyph_t *)
+                      realloc((char *) f->unencoded,
+                              sizeof(bdf_glyph_t) * (f->unencoded_size + 1));
+                f->unencoded_size++;
+            }
+            (void) memcpy((char *) (f->unencoded + f->unencoded_used),
+                          (char *) &g, sizeof(bdf_glyph_t));
+            f->unencoded_used++;
+        } else if (g.encoding >= 0 && g.encoding <= 65535) {
+            /*
+             * Add the glyph to the encoded list.
+             */
+            if (f->glyphs_used == f->glyphs_size) {
+                /*
+                 * Expand by 128 glyphs at a time.
+                 */
+                if (f->glyphs_used == 0)
+                  f->glyphs = (bdf_glyph_t *)
+                      malloc(sizeof(bdf_glyph_t) << 7);
+                else
+                  f->glyphs = (bdf_glyph_t *)
+                      realloc((char *) f->glyphs,
+                              sizeof(bdf_glyph_t) * (f->glyphs_used + 128));
+                gp = f->glyphs + f->glyphs_used;
+                (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) << 7);
+                f->glyphs_size += 128;
+            }
+            gp = f->glyphs + f->glyphs_used++;
+            (void) memcpy((char *) gp, (char *) &g, sizeof(bdf_glyph_t));;
+        } else {
+            /*
+             * Free up the memory allocated for the temporary glyph so it
+             * doesn't leak.
+             */
+            if (g.bytes > 0)
+              free((char *) g.bitmap);
+        }
+
+        /*
+         * Make sure the temporary glyph is reinitialized.
+         */
+        (void) memset((char *) &g, 0, sizeof(bdf_glyph_t));
+
+       /*
+        * We're done with this glyph; forget we've seen its name
+        */
+       glyphname[0] = 0;
+
+        /*
+         * Call the callback if indicated.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.total = st.st_size;
+            cb.current = ftell(in);
+            (*callback)(&cb, data);
+        }
+
+        if (res == GF_post)
+          break;
+    }
+
+    /*
+     * Sort all the glyphs by encoding.
+     */
+    qsort((char *) f->glyphs, f->glyphs_used, sizeof(bdf_glyph_t),
+          by_encoding);
+
+    /*
+     * Adjust the font bounding box from the values collected.
+     */
+    f->bbx.width = maxrb - minlb;
+    f->bbx.height = f->bbx.ascent + f->bbx.descent;
+    f->bbx.x_offset = minlb;
+    f->bbx.y_offset = -f->bbx.descent;
+
+    /*
+     * Set the default character as being undefined.
+     */
+    f->default_glyph = -1;
+
+    /*
+     * Set the font ascent and descent.
+     */
+    f->font_ascent = f->bbx.ascent;
+    f->font_descent = f->bbx.descent;
+
+    /*
+     * Collect the remaining font info from the postamble.
+     */
+    (void) _bdf_mf_get32(in);
+
+    /*
+     * Get the point size and scale it down 
+     */
+    f->point_size = (int) (((float) _bdf_mf_get32(in)) /
+                            ((float) (1 << 20)));
+
+    /*
+     * Skip the checksum.
+     */
+    fseek(in, 4, 1L);
+
+    /*
+     * Get the horizontal resolution.
+     */
+    f->resolution_x = (int)
+        (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5);
+
+    /*
+     * Get the vertical resolution.
+     */
+    f->resolution_y = (int)
+        (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5);
+
+    /*
+     * Skip the overall font rows and columns because they have already
+     * been determined.
+     */
+    fseek(in, 16, 1L);
+
+    /*
+     * Determine the denominator used for scalable width calculations.
+     */
+    denom = ((double) f->point_size) *
+        ((double) f->resolution_x);
+
+    /*
+     * Cycle through the glyph specific info and set the device and scalable
+     * widths.
+     */
+    while ((res = getc(in)) != GF_post_post) {
+        /*
+         * Get the encoding and locate it in the font.
+         */
+        num = getc(in);
+        for (set = 0, gp = f->glyphs;
+             set < f->glyphs_used && gp->encoding != num; gp++, set++) ;
+        /*
+         * If the glyph is not found for some reason, make the glyph pointer
+         * point to the temporary glyph storage.
+         */
+        if (set == f->glyphs_used)
+          gp = &g;
+
+        if (res == GF_char_loc) {
+            /*
+             * Get both horizontal and vertical escapement, only keeping
+             * the horizontal for the device width.
+             */
+            num = _bdf_mf_get32(in);
+            gp->dwidth = num >> 16;
+            dw = (double) gp->dwidth;
+            gp->swidth = (unsigned short) ((dw * 72000.0) / denom);
+            (void) _bdf_mf_get32(in);
+        } else if (res == GF_char_loc0) {
+            gp->dwidth = (unsigned short) getc(in);
+            dw = (double) gp->dwidth;
+            gp->swidth = (unsigned short) ((dw * 72000.0) / denom);
+        }
+
+        /*
+         * Skip the TFM width and the glyph file offset.
+         */
+        fseek(in, 8, 1L);
+
+        /*
+         * Call the callback if indicated.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.total = st.st_size;
+            cb.current = ftell(in);
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Now add the properties to the font.
+     */
+    prop.name = "POINT_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->point_size * 10;
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Calculate and add the pixel size.
+     */
+    denom = (double) f->resolution_y;
+    dw = (double) (f->point_size * 10);
+    prop.name = "PIXEL_SIZE";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (int) (((denom * dw) / 722.7) + 0.5);
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_X";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) f->resolution_x;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_Y";
+    prop.format = BDF_CARDINAL;
+    prop.value.card32 = (unsigned int) f->resolution_y;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->font_ascent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = f->font_descent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "AVERAGE_WIDTH";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = (awidth / (f->unencoded_used + f->glyphs_used)) * 10;
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Default all PK fonts to proportional spacing.
+     */
+    prop.name = "SPACING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = "P";
+    switch (f->spacing) {
+      case BDF_PROPORTIONAL: prop.value.atom = "P"; break;
+      case BDF_MONOWIDTH: prop.value.atom = "M"; break;
+      case BDF_CHARCELL: prop.value.atom = "C"; break;
+    }
+    bdf_add_font_property(f, &prop);
+
+    /*
+     * Call the callback one last time if necessary.
+     */
+    if (callback != 0 && cb.current != cb.total) {
+        cb.reason = BDF_LOADING;
+        cb.total = cb.current = st.st_size;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Add a message indicating the font was converted.
+     */
+    _bdf_add_comment(f, "Font converted from GF to BDF.", 30);
+    _bdf_add_acmsg(f, "Font converted from GF to BDF.", 30);
+
+    return BDF_OK;
+}
+
+int
+bdf_load_mf_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback,
+                 void *data, bdf_font_t **font)
+{
+    unsigned char mfmag[2];
+
+    /*
+     * Load the header to see if this is a GF or PK font.
+     */
+    fread((char *) mfmag, 2, 1, in);
+    if (mfmag[0] != GF_pre || (mfmag[1] != PK_id && mfmag[1] != 0x83))
+      return BDF_NOT_MF_FONT;
+    return (mfmag[1] == PK_id) ?
+        _bdf_load_pk_font(in, opts, callback, data, font) :
+        _bdf_load_gf_font(in, opts, callback, data, font);
+}
diff --git a/bdfpsf.c b/bdfpsf.c
new file mode 100644 (file)
index 0000000..66f8534
--- /dev/null
+++ b/bdfpsf.c
@@ -0,0 +1,971 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "bdfP.h"
+
+/**************************************************************************
+ *
+ * PSF1 Macros and values.
+ *
+ **************************************************************************/
+
+/*
+ * Macros that specify the indexes of the mode and height fields of the header
+ * info passed to the PSF1 loader.
+ */
+#define _BDF_PSF1MODE   2
+#define _BDF_PSF1HEIGHT 3
+
+/*
+ * Flags which can appear in the third byte of the header (PSF1 mode).  HAS512
+ * means the font contains up to 512 glyphs.  Otherwise the font has 256
+ * glyphs.  HASTAB and HASSEQ indicate the glyphs are followed by a Unicode
+ * mapping table.
+ *
+ * The HASTAB and HASSEQ flags appear to be essentially equivalent.
+ */
+#define _BDF_PSF1_HAS512 0x01
+#define _BDF_PSF1_HASTAB 0x02
+#define _BDF_PSF1_HASSEQ 0x04
+
+/**************************************************************************
+ *
+ * PSF2 Macros
+ *
+ **************************************************************************/
+
+#define _BDF_PSF2_HASTAB 0x01
+
+/*
+ * Little endian versions of the PSF magic numbers.
+ */
+unsigned char _bdf_psf1magic[] = {0x36, 0x04};
+unsigned char _bdf_psf2magic[] = {0x72, 0xb5, 0x4a, 0x86};
+
+/*
+ * The special header for PSF fonts that specify a list of partial
+ * fonts.
+ */
+char _bdf_psfcombined[] = {'#', ' ', 'c', 'o'};
+
+/*
+ * The PSF2 internal header.
+ */
+typedef struct {
+    unsigned int version;
+    unsigned int headersize;
+    unsigned int flags;
+    unsigned int length;
+    unsigned int bpc;
+    unsigned int height;
+    unsigned int width;
+} _bdf_psfhdr_t;
+
+/**************************************************************************
+ *
+ * Support functions.
+ *
+ **************************************************************************/
+
+#define _swap_endian(n) ((n) >> 16) | (((n) & 0xffff) << 16)
+
+static int
+_bdf_psf_load_map(FILE *in, bdf_font_t *font, int psf2, int *res)
+{
+    int i, more, c0, c1, cnt;
+    unsigned int code;
+    unsigned char buf[4];
+    bdf_glyph_t *gp;
+
+    gp = font->glyphs;
+
+    while ((c0 = getc(in)) >= 0) {
+        /*
+         * If we are still reading bytes after the end of the glyphs,
+         * the table is too int.
+         */
+        if (gp == font->glyphs + font->glyphs_used)
+          return BDF_PSF_LONG_TABLE;
+
+        cnt = 0;
+        *res = gp->encoding;
+        if (!psf2) {
+            if ((c1 = getc(in)) < 0)
+              return BDF_PSF_SHORT_TABLE;
+            if (bdf_little_endian())
+              code = (c1 << 8) | (c0 & 0xff);
+            else
+              code = (c0 << 8) | (c1 & 0xff);
+
+            /*
+             * Convert to UTF-8.
+             */
+            if (code != 0xffff) {
+                if (code < 0x80)
+                  buf[cnt++] = code & 0xff;
+                else if (code < 0x800) {
+                    buf[cnt++] = 0xc0 | (code >> 6);
+                    buf[cnt++] = 0x80 | (code & 0x3f);
+                } else if (code < 0x10000) {
+                    buf[cnt++] = 0xe0 | (code >> 12);
+                    buf[cnt++] = 0x80 | ((code >> 6) & 0x3f);
+                    buf[cnt++] = 0x80 | (code & 0x3f);
+                } else if (code < 0x200000) {
+                    buf[cnt++] = 0xf0 | (code >> 18);
+                    buf[cnt++] = 0x80 | ((code >> 12) & 0x3f);
+                    buf[cnt++] = 0x80 | ((code >> 6) & 0x3f);
+                    buf[cnt++] = 0x80 | (code & 0x3f);
+                } else if (code < 0x4000000) {
+                    buf[cnt++] = 0xf8 | (code >> 24);
+                    buf[cnt++] = 0x80 | ((code >> 18) & 0x3f);
+                    buf[cnt++] = 0x80 | ((code >> 12) & 0x3f);
+                    buf[cnt++] = 0x80 | ((code >> 6) & 0x3f);
+                    buf[cnt++] = 0x80 | (code & 0x3f);
+                } else if (code < 0x7fffffff) {
+                    buf[cnt++] = 0xfc | (code >> 30);
+                    buf[cnt++] = 0x80 | ((code >> 24) & 0x3f);
+                    buf[cnt++] = 0x80 | ((code >> 18) & 0x3f);
+                    buf[cnt++] = 0x80 | ((code >> 12) & 0x3f);
+                    buf[cnt++] = 0x80 | ((code >> 6) & 0x3f);
+                    buf[cnt++] = 0x80 | (code & 0x3f);
+                }
+            } else
+              buf[cnt++] = 0xff;
+        } else {
+            buf[cnt++] = c0;
+            if (c0 < 0xfd && (c0 & 0x80) != 0) {
+                /*
+                 * Only look for more if the byte is not 0xfe or 0xff,
+                 * PSF separators.
+                 */
+                more = 0;
+                if ((c0 & 0xe0) == 0xc0)
+                  more = 1;
+                else if ((c0 & 0xf0) == 0xe0)
+                  more = 2;
+                else if ((c0 & 0xf0) == 0xf0)
+                  more = 3;
+
+                for (i = 0; i < more; i++) {
+                    if ((c0 = getc(in)) < 0)
+                      return BDF_PSF_SHORT_TABLE;
+                    else if (c0 > 0xfd)
+                      return BDF_PSF_CORRUPT_UTF8;
+                    buf[cnt++] = c0;
+                }
+            }
+        }
+        if (buf[0] != 0xff) {
+            if (gp->unicode.map_used + cnt > gp->unicode.map_size) {
+                more = ((cnt >> 2) + ((cnt & 3) ? 1 : 0)) << 2;
+                if (gp->unicode.map_size == 0)
+                  gp->unicode.map = (unsigned char *)
+                      malloc(sizeof(unsigned char) * more);
+                else
+                  gp->unicode.map = (unsigned char *)
+                      realloc((char *) gp->unicode.map,
+                              sizeof(unsigned char) *
+                              (gp->unicode.map_size + more));
+                gp->unicode.map_size += more;
+            }
+            (void) memcpy((char *) (gp->unicode.map + gp->unicode.map_used),
+                          (char *) buf, sizeof(unsigned char) * cnt);
+            gp->unicode.map_used += cnt;
+        } else
+          gp++;
+
+    }
+    return BDF_OK;
+}
+
+static int
+_bdf_psf_dump_map(FILE *out, bdf_font_t *font, bdf_glyphlist_t *glyphs)
+{
+    int seq;
+    unsigned int i, nglyphs, n;
+    int code = -1;
+    unsigned char *map, *map_end;
+    bdf_glyph_t *gp;
+
+    nglyphs = (glyphs->glyphs_used > 512) ? 512 : glyphs->glyphs_used;
+    for (i = 0, gp = glyphs->glyphs; i < nglyphs; i++, gp++) {
+
+        if (nglyphs > 256)
+          fprintf(out, "0x%03x", i);
+        else
+          fprintf(out, "0x%02x", i);
+
+        map = gp->unicode.map;
+        map_end = map + gp->unicode.map_used;
+
+        seq = 0;
+        while (map < map_end) {
+            n = 1;
+
+            if (*map == 0xfe) {
+                seq = 2;
+                map++;
+                continue;
+            }
+
+            /*
+             * Convert from UTF-8 to UTF-32.
+             */
+            if ((*map & 0x80) == 0)
+              /*
+               * One byte character.
+               */
+              code = (unsigned short) *map;
+            else if (*map < 0xe0) {
+                /*
+                 * Two byte character.
+                 */
+                if (map + 2 >= map_end)
+                  return BDF_PSF_CORRUPT_UTF8;
+                code = ((*map & 0x1f) << 6) | (*(map + 1) & 0x3f);
+                n = 2;
+            } else if (*map < 0xf0) {
+                /*
+                 * Three byte character.
+                 */
+                if (map + 3 >= map_end)
+                  return BDF_PSF_CORRUPT_UTF8;
+                code = ((*map & 0x0f) << 12) |
+                    ((*(map + 1) & 0x3f) << 6) | (*(map + 2) & 0x3f);
+                n = 3;
+            } else if (*map < 0xf8) {
+                /*
+                 * Four byte character.
+                 */
+                if (map + 4 >= map_end)
+                  return BDF_PSF_CORRUPT_UTF8;
+                code = ((*map & 0x07) << 18) |
+                    ((*(map + 1) & 0x3f) << 12) |
+                    ((*(map + 2) & 0x3f) << 6) | (*(map + 3) & 0x3f);
+                n = 4;
+            } else if (*map < 0xfc) {
+                /*
+                 * Five byte character.
+                 */
+                if (map + 5 >= map_end)
+                  return BDF_PSF_CORRUPT_UTF8;
+                code = ((*map & 0x03) << 24) |
+                    ((*(map + 1) & 0x3f) << 18) |
+                    ((*(map + 2) & 0x3f) << 12) |
+                    ((*(map + 3) & 0x3f) << 6) |
+                    (*(map + 4) & 0x3f);
+                n = 5;
+            } else if (*map < 0xfe) {
+                /*
+                 * Six byte character.
+                 */
+                if (map + 6 >= map_end)
+                  return BDF_PSF_CORRUPT_UTF8;
+                code = ((*map & 0x01) << 30) |
+                    ((*(map + 1) & 0x3f) << 24) |
+                    ((*(map + 2) & 0x3f) << 18) |
+                    ((*(map + 3) & 0x3f) << 12) |
+                    ((*(map + 4) & 0x3f) << 6) |
+                    (*(map + 5) & 0x3f);
+                n = 6;
+            }
+            /*
+             * Print the code(s).  If we are printing the first one,
+             * then print a tab, otherwise we are printing separating
+             * spaces.
+             */
+            if (map == gp->unicode.map)
+              putc('\t', out);
+            else
+              putc(((seq == 1) ? ',' : ' '), out);
+            if (n < 5)
+              fprintf(out, "U+%04X", code);
+            else
+              fprintf(out, "U+%06X", code);
+            map += n;
+            seq -= (seq == 2);
+        }
+
+        /*
+         * Print the line separator.
+         */
+        putc('\n', out);
+
+        /*
+         * Free the current glyph storage.
+         */
+        if (gp->name != 0)
+          free(gp->name);
+        if (gp->bytes > 0)
+          free((char *) gp->bitmap);
+        if (gp->unicode.map_size > 0)
+          free((char *) gp->unicode.map);
+    }
+
+    /*
+     * Free the storage for the glyph list.
+     */
+    if (glyphs->glyphs_size > 0)
+      free((char *) glyphs->glyphs);
+
+    return BDF_OK;
+}
+
+/**************************************************************************
+ *
+ * Public functions.
+ *
+ **************************************************************************/
+
+/*
+ * Return an array of strings with the Unicode encodings already formatted for
+ * use with the GUI.
+ */
+char **
+_bdf_psf_unpack_mapping(bdf_psf_unimap_t *unimap, int *num_seq)
+{
+    int ns, nc, sum, c;
+    int code = -1;
+    unsigned char *mp, *ep;
+    char **list, *lp;
+
+    list = 0;
+    if (!num_seq)
+      return list;
+    *num_seq = 0;
+
+    if (unimap == 0 || unimap->map_used == 0)
+      return list;
+
+    /*
+     * This routine will calculate the amount of storage is needed to be
+     * allocated as one big block so an array of strings can be returned.
+     * That way it can be deallocated as a single item by the caller.
+     */
+
+
+    /*
+     * Count the total number of characters and sequences at the same time.
+     */
+    mp = unimap->map;
+    ep = mp + unimap->map_used;
+    sum = ns = nc = 0;
+    while (mp < ep) {
+        if (*mp == 0xfe) {
+            sum = 1;
+            ns++;
+            mp++;
+            continue;
+        }
+
+        c = 1;
+        if (*mp < 0xe0)
+          c = 2;
+        else if (*mp < 0xf0)
+          c = 3;
+        else if (*mp < 0xf8)
+          c = 4;
+        else if (*mp < 0xfc)
+          c = 5;
+        else if (*mp < 0xfe)
+          c = 6;
+
+        nc++;
+
+        mp += c;
+        ns += !sum;
+    }
+
+    *num_seq = ns;
+
+    /*
+     * The block of storage will need this many bytes for pointers to the
+     * sequences.
+     */
+    sum = sizeof(unsigned char *) * ns;
+
+    /*
+     * Each character uses up to 8 bytes in U+XXXXXX form and each is followed
+     * by either space or a null, so each is basically nine bytes including
+     * the trailing NULL.
+     */
+    sum += nc * 9;
+
+    list = (char **) malloc(sum);
+    lp = (char *) (list + ns);
+
+    /*
+     * Now generate the codes one at a time.
+     */
+    list[0] = lp;
+
+    ns = sum = 0;
+    mp = unimap->map;
+    while (mp < ep) {
+        if (*mp == 0xfe) {
+            if (sum == 1)
+              /*
+               * The last thing added was a sequence, so move up to the
+               * next sequence.
+               */
+              list[++ns] = ++lp;
+
+            sum = 1;
+            mp++;
+            continue;
+        }
+
+        nc = 1;
+
+        /*
+         * Convert from UTF-8 to UTF-32.
+         */
+        if (*mp < 0x80)
+          /*
+           * One byte character.
+           */
+          code = (unsigned short) *mp;
+        else if (*mp < 0xe0) {
+            /*
+             * Two byte character.
+             */
+            code = ((*mp & 0x1f) << 6) | (*(mp + 1) & 0x3f);
+            nc = 2;
+        } else if (*mp < 0xf0) {
+            /*
+             * Three byte character.
+             */
+            code = ((*mp & 0x0f) << 12) |
+                ((*(mp + 1) & 0x3f) << 6) | (*(mp + 2) & 0x3f);
+            nc = 3;
+        } else if (*mp < 0xf8) {
+            /*
+             * Four byte character.
+             */
+            code = ((*mp & 0x07) << 18) |
+                ((*(mp + 1) & 0x3f) << 12) |
+                ((*(mp + 2) & 0x3f) << 6) | (*(mp + 3) & 0x3f);
+            nc = 4;
+        } else if (*mp < 0xfc) {
+            /*
+             * Five byte character.
+             */
+            code = ((*mp & 0x03) << 24) |
+                ((*(mp + 1) & 0x3f) << 18) |
+                ((*(mp + 2) & 0x3f) << 12) |
+                ((*(mp + 3) & 0x3f) << 6) |
+                (*(mp + 4) & 0x3f);
+            nc = 5;
+        } else if (*mp < 0xfe) {
+            /*
+             * Six byte character.
+             */
+            code = ((*mp & 0x01) << 30) |
+                ((*(mp + 1) & 0x3f) << 24) |
+                ((*(mp + 2) & 0x3f) << 18) |
+                ((*(mp + 3) & 0x3f) << 12) |
+                ((*(mp + 4) & 0x3f) << 6) |
+                (*(mp + 5) & 0x3f);
+            nc = 6;
+        }
+
+        /*
+         * Add to the string.
+         */
+
+        if (lp > list[ns])
+          *lp++ = ' ';
+        if (nc < 5)
+          sprintf(lp, "U+%04X", code);
+        else
+          sprintf(lp, "U+%06X", code);
+        lp += 7;
+
+        mp += nc;
+
+        if (mp < ep && !sum)
+          list[++ns] = ++lp;
+    }
+    return list;
+}
+
+/*
+ * Routine used to insure the list of mappings is in ascending order
+ * by length of string.
+ */
+static int
+cmplen(const void *a, const void *b)
+{
+    int n;
+    char *as = *((char **)a);
+    char *bs = *((char **)b);
+
+    n = strlen(as) - strlen(bs);
+    return (n) ? n : strcmp(as, bs);
+}
+
+/*
+ * Taking a list of strings, generate a packed UTF-8 representation to
+ * be stored back into a Unicode map.
+ */
+int
+_bdf_psf_pack_mapping(char **list, int len, int encoding,
+                      bdf_psf_unimap_t *map)
+{
+    int i, j, ncodes, bytes = 3;
+    char *lp, *elp;
+    unsigned int codes[128];
+
+    if (list == 0 || len == 0 || map == 0)
+      return 0;
+
+    map->map_used = 0;
+
+    /*
+     * First thing that needs to be done is to make sure the list is sorted by
+     * length so the single character mappings come first.
+     */
+    qsort((char *) list, len, sizeof(char *), cmplen);
+
+    for (i = 0; i < len; i++) {
+        lp = list[i];
+        ncodes = 0;
+        while (*lp) {
+            /*
+             * Skip anything that isn't expected.
+             */
+            while (*lp && *lp != 'U' && *lp != 'u' && *lp != '0')
+              lp++;
+            if (*lp == 0)
+              continue;
+            codes[ncodes] = _bdf_atoul(lp, &elp, 16);
+
+            /*
+             * Determine how many UTF-8 bytes the current code will
+             * take.
+             */
+            if (codes[ncodes] < 0x80)
+              bytes++;
+            else if (codes[ncodes] < 0x800)
+              bytes += 2;
+            else if (codes[ncodes] < 0x10000)
+              bytes += 3;
+            else if (codes[ncodes] < 0x200000)
+              bytes += 4;
+            else if (codes[ncodes] < 0x4000000)
+              bytes += 5;
+            else if (codes[ncodes] < 0x7fffffff)
+              bytes += 6;
+            ncodes++;
+            lp = elp;
+        }
+        /*
+         * Make sure there is enough room in the map for this number of bytes.
+         * The number includes the encoding and the 0xff at the end on the
+         * first pass.
+         */
+        if (map->map_used + bytes > map->map_size) {
+            if (map->map_size == 0)
+              map->map = (unsigned char *)
+                  malloc(sizeof(unsigned char *) * 128);
+            else
+              map->map = (unsigned char *)
+                  realloc((char *) map->map,
+                          sizeof(unsigned char) * (map->map_size + 128));
+            map->map_size += 128;
+        }
+
+        if (ncodes > 1)
+          /*
+           * Have to increment the number of bytes by 1 to include the
+           * PSF2 sequence marker.
+           */
+          map->map[map->map_used++] = 0xfe;
+
+        /*
+         * Go through the codes and convert to UTF-8.
+         */
+        for (j = 0; j < ncodes; j++) {
+            if (codes[j] < 0x80)
+              map->map[map->map_used++] = (codes[j] & 0x7f);
+            else if (codes[j] < 0x800) {
+                map->map[map->map_used++] = 0xc0 | ((codes[j] >> 6) & 0xff);
+                map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f);
+            } else if (codes[j] < 0x10000) {
+                map->map[map->map_used++] = 0xe0 | ((codes[j] >> 12) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f);
+                map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f);
+            } else if (codes[j] < 0x200000) {
+                map->map[map->map_used++] = 0xf0 | ((codes[j] >> 18) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 12) & 0x3f);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f);
+                map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f);
+            } else if (codes[j] < 0x4000000) {
+                map->map[map->map_used++] = 0xf8 | ((codes[j] >> 24) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 18) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 12) & 0x3f);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f);
+                map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f);
+            } else if (codes[j] < 0x7fffffff) {
+                map->map[map->map_used++] = 0xfc | ((codes[j] >> 30) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 24) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 18) & 0xff);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 12) & 0x3f);
+                map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f);
+                map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f);
+            }
+        }
+        bytes = 0;
+    }
+
+    return BDF_OK;
+}
+
+bdf_font_t *
+bdf_load_psf(FILE *in, unsigned char *magic, bdf_options_t *opts,
+             bdf_callback_t callback, void *data, int *awidth)
+{
+    int i, enc;
+    unsigned short dwidth, swidth;
+    bdf_glyph_t *gp;
+    bdf_font_t *fp;
+    _bdf_psfhdr_t hdr;
+    bdf_callback_struct_t cb;
+    char msgbuf[1024];
+
+    /*
+     * Check options for loading the Unicode table or not.
+     */
+
+    if (*magic == 0x36) {
+        /*
+         * PSF1 font.
+         */
+        hdr.version = 0;
+        hdr.width = 8;
+        hdr.height = hdr.bpc = (int) magic[_BDF_PSF1HEIGHT];
+        hdr.length = (magic[_BDF_PSF1MODE] & _BDF_PSF1_HAS512) ? 512 : 256;
+        hdr.flags = (magic[_BDF_PSF1MODE] & _BDF_PSF1_HASTAB) ?
+            _BDF_PSF2_HASTAB : 0;
+    } else {
+        /*
+         * PSF2 font.
+         */
+        fread((char *) &hdr, sizeof(_bdf_psfhdr_t), 1, in);
+        if (!bdf_little_endian()) {
+            /*
+             * Need to convert all the integers to big endian.
+             */
+            hdr.version = _swap_endian(hdr.version);
+            hdr.headersize = _swap_endian(hdr.headersize);
+            hdr.flags = _swap_endian(hdr.flags);
+            hdr.length = _swap_endian(hdr.length);
+            hdr.bpc = _swap_endian(hdr.bpc);
+            hdr.height = _swap_endian(hdr.height);
+            hdr.width = _swap_endian(hdr.width);
+        }
+    }
+
+    /*
+     * The point size of the font will be the height, the resolution will
+     * default to 72dpi, and the spacing will default to character cell.
+     */
+    fp = bdf_new_font(0, (int) hdr.height, 72, 72, BDF_CHARCELL, 1);
+
+    /*
+     * Force the bits per pixel to be 1.
+     */
+    fp->bpp = 1;
+
+    /*
+     * Set the font width and average width.
+     */
+    *awidth = fp->bbx.width = hdr.width;
+
+    /*
+     * Set the rest of the font bounding box parameters.
+     */
+    fp->font_ascent = fp->bbx.ascent;
+    fp->font_descent = fp->bbx.descent;
+
+#if 0
+    /*
+     * MAY NOT BE NEEDED ANY MORE.
+     */
+
+    /*
+     * Adjust the ascent and descent by hand for point sizes other than 16.
+     */
+    if (hdr.height != 16) {
+        fp->bbx.ascent++;
+        fp->bbx.descent--;
+    }
+#endif
+
+    /*
+     * Default the font ascent and descent to that of the bounding box.
+     */
+    fp->font_ascent = fp->bbx.ascent;
+    fp->font_descent = fp->bbx.descent;
+
+    /*
+     * Allocate the expected number of glyphs.
+     */
+    fp->glyphs_size = hdr.length;
+    fp->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * fp->glyphs_size);
+    (void) memset((char *) fp->glyphs, 0,
+                  sizeof(bdf_glyph_t) * fp->glyphs_size);
+
+    /*
+     * Determine the default scalable and device width for each character.
+     */
+    dwidth = fp->bbx.width;
+    swidth = (unsigned short)
+        (((double) dwidth) * 72000.0) /
+        ((double) fp->point_size * fp->resolution_x);
+
+    /*
+     * Set up to call the callback.
+     */
+    if (callback != 0) {
+        cb.reason = BDF_LOAD_START;
+        cb.current = 0;
+        cb.total = fp->glyphs_size;
+        (*callback)(&cb, data);
+    }
+
+    /*
+     * Now load the glyphs, assigning a default encoding.
+     */
+    for (i = 0, gp = fp->glyphs; i < fp->glyphs_size; i++, gp++) {
+        gp->encoding = i;
+        gp->dwidth = dwidth;
+        gp->swidth = swidth;
+        (void) memcpy((char *) &gp->bbx, (char *) &fp->bbx, sizeof(bdf_bbx_t));
+
+        gp->bytes = hdr.bpc;
+        gp->bitmap = (unsigned char *) malloc(hdr.bpc);
+        fread((char *) gp->bitmap, hdr.bpc, 1, in);
+        fp->glyphs_used++;
+
+        /*
+         * Call the callback if indicated.
+         */
+        if (callback != 0) {
+            cb.reason = BDF_LOADING;
+            cb.total = fp->glyphs_size;
+            cb.current = fp->glyphs_used;
+            (*callback)(&cb, data);
+        }
+    }
+
+    /*
+     * Now load the Unicode mapping table if it exists.
+     */
+    if (hdr.flags & _BDF_PSF2_HASTAB) {
+        msgbuf[0] = 0;
+        switch (_bdf_psf_load_map(in, fp, (*magic == 0x72), &enc)) {
+          case BDF_PSF_SHORT_TABLE:
+            sprintf(msgbuf, "PSF Unicode table too short at 0x%04X (%d).",
+                    (unsigned short) (enc & 0xffff), enc);
+            break;
+          case BDF_PSF_LONG_TABLE:
+            strcpy(msgbuf, "PSF Unicode table too int.");
+            break;
+          case BDF_PSF_CORRUPT_UTF8:
+            sprintf(msgbuf, "PSF UTF-8 sequence corrupt at 0x%04X (%d).",
+                    (unsigned short) (enc & 0xffff), enc);
+            break;
+          case BDF_PSF_BUFFER_OVRFL:
+            sprintf(msgbuf, "PSF mapping buffer overflow at 0x%04X (%d).",
+                    (unsigned short) (enc & 0xffff), enc);
+            break;
+        }
+        if (msgbuf[0] != 0)
+          _bdf_add_acmsg(fp, msgbuf, strlen(msgbuf));
+    }
+
+    sprintf(msgbuf, "Font converted from PSF%c to BDF.",
+            (*magic == 0x36) ? '1' : '2');
+    _bdf_add_comment(fp, msgbuf, 32);
+    _bdf_add_acmsg(fp, msgbuf, 32);
+
+    return fp;
+}
+
+/*
+ * Exports all PSF fonts in PSF2 format for now.  start and end are
+ * supplied when a partial font needs to be created.
+ */
+int
+bdf_export_psf(FILE *out, bdf_font_t *font, bdf_options_t *opts, int start,
+               int end)
+{
+    unsigned int i, nglyphs, flags;
+    _bdf_psfhdr_t hdr;
+    bdf_glyph_t *gp;
+    bdf_font_t tmpfont;
+    bdf_glyphlist_t glyphs;
+    bdf_glyph_t cell;
+
+    if (font->glyphs_used == 0)
+      return BDF_EMPTY_FONT;
+
+    /*
+     * This routine only exports from CHARCELL and MONOWIDTH fonts, padding
+     * the glyphs as it writes.
+     */
+    if (font->spacing == BDF_PROPORTIONAL)
+      return BDF_EMPTY_FONT;
+
+    if (start == end)
+      return BDF_BAD_RANGE;
+
+    /*
+     * Make a copy of the glyphs so we can get the smallest bounding box for
+     * the glyphs being exported.  This also does a bit of range checking.
+     */
+    (void) memset((char *) &glyphs, 0, sizeof(bdf_glyphlist_t));
+    bdf_copy_glyphs(font, start, end, &glyphs, 0);
+
+    /*
+     * At this point, if only the Unicode table is desired, then
+     * call the routine that prints the plain text version.
+     */
+    if ((opts->psf_flags == BDF_PSF_UNIMAP))
+      return _bdf_psf_dump_map(out, font, &glyphs);
+
+    /*
+     * Set up the temporary font so glyph padding will happen like it is
+     * supposed to.
+     */
+    tmpfont.bpp = glyphs.bpp;
+    (void) memcpy((char *) &tmpfont.bbx, (char *) &glyphs.bbx,
+                  sizeof(bdf_bbx_t));
+
+    /*
+     * Create the header.  The extra 4 on the header size account
+     * for the magic number.
+     *
+     * Number of glyphs and flags have to be calculated properly before writing
+     * so it isn't necessary to go back and rewrite the header after the font
+     * has been written.  That causes havoc when writing to stdout.
+     */
+    hdr.version = hdr.flags = 0;
+    hdr.headersize = sizeof(_bdf_psfhdr_t) + 4;
+    hdr.length = (glyphs.glyphs_used > 512) ? 512 : glyphs.glyphs_used;
+    hdr.width = glyphs.bbx.width;
+    hdr.height = glyphs.bbx.height;
+    hdr.bpc = hdr.height * ((hdr.width + 7) >> 3);
+
+    /*
+     * Determine if the font will have a Unicode mapping table.
+     */
+    for (i = 0; i < hdr.length; i++) {
+        if (glyphs.glyphs[i].unicode.map_used > 0) {
+            hdr.flags |= _BDF_PSF2_HASTAB;
+            break;
+        }
+    }
+
+    /*
+     * Save these values so it doesn't get whacked in an endian conversion.
+     */
+    nglyphs = hdr.length;
+    flags = hdr.flags;
+
+    /*
+     * Set up a structure for padding glyphs to cell boundaries.
+     */
+    cell.bytes = hdr.bpc;
+    cell.bitmap = (unsigned char *) malloc(cell.bytes);
+    (void) memcpy((char *) &cell.bbx, (char *) &glyphs.bbx, sizeof(bdf_bbx_t));
+
+    if (!bdf_little_endian()) {
+        /*
+         * Swap the integers into little endian order before writing.
+         */
+        hdr.version = _swap_endian(hdr.version);
+        hdr.headersize = _swap_endian(hdr.headersize);
+        hdr.flags = _swap_endian(hdr.flags);
+        hdr.length = _swap_endian(hdr.length);
+        hdr.bpc = _swap_endian(hdr.bpc);
+        hdr.height = _swap_endian(hdr.height);
+        hdr.width = _swap_endian(hdr.width);
+    }
+
+    /*
+     * Write the header.
+     */
+    fwrite((char *) _bdf_psf2magic, sizeof(unsigned char), 4, out);
+    fwrite((char *) &hdr, sizeof(_bdf_psfhdr_t), 1, out);
+
+    /*
+     * Generate the glyphs, padding them out to the dimensions of the
+     * font.
+     */
+    for (i = 0, gp = glyphs.glyphs; i < nglyphs; i++, gp++) {
+        /*
+         * We only need to do cropping on CHARCELL glyphs because MONOWIDTH
+         * glyphs are already cropped to their minimum dimensions.
+         */
+        if (font->spacing == BDF_CHARCELL)
+          _bdf_crop_glyph(&tmpfont, gp);
+        _bdf_pad_cell(&tmpfont, gp, &cell);
+        fwrite((char *) cell.bitmap, sizeof(unsigned char), cell.bytes, out);
+    }
+
+    /*
+     * Now generate the Unicode table if called for.
+     */
+    if ((opts->psf_flags & BDF_PSF_UNIMAP) && (flags & _BDF_PSF2_HASTAB)) {
+        for (gp = glyphs.glyphs, i = 0; i < nglyphs; i++, gp++) {
+            if (gp->unicode.map_used > 0)
+              fwrite((char *) gp->unicode.map, sizeof(unsigned char),
+                     gp->unicode.map_used, out);
+            putc(0xff, out);
+        }
+    }
+
+    /*
+     * Finally, dispose of the glyph copies.
+     */
+    for (i = 0, gp = glyphs.glyphs; i < glyphs.glyphs_used; i++, gp++) {
+        if (gp->name != 0)
+          free(gp->name);
+        if (gp->bytes > 0)
+          free((char *) gp->bitmap);
+        if (gp->unicode.map_size > 0)
+          free((char *) gp->unicode.map);
+    }
+    if (glyphs.glyphs_size > 0)
+      free((char *) glyphs.glyphs);
+
+    return BDF_OK;
+}
+
+#undef _swap_endian
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..3c5c22f
--- /dev/null
+++ b/configure
@@ -0,0 +1,7086 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.61.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+  if (eval ":") 2>/dev/null; then
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+
+  if test $as_have_required = yes &&    (eval ":
+(as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=\$LINENO
+  as_lineno_2=\$LINENO
+  test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+  test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+  :
+else
+  as_candidate_shells=
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  case $as_dir in
+        /*)
+          for as_base in sh bash ksh sh5; do
+            as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+          done;;
+       esac
+done
+IFS=$as_save_IFS
+
+
+      for as_shell in $as_candidate_shells $SHELL; do
+        # Try only shells that exist, to save several forks.
+        if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+               { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+  CONFIG_SHELL=$as_shell
+              as_have_required=yes
+              if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+  (exit $1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+  break
+fi
+
+fi
+
+      done
+
+      if test "x$CONFIG_SHELL" != x; then
+  for as_var in BASH_ENV ENV
+        do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+        done
+        export CONFIG_SHELL
+        exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+    if test $as_have_required = no; then
+  echo This script requires a shell more modern than all the
+      echo shells that I found on your system.  Please install a
+      echo modern shell, or manually run the script under such a
+      echo shell if you do have one.
+      { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+  echo No shell found that supports shell functions.
+  echo Please tell autoconf@gnu.org about your system,
+  echo including any error possibly output before this
+  echo message
+}
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s='ln -s'
+  # ... but there are two gotchas:
+  # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+  # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+  # In both cases, we have to default to `cp -p'.
+  ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+    as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+        test -d "$1/.";
+      else
+       case $1 in
+        -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="gbdfed.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL
+PATH_SEPARATOR
+PACKAGE_NAME
+PACKAGE_TARNAME
+PACKAGE_VERSION
+PACKAGE_STRING
+PACKAGE_BUGREPORT
+exec_prefix
+prefix
+program_transform_name
+bindir
+sbindir
+libexecdir
+datarootdir
+datadir
+sysconfdir
+sharedstatedir
+localstatedir
+includedir
+oldincludedir
+docdir
+infodir
+htmldir
+dvidir
+pdfdir
+psdir
+libdir
+localedir
+mandir
+DEFS
+ECHO_C
+ECHO_N
+ECHO_T
+LIBS
+build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+CPPFLAGS
+ac_ct_CC
+EXEEXT
+OBJEXT
+XX_CFLAGS
+RM
+CP
+CPP
+GREP
+EGREP
+LIBOBJS
+PKG_CONFIG
+FREETYPE_CFLAGS
+FREETYPE_LIBS
+GTK_CFLAGS
+GTK_LIBS
+XMKMF
+X_CFLAGS
+X_PRE_LIBS
+X_LIBS
+X_EXTRA_LIBS
+DEFINES
+HBFSRC
+HBFOBJ
+BDFGRABSRC
+BDFGRABOBJ
+LTLIBOBJS'
+ac_subst_files=''
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+XMKMF'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *)   ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+    eval enable_$ac_feature=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+    eval enable_$ac_feature=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+    eval with_$ac_package=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+    eval with_$ac_package=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute directory names.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  { echo "$as_me: error: Working directory cannot be determined" >&2
+   { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  { echo "$as_me: error: pwd does not report name of working directory" >&2
+   { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$0" ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$0" : 'X\(//\)[^/]' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2
+   { (exit 1); exit 1; }; }
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                         [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                         [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --datarootdir=DIR      read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR          read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR          info documentation [DATAROOTDIR/info]
+  --localedir=DIR        locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR           man documentation [DATAROOTDIR/man]
+  --docdir=DIR           documentation root [DATAROOTDIR/doc/PACKAGE]
+  --htmldir=DIR          html documentation [DOCDIR]
+  --dvidir=DIR           dvi documentation [DOCDIR]
+  --pdfdir=DIR           pdf documentation [DOCDIR]
+  --psdir=DIR            ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+X features:
+  --x-includes=DIR    X include files are in DIR
+  --x-libraries=DIR   X library files are in DIR
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-x                use the X Window System
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  XMKMF       Path to xmkmf, Makefile generator for X Window System
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" || continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.61
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.61.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      ac_configure_args="$ac_configure_args '$ac_arg'"
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+  set x "$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+  set x "$prefix/share/config.site" "$prefix/etc/config.site"
+else
+  set x "$ac_default_prefix/share/config.site" \
+       "$ac_default_prefix/etc/config.site"
+fi
+shift
+for ac_site_file
+do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+       { echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+       { echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+       ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet.  If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet.  If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet.  If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet.  If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compiler --version >&5") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compiler -v >&5") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compiler -V >&5") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; }
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+#
+# List of possible output files, starting from the most likely.
+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)
+# only as a last resort.  b.out is created by i960 compilers.
+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'
+#
+# The IRIX 6 linker writes into existing files which may not be
+# executable, retaining their permissions.  Remove them first so a
+# subsequent execution test works.
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+        if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+
+{ echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6; }
+if test -z "$ac_file"; then
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+{ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6; }
+
+{ echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       CFLAGS=""
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_c89=$ac_arg
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6; } ;;
+  xno)
+    { echo "$as_me:$LINENO: result: unsupported" >&5
+echo "${ECHO_T}unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+if test "x$CC" = xgcc; then
+       XX_CFLAGS="-Wall -pedantic"
+else
+       case "$host" in
+               alpha-dec-osf*)
+               XX_CFLAGS="-std1 -O2 -g3"
+                       ;;
+               *)
+               XX_CFLAGS=
+                       ;;
+       esac
+fi
+
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_RM+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RM"; then
+  ac_cv_prog_RM="$RM" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_RM="rm"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RM=$ac_cv_prog_RM
+if test -n "$RM"; then
+  { echo "$as_me:$LINENO: result: $RM" >&5
+echo "${ECHO_T}$RM" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CP"; then
+  ac_cv_prog_CP="$CP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CP="cp"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CP=$ac_cv_prog_CP
+if test -n "$CP"; then
+  { echo "$as_me:$LINENO: result: $CP" >&5
+echo "${ECHO_T}$CP" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # Extract the first word of "grep ggrep" to use in msg output
+if test -z "$GREP"; then
+set dummy grep ggrep; ac_prog_name=$2
+if test "${ac_cv_path_GREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_path_GREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in grep ggrep; do
+  for ac_exec_ext in '' $ac_executable_extensions; do
+    ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+    { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+    # Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+    $ac_path_GREP_found && break 3
+  done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+GREP="$ac_cv_path_GREP"
+if test -z "$GREP"; then
+  { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+echo "${ECHO_T}$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     # Extract the first word of "egrep" to use in msg output
+if test -z "$EGREP"; then
+set dummy egrep; ac_prog_name=$2
+if test "${ac_cv_path_EGREP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_path_EGREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in egrep; do
+  for ac_exec_ext in '' $ac_executable_extensions; do
+    ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+    { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+    # Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+    $ac_path_EGREP_found && break 3
+  done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+EGREP="$ac_cv_path_EGREP"
+if test -z "$EGREP"; then
+  { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+
+   fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+for ac_header in libintl.h stddef.h stdlib.h string.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+{ echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; }
+if test "${ac_cv_c_const+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+  /* Ultrix mips cc rejects this.  */
+  typedef int charset[2];
+  const charset cs;
+  /* SunOS 4.1.1 cc rejects this.  */
+  char const *const *pcpcc;
+  char **ppc;
+  /* NEC SVR4.0.2 mips cc rejects this.  */
+  struct point {int x, y;};
+  static struct point const zero = {0,0};
+  /* AIX XL C 1.02.0.0 rejects this.
+     It does not let you subtract one const X* pointer from another in
+     an arm of an if-expression whose if-part is not a constant
+     expression */
+  const char *g = "string";
+  pcpcc = &g + (g ? g-g : 0);
+  /* HPUX 7.0 cc rejects these. */
+  ++pcpcc;
+  ppc = (char**) pcpcc;
+  pcpcc = (char const *const *) ppc;
+  { /* SCO 3.2v4 cc rejects this.  */
+    char *t;
+    char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+    *t++ = 0;
+    if (s) return 0;
+  }
+  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+    int x[] = {25, 17};
+    const int *foo = &x[0];
+    ++foo;
+  }
+  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+    typedef const int *iptr;
+    iptr p = 0;
+    ++p;
+  }
+  { /* AIX XL C 1.02.0.0 rejects this saying
+       "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+    struct s { int j; const int *ap[3]; };
+    struct s *b; b->j = 5;
+  }
+  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+    const int foo = 10;
+    if (!foo) return 0;
+  }
+  return !cs[0] && !zero.x;
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_c_const=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_c_const=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+echo "${ECHO_T}$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const
+_ACEOF
+
+fi
+
+
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5
+echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6; }
+if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_malloc_0_nonnull=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *malloc ();
+#endif
+
+int
+main ()
+{
+return ! malloc (0);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_malloc_0_nonnull=yes
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_malloc_0_nonnull=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5
+echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6; }
+if test $ac_cv_func_malloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 1
+_ACEOF
+
+else
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 0
+_ACEOF
+
+   case " $LIBOBJS " in
+  *" malloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define malloc rpl_malloc
+_ACEOF
+
+fi
+
+
+
+{ echo "$as_me:$LINENO: checking for working memcmp" >&5
+echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6; }
+if test "${ac_cv_func_memcmp_working+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_memcmp_working=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+  /* Some versions of memcmp are not 8-bit clean.  */
+  char c0 = '\100', c1 = '\200', c2 = '\201';
+  if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+    return 1;
+
+  /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+     or more and with at least one buffer not starting on a 4-byte boundary.
+     William Lewis provided this test program.   */
+  {
+    char foo[21];
+    char bar[21];
+    int i;
+    for (i = 0; i < 4; i++)
+      {
+       char *a = foo + i;
+       char *b = bar + i;
+       strcpy (a, "--------01111111");
+       strcpy (b, "--------10000000");
+       if (memcmp (a, b, 16) >= 0)
+         return 1;
+      }
+    return 0;
+  }
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_memcmp_working=yes
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_memcmp_working=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5
+echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+  *" memcmp.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5
+echo $ECHO_N "checking for GNU libc compatible realloc... $ECHO_C" >&6; }
+if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_realloc_0_nonnull=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *realloc ();
+#endif
+
+int
+main ()
+{
+return ! realloc (0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_realloc_0_nonnull=yes
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_realloc_0_nonnull=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5
+echo "${ECHO_T}$ac_cv_func_realloc_0_nonnull" >&6; }
+if test $ac_cv_func_realloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALLOC 1
+_ACEOF
+
+else
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALLOC 0
+_ACEOF
+
+   case " $LIBOBJS " in
+  *" realloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS realloc.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define realloc rpl_realloc
+_ACEOF
+
+fi
+
+
+
+
+for ac_func in vprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+{ echo "$as_me:$LINENO: checking for _doprnt" >&5
+echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6; }
+if test "${ac_cv_func__doprnt+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub__doprnt || defined __stub____doprnt
+choke me
+#endif
+
+int
+main ()
+{
+return _doprnt ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_func__doprnt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func__doprnt=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+echo "${ECHO_T}$ac_cv_func__doprnt" >&6; }
+if test $ac_cv_func__doprnt = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+
+
+
+
+
+for ac_func in memmove memset strchr strdup strrchr strstr
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+  succeeded=no
+
+  if test -z "$PKG_CONFIG"; then
+    # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
+echo "${ECHO_T}$PKG_CONFIG" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  fi
+
+  if test "$PKG_CONFIG" = "no" ; then
+     echo "*** The pkg-config script could not be found. Make sure it is"
+     echo "*** in your path, or set the PKG_CONFIG environment variable"
+     echo "*** to the full path to pkg-config."
+     echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
+  else
+     PKG_CONFIG_MIN_VERSION=0.9.0
+     if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
+        { echo "$as_me:$LINENO: checking for freetype2 >= 2.0" >&5
+echo $ECHO_N "checking for freetype2 >= 2.0... $ECHO_C" >&6; }
+
+        if $PKG_CONFIG --exists "freetype2 >= 2.0" ; then
+            { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+            succeeded=yes
+
+            { echo "$as_me:$LINENO: checking FREETYPE_CFLAGS" >&5
+echo $ECHO_N "checking FREETYPE_CFLAGS... $ECHO_C" >&6; }
+            FREETYPE_CFLAGS=`$PKG_CONFIG --cflags "freetype2 >= 2.0"`
+            { echo "$as_me:$LINENO: result: $FREETYPE_CFLAGS" >&5
+echo "${ECHO_T}$FREETYPE_CFLAGS" >&6; }
+
+            { echo "$as_me:$LINENO: checking FREETYPE_LIBS" >&5
+echo $ECHO_N "checking FREETYPE_LIBS... $ECHO_C" >&6; }
+            FREETYPE_LIBS=`$PKG_CONFIG --libs "freetype2 >= 2.0"`
+            { echo "$as_me:$LINENO: result: $FREETYPE_LIBS" >&5
+echo "${ECHO_T}$FREETYPE_LIBS" >&6; }
+        else
+            FREETYPE_CFLAGS=""
+            FREETYPE_LIBS=""
+            ## If we have a custom action on failure, don't print errors, but
+            ## do set a variable so people can do so.
+            FREETYPE_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "freetype2 >= 2.0"`
+            echo $FREETYPE_PKG_ERRORS
+        fi
+
+
+
+     else
+        echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
+        echo "*** See http://www.freedesktop.org/software/pkgconfig"
+     fi
+  fi
+
+  if test $succeeded = yes; then
+     DEFINES="-DHAVE_FREETYPE" CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" LIBS="$LIBS $FREETYPE_LIBS"
+  else
+     { { echo "$as_me:$LINENO: error: Library requirements (freetype2 >= 2.0) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them." >&5
+echo "$as_me: error: Library requirements (freetype2 >= 2.0) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them." >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+
+
+  succeeded=no
+
+  if test -z "$PKG_CONFIG"; then
+    # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
+echo "${ECHO_T}$PKG_CONFIG" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+  fi
+
+  if test "$PKG_CONFIG" = "no" ; then
+     echo "*** The pkg-config script could not be found. Make sure it is"
+     echo "*** in your path, or set the PKG_CONFIG environment variable"
+     echo "*** to the full path to pkg-config."
+     echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
+  else
+     PKG_CONFIG_MIN_VERSION=0.9.0
+     if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
+        { echo "$as_me:$LINENO: checking for gtk+-2.0 >= 2.6" >&5
+echo $ECHO_N "checking for gtk+-2.0 >= 2.6... $ECHO_C" >&6; }
+
+        if $PKG_CONFIG --exists "gtk+-2.0 >= 2.6" ; then
+            { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+            succeeded=yes
+
+            { echo "$as_me:$LINENO: checking GTK_CFLAGS" >&5
+echo $ECHO_N "checking GTK_CFLAGS... $ECHO_C" >&6; }
+            GTK_CFLAGS=`$PKG_CONFIG --cflags "gtk+-2.0 >= 2.6"`
+            { echo "$as_me:$LINENO: result: $GTK_CFLAGS" >&5
+echo "${ECHO_T}$GTK_CFLAGS" >&6; }
+
+            { echo "$as_me:$LINENO: checking GTK_LIBS" >&5
+echo $ECHO_N "checking GTK_LIBS... $ECHO_C" >&6; }
+            GTK_LIBS=`$PKG_CONFIG --libs "gtk+-2.0 >= 2.6"`
+            { echo "$as_me:$LINENO: result: $GTK_LIBS" >&5
+echo "${ECHO_T}$GTK_LIBS" >&6; }
+        else
+            GTK_CFLAGS=""
+            GTK_LIBS=""
+            ## If we have a custom action on failure, don't print errors, but
+            ## do set a variable so people can do so.
+            GTK_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "gtk+-2.0 >= 2.6"`
+            echo $GTK_PKG_ERRORS
+        fi
+
+
+
+     else
+        echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
+        echo "*** See http://www.freedesktop.org/software/pkgconfig"
+     fi
+  fi
+
+  if test $succeeded = yes; then
+     CPPFLAGS="$CPPFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS"
+  else
+     { { echo "$as_me:$LINENO: error: Library requirements (gtk+-2.0 >= 2.6) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them." >&5
+echo "$as_me: error: Library requirements (gtk+-2.0 >= 2.6) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them." >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+
+
+{ echo "$as_me:$LINENO: checking for hbf.c" >&5
+echo $ECHO_N "checking for hbf.c... $ECHO_C" >&6; }
+if test "${ac_cv_file_hbf_c+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  test "$cross_compiling" = yes &&
+  { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
+echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
+   { (exit 1); exit 1; }; }
+if test -r "hbf.c"; then
+  ac_cv_file_hbf_c=yes
+else
+  ac_cv_file_hbf_c=no
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_file_hbf_c" >&5
+echo "${ECHO_T}$ac_cv_file_hbf_c" >&6; }
+if test $ac_cv_file_hbf_c = yes; then
+  DEFINES="$DEFINES -DHAVE_HBF" HBFSRC="hbf.c" HBFOBJ="hbf.o"
+fi
+
+
+{ echo "$as_me:$LINENO: checking for X" >&5
+echo $ECHO_N "checking for X... $ECHO_C" >&6; }
+
+
+# Check whether --with-x was given.
+if test "${with_x+set}" = set; then
+  withval=$with_x;
+fi
+
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+  # The user explicitly disabled X.
+  have_x=disabled
+else
+  case $x_includes,$x_libraries in #(
+    *\'*) { { echo "$as_me:$LINENO: error: Cannot use X directory names containing '" >&5
+echo "$as_me: error: Cannot use X directory names containing '" >&2;}
+   { (exit 1); exit 1; }; };; #(
+    *,NONE | NONE,*) if test "${ac_cv_have_x+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=no ac_x_libraries=no
+rm -f -r conftest.dir
+if mkdir conftest.dir; then
+  cd conftest.dir
+  cat >Imakefile <<'_ACEOF'
+incroot:
+       @echo incroot='${INCROOT}'
+usrlibdir:
+       @echo usrlibdir='${USRLIBDIR}'
+libdir:
+       @echo libdir='${LIBDIR}'
+_ACEOF
+  if (export CC; ${XMKMF-xmkmf}) >/dev/null 2>/dev/null && test -f Makefile; then
+    # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+    for ac_var in incroot usrlibdir libdir; do
+      eval "ac_im_$ac_var=\`\${MAKE-make} $ac_var 2>/dev/null | sed -n 's/^$ac_var=//p'\`"
+    done
+    # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+    for ac_extension in a so sl; do
+      if test ! -f "$ac_im_usrlibdir/libX11.$ac_extension" &&
+        test -f "$ac_im_libdir/libX11.$ac_extension"; then
+       ac_im_usrlibdir=$ac_im_libdir; break
+      fi
+    done
+    # Screen out bogus values from the imake configuration.  They are
+    # bogus both because they are the default anyway, and because
+    # using them would break gcc on systems where it needs fixed includes.
+    case $ac_im_incroot in
+       /usr/include) ac_x_includes= ;;
+       *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;;
+    esac
+    case $ac_im_usrlibdir in
+       /usr/lib | /lib) ;;
+       *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;;
+    esac
+  fi
+  cd ..
+  rm -f -r conftest.dir
+fi
+
+# Standard set of common directories for X headers.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ac_x_header_dirs='
+/usr/X11/include
+/usr/X11R6/include
+/usr/X11R5/include
+/usr/X11R4/include
+
+/usr/include/X11
+/usr/include/X11R6
+/usr/include/X11R5
+/usr/include/X11R4
+
+/usr/local/X11/include
+/usr/local/X11R6/include
+/usr/local/X11R5/include
+/usr/local/X11R4/include
+
+/usr/local/include/X11
+/usr/local/include/X11R6
+/usr/local/include/X11R5
+/usr/local/include/X11R4
+
+/usr/X386/include
+/usr/x386/include
+/usr/XFree86/include/X11
+
+/usr/include
+/usr/local/include
+/usr/unsupported/include
+/usr/athena/include
+/usr/local/x11r5/include
+/usr/lpp/Xamples/include
+
+/usr/openwin/include
+/usr/openwin/share/include'
+
+if test "$ac_x_includes" = no; then
+  # Guess where to find include files, by looking for Xlib.h.
+  # First, try using that file with no special directory specified.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <X11/Xlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  for ac_dir in $ac_x_header_dirs; do
+  if test -r "$ac_dir/X11/Xlib.h"; then
+    ac_x_includes=$ac_dir
+    break
+  fi
+done
+fi
+
+rm -f conftest.err conftest.$ac_ext
+fi # $ac_x_includes = no
+
+if test "$ac_x_libraries" = no; then
+  # Check for the libraries.
+  # See if we find them without any special options.
+  # Don't add to $LIBS permanently.
+  ac_save_LIBS=$LIBS
+  LIBS="-lX11 $LIBS"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <X11/Xlib.h>
+int
+main ()
+{
+XrmInitialize ()
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  LIBS=$ac_save_LIBS
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       LIBS=$ac_save_LIBS
+for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g`
+do
+  # Don't even attempt the hair of trying to link an X program!
+  for ac_extension in a so sl; do
+    if test -r "$ac_dir/libX11.$ac_extension"; then
+      ac_x_libraries=$ac_dir
+      break 2
+    fi
+  done
+done
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi # $ac_x_libraries = no
+
+case $ac_x_includes,$ac_x_libraries in #(
+  no,* | *,no | *\'*)
+    # Didn't find X, or a directory has "'" in its name.
+    ac_cv_have_x="have_x=no";; #(
+  *)
+    # Record where we found X for the cache.
+    ac_cv_have_x="have_x=yes\
+       ac_x_includes='$ac_x_includes'\
+       ac_x_libraries='$ac_x_libraries'"
+esac
+fi
+;; #(
+    *) have_x=yes;;
+  esac
+  eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+  { echo "$as_me:$LINENO: result: $have_x" >&5
+echo "${ECHO_T}$have_x" >&6; }
+  no_x=yes
+else
+  # If each of the values was on the command line, it overrides each guess.
+  test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+  test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+  # Update the cache value to reflect the command line values.
+  ac_cv_have_x="have_x=yes\
+       ac_x_includes='$x_includes'\
+       ac_x_libraries='$x_libraries'"
+  { echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5
+echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6; }
+fi
+
+if test "$no_x" = yes; then
+  # Not all programs may use this symbol, but it does not hurt to define it.
+
+cat >>confdefs.h <<\_ACEOF
+#define X_DISPLAY_MISSING 1
+_ACEOF
+
+  X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS=
+else
+  if test -n "$x_includes"; then
+    X_CFLAGS="$X_CFLAGS -I$x_includes"
+  fi
+
+  # It would also be nice to do this for all -L options, not just this one.
+  if test -n "$x_libraries"; then
+    X_LIBS="$X_LIBS -L$x_libraries"
+    # For Solaris; some versions of Sun CC require a space after -R and
+    # others require no space.  Words are not sufficient . . . .
+    { echo "$as_me:$LINENO: checking whether -R must be followed by a space" >&5
+echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6; }
+    ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries"
+    ac_xsave_c_werror_flag=$ac_c_werror_flag
+    ac_c_werror_flag=yes
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+       X_LIBS="$X_LIBS -R$x_libraries"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       LIBS="$ac_xsave_LIBS -R $x_libraries"
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+         X_LIBS="$X_LIBS -R $x_libraries"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       { echo "$as_me:$LINENO: result: neither works" >&5
+echo "${ECHO_T}neither works" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+    ac_c_werror_flag=$ac_xsave_c_werror_flag
+    LIBS=$ac_xsave_LIBS
+  fi
+
+  # Check for system-dependent libraries X programs must link with.
+  # Do this before checking for the system-independent R6 libraries
+  # (-lICE), since we may need -lsocket or whatever for X linking.
+
+  if test "$ISC" = yes; then
+    X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet"
+  else
+    # Martyn Johnson says this is needed for Ultrix, if the X
+    # libraries were built with DECnet support.  And Karl Berry says
+    # the Alpha needs dnet_stub (dnet does not exist).
+    ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char XOpenDisplay ();
+int
+main ()
+{
+return XOpenDisplay ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       { echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet" >&5
+echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6; }
+if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldnet  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dnet_ntoa ();
+int
+main ()
+{
+return dnet_ntoa ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_dnet_dnet_ntoa=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dnet_dnet_ntoa=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_dnet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6; }
+if test $ac_cv_lib_dnet_dnet_ntoa = yes; then
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
+fi
+
+    if test $ac_cv_lib_dnet_dnet_ntoa = no; then
+      { echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet_stub" >&5
+echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6; }
+if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldnet_stub  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dnet_ntoa ();
+int
+main ()
+{
+return dnet_ntoa ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_dnet_stub_dnet_ntoa=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dnet_stub_dnet_ntoa=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6; }
+if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
+fi
+
+    fi
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+    LIBS="$ac_xsave_LIBS"
+
+    # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
+    # to get the SysV transport functions.
+    # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4)
+    # needs -lnsl.
+    # The nsl library prevents programs from opening the X display
+    # on Irix 5.2, according to T.E. Dickey.
+    # The functions gethostbyname, getservbyname, and inet_addr are
+    # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking.
+    { echo "$as_me:$LINENO: checking for gethostbyname" >&5
+echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6; }
+if test "${ac_cv_func_gethostbyname+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define gethostbyname to an innocuous variant, in case <limits.h> declares gethostbyname.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define gethostbyname innocuous_gethostbyname
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char gethostbyname (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef gethostbyname
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostbyname ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_gethostbyname || defined __stub___gethostbyname
+choke me
+#endif
+
+int
+main ()
+{
+return gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_func_gethostbyname=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_gethostbyname=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6; }
+
+    if test $ac_cv_func_gethostbyname = no; then
+      { echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5
+echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6; }
+if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostbyname ();
+int
+main ()
+{
+return gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_nsl_gethostbyname=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_nsl_gethostbyname=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6; }
+if test $ac_cv_lib_nsl_gethostbyname = yes; then
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
+fi
+
+      if test $ac_cv_lib_nsl_gethostbyname = no; then
+       { echo "$as_me:$LINENO: checking for gethostbyname in -lbsd" >&5
+echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6; }
+if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbsd  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostbyname ();
+int
+main ()
+{
+return gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_bsd_gethostbyname=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_bsd_gethostbyname=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6; }
+if test $ac_cv_lib_bsd_gethostbyname = yes; then
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd"
+fi
+
+      fi
+    fi
+
+    # lieder@skyler.mavd.honeywell.com says without -lsocket,
+    # socket/setsockopt and other routines are undefined under SCO ODT
+    # 2.0.  But -lsocket is broken on IRIX 5.2 (and is not necessary
+    # on later versions), says Simon Leinen: it contains gethostby*
+    # variants that don't use the name server (or something).  -lsocket
+    # must be given before -lnsl if both are needed.  We assume that
+    # if connect needs -lnsl, so does gethostbyname.
+    { echo "$as_me:$LINENO: checking for connect" >&5
+echo $ECHO_N "checking for connect... $ECHO_C" >&6; }
+if test "${ac_cv_func_connect+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define connect to an innocuous variant, in case <limits.h> declares connect.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define connect innocuous_connect
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char connect (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef connect
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char connect ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_connect || defined __stub___connect
+choke me
+#endif
+
+int
+main ()
+{
+return connect ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_func_connect=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_connect=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_connect" >&5
+echo "${ECHO_T}$ac_cv_func_connect" >&6; }
+
+    if test $ac_cv_func_connect = no; then
+      { echo "$as_me:$LINENO: checking for connect in -lsocket" >&5
+echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6; }
+if test "${ac_cv_lib_socket_connect+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char connect ();
+int
+main ()
+{
+return connect ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_socket_connect=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_socket_connect=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_socket_connect" >&5
+echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6; }
+if test $ac_cv_lib_socket_connect = yes; then
+  X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
+fi
+
+    fi
+
+    # Guillermo Gomez says -lposix is necessary on A/UX.
+    { echo "$as_me:$LINENO: checking for remove" >&5
+echo $ECHO_N "checking for remove... $ECHO_C" >&6; }
+if test "${ac_cv_func_remove+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define remove to an innocuous variant, in case <limits.h> declares remove.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define remove innocuous_remove
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char remove (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef remove
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char remove ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_remove || defined __stub___remove
+choke me
+#endif
+
+int
+main ()
+{
+return remove ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_func_remove=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_remove=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_remove" >&5
+echo "${ECHO_T}$ac_cv_func_remove" >&6; }
+
+    if test $ac_cv_func_remove = no; then
+      { echo "$as_me:$LINENO: checking for remove in -lposix" >&5
+echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6; }
+if test "${ac_cv_lib_posix_remove+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lposix  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char remove ();
+int
+main ()
+{
+return remove ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_posix_remove=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_posix_remove=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_posix_remove" >&5
+echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6; }
+if test $ac_cv_lib_posix_remove = yes; then
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
+fi
+
+    fi
+
+    # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
+    { echo "$as_me:$LINENO: checking for shmat" >&5
+echo $ECHO_N "checking for shmat... $ECHO_C" >&6; }
+if test "${ac_cv_func_shmat+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define shmat to an innocuous variant, in case <limits.h> declares shmat.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define shmat innocuous_shmat
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shmat (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shmat
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shmat ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_shmat || defined __stub___shmat
+choke me
+#endif
+
+int
+main ()
+{
+return shmat ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_func_shmat=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_shmat=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_shmat" >&5
+echo "${ECHO_T}$ac_cv_func_shmat" >&6; }
+
+    if test $ac_cv_func_shmat = no; then
+      { echo "$as_me:$LINENO: checking for shmat in -lipc" >&5
+echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6; }
+if test "${ac_cv_lib_ipc_shmat+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lipc  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shmat ();
+int
+main ()
+{
+return shmat ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_ipc_shmat=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_ipc_shmat=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_ipc_shmat" >&5
+echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6; }
+if test $ac_cv_lib_ipc_shmat = yes; then
+  X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
+fi
+
+    fi
+  fi
+
+  # Check for libraries that X11R6 Xt/Xaw programs need.
+  ac_save_LDFLAGS=$LDFLAGS
+  test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries"
+  # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to
+  # check for ICE first), but we must link in the order -lSM -lICE or
+  # we get undefined symbols.  So assume we have SM if we have ICE.
+  # These have to be linked with before -lX11, unlike the other
+  # libraries we check for below, so use a different variable.
+  # John Interrante, Karl Berry
+  { echo "$as_me:$LINENO: checking for IceConnectionNumber in -lICE" >&5
+echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6; }
+if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lICE $X_EXTRA_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char IceConnectionNumber ();
+int
+main ()
+{
+return IceConnectionNumber ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_ICE_IceConnectionNumber=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_ICE_IceConnectionNumber=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5
+echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6; }
+if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then
+  X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
+fi
+
+  LDFLAGS=$ac_save_LDFLAGS
+
+fi
+
+
+if test "$have_x" != yes; then
+  { echo "$as_me:$LINENO: X11 not found. Disabling server font grabbing." >&5
+echo "$as_me: X11 not found. Disabling server font grabbing." >&6;}
+else
+  DEFINES="$DEFINES -DHAVE_XLIB"
+  BDFGRABSRC="bdfgrab.c"
+  BDFGRABOBJ="bdfgrab.o"
+fi
+
+#
+# Fix for implicit DSO linking issue.
+#
+
+{ echo "$as_me:$LINENO: checking for XCreatePixmap in -lX11" >&5
+echo $ECHO_N "checking for XCreatePixmap in -lX11... $ECHO_C" >&6; }
+if test "${ac_cv_lib_X11_XCreatePixmap+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lX11  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char XCreatePixmap ();
+int
+main ()
+{
+return XCreatePixmap ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_X11_XCreatePixmap=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_X11_XCreatePixmap=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_X11_XCreatePixmap" >&5
+echo "${ECHO_T}$ac_cv_lib_X11_XCreatePixmap" >&6; }
+if test $ac_cv_lib_X11_XCreatePixmap = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBX11 1
+_ACEOF
+
+  LIBS="-lX11 $LIBS"
+
+fi
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    test "x$cache_file" != "x/dev/null" &&
+      { echo "$as_me:$LINENO: updating cache $cache_file" >&5
+echo "$as_me: updating cache $cache_file" >&6;}
+    cat confcache >$cache_file
+  else
+    { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+t clear
+:clear
+s/^[    ]*#[    ]*define[       ][      ]*\([^  (][^    (]*([^)]*)\)[   ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[    ]*#[    ]*define[       ][      ]*\([^  ][^     ]*\)[   ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[     `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+       g
+       s/^\n//
+       s/\n/ /g
+       p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s='ln -s'
+  # ... but there are two gotchas:
+  # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+  # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+  # In both cases, we have to default to `cp -p'.
+  ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+    as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+        test -d "$1/.";
+      else
+       case $1 in
+        -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.61.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+                  instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.61,
+  with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2006 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    echo "$ac_cs_version"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --he | --h |  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  CONFIG_SHELL=$SHELL
+  export CONFIG_SHELL
+  exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp=
+  trap 'exit_status=$?
+  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+#
+# Set up the sed scripts for CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+
+_ACEOF
+
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  cat >conf$$subs.sed <<_ACEOF
+SHELL!$SHELL$ac_delim
+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim
+PACKAGE_NAME!$PACKAGE_NAME$ac_delim
+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim
+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim
+PACKAGE_STRING!$PACKAGE_STRING$ac_delim
+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim
+exec_prefix!$exec_prefix$ac_delim
+prefix!$prefix$ac_delim
+program_transform_name!$program_transform_name$ac_delim
+bindir!$bindir$ac_delim
+sbindir!$sbindir$ac_delim
+libexecdir!$libexecdir$ac_delim
+datarootdir!$datarootdir$ac_delim
+datadir!$datadir$ac_delim
+sysconfdir!$sysconfdir$ac_delim
+sharedstatedir!$sharedstatedir$ac_delim
+localstatedir!$localstatedir$ac_delim
+includedir!$includedir$ac_delim
+oldincludedir!$oldincludedir$ac_delim
+docdir!$docdir$ac_delim
+infodir!$infodir$ac_delim
+htmldir!$htmldir$ac_delim
+dvidir!$dvidir$ac_delim
+pdfdir!$pdfdir$ac_delim
+psdir!$psdir$ac_delim
+libdir!$libdir$ac_delim
+localedir!$localedir$ac_delim
+mandir!$mandir$ac_delim
+DEFS!$DEFS$ac_delim
+ECHO_C!$ECHO_C$ac_delim
+ECHO_N!$ECHO_N$ac_delim
+ECHO_T!$ECHO_T$ac_delim
+LIBS!$LIBS$ac_delim
+build_alias!$build_alias$ac_delim
+host_alias!$host_alias$ac_delim
+target_alias!$target_alias$ac_delim
+CC!$CC$ac_delim
+CFLAGS!$CFLAGS$ac_delim
+LDFLAGS!$LDFLAGS$ac_delim
+CPPFLAGS!$CPPFLAGS$ac_delim
+ac_ct_CC!$ac_ct_CC$ac_delim
+EXEEXT!$EXEEXT$ac_delim
+OBJEXT!$OBJEXT$ac_delim
+XX_CFLAGS!$XX_CFLAGS$ac_delim
+RM!$RM$ac_delim
+CP!$CP$ac_delim
+CPP!$CPP$ac_delim
+GREP!$GREP$ac_delim
+EGREP!$EGREP$ac_delim
+LIBOBJS!$LIBOBJS$ac_delim
+PKG_CONFIG!$PKG_CONFIG$ac_delim
+FREETYPE_CFLAGS!$FREETYPE_CFLAGS$ac_delim
+FREETYPE_LIBS!$FREETYPE_LIBS$ac_delim
+GTK_CFLAGS!$GTK_CFLAGS$ac_delim
+GTK_LIBS!$GTK_LIBS$ac_delim
+XMKMF!$XMKMF$ac_delim
+X_CFLAGS!$X_CFLAGS$ac_delim
+X_PRE_LIBS!$X_PRE_LIBS$ac_delim
+X_LIBS!$X_LIBS$ac_delim
+X_EXTRA_LIBS!$X_EXTRA_LIBS$ac_delim
+DEFINES!$DEFINES$ac_delim
+HBFSRC!$HBFSRC$ac_delim
+HBFOBJ!$HBFOBJ$ac_delim
+BDFGRABSRC!$BDFGRABSRC$ac_delim
+BDFGRABOBJ!$BDFGRABOBJ$ac_delim
+LTLIBOBJS!$LTLIBOBJS$ac_delim
+_ACEOF
+
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 67; then
+    break
+  elif $ac_last_try; then
+    { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+  ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+  ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+:end
+s/|#_!!_#|//g
+CEOF$ac_eof
+_ACEOF
+
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[    ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+fi # test -n "$CONFIG_FILES"
+
+
+for ac_tag in  :F $CONFIG_FILES
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5
+echo "$as_me: error: Invalid tag $ac_tag." >&2;}
+   { (exit 1); exit 1; }; };;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+   { (exit 1); exit 1; }; };;
+      esac
+      ac_file_inputs="$ac_file_inputs $ac_f"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input="Generated from "`IFS=:
+         echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    fi
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$tmp/stdin";;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  { as_dir="$ac_dir"
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+echo "$as_me: error: cannot create directory $as_dir" >&2;}
+   { (exit 1); exit 1; }; }; }
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+case `sed -n '/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+' $ac_file_inputs` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+    s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s&@configure_input@&$configure_input&;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+  { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&5
+echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&2;}
+
+  rm -f "$tmp/stdin"
+  case $ac_file in
+  -) cat "$tmp/out"; rm -f "$tmp/out";;
+  *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;
+  esac
+ ;;
+
+
+
+  esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..b0d7830
--- /dev/null
@@ -0,0 +1,67 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(gbdfed.c)
+
+AC_PROG_CC
+
+dnl Get Compiler flags right.
+
+if test "x$CC" = xgcc; then
+       XX_CFLAGS="-Wall -pedantic"
+else
+       case "$host" in
+               alpha-dec-osf*)
+               XX_CFLAGS="-std1 -O2 -g3"
+                       ;;
+               *)
+               XX_CFLAGS=
+                       ;;
+       esac
+fi
+AC_SUBST(XX_CFLAGS)
+
+AC_CHECK_PROG(RM, rm, rm)
+AC_CHECK_PROG(CP, cp, cp)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([libintl.h stddef.h stdlib.h string.h unistd.h])
+
+AC_C_CONST
+
+dnl Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_REALLOC
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([memmove memset strchr strdup strrchr strstr])
+
+dnl These use the pkgconfig macro (in aclocal.m4) to check on libraries.
+PKG_CHECK_MODULES(FREETYPE, freetype2 >= 2.0,DEFINES="-DHAVE_FREETYPE" CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" LIBS="$LIBS $FREETYPE_LIBS",)
+PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.6,CPPFLAGS="$CPPFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS",)
+
+AC_CHECK_FILE(hbf.c, DEFINES="$DEFINES -DHAVE_HBF" HBFSRC="hbf.c" HBFOBJ="hbf.o",)
+
+AC_PATH_XTRA
+
+if test "$have_x" != yes; then
+  AC_MSG_NOTICE(X11 not found. Disabling server font grabbing.)
+else
+  DEFINES="$DEFINES -DHAVE_XLIB"
+  BDFGRABSRC="bdfgrab.c"
+  BDFGRABOBJ="bdfgrab.o"
+fi
+
+#
+# Fix for implicit DSO linking issue.
+#
+AC_CHECK_LIB(X11, XCreatePixmap)
+
+AC_SUBST(DEFINES)
+AC_SUBST(HBFSRC)
+AC_SUBST(HBFOBJ)
+AC_SUBST(BDFGRABSRC)
+AC_SUBST(BDFGRABOBJ)
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/fontgrid.c b/fontgrid.c
new file mode 100644 (file)
index 0000000..b3222d5
--- /dev/null
@@ -0,0 +1,4846 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "fontgrid.h"
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkselection.h>
+
+#ifdef HAVE_XLIB
+#include <gdk/gdkx.h>
+#endif
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(s) dgettext(GETTEXT_PACKAGE,s)
+#else
+#define _(s) (s)
+#endif
+
+/*
+ * Macros that represent the properties used by this type of object.
+ */
+#define FONTGRID_CLIPBOARD gdk_atom_intern("FONTGRID_CLIPBOARD", FALSE)
+#define FONTGRID_GLYPHLIST gdk_atom_intern("FONTGRID_GLYPHLIST", FALSE)
+
+/*
+ * Set several defaults.
+ */
+#define FGRID_MAX_COLS            16
+#define FGRID_MAX_ROWS            16
+#define FGRID_DEFAULT_COLS        16
+#define FGRID_DEFAULT_ROWS        8
+#define FGRID_DEFAULT_CELL_HEIGHT 18
+
+/*
+ * Enums used for identifying properties.
+ */
+enum {
+    PROP_0 = 0,
+    PROP_CODE_BASE,
+    PROP_POWER2,
+    PROP_ORIENTATION,
+    PROP_FONT,
+    PROP_POINT_SIZE,
+    PROP_SPACING,
+    PROP_SKIP_BLANKS,
+    PROP_OVERWRITE,
+    PROP_COLORS,
+    PROP_INITIAL_GLYPH,
+    PROP_BPP,
+    PROP_HRES,
+    PROP_VRES
+};
+
+/**************************************************************************
+ *
+ * Selection macros for toggling & testing glyph selected state.
+ *
+ **************************************************************************/
+
+/*
+ * Macros for dealing with the selected state of glyphs in the font grid.
+ */
+#define IsSelected(code, map) (map[(code) >> 5] & (1 << ((code) & 31)))
+#define Select(code, map) (map[(code) >> 5] |= (1 << ((code) & 31)))
+#define Unselect(code, map) (map[(code) >> 5] &= ~(1 << ((code) & 31)))
+
+/**************************************************************************
+ *
+ * Signals.
+ *
+ **************************************************************************/
+
+/*
+ * Enums that represent the signals these objects send out.
+ */
+enum {
+    SELECTION_START = 0,
+    SELECTION_EXTEND,
+    SELECTION_END,
+    ACTIVATE,
+    MODIFIED,
+    TURN_TO_PAGE
+};
+
+static GtkWidgetClass *parent_class = 0;
+static guint fontgrid_signals[TURN_TO_PAGE + 1];
+
+static bdf_glyph_t empty_glyph;
+
+/**************************************************************************
+ *
+ * Digits for displaying the cell encoding.
+ *
+ **************************************************************************/
+
+/*
+ * Lists of points that describe the encoding digits.
+ */
+typedef struct {
+    GdkPoint *points;
+    guint npoints;
+} fontgrid_digit;
+
+static GdkPoint digit00[] = {{2, 0}, {1, 1}, {3, 1}, {0, 2}, {4, 2}, {0, 3},
+                             {4, 3}, {0, 4}, {4, 4}, {1, 5}, {3, 5}, {2, 6}}; 
+
+static GdkPoint digit01[] = {{2, 0}, {1, 1}, {2, 1}, {0, 2}, {2, 2}, {2, 3},
+                             {2, 4}, {2, 5}, {0, 6}, {1, 6}, {2, 6}, {3, 6},
+                             {4, 6}};
+
+static GdkPoint digit02[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {4, 2},
+                             {2, 3}, {3, 3}, {1, 4}, {0, 5}, {0, 6}, {1, 6},
+                             {2, 6}, {3, 6}, {4, 6}};
+
+static GdkPoint digit03[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {4, 1},
+                             {3, 2}, {2, 3},{3, 3}, {4, 4}, {0, 5}, {4, 5},
+                             {1, 6}, {2, 6}, {3, 6}};
+
+static GdkPoint digit04[] = {{3, 0}, {2, 1}, {3, 1}, {1, 2}, {3, 2}, {0, 3},
+                             {3, 3}, {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4},
+                             {3, 5}, {3, 6}};
+
+static GdkPoint digit05[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1},
+                             {0, 2}, {2, 2}, {3, 2}, {0, 3}, {1, 3}, {4, 3},
+                             {4, 4}, {0, 5}, {4, 5}, {1, 6}, {2, 6}, {3, 6}};
+
+static GdkPoint digit06[] = {{2, 0}, {3, 0}, {1, 1}, {0, 2}, {0, 3}, {2, 3},
+                             {3, 3}, {0, 4}, {1, 4}, {4, 4}, {0, 5}, {4, 5},
+                             {1, 6}, {2, 6}, {3, 6}};
+
+static GdkPoint digit07[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {4, 1},
+                             {3, 2}, {3, 3}, {2, 4}, {1, 5}, {1, 6}};
+
+static GdkPoint digit08[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {0, 2},
+                             {4, 2}, {1, 3}, {2, 3}, {3, 3}, {0, 4}, {4, 4},
+                             {0, 5}, {4, 5}, {1, 6}, {2, 6}, {3, 6}};
+
+static GdkPoint digit09[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {0, 2},
+                             {3, 2}, {4, 2}, {1, 3}, {2, 3}, {4, 3}, {4, 4},
+                             {3, 5}, {1, 6}, {2, 6}};
+
+static GdkPoint digit10[] = {{2, 0}, {1, 1}, {3, 1}, {0, 2}, {4, 2}, {0, 3},
+                             {4, 3}, {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4},
+                             {0, 5}, {4, 5}, {0, 6}, {4, 6}};
+
+static GdkPoint digit11[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {1, 1}, {4, 1},
+                             {1, 2}, {4, 2}, {1, 3}, {2, 3}, {3, 3}, {1, 4},
+                             {4, 4}, {1, 5}, {4, 5}, {0, 6}, {1, 6}, {2, 6},
+                             {3, 6}};
+
+static GdkPoint digit12[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {0, 2},
+                             {0, 3}, {0, 4},{0, 5}, {4, 5}, {1, 6}, {2, 6},
+                             {3, 6}};
+
+static GdkPoint digit13[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {1, 1}, {4, 1},
+                             {1, 2}, {4, 2}, {1, 3}, {4, 3}, {1, 4}, {4, 4},
+                             {1, 5}, {4, 5}, {0, 6}, {1, 6}, {2, 6}, {3, 6}};
+
+static GdkPoint digit14[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1},
+                             {0, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {0, 4},
+                             {0, 5}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}};
+
+static GdkPoint digit15[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1},
+                             {0, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {0, 4},
+                             {0, 5}, {0, 6}};
+static GdkPoint minus[] = {{1, 3}, {2, 3}, {3, 3}, {4,3}};
+
+static fontgrid_digit digits[] = {
+   {digit00, sizeof(digit00)/sizeof(GdkPoint)},
+   {digit01, sizeof(digit01)/sizeof(GdkPoint)},
+   {digit02, sizeof(digit02)/sizeof(GdkPoint)},
+   {digit03, sizeof(digit03)/sizeof(GdkPoint)},
+   {digit04, sizeof(digit04)/sizeof(GdkPoint)},
+   {digit05, sizeof(digit05)/sizeof(GdkPoint)},
+   {digit06, sizeof(digit06)/sizeof(GdkPoint)},
+   {digit07, sizeof(digit07)/sizeof(GdkPoint)},
+   {digit08, sizeof(digit08)/sizeof(GdkPoint)},
+   {digit09, sizeof(digit09)/sizeof(GdkPoint)},
+   {digit10, sizeof(digit10)/sizeof(GdkPoint)},
+   {digit11, sizeof(digit11)/sizeof(GdkPoint)},
+   {digit12, sizeof(digit12)/sizeof(GdkPoint)},
+   {digit13, sizeof(digit13)/sizeof(GdkPoint)},
+   {digit14, sizeof(digit14)/sizeof(GdkPoint)},
+   {digit15, sizeof(digit15)/sizeof(GdkPoint)},
+   {minus, sizeof(minus)/sizeof(GdkPoint)},
+};
+
+/*
+ * This array is used to hold a set of digits that will be displayed.  It
+ * provides for a max of 19 points per digit and a max of 6 digits.
+ */
+static GdkPoint encoding_digits[19*6];
+
+/*
+ * Used to determine spacing between digits when displaying.
+ */
+#define FONTGRID_DIGIT_WIDTH   6
+#define FONTGRID_DIGIT_HEIGHT 10
+
+/*
+ * A macro for getting the current foreground GC.
+ */
+#define WIDGET_FG_GC(w) ((w)->style->fg_gc[GTK_WIDGET_STATE(w)])
+
+#define HMARGINS(fw) ((fw)->hmargin << 1)
+#define VMARGINS(fw) ((fw)->vmargin << 1)
+
+static void
+fontgrid_set_cell_geometry(Fontgrid *fw)
+{
+    bdf_font_t *font;
+    gint lw;
+
+    font = fw->font;
+
+    lw = FONTGRID_DIGIT_WIDTH * 7;
+
+    /*
+     * The labels will always be numbers in base 8, 10, or 16, so we are only
+     * interested in the max ascent.  Add a 2-pixel margin on top and bottom.
+     */
+    fw->label_height = FONTGRID_DIGIT_HEIGHT + 4;
+
+    /*
+     * We want a minumum padding of 3 pixels on each side of the glyph bitmap
+     * in each cell. Thus the addition of 6 to each dimension.
+     */
+    if (font != 0) {
+        fw->cell_width = font->bbx.width + 6;
+        fw->cell_height = font->bbx.height + 6;
+    } else {
+        /*
+         * Hard-code a minimum size for NULL fonts. The initial height of
+         * an empty cell is 20 to give it a better visual appearance.
+         */
+        fw->cell_width = lw + 6;
+        fw->cell_height = FGRID_DEFAULT_CELL_HEIGHT + 6;
+    }
+
+    fw->cell_width = MAX(fw->cell_width, lw);
+    fw->cell_height = MAX(fw->cell_height, fw->label_height);
+
+    /*
+     * Now add the label size into the picture.
+     */
+    fw->cell_height += fw->label_height - 1;
+}
+
+static void
+fontgrid_set_rows_cols(Fontgrid *fw, GtkAllocation *core)
+{
+    gint i;
+    guint16 dw, dh, wd, ht;
+
+    /*
+     * Limit the window size to 7/8 of the actual screen dimensions.
+     */
+    dw = (gdk_screen_width() * 7) >> 3;
+    dh = (gdk_screen_height() * 7) >> 3;
+
+    if (!core) {
+        /*
+         * Adjust the rows and columns based on the preferred geometry.
+         */
+        wd = (fw->cell_width * fw->cell_cols) + HMARGINS(fw);
+        ht = (fw->cell_height * fw->cell_rows) + VMARGINS(fw);
+
+        if (wd > dw)
+          fw->cell_cols = (dw - HMARGINS(fw)) / fw->cell_width;
+
+        if (ht > dh)
+          fw->cell_rows = (dh - VMARGINS(fw)) / fw->cell_height;
+    } else {
+        /*
+         * Adjust the rows and columns based on the current geometry.
+         */
+        fw->cell_cols = (core->width - HMARGINS(fw)) / fw->cell_width;
+        fw->cell_rows = (core->height - VMARGINS(fw)) / fw->cell_height;
+    }
+
+    /*
+     * Adjust rows and columns to powers of two if necessary.
+     */
+    if (fw->power2) {
+        /*
+         * Make sure the columns are a power of 2.
+         */
+        for (i = 15; i >= 0; i--) {
+            if (fw->cell_cols & (1 << i)) {
+                fw->cell_cols = 1 << i;
+                break;
+            }
+        }
+
+        /*
+         * Make sure the rows are a power of 2.
+         */
+        for (i = 15; i >= 0; i--) {
+            if (fw->cell_rows & (1 << i)) {
+                fw->cell_rows = 1 << i;
+                break;
+            }
+        }
+    }
+
+    /*
+     * Fall back to a minimum of two rows.
+     */
+    if (fw->cell_rows == 0)
+      fw->cell_rows = 2;
+
+    /*
+     * Fall back to a minimum of two columns.
+     */
+    if (fw->cell_cols == 0)
+      fw->cell_cols = 2;
+
+    /*
+     * Make sure the number of rows and cols are within the max limits.
+     */
+    if (fw->cell_cols > FGRID_MAX_COLS)
+      fw->cell_cols = FGRID_MAX_COLS;
+
+    if (fw->cell_rows > FGRID_MAX_ROWS)
+      fw->cell_rows = FGRID_MAX_ROWS;
+
+    /*
+     * Set the new page size based on the calculated rows and columns.
+     */
+    fw->pagesize = fw->cell_rows * fw->cell_cols;
+}
+
+/**************************************************************************
+ *
+ * GObjectClass functions.
+ *
+ **************************************************************************/
+
+static void
+fontgrid_set_property(GObject *obj, guint prop_id, const GValue *value,
+                      GParamSpec *pspec)
+{
+    GtkWidget *widget;
+    Fontgrid *fw;
+
+    widget = GTK_WIDGET(obj);
+    fw = FONTGRID(obj);
+
+    switch (prop_id) {
+      case PROP_CODE_BASE:
+        fw->base = g_value_get_uint(value);
+        /*
+         * Force the encodings to be redisplayed here?
+         */
+        break;
+      case PROP_POWER2:
+        fw->power2 = g_value_get_boolean(value);
+        break;
+      case PROP_ORIENTATION:
+        fontgrid_set_orientation(fw, g_value_get_enum(value));
+        break;
+      case PROP_FONT:
+        /*
+         * Need to set the rows and columns back to their defaults when
+         * a new font is passed in case it is NULL.
+         */
+        fw->font = (bdf_font_t *) g_value_get_pointer(value);
+
+        fontgrid_set_cell_geometry(fw);
+        fontgrid_set_rows_cols(fw, 0);
+        break;
+      case PROP_POINT_SIZE:
+        fw->point_size = g_value_get_uint(value);
+        break;
+      case PROP_SPACING:
+        fw->spacing = g_value_get_int(value);
+        break;
+      case PROP_SKIP_BLANKS:
+        fw->noblanks = g_value_get_boolean(value);
+        break;
+      case PROP_OVERWRITE:
+        fw->overwrite = g_value_get_boolean(value);
+        break;
+      case PROP_COLORS:
+        fw->colors = (guint16 *) g_value_get_pointer(value);
+        break;
+      case PROP_INITIAL_GLYPH:
+        fw->initial_glyph = g_value_get_int(value);
+        break;
+      case PROP_BPP:
+        fw->bpp = g_value_get_int(value);
+        break;
+      case PROP_HRES:
+        fw->hres = g_value_get_int(value);
+        break;
+      case PROP_VRES:
+        fw->vres = g_value_get_int(value);
+        break;
+    }
+}
+
+static void
+fontgrid_get_property(GObject *obj, guint prop_id, GValue *value,
+                      GParamSpec *pspec)
+{
+    Fontgrid *f;
+
+    f = FONTGRID(obj);
+
+    switch (prop_id) {
+      case PROP_CODE_BASE:
+        g_value_set_uint(value, f->base);
+        break;
+      case PROP_POWER2:
+        g_value_set_boolean(value, f->power2);
+        break;
+      case PROP_ORIENTATION:
+        g_value_set_enum(value, f->orientation);
+        break;
+      case PROP_FONT:
+        g_value_set_pointer(value, f->font);
+        break;
+      case PROP_POINT_SIZE:
+        g_value_set_uint(value, f->point_size);
+        break;
+      case PROP_SPACING:
+        g_value_set_int(value, f->spacing);
+        break;
+      case PROP_SKIP_BLANKS:
+        g_value_set_boolean(value, f->noblanks);
+        break;
+      case PROP_COLORS:
+        g_value_set_pointer(value, f->colors);
+        break;
+      case PROP_INITIAL_GLYPH:
+        g_value_set_int(value, f->initial_glyph);
+        break;
+      case PROP_BPP:
+        g_value_set_int(value, f->bpp);
+        break;
+      case PROP_HRES:
+        g_value_set_int(value, f->hres);
+        break;
+      case PROP_VRES:
+        g_value_set_int(value, f->vres);
+        break;
+    }
+}
+
+/**************************************************************************
+ *
+ * GtkObjectClass functions.
+ *
+ **************************************************************************/
+
+static void
+fontgrid_destroy(GtkObject *obj)
+{
+    Fontgrid *f;
+    guint32 i;
+    bdf_glyphlist_t *gl;
+
+    /*
+     * Do some checks to make sure the incoming object exists and is the right
+     * kind.
+     */
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_FONTGRID(obj));
+
+    f = FONTGRID(obj);
+
+    /*
+     * Clean up this object instance.
+     */
+    if (f->font)
+      bdf_free_font(f->font);
+    f->font = 0;
+
+    if (f->xor_gc != 0)
+      g_object_unref(G_OBJECT(f->xor_gc));
+    f->xor_gc = 0;
+
+    if (f->points_size > 0)
+      g_free(f->points);
+    f->points_size = f->points_used = 0;
+
+    if (f->rgb_size > 0)
+      g_free(f->rgb);
+    f->rgb_used = f->rgb_size = 0;
+
+    /*
+     * Remove all ownership of selections.
+     */
+    gtk_selection_remove_all(GTK_WIDGET(obj));
+
+    /*
+     * Free up the clipboard contents if there are any.
+     */
+    gl = &f->clipboard;
+    for (i = 0; i < gl->glyphs_used; i++) {
+        if (gl->glyphs[i].name)
+          free(gl->glyphs[i].name);
+        if (gl->glyphs[i].bytes > 0)
+          free((char *) gl->glyphs[i].bitmap);
+    }
+    if (gl->glyphs_size > 0)
+      free((char *) gl->glyphs);
+    gl->glyphs_size = gl->glyphs_used = 0;
+
+    /*
+     * Follow the class chain back up to free up resources allocated in the
+     * parent classes.
+     */
+    GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+fontgrid_finalize(GObject *obj)
+{
+    /*
+     * Do some checks to make sure the incoming object exists and is the right
+     * kind.
+     */
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_FONTGRID(obj));
+
+    /*
+     * Follow the class chain back up to free up resources allocated in the
+     * parent classes.
+     */
+    G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+/**************************************************************************
+ *
+ * GtkWidgetClass functions.
+ *
+ **************************************************************************/
+
+static void
+fontgrid_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
+{
+    Fontgrid *fw;
+
+    fw = FONTGRID(widget);
+    preferred->width = (fw->cell_width * fw->cell_cols) + HMARGINS(fw);
+    preferred->height = (fw->cell_height * fw->cell_rows) + VMARGINS(fw);
+}
+
+static void
+fontgrid_actual_size(GtkWidget *widget, GtkAllocation *actual)
+{
+    Fontgrid *fw;
+
+    fw = FONTGRID(widget);
+
+    widget->allocation = *actual;
+
+    /*
+     * Make sure the rows and columns are adjusted to fit the actual allocated
+     * size.
+     */
+    fontgrid_set_rows_cols(fw, actual);
+
+    if (GTK_WIDGET_REALIZED(widget))
+      gdk_window_move_resize(widget->window, actual->x, actual->y,
+                             actual->width, actual->height);
+}
+
+static void
+fontgrid_realize(GtkWidget *widget)
+{
+    Fontgrid *fw;
+    GdkWindowAttr attributes;
+    GdkGCValues values;
+    gint attributes_mask;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_FONTGRID(widget));
+
+    fw = FONTGRID(widget);
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK|
+                              GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK|
+                              GDK_POINTER_MOTION_MASK|
+                              GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|
+                              GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK|
+                              GDK_PROPERTY_CHANGE_MASK);
+
+    attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP;
+
+    widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
+                                    &attributes, attributes_mask);
+    gdk_window_set_user_data(widget->window, widget);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
+
+    if (fw->xor_gc != 0)
+      g_object_unref(G_OBJECT(fw->xor_gc));
+
+    /*
+     * Create the GC used to display selected cells.
+     */
+    values.foreground.pixel =
+        widget->style->fg[GTK_WIDGET_STATE(widget)].pixel ^
+        widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
+    (void) memset((char *) &values.background, 0, sizeof(GdkColor));
+    values.function = GDK_XOR;
+    fw->xor_gc = gdk_gc_new_with_values(widget->window, &values,
+                                        GDK_GC_FOREGROUND|
+                                        GDK_GC_BACKGROUND|GDK_GC_FUNCTION);
+}
+
+static bdf_glyph_t *
+fontgrid_locate_glyph(bdf_glyph_t *glyphs, guint32 nglyphs, gint32 code,
+                      gboolean exact_match)
+{
+    gint32 l, r, m = 0;
+
+    if (code < 0 || glyphs == 0 || nglyphs == 0)
+      return 0;
+
+    for (l = 0, r = (gint32) (nglyphs - 1); l <= r; ) {
+        m = (l + r) >> 1;
+        if (glyphs[m].encoding < code)
+          l = m + 1;
+        else if (glyphs[m].encoding > code)
+          r = m - 1;
+        else {
+            if (exact_match)
+              return glyphs + m;
+            break;
+        }
+    }
+
+    if (exact_match)
+      return 0;
+
+    /*
+     * Adjust to the beginning or end if nothing was found in the search.
+     */
+    while (m > 0 && glyphs[m].encoding > code)
+      m--;
+    while (m < (gint32) nglyphs && glyphs[m].encoding < code)
+      m++;
+
+    return (m < (gint32) nglyphs) ? glyphs + m : 0;
+}
+
+static void
+fontgrid_get_glyph_points(Fontgrid *fw, gint x, gint y, gint rx, gint by,
+                          bdf_glyph_t *glyph)
+{
+    gint i, j, bpr, col;
+    unsigned char *bmap, *masks = 0;
+
+    switch (fw->bpp) {
+      case 1: masks = bdf_onebpp; break;
+      case 2: masks = bdf_twobpp; break;
+      case 4: masks = bdf_fourbpp; break;
+      case 8: masks = bdf_eightbpp; break;
+    }
+
+    fw->points_used = 0;
+    bmap = glyph->bitmap;
+    bpr = ((glyph->bbx.width * fw->bpp) + 7) >> 3;
+
+    for (i = 0; i + y - glyph->bbx.ascent < by && i < glyph->bbx.height; i++) {
+        for (col = j = 0; j + x < rx && j < glyph->bbx.width;
+             j++, col += fw->bpp) {
+            if (bmap[(i * bpr) + (col >> 3)] &
+                masks[(col & 7) / fw->bpp]) {
+                if (fw->points_used == fw->points_size) {
+                    if (fw->points_size == 0)
+                      fw->points =
+                          (GdkPoint *) g_malloc(sizeof(GdkPoint) << 7);
+                    else
+                      fw->points =
+                          (GdkPoint *) g_realloc(fw->points,
+                                                 sizeof(GdkPoint) *
+                                                 (fw->points_size + 128));
+                    fw->points_size += 128;
+                }
+
+                fw->points[fw->points_used].x = j + x;
+                fw->points[fw->points_used].y = i + y - glyph->bbx.ascent;
+                fw->points_used++;
+            }
+        }
+    }
+}
+
+#if 0
+static void
+fontgrid_get_glyph_points_color(Fontgrid *fw, gint x, gint y, gint rx, gint by,
+                                gint color_index, bdf_glyph_t *glyph)
+{
+    gint i, j, bpr, col, byte, di = 0, si, cidx = 0;
+    unsigned char *bmap, *masks = 0;
+
+    switch (fw->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    fw->points_used = 0;
+    bmap = glyph->bitmap;
+    bpr = ((glyph->bbx.width * fw->bpp) + 7) >> 3;
+
+    for (i = 0; i + y - glyph->bbx.ascent < by && i < glyph->bbx.height; i++) {
+        for (col = j = 0; j + x < rx && j < glyph->bbx.width;
+             j++, col += fw->bpp) {
+            si = (col & 7) / fw->bpp;
+            byte = bmap[(i * bpr) + (col >> 3)] & masks[si];
+            if (byte) {
+                /*
+                 * Check to see if the byte matches the color index being
+                 * collected.
+                 */
+                if (di > si)
+                  byte >>= (di - si) * fw->bpp;
+
+                if (byte == cidx) {
+                    if (fw->points_used == fw->points_size) {
+                        if (fw->points_size == 0)
+                          fw->points = (GdkPoint *)
+                              g_malloc(sizeof(GdkPoint) << 6);
+                        else
+                          fw->points = (GdkPoint *)
+                              g_realloc(fw->points, sizeof(GdkPoint) *
+                                        (fw->points_size + 64));
+                        fw->points_size += 64;
+                    }
+
+                    fw->points[fw->points_used].x = j + x;
+                    fw->points[fw->points_used].y = i + y - glyph->bbx.ascent;
+                    fw->points_used++;
+                }
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * This routine creates a 24 bits per pixel image of a glyph so it can be
+ * drawn using GtkRGB. This is less complicated than the old method of
+ * collecting and drawing individual pixels of each different color.
+ */
+static void
+fontgrid_make_rgb_image(Fontgrid *fw, bdf_glyph_t *glyph)
+{
+    GtkWidget *w = GTK_WIDGET(fw);
+    gint x, y, bpr, rgb_bpr, col, byte, di = 0, si;
+    guchar bg[4], pix[4], *bmap, *masks = 0;
+
+    /*
+     * Figure out the background color.
+     */
+    bg[0] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].red;
+    bg[1] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].green;
+    bg[2] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].blue;
+
+    switch (fw->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    bmap = glyph->bitmap;
+    bpr = ((glyph->bbx.width * fw->bpp) + 7) >> 3;
+
+    rgb_bpr = glyph->bbx.width * 3;
+    fw->rgb_used = rgb_bpr * glyph->bbx.height;
+
+    if (fw->rgb_size < fw->rgb_used) {
+        if (fw->rgb_size == 0)
+          fw->rgb = g_malloc(fw->rgb_used);
+        else
+          fw->rgb = g_realloc(fw->rgb, fw->rgb_used);
+        fw->rgb_size = fw->rgb_used;
+    }
+
+    for (y = 0; y < glyph->bbx.height; y++) {
+        for (col = x = 0; x < glyph->bbx.width; x++, col += fw->bpp) {
+            si = (col & 7) / fw->bpp;
+
+            byte = bmap[(y * bpr) + (col >> 3)] & masks[si];
+            if (di > si)
+              byte >>= (di - si) * fw->bpp;
+            if (byte) {
+                /*
+                 * Look up the color.
+                 */
+                switch (fw->bpp) {
+                  case 1: memset(pix, 0, 3); break;
+                  case 2: memset(pix, fw->colors[byte-1], 3); break;
+                  case 4: memset(pix, fw->colors[byte-1+4], 3); break;
+                  case 8: memset(pix, byte, 3); break;
+                }
+            } else
+              /*
+               * Set the pixel to the background color.
+               */
+              memcpy(pix, bg, 3);
+
+            memcpy(&fw->rgb[(y * rgb_bpr) + (x * 3)], pix, 3);
+        }
+    }
+}
+
+static void
+fontgrid_draw_encoding(GtkWidget *w, GdkGC *gc, gint x, gint y, gchar *num,
+                       gint numlen)
+{
+    gint i, j, d;
+    GdkPoint *dp;
+
+    if (!GTK_WIDGET_REALIZED(w))
+      return;
+
+    dp = encoding_digits;
+    for (i = 0; i < numlen; i++) {
+        if (num[i] == '-') 
+          d = 16;
+        else if (num[i] <= '9')
+          d = num[i] - '0';
+        else
+          d = (num[i] - 'A') + 10;
+
+        /*
+         * Copy the next digit into the display array.
+         */
+        (void) memcpy((char *) dp, (char *) digits[d].points,
+                      sizeof(GdkPoint) * digits[d].npoints);
+        /*
+         * Position the points.
+         */
+        for (j = 0; j < digits[d].npoints; j++) {
+            dp[j].x += x;
+            dp[j].y += y;
+        }
+        dp += digits[d].npoints;
+        x += 6;
+    }
+
+    /*
+     * Draw the points.
+     */
+    gdk_draw_points(w->window, gc, encoding_digits, dp - encoding_digits);
+}
+
+static void
+fontgrid_draw_cells(GtkWidget *widget, gint32 start, gint32 end,
+                    gboolean labels, gboolean glyphs)
+{
+    Fontgrid *fw;
+    gint x, y, wd, as, ds, len, lx, ly;
+    gint32 i, n, r, c;
+    guint32 nglyphs, ng;
+    gboolean mod;
+    bdf_font_t *font;
+    bdf_glyph_t *glyph, *gp;
+    FontgridInternalPageInfo *pi;
+    GdkGC *gc;
+    GdkRectangle rect;
+    gchar nbuf[16];
+
+    if (!GTK_WIDGET_REALIZED(widget) || (labels == FALSE && glyphs == FALSE))
+      return;
+
+    fw = FONTGRID(widget);
+
+    font = fw->font;
+
+    glyph = 0;
+    nglyphs = 0;
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        if (font) {
+            glyph = font->glyphs;
+            nglyphs = font->glyphs_used;
+        }
+    } else {
+        /*
+         * When viewing the unencoded glyph pages, all glyphs are labelled
+         * with an encoding of -1.
+         */
+        strcpy(nbuf, "-1");
+
+        pi = &fw->upage;
+        if (font) {
+            glyph = font->unencoded;
+            nglyphs = font->unencoded_used;
+        }
+    }
+
+    /*
+     * The initial code to work from.
+     */
+    n = pi->bcode;
+
+    /*
+     * Locate the glyph closest to the starting code.
+     */
+    if ((glyph = fontgrid_locate_glyph(glyph, nglyphs, start, FALSE)) == 0)
+      nglyphs = 0;
+
+    gp = glyph;
+
+    gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)];
+
+    for (ng = 0, i = start; i <= end; i++) {
+        /*
+         * Only draw those cells that are on the current page.
+         */
+        if (i < pi->bcode || i >= pi->bcode + fw->pagesize)
+          continue;
+
+        if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) {
+            r = (i - n) / fw->cell_cols;
+            c = (i - n) % fw->cell_cols;
+        } else {
+            c = (i - n) / fw->cell_rows;
+            r = (i - n) % fw->cell_rows;
+        }
+
+        x = fw->xoff + (c * fw->cell_width);
+        y = fw->yoff + (r * fw->cell_height);
+
+        if (labels) {
+            if (!fw->unencoded) {
+                switch (fw->base) {
+                  case 8: sprintf(nbuf, "%o", i); break;
+                  case 10: sprintf(nbuf, "%d", i); break;
+                  case 16: sprintf(nbuf, "%X", i); break;
+                }
+            }
+            rect.x = x + 1;
+            rect.y = y + 1;
+            rect.width = fw->cell_width - 2;
+            rect.height = fw->label_height - 2;
+            gdk_draw_rectangle(widget->window, gc, FALSE,
+                               rect.x, rect.y, rect.width, rect.height);
+
+            len = strlen(nbuf);
+            wd = len * 6;
+            as = 8;
+            ds = 0;
+
+            lx = (x + ((fw->cell_width >> 1) - (wd >> 1))) + 1;
+            ly = (y + ((fw->label_height >> 1) - ((as + ds) >> 1))) + 1;
+
+            mod = FALSE;
+            if (i <= 0xffff)
+              mod = (!fw->unencoded) ? bdf_glyph_modified(font, i, 0) :
+                bdf_glyph_modified(font, i, 1);
+
+            gdk_window_clear_area(widget->window, rect.x + 1, rect.y + 1,
+                                  rect.width - 1, rect.height - 1);
+
+            if (!fw->unencoded && mod) {
+                gdk_draw_rectangle(widget->window, gc, TRUE,
+                                   rect.x + 2, rect.y + 2,
+                                   rect.width - 3, rect.height - 3);
+                fontgrid_draw_encoding(widget, fw->xor_gc, lx, ly, nbuf, len);
+                if (gp && gp->encoding == i) {
+                    ng++;
+                    gp++;
+                    if (ng == nglyphs)
+                      gp = 0;
+                }
+            } else {
+                /*
+                 * If the glyph exists, then darken the rectangle to indicate
+                 * this.
+                 */
+                if (gp && gp->encoding == i) {
+                    gdk_draw_rectangle(widget->window, gc, FALSE,
+                                       rect.x + 1, rect.y + 1,
+                                       rect.width - 2, rect.height - 2);
+                    ng++;
+                    gp++;
+                    if (ng == nglyphs)
+                      gp = 0;
+                }
+                fontgrid_draw_encoding(widget, gc, lx, ly, nbuf, len);
+            }
+        }
+
+        if (glyphs) {
+            rect.x = x + 1;
+            rect.y = y + fw->label_height + 1;
+            rect.width = fw->cell_width - 2;
+            rect.height = (fw->cell_height - fw->label_height) - 2;
+
+            if (i <= 0xffff && nglyphs > 0 && glyph->encoding == i) {
+                /*
+                 * Draw the glyph.
+                 */
+
+                /*
+                 * Set the right and left limits for generating points.
+                 */
+                lx = x + fw->cell_width - 2;
+                ly = y + fw->cell_height - 2;
+
+                /*
+                 * Adjust the X,Y coordinate pair so the bitmap points will
+                 * be generated to center the glyphs horizontally and align
+                 * them to the BDF font's baseline vertically.
+                 */
+                x += (fw->cell_width >> 1) -
+                    ((font->bbx.width + font->bbx.x_offset) >> 1) + 1;
+                y += fw->label_height + font->bbx.ascent + 3;
+
+                if (IsSelected(glyph->encoding, pi->selmap)) {
+                    gdk_draw_rectangle(widget->window, gc, TRUE,
+                                       rect.x + 1, rect.y + 1,
+                                       rect.width - 1, rect.height - 1);
+                    if (glyph->bytes > 0) {
+                        fontgrid_get_glyph_points(fw, x, y, lx, ly, glyph);
+                        if (fw->points_used > 0)
+                          gdk_draw_points(widget->window, fw->xor_gc,
+                                          fw->points, fw->points_used);
+                    }
+                } else {
+                    /*
+                     * The glyph is not selected, so draw it according to
+                     * the bytes-per-pixel of the font.
+                     */
+                    gdk_window_clear_area(widget->window, rect.x, rect.y,
+                                          rect.width, rect.height);
+                    if (glyph->bytes > 0) {
+                        fontgrid_make_rgb_image(fw, glyph);
+                        gdk_draw_rgb_image(widget->window, gc,
+                                           x, y - glyph->bbx.ascent,
+                                           glyph->bbx.width,
+                                           glyph->bbx.height,
+                                           GDK_RGB_DITHER_NONE,
+                                           fw->rgb, glyph->bbx.width * 3);
+                    }
+                }
+                glyph++;
+                if (ng == nglyphs) {
+                    nglyphs = 0;
+                    glyph = 0;
+                }
+            } else {
+                /*
+                 * Clear the empty cell.
+                 */
+                if (i <= 0xffff && IsSelected(i, pi->selmap))
+                  gdk_draw_rectangle(widget->window, gc, TRUE,
+                                     rect.x + 1, rect.y + 1,
+                                     rect.width - 1, rect.height - 1);
+                else {
+                    gdk_window_clear_area(widget->window, rect.x, rect.y,
+                                          rect.width, rect.height);
+                    if (i > 0xffff) {
+                        gdk_draw_line(widget->window, gc, rect.x, rect.y,
+                                      rect.x + rect.width,
+                                      rect.y + rect.height);
+                        gdk_draw_line(widget->window, gc,
+                                      rect.x + rect.width, rect.y,
+                                      rect.x, rect.y + rect.height);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void
+fontgrid_draw(GtkWidget *widget, GdkRegion *region)
+{
+    Fontgrid *fw;
+    gint x, y, i;
+    guint16 wd, ht, gw, gh;
+    gint32 start, end;
+    GdkGC *gc;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_FONTGRID(widget));
+
+    fw = FONTGRID(widget);
+
+    gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)];
+
+    gw = fw->cell_width * fw->cell_cols;
+    gh = fw->cell_height * fw->cell_rows;
+    wd = widget->allocation.width;
+    ht = widget->allocation.height;
+    x = fw->xoff = ((wd >> 1) - (gw >> 1)) - 1;
+    y = fw->yoff = ((ht >> 1) - (gh >> 1)) - 1;
+
+    /*
+     * Draw the horizontal lines.
+     */
+    for (i = 0; i <= fw->cell_rows; i++) {
+        gdk_draw_line(widget->window, gc, x, y, x + gw, y);
+
+        /*
+         * Only draw the second line if this is not the last line.
+         */
+        if (i < fw->cell_rows)
+          gdk_draw_line(widget->window, gc, x, y + fw->label_height,
+                        x + gw, y + fw->label_height);
+
+        y += fw->cell_height;
+    }
+
+    /*
+     * Draw the vertical lines.
+     */
+    x = fw->xoff;
+    y = fw->yoff;
+
+    for (i = 0; i <= fw->cell_cols; i++) {
+        gdk_draw_line(widget->window, gc, x, y, x, y + gh);
+        x += fw->cell_width;
+    }
+
+    start = (!fw->unencoded) ? fw->npage.bcode : fw->upage.bcode;
+    end = start + (gint32) (fw->pagesize - 1);
+
+    fontgrid_draw_cells(widget, start, end, TRUE, TRUE);
+}
+
+static void
+fontgrid_select_range(Fontgrid *fw, gint32 start, gint32 end)
+{
+    gint32 i, tmp;
+    FontgridInternalPageInfo *pi;
+
+    if (start > end) {
+        tmp = start;
+        start = end;
+        end = tmp;
+    }
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    for (i = start; i <= end; i++)
+      Select(i, pi->selmap);
+
+    /*
+     * Adjust the start and end values to the current page to determine which
+     * cells need to be redrawn.
+     */
+    tmp = pi->bcode + (fw->pagesize - 1);
+    if (start >= tmp || end < pi->bcode)
+      return;
+
+    if (start < pi->bcode)
+      start = pi->bcode;
+    if (end > tmp)
+      end = tmp;
+    fontgrid_draw_cells(GTK_WIDGET(fw), start, end, FALSE, TRUE);
+}
+
+static void
+fontgrid_deselect_range(Fontgrid *fw, gint32 start, gint32 end)
+{
+    gint32 i, tmp;
+    FontgridInternalPageInfo *pi;
+
+    if (start > end) {
+        tmp = start;
+        start = end;
+        end = tmp;
+    }
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+    for (i = start; i <= end; i++) {
+        if (IsSelected(i, pi->selmap)) {
+            Unselect(i, pi->selmap);
+            if (i >= pi->bcode && i <= pi->bcode + (fw->pagesize - 1))
+              fontgrid_draw_cells(GTK_WIDGET(fw), i, i, FALSE, TRUE);
+        }
+    }
+}
+
+static void
+fontgrid_deselect_all(Fontgrid *fw)
+{
+    FontgridInternalPageInfo *pi, *opi;
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        opi = &fw->upage;
+    } else {
+        pi = &fw->upage;
+        opi = &fw->npage;
+    }
+
+    if (pi->sel_start != -1 || pi->sel_end != -1)
+      fontgrid_deselect_range(fw, pi->bcode, pi->bcode + fw->pagesize - 1);
+    else if (opi->sel_start != -1 || opi->sel_end != -1)
+      fontgrid_deselect_range(fw, opi->bcode, opi->bcode + fw->pagesize - 1);
+
+    /*
+     * Now clear the selected bitmaps.
+     */
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+    (void) memset((char *) opi->selmap, 0, sizeof(guint32) * 2048);
+
+    /*
+     * Reset the selection start and end points.
+     */
+    pi->sel_start = pi->sel_end = opi->sel_start = opi->sel_end = -1;
+}
+
+static void
+fontgrid_draw_focus(GtkWidget *widget, GdkRectangle *area)
+{
+    GdkGC *gc;
+    gint x, y, wd, ht, fwidth, fpad;
+
+    /*
+     * Do something with this later to make sure the focus line width
+     * is set in the GC's.
+     */
+    gtk_widget_style_get(widget,
+                         "focus-line-width", &fwidth,
+                         "focus-padding", &fpad, NULL);
+
+    gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
+
+    x = (widget->style->xthickness + fwidth + fpad) - 1;
+    y = (widget->style->ythickness + fwidth + fpad) - 1;
+    wd = (widget->allocation.width - (x * 2));
+    ht = (widget->allocation.height - (y * 2));
+
+    if (GTK_WIDGET_HAS_FOCUS(widget))
+      gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget),
+                      area, widget, "fontgrid", x, y, wd, ht);
+    else {
+        gdk_gc_set_clip_rectangle(gc, area);
+        gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1);
+        gdk_gc_set_clip_rectangle(gc, 0);
+    }
+}
+
+static gint
+fontgrid_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+    /*
+     * Paint the shadow first.
+     */
+    if (GTK_WIDGET_DRAWABLE(widget))
+      gtk_paint_shadow(widget->style, widget->window,
+                       GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
+                       &event->area, widget, "fontgrid",
+                       0, 0,
+                       widget->allocation.width,
+                       widget->allocation.height);
+
+    fontgrid_draw(widget, event->region);
+
+    fontgrid_draw_focus(widget, &event->area);
+
+    return FALSE;
+}
+
+static gint
+fontgrid_focus_in(GtkWidget *widget, GdkEventFocus *event)
+{
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_FONTGRID(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+    fontgrid_draw_focus(widget, 0);
+
+    return FALSE;
+}
+
+static gint
+fontgrid_focus_out(GtkWidget *widget, GdkEventFocus *event)
+{
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_FONTGRID(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
+    fontgrid_draw_focus(widget, 0);
+
+    return FALSE;
+}
+
+static gint
+fontgrid_lose_selection(GtkWidget *widget, GdkEventSelection *event)
+{
+    Fontgrid *fw;
+    FontgridInternalPageInfo *pi;
+    gint32 code;
+
+    fw = FONTGRID(widget);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (pi->sel_start != pi->sel_end) {
+        code = pi->sel_start;
+        fontgrid_deselect_all(fw);
+        pi->sel_end = pi->sel_start = code;
+        Select(pi->sel_start, pi->selmap);
+        fontgrid_draw_cells(widget, code, code, FALSE, TRUE);
+    }
+
+    return TRUE;
+}
+
+/**************************************************************************
+ *
+ * Paging routines.
+ *
+ **************************************************************************/
+
+static void
+fontgrid_neighbor_pages(Fontgrid *fw, gint32 page, gint32 *prev, gint32 *next)
+{
+    gint32 bcode, l, r, m;
+    guint32 nglyphs;
+    bdf_glyph_t *glyphs;
+    FontgridInternalPageInfo *pip;
+
+    pip = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (fw->noblanks == FALSE ||
+        (fw->unencoded == FALSE &&
+         (fw->font == 0 || fw->font->glyphs_used == 0))) {
+        *prev = page - 1;
+        *next = (page < pip->maxpage) ? page + 1 : -1;
+        return;
+    }
+
+    bcode = page * fw->pagesize;
+
+    if (!fw->unencoded) {
+        glyphs = fw->font->glyphs;
+        nglyphs = fw->font->glyphs_used;
+    } else {
+        glyphs = fw->font->unencoded;
+        nglyphs = fw->font->unencoded_used;
+    }
+
+    /*
+     * Do a binary search to find the the preceding page number.
+     */
+    for (l = m = 0, r = nglyphs - 1; l < r; ) {
+        m = (l + r) >> 1;
+        if (glyphs[m].encoding < bcode)
+          l = m + 1;
+        else if (glyphs[m].encoding > bcode)
+          r = m - 1;
+        else {
+            /*
+             * Exact match.
+             */
+            l = r = m - 1;
+            break;
+        }
+    }
+
+    /*
+     * In case the search ends on a code in the specified page.
+     */
+    while (r >= 0 && glyphs[r].encoding >= bcode)
+      r--;
+
+    /*
+     * Set the previous page code.
+     */
+    *prev = (r >= 0) ? glyphs[r].encoding / fw->pagesize : -1;
+
+    /*
+     * Determine the following page code.
+     */
+    if (r < 0)
+      r = 0;
+    while (r < nglyphs && glyphs[r].encoding < bcode + fw->pagesize)
+      r++;
+
+    *next = (r < nglyphs) ? glyphs[r].encoding / fw->pagesize : -1;
+}
+
+/**************************************************************************
+ *
+ * Selection routines.
+ *
+ **************************************************************************/
+
+static void
+start_selection(GtkWidget *widget, GdkEventButton *event)
+{
+    Fontgrid *fw;
+    gint16 x, y, row, col;
+    gint32 code;
+    bdf_glyph_t *gp;
+    FontgridInternalPageInfo *pi, *opi;
+    FontgridSelectionInfo sinfo;
+
+    fw = FONTGRID(widget);
+
+    /*
+     * Deal with the focus issue first.
+     */
+    if (!GTK_WIDGET_HAS_FOCUS(widget)) {
+        GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+        (void) fontgrid_draw_focus(widget, NULL);
+    }
+
+    x = (gint16) event->x;
+    y = (gint16) event->y;
+
+    col = fw->xoff + (fw->cell_width * fw->cell_cols);
+    row = fw->yoff + (fw->cell_height * fw->cell_rows);
+
+    /*
+     * If the button press is not in the font grid proper, just return.
+     */
+    if (x < fw->xoff || x >= col || y < fw->yoff || y >= row)
+      return;
+
+    /*
+     * Calculate the row and column that was clicked.
+     */
+    row = (y - fw->yoff) / fw->cell_height;
+    col = (x - fw->xoff) / fw->cell_width;
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        opi = &fw->upage;
+    } else {
+        pi = &fw->upage;
+        opi = &fw->npage;
+    }
+
+    if (fw->orientation == GTK_ORIENTATION_HORIZONTAL)
+      code = pi->bcode + (row * fw->cell_cols) + col;
+    else
+      code = pi->bcode + (col * fw->cell_rows) + row;
+
+    /*
+     * Any code greater than the maximum is ignored.
+     */
+    if (code > 0xffff)
+      return;
+
+    gp = 0;
+    if (fw->font) {
+        if (!fw->unencoded)
+          gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                     code, TRUE);
+        else
+          gp = fontgrid_locate_glyph(fw->font->unencoded,
+                                     fw->font->unencoded_used,
+                                     code, TRUE);
+    }
+
+    if (gp == 0) {
+        empty_glyph.encoding = code;
+        gp = &empty_glyph;
+    }
+
+    if (code != pi->sel_start || code != pi->sel_end) {
+        /*
+         * Clear any existing selection.
+         */
+        if (pi->sel_start != -1 || pi->sel_end != -1 ||
+            opi->sel_start != -1 || opi->sel_end != -1)
+          fontgrid_deselect_all(fw);
+
+        Select(code, pi->selmap);
+
+        fontgrid_draw_cells(widget, code, code, FALSE, TRUE);
+
+        pi->sel_start = pi->sel_end = code;
+
+        /*
+         * Clear the last click time to avoid situations where the second
+         * click on a different cell will cause the select callback to be
+         * called.
+         */
+        fw->last_click = 0;
+    }
+
+    sinfo.glyphs = gp;
+    sinfo.num_glyphs = 1;
+    sinfo.start = pi->sel_start;
+    sinfo.end = pi->sel_end;
+    sinfo.base = fw->base;
+    sinfo.unencoded = fw->unencoded;
+    if (event->type == GDK_BUTTON_PRESS &&
+        event->time - fw->last_click >= fw->mclick_time) {
+        sinfo.reason = FONTGRID_START_SELECTION;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                      &sinfo);
+    } else if (event->type == GDK_2BUTTON_PRESS) {
+        sinfo.reason = FONTGRID_ACTIVATE;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[ACTIVATE], 0,
+                      &sinfo);
+    }
+    fw->last_click = event->time;
+}
+
+static void
+extend_selection(GtkWidget *widget, gint16 x, gint16 y)
+{
+    Fontgrid *fw;
+    gint16 row, col;
+    gint32 code;
+    bdf_glyph_t *gp;
+    gboolean call_extend;
+    FontgridInternalPageInfo *pi;
+    FontgridSelectionInfo sinfo;
+
+    fw = FONTGRID(widget);
+
+    /*
+     * Deal with the focus issue first.
+     */
+    if (!GTK_WIDGET_HAS_FOCUS(widget)) {
+        GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+        (void) fontgrid_draw_focus(widget, NULL);
+    }
+
+    col = fw->xoff + (fw->cell_width * fw->cell_cols);
+    row = fw->yoff + (fw->cell_height * fw->cell_rows);
+
+    /*
+     * If the button press is not in the font grid proper, just return.
+     */
+    if (x < fw->xoff || x >= col || y < fw->yoff || y >= row)
+      return;
+
+    /*
+     * Calculate the row and column that was clicked.
+     */
+    row = (y - fw->yoff) / fw->cell_height;
+    col = (x - fw->xoff) / fw->cell_width;
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (fw->orientation == GTK_ORIENTATION_HORIZONTAL)
+      code = pi->bcode + (row * fw->cell_cols) + col;
+    else
+      code = pi->bcode + (col * fw->cell_rows) + row;
+
+    /*
+     * Any code greater than the maximum is ignored.
+     */
+    if (code > 0xffff)
+      return;
+
+    gp = 0;
+    if (fw->font) {
+        if (!fw->unencoded)
+          gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                     code, TRUE);
+        else
+          gp = fontgrid_locate_glyph(fw->font->unencoded,
+                                     fw->font->unencoded_used,
+                                     code, TRUE);
+        if (gp == 0) {
+            empty_glyph.encoding = code;
+            gp = &empty_glyph;
+        }
+    }
+
+    call_extend = FALSE;
+    if (code > pi->sel_end) {
+        call_extend = TRUE;
+        if (code <= pi->sel_start)
+          fontgrid_deselect_range(fw, pi->sel_end, code - 1);
+        else {
+            if (pi->sel_end < pi->sel_start) {
+                fontgrid_deselect_range(fw, pi->sel_end, pi->sel_start - 1);
+                fontgrid_select_range(fw, pi->sel_start + 1, code);
+            } else
+              fontgrid_select_range(fw, pi->sel_end, code);
+        }
+    } else if (code < pi->sel_end) {
+        call_extend = TRUE;
+        if (code < pi->sel_start) {
+            if (pi->sel_end > pi->sel_start) {
+                fontgrid_deselect_range(fw, pi->sel_start + 1, pi->sel_end);
+                fontgrid_select_range(fw, code, pi->sel_start);
+            } else
+              fontgrid_select_range(fw, code, pi->sel_end);
+        } else
+          fontgrid_deselect_range(fw, code + 1, pi->sel_end);
+    }
+
+    pi->sel_end = code;
+
+    if (call_extend == TRUE) {
+        if (pi->sel_start == pi->sel_end) {
+            sinfo.glyphs = gp;
+            sinfo.num_glyphs = 1;
+        } else {
+            sinfo.glyphs = 0;
+            sinfo.num_glyphs = 0;
+        }
+        sinfo.start = pi->sel_start;
+        sinfo.end = pi->sel_end;
+        sinfo.base = fw->base;
+        sinfo.unencoded = fw->unencoded;
+        sinfo.reason = FONTGRID_EXTEND_SELECTION;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_EXTEND], 0,
+                      &sinfo);
+    }
+}
+
+static void
+end_selection(GtkWidget *widget, GdkEventButton *event)
+{
+    Fontgrid *fw;
+    bdf_glyph_t *gp;
+    FontgridInternalPageInfo *pi;
+    FontgridSelectionInfo sinfo;
+
+    fw = FONTGRID(widget);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (pi->sel_start != pi->sel_end) {
+        /*
+         * Assert ownership of the clipboard if there is a selection of
+         * more than one glyph.
+         */
+        gdk_selection_owner_set(widget->window, FONTGRID_CLIPBOARD,
+                                GDK_CURRENT_TIME, FALSE);
+        sinfo.glyphs = 0;
+        sinfo.num_glyphs = 0;
+    } else {
+        gp = 0;
+        if (fw->font) {
+            if (!fw->unencoded)
+              gp = fontgrid_locate_glyph(fw->font->glyphs,
+                                         fw->font->glyphs_used,
+                                         pi->sel_start, TRUE);
+            else
+              gp = fontgrid_locate_glyph(fw->font->unencoded,
+                                         fw->font->unencoded_used,
+                                         pi->sel_start, TRUE);
+            if (gp == 0) {
+                empty_glyph.encoding = pi->sel_start;
+                gp = &empty_glyph;
+            }
+        }
+
+        sinfo.glyphs = gp;
+        sinfo.num_glyphs = 1;
+    }
+    sinfo.start = pi->sel_start;
+    sinfo.end = pi->sel_end;
+    sinfo.base = fw->base;
+    sinfo.unencoded = fw->unencoded;
+    sinfo.reason = FONTGRID_END_SELECTION;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_END], 0,
+                  &sinfo);
+}
+
+static void
+paste_selection(GtkWidget *widget, GdkEventButton *event)
+{
+    start_selection(widget, event);
+
+    if (event->state & GDK_SHIFT_MASK)
+      fontgrid_paste_selection(FONTGRID(widget), FONTGRID_INSERT_PASTE);
+    else if (event->state & GDK_CONTROL_MASK)
+      fontgrid_paste_selection(FONTGRID(widget), FONTGRID_MERGE_PASTE);
+    else
+      fontgrid_paste_selection(FONTGRID(widget), FONTGRID_NORMAL_PASTE);
+}
+
+static void
+copy_selection(GtkWidget *widget, GdkEventButton *event)
+{
+    fontgrid_copy_selection(FONTGRID(widget));
+}
+
+/**************************************************************************
+ *
+ * Button, pointer motion, and keyboard handling routines.
+ *
+ **************************************************************************/
+
+static gint
+fontgrid_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+    switch (event->button) {
+      case 1:
+        if (event->state & GDK_SHIFT_MASK)
+          extend_selection(widget, (gint16) event->x, (gint16) event->y);
+        else
+          start_selection(widget, event);
+        break;
+      case 2: paste_selection(widget, event); break;
+      case 3: copy_selection(widget, event); break;
+    }
+
+    return FALSE;
+}
+
+static gint
+fontgrid_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+    switch (event->button) {
+      case 1: end_selection(widget, event); break;
+      case 2: break;
+      case 3: break;
+    }
+    return FALSE;
+}
+
+static gint
+fontgrid_motion_notify(GtkWidget *widget, GdkEventMotion *event)
+{
+    if (event->state & GDK_BUTTON1_MASK)
+      extend_selection(widget, (gint16) event->x, (gint16) event->y);
+#if 0
+    /*
+     * Don't need these at the moment.
+     */
+    if (event->state & GDK_BUTTON2_MASK) {
+    }
+    if (event->state & GDK_BUTTON3_MASK) {
+    }
+#endif
+    return FALSE;
+}
+
+static gint
+fontgrid_shift_key_press(GtkWidget *widget, GdkEventKey *event)
+{
+    Fontgrid *fw;
+    bdf_glyph_t *gp;
+    guint keyval;
+    gint32 code, pageno;
+    guint32 count;
+    gboolean signal_extend, activate;
+    FontgridInternalPageInfo *pi, *opi;
+    FontgridSelectionInfo sinfo;
+
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_FONTGRID(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    fw = FONTGRID(widget);
+
+    /*
+     * For number keys, use them to add up a count that will effect the
+     * behavior of the other keys.
+     */
+    if (event->keyval >= GDK_0 && event->keyval <= GDK_9) {
+        fw->count = (fw->count * 10) + (event->keyval - GDK_0);
+        return FALSE;
+    }
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        opi = &fw->upage;
+        gp = (fw->font && fw->font->glyphs_used) ?
+            (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0;
+    } else {
+        pi = &fw->upage;
+        opi = &fw->npage;
+        gp = (fw->font && fw->font->unencoded_used) ?
+            (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0;
+    }
+
+    activate = FALSE;
+
+    code = pi->sel_end;
+
+    if ((count = fw->count) == 0)
+      count = 1;
+
+    keyval = event->keyval;
+    switch (event->keyval) {
+      case GDK_Page_Up:
+      case GDK_KP_Page_Up:
+        count *= fw->pagesize;
+        keyval = GDK_Left;
+        break;
+      case GDK_Page_Down:
+      case GDK_KP_Page_Down:
+        count *= fw->pagesize;
+        keyval = GDK_Right;
+        break;
+      case GDK_Home:
+      case GDK_KP_Home:
+        count = (pi->pageno - pi->minpage) * fw->pagesize;
+        keyval = GDK_Left;
+        break;
+      case GDK_End:
+      case GDK_KP_End:
+        count = (pi->maxpage - pi->pageno) * fw->pagesize;
+        keyval = GDK_Right;
+        break;
+    }
+
+    switch (keyval) {
+      case GDK_Left:
+      case GDK_KP_Left:
+        if (code == 0) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_VERTICAL)
+          code -= (fw->cell_rows * count);
+        else
+          code -= count;
+
+        if (code < 0)
+          code = 0;
+
+        break;
+      case GDK_Right:
+      case GDK_KP_Right:
+        /*
+         * Make sure that when on the unencoded pages, the final glyph is
+         * the limit unlike the encoded pages where the max value is 0xffff.
+         */
+        if ((fw->unencoded &&
+             (gp == 0 || code == gp->encoding)) ||
+            code == 0xffff) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_VERTICAL)
+          code += (fw->cell_rows * count);
+        else
+          code += count;
+
+        if (fw->unencoded && code > gp->encoding)
+          code = gp->encoding;
+        else if (code > 0xffff)
+          code = 0xffff;
+
+        break;
+      case GDK_Up:
+      case GDK_KP_Up:
+        if (code == 0) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_HORIZONTAL)
+          code -= (fw->cell_cols * count);
+        else
+          code -= count;
+
+        if (code < 0)
+          code = 0;
+
+        break;
+      case GDK_Down:
+      case GDK_KP_Down:
+        /*
+         * Make sure that when on the unencoded pages, the final glyph is
+         * the limit unlike the encoded pages where the max value is 0xffff.
+         */
+        if ((fw->unencoded &&
+             (gp == 0 || code == gp->encoding)) ||
+            code == 0xffff) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_HORIZONTAL)
+          code += (fw->cell_cols * count);
+        else
+          code += count;
+
+        if (fw->unencoded && code > gp->encoding)
+          code = gp->encoding;
+        else if (code > 0xffff)
+          code = 0xffff;
+
+        break;
+      case GDK_Return:
+      case GDK_KP_Enter:
+        pi->sel_end = pi->sel_start;
+        activate = TRUE;
+        break;
+      default:
+        return FALSE;
+    }
+
+    signal_extend = FALSE;
+    if (code > pi->sel_end) {
+        signal_extend = TRUE;
+        if (code <= pi->sel_start)
+          fontgrid_deselect_range(fw, pi->sel_end, code - 1);
+        else {
+            if (pi->sel_end < pi->sel_start) {
+                fontgrid_deselect_range(fw, pi->sel_end, pi->sel_start - 1);
+                fontgrid_select_range(fw, pi->sel_start + 1, code);
+            } else
+              fontgrid_select_range(fw, pi->sel_end, code);
+        }
+    } else if (code < pi->sel_end) {
+        signal_extend = TRUE;
+        if (code < pi->sel_start) {
+            if (pi->sel_end > pi->sel_start) {
+                fontgrid_deselect_range(fw, pi->sel_start + 1, pi->sel_end);
+                fontgrid_select_range(fw, code, pi->sel_start);
+            } else
+              fontgrid_select_range(fw, code, pi->sel_end);
+        } else
+          fontgrid_deselect_range(fw, code + 1, pi->sel_end);
+    }
+
+    pi->sel_end = code;
+
+    /*
+     * If the selection endpoint is on some page other than the current
+     * page, make sure the page holding the end point is made visible.
+     */
+    pageno = code / fw->pagesize;
+    if (pageno != pi->pageno) {
+        fw->no_sel_callback = TRUE;
+        fontgrid_goto_page(fw, pageno);
+    }
+
+    /*
+     * Reset the count.
+     */
+    fw->count = 0;
+
+    if (signal_extend) {
+        if (pi->sel_start == pi->sel_end) {
+            /*
+             * Set up and emit the selection start signal.
+             */
+            if (!fw->unencoded)
+              gp = fontgrid_locate_glyph(fw->font->glyphs,
+                                         fw->font->glyphs_used,
+                                         code, TRUE);
+            else
+              gp = fontgrid_locate_glyph(fw->font->unencoded,
+                                         fw->font->unencoded_used,
+                                         code, TRUE);
+            if (gp == 0) {
+                empty_glyph.encoding = code;
+                gp = &empty_glyph;
+            }
+            sinfo.glyphs = gp;
+            sinfo.num_glyphs = 1;
+        } else {
+            sinfo.glyphs = 0;
+            sinfo.num_glyphs = 0;
+        }
+        sinfo.start = pi->sel_start;
+        sinfo.end = pi->sel_end;
+        sinfo.base = fw->base;
+        sinfo.unencoded = fw->unencoded;
+
+        if (!activate) {
+            sinfo.reason = FONTGRID_EXTEND_SELECTION;
+            g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_EXTEND],
+                          0, &sinfo);
+        } else {
+            sinfo.reason = FONTGRID_ACTIVATE;
+            g_signal_emit(G_OBJECT(fw), fontgrid_signals[ACTIVATE], 0,
+                          &sinfo);
+        }
+    }
+
+    return TRUE;
+}
+
+static gint
+fontgrid_key_press(GtkWidget *widget, GdkEventKey *event)
+{
+    Fontgrid *fw;
+    bdf_glyph_t *gp;
+    gint32 code, pageno;
+    guint32 count;
+    gboolean activate;
+    FontgridInternalPageInfo *pi, *opi;
+    FontgridSelectionInfo sinfo;
+
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_FONTGRID(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    if (event->state & GDK_SHIFT_MASK)
+      return fontgrid_shift_key_press(widget, event);
+
+    fw = FONTGRID(widget);
+
+    /*
+     * For number keys, use them to add up a count that will effect the
+     * behavior of the other keys.
+     */
+    if (event->keyval >= GDK_0 && event->keyval <= GDK_9) {
+        fw->count = (fw->count * 10) + (event->keyval - GDK_0);
+        return FALSE;
+    }
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        opi = &fw->upage;
+        gp = (fw->font && fw->font->glyphs_used) ?
+            (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0;
+    } else {
+        pi = &fw->upage;
+        opi = &fw->npage;
+        gp = (fw->font && fw->font->unencoded_used) ?
+            (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0;
+    }
+
+    activate = FALSE;
+
+    code = pi->sel_start;
+
+    if ((count = fw->count) == 0)
+      count = 1;
+
+    switch (event->keyval) {
+      case GDK_Left:
+      case GDK_KP_Left:
+        if (code == 0) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_VERTICAL)
+          code -= (fw->cell_rows * count);
+        else
+          code -= count;
+
+        if (code < 0)
+          code = 0;
+
+        break;
+      case GDK_Right:
+      case GDK_KP_Right:
+        /*
+         * Make sure that when on the unencoded pages, the final glyph is
+         * the limit unlike the encoded pages where the max value is 0xffff.
+         */
+        if ((fw->unencoded &&
+             (gp == 0 || code == gp->encoding)) ||
+            code == 0xffff) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_VERTICAL)
+          code += (fw->cell_rows * count);
+        else
+          code += count;
+
+        if (fw->unencoded && code > gp->encoding)
+          code = gp->encoding;
+        else if (code > 0xffff)
+          code = 0xffff;
+
+        break;
+      case GDK_Up:
+      case GDK_KP_Up:
+        if (code == 0) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_HORIZONTAL)
+          code -= (fw->cell_cols * count);
+        else
+          code -= count;
+
+        if (code < 0)
+          code = 0;
+
+        break;
+      case GDK_Down:
+      case GDK_KP_Down:
+        /*
+         * Make sure that when on the unencoded pages, the final glyph is
+         * the limit unlike the encoded pages where the max value is 0xffff.
+         */
+        if ((fw->unencoded &&
+             (gp == 0 || code == gp->encoding)) ||
+            code == 0xffff) {
+            gdk_beep();
+            return TRUE;
+        }
+
+        if (fw->orientation == GTK_ORIENTATION_HORIZONTAL)
+          code += (fw->cell_cols * count);
+        else
+          code += count;
+
+        if (fw->unencoded && code > gp->encoding)
+          code = gp->encoding;
+        else if (code > 0xffff)
+          code = 0xffff;
+
+        break;
+      case GDK_Page_Up:
+      case GDK_KP_Page_Up:
+        fw->from_keyboard = TRUE;
+        fontgrid_goto_previous_page(fw);
+        return TRUE;
+        break;
+      case GDK_Page_Down:
+      case GDK_KP_Page_Down:
+        fw->from_keyboard = TRUE;
+        fontgrid_goto_next_page(fw);
+        return TRUE;
+        break;
+      case GDK_Home:
+      case GDK_KP_Home:
+        fw->from_keyboard = TRUE;
+        fontgrid_goto_first_page(fw);
+        return TRUE;
+        break;
+      case GDK_End:
+      case GDK_KP_End:
+        fw->from_keyboard = TRUE;
+        fontgrid_goto_last_page(fw);
+        return TRUE;
+        break;
+      case GDK_Return:
+      case GDK_KP_Enter:
+        pi->sel_end = pi->sel_start;
+        activate = TRUE;
+        break;
+      case GDK_BackSpace:
+      case GDK_Delete:
+      case GDK_KP_Delete:
+        fontgrid_cut_selection(fw);
+        return TRUE;
+      default:
+        return FALSE;
+    }
+
+    /*
+     * This turns off the selection which means the cursor is effectively
+     * turned off even for the fontgrid_goto_page() call.  The reason is that
+     * for keyboard navigation, the cursor should move up and down by rows and
+     * not whole pages when a page change occurs.
+     */
+    fontgrid_deselect_all(fw);
+
+    pageno = code / fw->pagesize;
+    if (pageno != pi->pageno) {
+        fw->no_sel_callback = TRUE;
+        fontgrid_goto_page(fw, pageno);
+    }
+
+    pi->sel_start = pi->sel_end = code;
+    Select(code, pi->selmap);
+    fontgrid_draw_cells(widget, code, code, FALSE, TRUE);
+
+    /*
+     * Reset the count.
+     */
+    fw->count = 0;
+
+    /*
+     * Set up and emit the selection start signal.
+     */
+    if (!fw->unencoded)
+      gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                 code, TRUE);
+    else
+      gp = fontgrid_locate_glyph(fw->font->unencoded, fw->font->unencoded_used,
+                                 code, TRUE);
+    if (gp == 0) {
+        empty_glyph.encoding = code;
+        gp = &empty_glyph;
+    }
+    sinfo.glyphs = gp;
+    sinfo.num_glyphs = 1;
+    sinfo.start = pi->sel_start;
+    sinfo.end = pi->sel_end;
+    sinfo.base = fw->base;
+    sinfo.unencoded = fw->unencoded;
+
+    if (!activate) {
+        sinfo.reason = FONTGRID_START_SELECTION;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                      &sinfo);
+    } else {
+        sinfo.reason = FONTGRID_ACTIVATE;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[ACTIVATE], 0, &sinfo);
+    }
+
+    return TRUE;
+}
+
+/**************************************************************************
+ *
+ * Class and instance setup.
+ *
+ **************************************************************************/
+
+static void
+fontgrid_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass *gocp = G_OBJECT_CLASS(g_class);
+    GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class);
+    GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class);
+
+    /*
+     * Set the class global variables.
+     */
+    parent_class = g_type_class_peek_parent(g_class);
+
+    /*
+     * GObject class functions.
+     */
+    gocp->set_property = fontgrid_set_property;
+    gocp->get_property = fontgrid_get_property;
+    gocp->finalize = fontgrid_finalize;
+
+    /*
+     * GtkObjectClass functions.
+     */
+    ocp->destroy = fontgrid_destroy;
+
+    /*
+     * Instance functions.
+     */
+    wcp->size_request = fontgrid_preferred_size;
+    wcp->size_allocate = fontgrid_actual_size;
+    wcp->realize = fontgrid_realize;
+    wcp->expose_event = fontgrid_expose;
+    wcp->focus_in_event = fontgrid_focus_in;
+    wcp->focus_out_event = fontgrid_focus_out;
+    wcp->button_press_event = fontgrid_button_press;
+    wcp->button_release_event = fontgrid_button_release;
+    wcp->motion_notify_event = fontgrid_motion_notify;
+    wcp->key_press_event = fontgrid_key_press;
+    wcp->selection_clear_event = fontgrid_lose_selection;
+
+    /*
+     * Add parameters (a.k.a. resource) types.
+     */
+    g_object_class_install_property(gocp, PROP_CODE_BASE,
+                                    g_param_spec_uint("codeBase",
+                                                      _("Code base"),
+                                                      _("Override for the code base (oct, dec, hex) for glyph codes."),
+                                                      8,
+                                                      16,
+                                                      16,
+                                                      G_PARAM_READWRITE));
+    g_object_class_install_property(gocp, PROP_POWER2,
+                                    g_param_spec_boolean("powersOfTwo",
+                                                         _("Powers of two"),
+                                                         _("Indicate whether the grid display should be a power-of-two rows."),
+                                                         TRUE,
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_ORIENTATION,
+                                    g_param_spec_enum("orientation",
+                                                      _("Orientation"),
+                                                      _("Should the grid display vertically or horizontally."),
+                                                      GTK_TYPE_ORIENTATION,
+                                                      GTK_ORIENTATION_HORIZONTAL,
+                                                      G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_FONT,
+                                    g_param_spec_pointer("font",
+                                                         _("Font"),
+                                                         _("Font to be displayed."),
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_POINT_SIZE,
+                                    g_param_spec_uint("pointSize",
+                                                       _("Point size"),
+                                                       _("Set the default point size for new fonts."),
+                                                       2,
+                                                       256,
+                                                       12,
+                                                       G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_SPACING,
+                                    g_param_spec_int("spacing",
+                                                     _("Spacing"),
+                                                     _("Set the default glyph spacing."),
+                                                       BDF_PROPORTIONAL,
+                                                       BDF_CHARCELL,
+                                                       BDF_PROPORTIONAL,
+                                                       G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_SKIP_BLANKS,
+                                    g_param_spec_boolean("skipBlankPages",
+                                                         _("Skip blank pages"),
+                                                         _("Avoid displaying pages with no glyphs."),
+                                                         TRUE,
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_OVERWRITE,
+                                    g_param_spec_boolean("overwriteMode",
+                                                         _("Overwrite mode"),
+                                                         _("Pasting the selection overwrites."),
+                                                         TRUE,
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_COLORS,
+                                    g_param_spec_pointer("colorList",
+                                                         _("Color list"),
+                                                         _("Colors to be used for glyphs having bits-per-pixel > 1."),
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_INITIAL_GLYPH,
+                                    g_param_spec_int("initialGlyph",
+                                                      _("Initial glyph"),
+                                                      _("Code of the glyph to be displayed first."),
+                                                      -1,
+                                                      0xffff,
+                                                      -1,
+                                                      G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_BPP,
+                                    g_param_spec_int("bitsPerPixel",
+                                                      _("Bits per pixel"),
+                                                      _("Number of bits per pixel for grayscale glyphs."),
+                                                      1,
+                                                      4,
+                                                      1,
+                                                      G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_HRES,
+                                    g_param_spec_int("horizontalResolution",
+                                                      _("Horizontal resolution"),
+                                                      _("Set the default horizontal resolution for new fonts."),
+                                                      1,
+                                                      2400,
+                                                      100,
+                                                      G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_VRES,
+                                    g_param_spec_int("verticalResolution",
+                                                      _("Vertical resolution"),
+                                                      _("Set the default vertical resolution for new fonts."),
+                                                      1,
+                                                      2400,
+                                                      100,
+                                                      G_PARAM_READWRITE));
+
+    /*
+     * Add the signals these objects emit.
+     */
+    fontgrid_signals[SELECTION_START] =
+        g_signal_new("selection-start",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(FontgridClass, selection_start),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER);
+    fontgrid_signals[SELECTION_EXTEND] =
+        g_signal_new("selection-extend",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(FontgridClass, selection_extend),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER);
+    fontgrid_signals[SELECTION_END] =
+        g_signal_new("selection-end",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(FontgridClass, selection_end),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER);
+    fontgrid_signals[ACTIVATE] =
+        g_signal_new("activate",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(FontgridClass, activate),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER);
+    fontgrid_signals[MODIFIED] =
+        g_signal_new("modified",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(FontgridClass, modified),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER);
+    fontgrid_signals[TURN_TO_PAGE] =
+        g_signal_new("turn_to_page",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(FontgridClass, page),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER);
+}
+
+static void
+fontgrid_init(GTypeInstance *obj, gpointer g_class)
+{
+    Fontgrid *fw = FONTGRID(obj);
+    FontgridInternalPageInfo *pi;
+    GdkScreen *screen;
+    gint fwidth, fpad;
+
+    GTK_WIDGET_SET_FLAGS(fw, GTK_CAN_FOCUS);
+
+    gtk_widget_style_get(GTK_WIDGET(fw),
+                         "focus-line-width", &fwidth,
+                         "focus-padding", &fpad,
+                         NULL);
+
+    fw->base = 16;
+    fw->power2 = TRUE;
+    fw->overwrite = TRUE;
+    fw->noblanks = TRUE;
+    fw->orientation = GTK_ORIENTATION_HORIZONTAL;
+    fw->point_size = 12;
+    fw->spacing = BDF_CHARCELL;
+    fw->colors = 0;
+    fw->initial_glyph = 0;
+    fw->bpp = 1;
+
+    screen =
+        gdk_drawable_get_screen(GDK_DRAWABLE(gdk_get_default_root_window()));
+    fw->hres = (gint32) ((((double) gdk_screen_get_width(screen)) * 25.4) /
+                         ((double) gdk_screen_get_width_mm(screen)) + 0.5);
+    fw->vres = (gint32) ((((double) gdk_screen_get_height(screen)) * 25.4) /
+                         ((double) gdk_screen_get_height_mm(screen)) + 0.5);
+
+    fw->cell_rows = FGRID_DEFAULT_ROWS;
+    fw->cell_cols = FGRID_DEFAULT_COLS;
+    fw->border = 4;
+    fw->hmargin = fw->widget.style->xthickness + fwidth + fpad + fw->border;
+    fw->vmargin = fw->widget.style->ythickness + fwidth + fpad + fw->border;
+
+    fontgrid_set_cell_geometry(fw);
+    fontgrid_set_rows_cols(fw, 0);
+
+    /*
+     * Private variables.
+     */
+    fw->unencoded = FALSE;
+    fw->debug = FALSE;
+    fw->xor_gc = 0;
+    fw->points_used = 0;
+    fw->points_size = 0;
+    fw->rgb_used = 0;
+    fw->rgb_size = 0;
+
+    fw->last_click = 0;
+    fw->mclick_time = 0;
+
+    fw->count = 0;
+    memset((char *) &fw->clipboard, 0, sizeof(bdf_glyphlist_t));
+
+    /*
+     * Initialize the page information.
+     */
+    pi = &fw->upage;
+    pi->minpage = 0;
+    pi->maxpage = 0xffff / fw->pagesize;
+    pi->npage = pi->ppage = -1;
+    pi->pageno = pi->bcode = 0;
+    pi->sel_start = pi->sel_end = -1;
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+    pi = &fw->npage;
+    pi->minpage = 0;
+    pi->maxpage = 0xffff / fw->pagesize;
+    pi->npage = pi->ppage = -1;
+    pi->pageno = pi->bcode = 0;
+    pi->sel_start = pi->sel_end = -1;
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+}
+
+/**************************************************************************
+ *
+ * API.
+ *
+ **************************************************************************/
+
+/*
+ * Type instantiation routines.
+ */
+GType
+fontgrid_get_type(void)
+{
+    static GType fontgrid_type = 0;
+
+    if (!fontgrid_type) {
+        static const GTypeInfo fontgrid_info = {
+            sizeof (FontgridClass),            /* class_size           */
+            0,                                 /* base_init            */
+            0,                                 /* base_finalize        */
+            fontgrid_class_init,               /* class_init           */
+            0,                                 /* class_finalize       */
+            0,                                 /* class_data           */
+            sizeof(Fontgrid),                  /* instance_size        */
+            0,                                 /* n_preallocs          */
+            fontgrid_init,                     /* instance_init        */
+            0,                                 /* value_table          */
+        };
+        fontgrid_type = g_type_register_static(GTK_TYPE_WIDGET,
+                                               "Fontgrid", &fontgrid_info, 0);
+    }
+
+    return fontgrid_type;
+}
+
+GtkWidget *
+fontgrid_new(const gchar *prop1, ...)
+{
+    GtkWidget *w;
+    va_list var_args;
+
+    va_start(var_args, prop1);
+    w = GTK_WIDGET(g_object_new_valist(fontgrid_get_type(), prop1, var_args));
+    va_end(var_args);
+
+    return w;
+}
+
+GtkWidget *
+fontgrid_newv(bdf_font_t *font, guint32 pointSize, gint32 spacing,
+              gboolean skipBlankPages, gboolean overwriteMode,
+              gboolean powersOfTwo, guint16 *colorList, gint32 initialGlyph,
+              guint codeBase, GtkOrientation orientation,
+              gint32 bitsPerPixel, gint32 horizontalResolution,
+              gint32 verticalResolution, FontgridPageInfo *initialPageInfo)
+{
+    Fontgrid *fw = FONTGRID(g_object_new(fontgrid_get_type(), NULL));
+    gint32 i, boundary;
+    FontgridInternalPageInfo *pi;
+
+    fw->font = font;
+    fw->point_size = pointSize;
+    fw->spacing = spacing;
+    fw->colors = colorList;
+    fw->noblanks = skipBlankPages;
+    fw->overwrite = overwriteMode;
+    fw->power2 = powersOfTwo;
+    fw->initial_glyph = initialGlyph;
+    fw->base = codeBase;
+    fw->orientation = orientation;
+    fw->bpp = (font) ? font->bpp : bitsPerPixel;
+    fw->hres = horizontalResolution;
+    fw->vres = verticalResolution;
+
+    /*
+     * If no font has been provided, make sure a default is created.
+     * Too many other things depend on a font existing.
+     */
+    if (font == 0) {
+        fw->font = bdf_new_font(0, fw->point_size, fw->hres, fw->vres,
+                                fw->spacing, fw->bpp);
+        if (fw->font->name == 0)
+          fw->font->name = bdf_make_xlfd_name(fw->font, g_get_prgname(),
+                                              "Unknown");
+    }
+
+    fontgrid_set_cell_geometry(fw);
+    fontgrid_set_rows_cols(fw, 0);
+
+    /*
+     * Initialize the page information.
+     */
+    pi = &fw->upage;
+    pi->minpage = 0;
+    pi->maxpage = 0xffff / fw->pagesize;
+    pi->npage = pi->ppage = -1;
+    pi->pageno = pi->bcode = 0;
+    pi->sel_start = pi->sel_end = -1;
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+    pi = &fw->npage;
+    pi->minpage = 0;
+    pi->maxpage = 0xffff / fw->pagesize;
+    pi->npage = pi->ppage = -1;
+    pi->pageno = pi->bcode = 0;
+    pi->sel_start = pi->sel_end = -1;
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+
+    /*
+     * Determine the page info from the initial glyph setting.
+     */
+    if (font != 0) {
+        if (fw->initial_glyph == -1)
+          fw->initial_glyph = (font->glyphs_used > 0) ?
+              font->glyphs->encoding : 0;
+
+        pi = &fw->npage;
+        pi->pageno = fw->initial_glyph / fw->pagesize;
+        pi->bcode = pi->pageno * fw->pagesize;
+        pi->sel_start = pi->sel_end = fw->initial_glyph;
+        Select(fw->initial_glyph, pi->selmap);
+        fontgrid_neighbor_pages(fw, pi->pageno, &pi->ppage, &pi->npage);
+
+        /*
+         * Set the min/max page numbers for the encoded glyphs.
+         */
+        if (font->glyphs_used > 0) {
+            if (fw->noblanks) {
+                pi->minpage = font->glyphs->encoding / fw->pagesize;
+                pi->maxpage =
+                    font->glyphs[font->glyphs_used-1].encoding / fw->pagesize;
+            } else {
+                pi->minpage = 0;
+                pi->maxpage = 0xffff / fw->pagesize;
+            }
+        }
+
+        /*
+         * Set the min/max page numbers for the unencoded glyphs.
+         */
+        if (font->unencoded_used > 0) {
+            pi = &fw->upage;
+
+            if (fw->noblanks) {
+                pi->pageno = pi->minpage =
+                    font->glyphs->encoding / fw->pagesize;
+                pi->maxpage =
+                    font->unencoded[font->unencoded_used-1].encoding /
+                    fw->pagesize;
+                pi->bcode = pi->pageno * fw->pagesize;
+                pi->ppage = -1;
+
+                /*
+                 * Lower boundary for the next page.
+                 */
+                boundary = pi->bcode + fw->pagesize;
+                for (i = 0; i < font->unencoded_used &&
+                         font->unencoded[i].encoding < boundary; i++) ;
+                pi->npage = (i == font->unencoded_used) ?
+                    -1 : font->unencoded[i].encoding / fw->pagesize;
+                  
+            } else {
+                pi->pageno = pi->minpage = 0;
+                pi->maxpage = 0xffff / fw->pagesize;
+                pi->ppage = -1;
+                pi->npage = pi->pageno + 1;
+            }
+        }
+    }
+
+    /*
+     * Provide the initial page info the calling application will need
+     * to set up the page changing labels.
+     */
+    initialPageInfo->unencoded_page = fw->unencoded;
+    initialPageInfo->encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0;
+    initialPageInfo->unencoded_glyphs =
+        (fw->font) ? fw->font->unencoded_used : 0;
+
+    if (!fw->unencoded) {
+        initialPageInfo->previous_page = fw->npage.ppage;
+        initialPageInfo->current_page = fw->npage.pageno;
+        initialPageInfo->next_page = fw->npage.npage;
+    } else {
+        initialPageInfo->previous_page = fw->upage.ppage;
+        initialPageInfo->current_page = fw->upage.pageno;
+        initialPageInfo->next_page = fw->upage.npage;
+    }
+
+    return GTK_WIDGET(fw);
+}
+
+gboolean
+fontgrid_has_selection(Fontgrid *fw, FontgridSelectionInfo *sinfo)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_val_if_fail(fw != 0, FALSE);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    /*
+     * Set up the selection info to alert the application that the
+     * base changed.
+     */
+    if (sinfo != 0) {
+        /*
+         * Initialize the selection info structure.
+         */
+        (void) memset((char *) sinfo, 0, sizeof(FontgridSelectionInfo));
+
+        if (pi->sel_start == pi->sel_end) {
+            if (fw->font) {
+                if (!fw->unencoded)
+                  sinfo->glyphs =
+                      fontgrid_locate_glyph(fw->font->glyphs,
+                                            fw->font->glyphs_used,
+                                            pi->sel_start, TRUE);
+                else
+                  sinfo->glyphs =
+                      fontgrid_locate_glyph(fw->font->unencoded,
+                                            fw->font->unencoded_used,
+                                            pi->sel_start, TRUE);
+                if (sinfo->glyphs == 0) {
+                    empty_glyph.encoding = pi->sel_start;
+                    sinfo->glyphs = &empty_glyph;
+                }
+                sinfo->num_glyphs = 1;
+            }
+        } else {
+            sinfo->glyphs = 0;
+            sinfo->num_glyphs = 0;
+        }
+
+        sinfo->start = pi->sel_start;
+        sinfo->end = pi->sel_end;
+        sinfo->base = fw->base;
+        sinfo->unencoded = fw->unencoded;
+        sinfo->reason = FONTGRID_START_SELECTION;
+    }
+
+    return (pi->sel_start == -1) ? FALSE : TRUE;
+}
+
+bdf_font_t *
+fontgrid_get_font(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, 0);
+
+    return fw->font;
+}
+
+void
+fontgrid_set_font(Fontgrid *fw, bdf_font_t *font, gint32 initial_glyph)
+{
+    GtkWidget *w;
+    gint32 i, boundary;
+    FontgridInternalPageInfo *pi;
+    FontgridPageInfo pageinfo;
+
+    g_return_if_fail(fw != 0);
+
+    if (font == fw->font)
+      return;
+
+    w = GTK_WIDGET(fw);
+
+    /*
+     * Free up the existing font.
+     */
+    if (fw->font != 0)
+      bdf_free_font(fw->font);
+    fw->font = font;
+
+    /*
+     * Make sure the encoded pages are the default for newly loaded fonts.
+     */
+    fw->unencoded = FALSE;
+
+    /*
+     * Set the bits-per-pixel from the font.
+     */
+    fw->bpp = (font != 0) ? font->bpp : 1;
+
+    /*
+     * Set the initial glyph code.
+     */
+    fw->initial_glyph = initial_glyph;
+
+    /*
+     * Calculate the cell geometry and the rows and columns.
+     */
+    fontgrid_set_cell_geometry(fw);
+    fontgrid_set_rows_cols(fw, 0);
+
+    /*
+     * Initialize the page information.
+     */
+    pi = &fw->upage;
+    pi->minpage = 0;
+    pi->maxpage = 0xffff / fw->pagesize;
+    pi->npage = pi->ppage = -1;
+    pi->pageno = pi->bcode = 0;
+    pi->sel_start = pi->sel_end = -1;
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+    pi = &fw->npage;
+    pi->minpage = 0;
+    pi->maxpage = 0xffff / fw->pagesize;
+    pi->npage = pi->ppage = -1;
+    pi->pageno = pi->bcode = 0;
+    pi->sel_start = pi->sel_end = -1;
+    (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048);
+
+    /*
+     * Determine the page info from the initial glyph setting.
+     */
+    if (font != 0) {
+        if (fw->initial_glyph == -1)
+          fw->initial_glyph = (font->glyphs_used > 0) ?
+              font->glyphs->encoding : 0;
+
+        pi = &fw->npage;
+        pi->pageno = fw->initial_glyph / fw->pagesize;
+        pi->bcode = pi->pageno * fw->pagesize;
+        pi->sel_start = pi->sel_end = fw->initial_glyph;
+        Select(fw->initial_glyph, pi->selmap);
+        fontgrid_neighbor_pages(fw, pi->pageno, &pi->ppage, &pi->npage);
+
+        /*
+         * Set the min/max page numbers for the encoded glyphs.
+         */
+        if (font->glyphs_used > 0) {
+            if (fw->noblanks) {
+                pi->minpage = font->glyphs->encoding / fw->pagesize;
+                pi->maxpage =
+                    font->glyphs[font->glyphs_used-1].encoding / fw->pagesize;
+            } else {
+                pi->minpage = 0;
+                pi->maxpage = 0xffff / fw->pagesize;
+            }
+        }
+
+        /*
+         * Set the min/max page numbers for the unencoded glyphs.
+         */
+        if (font->unencoded_used > 0) {
+            pi = &fw->upage;
+
+            if (fw->noblanks) {
+                pi->pageno = pi->minpage =
+                    font->glyphs->encoding / fw->pagesize;
+                pi->maxpage =
+                    font->unencoded[font->unencoded_used-1].encoding /
+                    fw->pagesize;
+                pi->bcode = pi->pageno * fw->pagesize;
+                pi->ppage = -1;
+
+                /*
+                 * Lower boundary for the next page.
+                 */
+                boundary = pi->bcode + fw->pagesize;
+                for (i = 0; i < font->unencoded_used &&
+                         font->unencoded[i].encoding < boundary; i++) ;
+                pi->npage = (i == font->unencoded_used) ?
+                    -1 : font->unencoded[i].encoding / fw->pagesize;
+                  
+            } else {
+                pi->pageno = pi->minpage = 0;
+                pi->maxpage = 0xffff / fw->pagesize;
+                pi->ppage = -1;
+                pi->npage = pi->pageno + 1;
+            }
+        }
+    }
+
+    /*
+     * Signal that a page change has taken place so the application can do
+     * setup that it needs.
+     */
+    pageinfo.unencoded_page = fw->unencoded;
+    pageinfo.encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0;
+    pageinfo.unencoded_glyphs = (fw->font) ? fw->font->unencoded_used : 0;
+
+    if (!fw->unencoded) {
+        pageinfo.previous_page = fw->npage.ppage;
+        pageinfo.current_page = fw->npage.pageno;
+        pageinfo.next_page = fw->npage.npage;
+    } else {
+        pageinfo.previous_page = fw->upage.ppage;
+        pageinfo.current_page = fw->upage.pageno;
+        pageinfo.next_page = fw->upage.npage;
+    }
+
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[TURN_TO_PAGE], 0, &pageinfo);
+
+    /*
+     * Queue up a resize so the grid will change size.
+     */
+    gtk_widget_queue_resize(w);
+}
+
+gchar *
+fontgrid_get_font_messages(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, 0);
+
+    return (fw->font) ? fw->font->acmsgs : 0;
+}
+
+guint
+fontgrid_get_code_base(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, 0);
+
+    return fw->base;
+}
+
+void
+fontgrid_set_code_base(Fontgrid *fw, guint base)
+{
+    FontgridInternalPageInfo *pi;
+    FontgridSelectionInfo sinfo;
+
+    g_return_if_fail(fw != 0);
+
+    switch (base) {
+      case 8: case 10: case 16:
+        if (fw->base != base) {
+            fw->base = base;
+            if (!fw->unencoded) {
+                pi = &fw->npage;
+                fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode,
+                                    fw->npage.bcode + fw->pagesize,
+                                    TRUE, FALSE);
+            } else
+              pi = &fw->upage;
+
+            /*
+             * Set up the selection info to alert the application that the
+             * base changed.
+             */
+            if (pi->sel_start == pi->sel_end) {
+                if (fw->font) {
+                    if (!fw->unencoded)
+                      sinfo.glyphs =
+                          fontgrid_locate_glyph(fw->font->glyphs,
+                                                fw->font->glyphs_used,
+                                                pi->sel_start, TRUE);
+                    else
+                      sinfo.glyphs =
+                          fontgrid_locate_glyph(fw->font->unencoded,
+                                                fw->font->unencoded_used,
+                                                pi->sel_start, TRUE);
+                    if (sinfo.glyphs == 0) {
+                        empty_glyph.encoding = pi->sel_start;
+                        sinfo.glyphs = &empty_glyph;
+                    }
+                    sinfo.num_glyphs = 1;
+                }
+            } else {
+                sinfo.glyphs = 0;
+                sinfo.num_glyphs = 0;
+            }
+
+            sinfo.start = pi->sel_start;
+            sinfo.end = pi->sel_end;
+            sinfo.base = fw->base;
+            sinfo.unencoded = fw->unencoded;
+            sinfo.reason = FONTGRID_BASE_CHANGE;
+            g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                          &sinfo);
+        }
+        break;
+    }
+}
+
+GtkOrientation
+fontgrid_get_orientation(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, GTK_ORIENTATION_HORIZONTAL);
+
+    return fw->orientation;
+}
+
+void
+fontgrid_set_orientation(Fontgrid *fw, GtkOrientation dir)
+{
+    guint16 tmp;
+
+    g_return_if_fail(fw != 0);
+
+    if (dir != fw->orientation) {
+        fw->orientation = dir;
+
+        /*
+         * Need to swap rows and cols and attempt a resize if the object
+         * has been constructed.
+         */
+        tmp = fw->cell_rows;
+        fw->cell_rows = fw->cell_cols;
+        fw->cell_cols = tmp;
+
+        gtk_widget_queue_resize(GTK_WIDGET(fw));
+    }
+}
+
+void
+fontgrid_get_page_info(Fontgrid *fw, FontgridPageInfo *pageinfo)
+{
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(pageinfo != 0);
+
+    pageinfo->unencoded_page = fw->unencoded;
+    pageinfo->encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0;
+    pageinfo->unencoded_glyphs = (fw->font) ? fw->font->unencoded_used : 0;
+
+    if (!fw->unencoded) {
+        pageinfo->previous_page = fw->npage.ppage;
+        pageinfo->current_page = fw->npage.pageno;
+        pageinfo->next_page = fw->npage.npage;
+    } else {
+        pageinfo->previous_page = fw->upage.ppage;
+        pageinfo->current_page = fw->upage.pageno;
+        pageinfo->next_page = fw->upage.npage;
+    }
+}
+
+/*
+ * This is the routine that does the majority of the work for updating
+ * page changes.
+ */
+static void
+fontgrid_page_change_update(Fontgrid *fw, FontgridInternalPageInfo *pi)
+{
+    gint32 code;
+    FontgridPageInfo pageinfo;
+    FontgridSelectionInfo selinfo;
+
+    code = pi->sel_start - pi->bcode;
+    pi->bcode = pi->pageno * fw->pagesize;
+
+    if (fw->from_keyboard) {
+        fontgrid_deselect_all(fw);
+        code += pi->bcode;
+        pi->sel_start = pi->sel_end = code;
+        Select(code, pi->selmap);
+        fw->from_keyboard = FALSE;
+    }
+    fontgrid_neighbor_pages(fw, pi->pageno, &pi->ppage, &pi->npage);
+
+    fontgrid_draw_cells(GTK_WIDGET(fw), pi->bcode,
+                        pi->bcode + (fw->pagesize - 1), TRUE, TRUE);
+
+    pageinfo.unencoded_page = fw->unencoded;
+    pageinfo.encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0;
+    pageinfo.unencoded_glyphs = (fw->font) ? fw->font->unencoded_used : 0;
+
+    pageinfo.previous_page = pi->ppage;
+    pageinfo.current_page = pi->pageno;
+    pageinfo.next_page = pi->npage;
+
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[TURN_TO_PAGE], 0, &pageinfo);
+
+    /*
+     * If this was called from the keyboard, then indicate the changed
+     * selection.
+     */
+    if (!fw->no_sel_callback && fw->from_keyboard) {
+        selinfo.glyphs = 0;
+        selinfo.num_glyphs = 1;
+        if (fw->font) {
+            selinfo.glyphs = (!fw->unencoded) ?
+                fontgrid_locate_glyph(fw->font->glyphs,
+                                      fw->font->glyphs_used,
+                                      code, TRUE) :
+                fontgrid_locate_glyph(fw->font->unencoded,
+                                      fw->font->unencoded_used,
+                                      code, TRUE);
+        }
+        if (selinfo.glyphs == 0) {
+            empty_glyph.encoding = code;
+            selinfo.glyphs = &empty_glyph;
+        }
+
+        selinfo.reason = FONTGRID_START_SELECTION;
+        selinfo.start = pi->sel_start;
+        selinfo.end = pi->sel_end;
+        selinfo.base = fw->base;
+        selinfo.unencoded = fw->unencoded;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                      &selinfo);
+    }
+}
+
+void
+fontgrid_goto_page(Fontgrid *fw, gint32 pageno)
+{
+    guint32 mpage;
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    mpage = 0xffff / fw->pagesize;
+
+    if (pageno < 0)
+      pageno = 0;
+    if (pageno > mpage)
+      pageno = mpage;
+
+    if (pageno != pi->pageno) {
+        pi->pageno = pageno;
+        fontgrid_page_change_update(fw, pi);
+    }
+}
+
+void
+fontgrid_goto_code(Fontgrid *fw, gint32 code)
+{
+    gint32 pageno;
+    FontgridInternalPageInfo *pi;
+    FontgridSelectionInfo selinfo;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (code < 0)
+      code = 0;
+    if (code > 0xffff)
+      code = 0xffff;
+
+    pageno = code / fw->pagesize;
+
+    if (pageno != pi->pageno) {
+        fw->no_sel_callback = TRUE;
+        pi->pageno = pageno;
+        fontgrid_page_change_update(fw, pi);
+    }
+
+    fontgrid_deselect_all(fw);
+    Select(code, pi->selmap);
+    pi->sel_start = pi->sel_end = code;
+    fontgrid_draw_cells(GTK_WIDGET(fw), code, code, FALSE, TRUE);
+
+    selinfo.glyphs = 0;
+    selinfo.num_glyphs = 1;
+    if (fw->font) {
+        selinfo.glyphs = (!fw->unencoded) ?
+            fontgrid_locate_glyph(fw->font->glyphs,
+                                  fw->font->glyphs_used,
+                                  code, TRUE) :
+            fontgrid_locate_glyph(fw->font->unencoded,
+                                  fw->font->unencoded_used,
+                                  code, TRUE);
+    }
+    if (selinfo.glyphs == 0) {
+        empty_glyph.encoding = code;
+        selinfo.glyphs = &empty_glyph;
+    }
+
+    selinfo.reason = FONTGRID_START_SELECTION;
+    selinfo.start = pi->sel_start;
+    selinfo.end = pi->sel_end;
+    selinfo.base = fw->base;
+    selinfo.unencoded = fw->unencoded;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                  &selinfo);
+}
+
+void
+fontgrid_goto_first_page(Fontgrid *fw)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (pi->pageno == pi->minpage)
+      return;
+
+    pi->pageno = pi->minpage;
+    fontgrid_page_change_update(fw, pi);
+}
+
+void
+fontgrid_goto_last_page(Fontgrid *fw)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (pi->pageno == pi->maxpage)
+      return;
+
+    pi->pageno = pi->maxpage;
+    fontgrid_page_change_update(fw, pi);
+}
+
+void
+fontgrid_goto_next_page(Fontgrid *fw)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (pi->pageno == pi->maxpage)
+      return;
+
+    pi->pageno = pi->npage;
+    fontgrid_page_change_update(fw, pi);
+}
+
+void
+fontgrid_goto_previous_page(Fontgrid *fw)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+
+    if (pi->pageno == pi->minpage)
+      return;
+
+    pi->pageno = pi->ppage;
+    fontgrid_page_change_update(fw, pi);
+}
+
+gboolean
+fontgrid_viewing_unencoded(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, FALSE);
+
+    return fw->unencoded;
+}
+
+void
+fontgrid_switch_encoding_view(Fontgrid *fw)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    fw->unencoded = !fw->unencoded;
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+    fontgrid_draw_cells(GTK_WIDGET(fw), pi->bcode, pi->bcode + fw->pagesize,
+                        TRUE, TRUE);
+}
+
+gchar *
+fontgrid_get_font_name(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, 0);
+
+    return (fw->font) ? fw->font->name : "";
+}
+
+void
+fontgrid_set_font_name(Fontgrid *fw, gchar *name)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(fw->font != 0);
+
+    if (fw->font->name != 0)
+      free(fw->font->name);
+
+    if (name == 0 || *name == 0)
+      fw->font->name = bdf_make_xlfd_name(fw->font, g_get_prgname(),
+                                          "Unknown");
+    else
+      fw->font->name = g_strdup(name);
+
+    bdf_set_modified(fw->font, 1);
+
+    minfo.reason = FONTGRID_MODIFIED;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+}
+
+gboolean
+fontgrid_get_font_modified(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, FALSE);
+
+    return (fw->font) ? ((fw->font->modified) ? TRUE : FALSE) : FALSE;
+}
+
+void
+fontgrid_set_font_modified(Fontgrid *fw, gboolean mod)
+{
+    FontgridInternalPageInfo *pi;
+
+    g_return_if_fail(fw != 0);
+
+    if (fw->font && fw->font->modified != mod) {
+        bdf_set_modified(fw->font, mod);
+
+        if (mod == FALSE) {
+            /*
+             * Redraw all the labels to clear those that were showing as
+             * modified.
+             */
+            pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+            fontgrid_draw_cells(GTK_WIDGET(fw), pi->bcode,
+                                (pi->bcode + fw->pagesize) - 1, TRUE, FALSE);
+        } else {
+            /*
+             * If the font is being marked as modified, then signal the
+             * application of this state.
+             */
+            fprintf(stderr, "MOD\n");
+        }
+    }
+}
+
+void
+fontgrid_set_unicode_glyph_names(Fontgrid *fw, FILE *in)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(in != 0);
+
+    if (bdf_set_unicode_glyph_names(in, fw->font, 0)) {
+        /*
+         * Redraw the labels.
+         */
+        fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode,
+                            fw->npage.bcode + fw->pagesize, TRUE, FALSE);
+        minfo.reason = FONTGRID_GLYPH_NAMES_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_set_adobe_glyph_names(Fontgrid *fw, FILE *in)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(in != 0);
+
+    if (bdf_set_adobe_glyph_names(in, fw->font, 0)) {
+        /*
+         * Redraw the labels.
+         */
+        fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode,
+                            fw->npage.bcode + fw->pagesize, TRUE, FALSE);
+        minfo.reason = FONTGRID_GLYPH_NAMES_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_set_code_glyph_names(Fontgrid *fw, gint ch)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    if (bdf_set_glyph_code_names(ch, fw->font, 0)) {
+        /*
+         * Redraw the labels.
+         */
+        fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode,
+                            fw->npage.bcode + fw->pagesize, TRUE, FALSE);
+        minfo.reason = FONTGRID_GLYPH_NAMES_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_make_xlfd_font_name(Fontgrid *fw)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    if ((minfo.name = bdf_make_xlfd_name(fw->font, "Foundry",
+                                         "FaceName")) != 0) {
+        minfo.reason = FONTGRID_NAME_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_update_font_name_from_properties(Fontgrid *fw)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    if (bdf_has_xlfd_name(fw->font)) {
+        bdf_update_name_from_properties(fw->font);
+
+        minfo.reason = FONTGRID_NAME_MODIFIED;
+        minfo.name = fw->font->name;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_update_properties_from_font_name(Fontgrid *fw)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    if (bdf_update_properties_from_name(fw->font)) {
+        minfo.reason = FONTGRID_PROPERTIES_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_set_font_property(Fontgrid *fw, bdf_property_t *prop)
+{
+    FontgridModificationInfo minfo;
+    gboolean changed;
+    bdf_property_t *p;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(prop != 0);
+
+    changed = FALSE;
+
+    if ((p = bdf_get_font_property(fw->font, prop->name)) == 0)
+      changed = TRUE;
+    else if (p->format == prop->format) {
+        switch (p->format) {
+          case BDF_ATOM:
+            /*
+             * If the atoms are different or one is NULL and the other isn't,
+             * then the property will be changed.
+             */
+            if ((p->value.atom && prop->value.atom &&
+                 strcmp(p->value.atom, prop->value.atom) != 0) ||
+                p->value.atom != prop->value.atom)
+              changed = TRUE;
+            break;
+          case BDF_INTEGER:
+            if (p->value.int32 != prop->value.int32)
+              changed = TRUE;
+            break;
+          case BDF_CARDINAL:
+            if (p->value.card32 != prop->value.card32)
+              changed = TRUE;
+            break;
+        }
+    }
+
+    /*
+     * If this causes no change, just return.
+     */
+    if (changed == FALSE)
+      return;
+
+    bdf_add_font_property(fw->font, prop);
+    minfo.reason = FONTGRID_PROPERTIES_MODIFIED;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+}
+
+void
+fontgrid_delete_font_property(Fontgrid *fw, gchar *prop_name)
+{
+    FontgridModificationInfo minfo;
+    bdf_property_t *p;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(prop_name != 0);
+
+    /*
+     * If the property doesn't exist, then just return.
+     */
+    if ((p = bdf_get_font_property(fw->font, prop_name)) == 0)
+      return;
+
+    bdf_delete_font_property(fw->font, prop_name);
+    minfo.reason = FONTGRID_PROPERTIES_MODIFIED;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+}
+
+guint32
+fontgrid_get_font_comments(Fontgrid *fw, gchar **comments)
+{
+    g_return_val_if_fail(fw != 0, 0);
+
+    if (comments != 0)
+      *comments = fw->font->comments;
+
+    return fw->font->comments_len;
+}
+
+void
+fontgrid_set_font_comments(Fontgrid *fw, gchar *comments)
+{
+    FontgridModificationInfo minfo;
+    unsigned int len;
+
+    g_return_if_fail(fw != 0);
+
+    len = (comments) ? (unsigned int) strlen(comments) : 0;
+    if (bdf_replace_comments(fw->font, comments, len)) {
+        minfo.reason = FONTGRID_COMMENTS_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+gint
+fontgrid_get_font_spacing(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, -1);
+
+    return fw->font->spacing;
+}
+
+void
+fontgrid_set_font_spacing(Fontgrid *fw, gint spacing)
+{
+    FontgridModificationInfo minfo;
+    bdf_property_t p;
+
+    g_return_if_fail(fw != 0);
+
+    if (spacing < BDF_PROPORTIONAL || spacing > BDF_CHARCELL ||
+        fw->font->spacing == spacing)
+      return;
+
+    p.name = "SPACING";
+    p.format = BDF_ATOM;
+    switch (spacing) {
+      case BDF_PROPORTIONAL: p.value.atom = "P"; break;
+      case BDF_MONOWIDTH: p.value.atom = "M"; break;
+      case BDF_CHARCELL: p.value.atom = "C"; break;
+    }
+
+    bdf_add_font_property(fw->font, &p);
+    minfo.reason = FONTGRID_PROPERTIES_MODIFIED;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+}
+
+guint16
+fontgrid_get_font_device_width(Fontgrid *fw)
+{
+    g_return_val_if_fail(fw != 0, 0);
+
+    return fw->font->monowidth;
+}
+
+void
+fontgrid_set_font_device_width(Fontgrid *fw, guint16 dwidth)
+{
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    /*
+     * Only set the global device width if this is not a proportional font or
+     * if there the device width changed.
+     */
+    if (fw->font->spacing == BDF_PROPORTIONAL ||
+        fw->font->monowidth == dwidth)
+      return;
+
+    fw->font->monowidth = dwidth;
+    fw->font->modified = 1;
+
+    minfo.reason = FONTGRID_DEVICE_WIDTH_MODIFIED;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+}
+
+void
+fontgrid_get_font_info(Fontgrid *fw, FontgridFontInfo *info)
+{
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(fw->font != 0);
+    g_return_if_fail(info != 0);
+
+    info->name = fw->font->name;
+    info->comments = fw->font->comments;
+    info->messages = fw->font->acmsgs;
+    info->default_char = fw->font->default_glyph;
+    info->monowidth = fw->font->monowidth;
+    info->spacing = (guint16) fw->font->spacing;
+    info->font_ascent = fw->font->font_ascent;
+    info->font_descent = fw->font->font_descent;
+    info->font_descent = fw->font->font_descent;
+    info->resolution_x = fw->font->resolution_x;
+    info->resolution_y = fw->font->resolution_y;
+    info->bits_per_pixel = fw->font->bpp;
+    memcpy((char *) &info->bbx, (char *) &fw->font->bbx, sizeof(bdf_bbx_t));
+}
+
+void
+fontgrid_set_font_info(Fontgrid *fw, FontgridFontInfo *info)
+{
+    int mod;
+    bdf_font_t *f;
+    bdf_property_t prop;
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(fw->font != 0);
+    g_return_if_fail(info != 0);
+
+    f = fw->font;
+
+    minfo.reason = FONTGRID_MODIFIED;
+
+    /*
+     * Do some special stuff with the modified field so we know whether to
+     * call the modified callback or not.
+     */
+    mod = f->modified;
+    f->modified = 0;
+
+    /*
+     * Handle the default character field.  If it happens to be -1, then
+     * delete the font property.  Otherwise add it.
+     */
+    if (info->default_char < 0)
+      bdf_delete_font_property(f, "DEFAULT_CHAR");
+    else {
+        prop.name = "DEFAULT_CHAR";
+        prop.format = BDF_CARDINAL;
+        prop.value.card32 = info->default_char;
+        bdf_add_font_property(f, &prop);
+    }
+
+    prop.name = "FONT_ASCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = info->font_ascent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "FONT_DESCENT";
+    prop.format = BDF_INTEGER;
+    prop.value.int32 = info->font_descent;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_X";
+    prop.format = BDF_CARDINAL;
+    prop.value.int32 = info->resolution_x;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "RESOLUTION_Y";
+    prop.format = BDF_CARDINAL;
+    prop.value.int32 = info->resolution_y;
+    bdf_add_font_property(f, &prop);
+
+    prop.name = "SPACING";
+    prop.format = BDF_ATOM;
+    prop.value.atom = 0;
+    switch (info->spacing) {
+      case BDF_PROPORTIONAL: prop.value.atom = "P"; break;
+      case BDF_MONOWIDTH: prop.value.atom = "M"; break;
+      case BDF_CHARCELL: prop.value.atom = "C"; break;
+    }
+    if (prop.value.atom != 0)
+      bdf_add_font_property(f, &prop);
+
+    /*
+     * If the font was modified, and has an XLFD name, make sure the XLFD name
+     * gets updated from the properties and the appropriate callback is
+     * called.
+     */
+    if (f->modified && bdf_has_xlfd_name(f))
+      fontgrid_update_font_name_from_properties(fw);
+
+    /*
+     * Now determine if the monowidth field will have a resize affect on
+     * things.
+     */
+    if (f->spacing != BDF_PROPORTIONAL) {
+        if (f->monowidth == 0) {
+            /*
+             * Handle the special case of a proportional font being changed to
+             * some other spacing.
+             */
+            f->monowidth = f->bbx.width;
+            f->modified = 1;
+        }
+        if (info->monowidth != f->monowidth) {
+            /*
+             * Go ahead and queue up a resize in case the monowidth
+             * really does change the size.
+             */
+            gtk_widget_queue_resize(GTK_WIDGET(fw));
+            f->monowidth = f->bbx.width = info->monowidth;
+            f->modified = 1;
+        }
+    }
+    if (f->modified)
+      g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    f->modified |= mod;
+}
+
+void
+fontgrid_translate_glyphs(Fontgrid *fw, gint16 dx, gint16 dy,
+                          gboolean all_glyphs)
+{
+    GtkWidget *w = (GtkWidget *) fw;
+    gint32 start, end;
+    FontgridInternalPageInfo *pi;
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage: &fw->upage;
+
+    if (all_glyphs) {
+        start = pi->minpage * fw->pagesize;
+        end = (pi->maxpage * fw->pagesize) + fw->pagesize;
+    } else {
+        start = pi->sel_start;
+        end = pi->sel_end;
+    }
+
+    if (bdf_translate_glyphs(fw->font, dx, dy, start, end, 0, 0,
+                             fw->unencoded)) {
+        gtk_widget_queue_resize(w);
+        if (GTK_WIDGET_REALIZED(w))
+          gdk_window_clear(w->window);
+
+        gtk_widget_queue_resize(w);
+        if (GTK_WIDGET_REALIZED(w))
+          gdk_window_clear(w->window);
+
+        minfo.reason = FONTGRID_GLYPHS_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_rotate_glyphs(Fontgrid *fw, gint16 degrees, gboolean all_glyphs)
+{
+    GtkWidget *w = (GtkWidget *) fw;
+    gint32 start, end;
+    FontgridInternalPageInfo *pi;
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage: &fw->upage;
+
+    if (all_glyphs) {
+        start = pi->minpage * fw->pagesize;
+        end = (pi->maxpage * fw->pagesize) + fw->pagesize;
+    } else {
+        start = pi->sel_start;
+        end = pi->sel_end;
+    }
+
+    if (bdf_rotate_glyphs(fw->font, degrees, start, end, 0, 0,
+                          fw->unencoded)) {
+        gtk_widget_queue_resize(w);
+        if (GTK_WIDGET_REALIZED(w))
+          gdk_window_clear(w->window);
+
+        minfo.reason = FONTGRID_GLYPHS_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_shear_glyphs(Fontgrid *fw, gint16 degrees, gboolean all_glyphs)
+{
+    GtkWidget *w = (GtkWidget *) fw;
+    gint32 start, end;
+    FontgridInternalPageInfo *pi;
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage: &fw->upage;
+
+    if (all_glyphs) {
+        start = pi->minpage * fw->pagesize;
+        end = (pi->maxpage * fw->pagesize) + fw->pagesize;
+    } else {
+        start = pi->sel_start;
+        end = pi->sel_end;
+    }
+
+    if (bdf_shear_glyphs(fw->font, degrees, start, end, 0, 0,
+                          fw->unencoded)) {
+        gtk_widget_queue_resize(w);
+        if (GTK_WIDGET_REALIZED(w))
+          gdk_window_clear(w->window);
+
+        minfo.reason = FONTGRID_GLYPHS_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+void
+fontgrid_embolden_glyphs(Fontgrid *fw, gboolean all_glyphs)
+{
+    GtkWidget *w = (GtkWidget *) fw;
+    gint resize;
+    gint32 start, end;
+    FontgridInternalPageInfo *pi;
+    FontgridModificationInfo minfo;
+
+    g_return_if_fail(fw != 0);
+
+    pi = (!fw->unencoded) ? &fw->npage: &fw->upage;
+
+    if (all_glyphs) {
+        start = pi->minpage * fw->pagesize;
+        end = (pi->maxpage * fw->pagesize) + fw->pagesize;
+    } else {
+        start = pi->sel_start;
+        end = pi->sel_end;
+    }
+
+    resize = 0;
+    if (bdf_embolden_glyphs(fw->font, start, end, 0, 0,
+                            fw->unencoded, &resize)) {
+        if (resize) {
+            gtk_widget_queue_resize(w);
+            if (GTK_WIDGET_REALIZED(w))
+              gdk_window_clear(w->window);
+        } else
+          /*
+           * Just redisplay the selection.
+           */
+          fontgrid_draw_cells(w, start, end, TRUE, TRUE);
+
+        minfo.reason = FONTGRID_GLYPHS_MODIFIED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+    }
+}
+
+gboolean
+fontgrid_clipboard_empty(Fontgrid *fw)
+{
+    GdkWindow *owner;
+    gboolean empty = TRUE;
+    GdkAtom atype;
+    gint aformat, nitems;
+    guchar *data;
+
+    g_return_val_if_fail(fw != 0, empty);
+
+    if ((owner = gdk_selection_owner_get(FONTGRID_CLIPBOARD)) == 0)
+      return empty;
+
+    /*
+     * Check to see if the clipboard contents are empty or not.
+     *
+     * This is handled specially to allow determination of this without
+     * using up what might be a lot of memory to get the whole contents.  It
+     * will have to be changed for Windows.
+     */
+    if (gdk_property_get(owner, FONTGRID_CLIPBOARD, FONTGRID_GLYPHLIST,
+                         0, 4, FALSE, &atype, &aformat, &nitems, &data)) {
+        if (nitems > 0) {
+            empty = FALSE;
+            free((char *) data);
+        }
+    }
+
+    return empty;
+}
+
+static unsigned char *
+fontgrid_encode_selection(Fontgrid *fw, guint32 *bytes)
+{
+    FontgridInternalPageInfo *pi;
+    bdf_glyph_t *gp;
+    bdf_glyphlist_t *gl;
+    guint16 a;
+    guint32 i, nlen, bcount;
+    guchar *sel, *sp;
+
+    *bytes = 0;
+
+    gl = &fw->clipboard;
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+    bdf_copy_glyphs(fw->font, pi->sel_start, pi->sel_end, gl, fw->unencoded);
+
+    /*
+     * Calculate the number of bytes that will be needed for everything except
+     * the name strings and the bitmap data.
+     */
+    bcount = (sizeof(unsigned int) << 1) + (6 * sizeof(unsigned short)) +
+        (((6 * sizeof(unsigned short)) + sizeof(unsigned int)) *
+         gl->glyphs_used);
+
+    /*
+     * Figure out how much extra will be needed for the names, bitmaps, and
+     * PSF Unicode mappings.
+     */
+    for (i = 0, gp = gl->glyphs; i < gl->glyphs_used; i++, gp++) {
+        nlen = (gp->name) ? (guint32) (strlen(gp->name) + 1) : 0;
+        /*
+         * The extra 2 bytes is for encoding the number of bytes used for the
+         * Unicode mappings, even if it is 0.  This could be a problem later
+         * if a set of mappings legitimately exceeds 2^16 in length.
+         */
+        bcount += nlen + gp->bytes + 2 + gp->unicode.map_used;
+    }
+
+    /*
+     * Allocate the storage space needed for the encoded form.
+     */
+    sel = sp = g_malloc(bcount);
+
+    /*
+     * Set the returned byte count.
+     */
+    *bytes = bcount;
+
+    /*
+     * Encode the 20-byte header.
+     */
+    a = (guint16) gl->bpp;
+    *sp++ = (a >> 8) & 0xff;
+    *sp++ = a & 0xff;
+
+    nlen = (guint32) gl->start;
+    *sp++ = (nlen >> 24) & 0xff;
+    *sp++ = (nlen >> 16) & 0xff;
+    *sp++ = (nlen >> 8) & 0xff;
+    *sp++ = nlen & 0xff;
+
+    nlen = (guint32) gl->end;
+    *sp++ = (nlen >> 24) & 0xff;
+    *sp++ = (nlen >> 16) & 0xff;
+    *sp++ = (nlen >> 8) & 0xff;
+    *sp++ = nlen & 0xff;
+
+    a = (guint16) gl->glyphs_used;
+    *sp++ = (a >> 8) & 0xff;
+    *sp++ = a & 0xff;
+
+    a = (guint16) gl->bbx.width;
+    *sp++ = (a >> 8) & 0xff;
+    *sp++ = a & 0xff;
+
+    a = (guint16) gl->bbx.x_offset;
+    *sp++ = (a >> 8) & 0xff;
+    *sp++ = a & 0xff;
+
+    a = (guint16) gl->bbx.ascent;
+    *sp++ = (a >> 8) & 0xff;
+    *sp++ = a & 0xff;
+
+    a = (guint16) gl->bbx.descent;
+    *sp++ = (a >> 8) & 0xff;
+    *sp++ = a & 0xff;
+
+    /*
+     * Go through each glyph entry and encode the data.
+     */
+    for (i = 0, gp = gl->glyphs; i < gl->glyphs_used; i++, gp++) {
+        /*
+         * Encode the glyph encoding.
+         */
+        nlen = (guint32) gp->encoding;
+        *sp++ = (nlen >> 24) & 0xff;
+        *sp++ = (nlen >> 16) & 0xff;
+        *sp++ = (nlen >> 8) & 0xff;
+        *sp++ = nlen & 0xff;
+
+        /*
+         * Encode the glyph device width.
+         */
+        a = (guint16) gp->dwidth;
+        *sp++ = (a >> 8) & 0xff;
+        *sp++ = a & 0xff;
+
+        /*
+         * Encode the glyph name length.
+         */
+        nlen = (gp->name) ? (guint32) (strlen(gp->name) + 1) : 0;
+        a = (guint16) nlen;
+        *sp++ = (a >> 8) & 0xff;
+        *sp++ = a & 0xff;
+
+        /*
+         * Encode the four bounding box values needed.
+         */
+        a = (guint16) gp->bbx.width;
+        *sp++ = (a >> 8) & 0xff;
+        *sp++ = a & 0xff;
+
+        a = (guint16) gp->bbx.x_offset;
+        *sp++ = (a >> 8) & 0xff;
+        *sp++ = a & 0xff;
+
+        a = (guint16) gp->bbx.ascent;
+        *sp++ = (a >> 8) & 0xff;
+        *sp++ = a & 0xff;
+
+        a = (guint16) gp->bbx.descent;
+        *sp++ = (a >> 8) & 0xff;
+        *sp++ = a & 0xff;
+
+        /*
+         * Encode the name if it exists.
+         */
+        if (nlen > 0) {
+            (void) memcpy((char *) sp, gp->name, nlen);
+            sp += nlen;
+        }
+
+        /*
+         * Encode the bitmap.
+         */
+        if (gp->bytes > 0) {
+            (void) memcpy((char *) sp, (char *) gp->bitmap, gp->bytes);
+            sp += gp->bytes;
+        }
+
+        /*
+         * Encode the PSF Unicode mappings.  Even if there aren't any, add
+         * the encoding.
+         */
+        *sp++ = (gp->unicode.map_used >> 8) & 0xff;
+        *sp++ = gp->unicode.map_used & 0xff;
+        if (gp->unicode.map_used > 0) {
+            (void) memcpy((char *) sp, (char *) gp->unicode.map,
+                          sizeof(unsigned char) * gp->unicode.map_used);
+            sp += gp->unicode.map_used;
+        }
+    }
+
+    /*
+     * Return the selection encoded as a byte stream.
+     */
+    return sel;
+}
+
+#define GETSHORT(s) ((s[0] << 8) | s[1])
+#define GETLONG(s) ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])
+
+static void
+fontgrid_decode_selection(Fontgrid *fw, guchar *sel)
+{
+    guint32 i, range, nlen;
+    bdf_glyph_t *gp;
+    bdf_glyphlist_t *gl;
+
+    if (sel == 0)
+      return;
+
+    gl = &fw->clipboard;
+
+    /*
+     * Clear out the bitmaps and names from the existing glyphs.
+     */
+    for (gp = gl->glyphs, i = 0; i < gl->glyphs_size; i++, gp++) {
+        if (gp->name != 0)
+          free(gp->name);
+        if (gp->bytes > 0)
+          free((char *) gp->bitmap);
+    }
+
+    /*
+     * Extract the glyph list bits per pixel.
+     */
+    gl->bpp = GETSHORT(sel);
+    sel += 2;
+
+    /*
+     * Extract the glyph list starting and ending encodings.
+     */
+    gl->start = (int) GETLONG(sel);
+    sel += 4;
+
+    gl->end = (int) GETLONG(sel);
+    sel += 4;
+
+    /*
+     * Extract the number of encoded glyphs.
+     */
+    range = (guint32) GETSHORT(sel);
+    sel += 2;
+
+    /*
+     * Resize the internal glyph list clipboard if necessary.
+     */
+    if (range > gl->glyphs_size) {
+        if (gl->glyphs_size == 0)
+          gl->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * range);
+        else
+          gl->glyphs = (bdf_glyph_t *) realloc((char *) gl->glyphs,
+                                               sizeof(bdf_glyph_t) * range);
+        gl->glyphs_size = range;
+    }
+
+    /*
+     * Initialize the glyph list.
+     */
+    (void) memset((char *) &gl->bbx, 0, sizeof(bdf_bbx_t));
+    (void) memset((char *) gl->glyphs, 0,
+                  sizeof(bdf_glyph_t) * gl->glyphs_size);
+
+    gl->glyphs_used = range;
+
+    /*
+     * Decode the overall metrics of the glyph list.
+     */
+    gl->bbx.width = GETSHORT(sel);
+    sel += 2;
+    gl->bbx.x_offset = GETSHORT(sel);
+    sel += 2;
+    gl->bbx.ascent = GETSHORT(sel);
+    sel += 2;
+    gl->bbx.descent = GETSHORT(sel);
+    sel += 2;
+    gl->bbx.height = gl->bbx.ascent + gl->bbx.descent;
+    gl->bbx.y_offset = -gl->bbx.descent;
+
+    /*
+     * Decode the glyphs.
+     */
+    for (i = 0, gp = gl->glyphs; i < range; i++, gp++) {
+        /*
+         * Get the glyph encoding.
+         */
+        gp->encoding = (int) GETLONG(sel);
+        sel += 4;
+
+        /*
+         * Get the device width.
+         */
+        gp->dwidth = GETSHORT(sel);
+        sel += 2;
+
+        /*
+         * Get the name length.
+         */
+        nlen = GETSHORT(sel);
+        sel += 2;
+
+        /*
+         * Get the bounding box.
+         */
+        gp->bbx.width = GETSHORT(sel);
+        sel += 2;
+        gp->bbx.x_offset = GETSHORT(sel);
+        sel += 2;
+        gp->bbx.ascent = GETSHORT(sel);
+        sel += 2;
+        gp->bbx.descent = GETSHORT(sel);
+        sel += 2;
+        gp->bbx.height = gp->bbx.ascent + gp->bbx.descent;
+        gp->bbx.y_offset = -gp->bbx.descent;
+
+        /*
+         * Get the name.
+         */
+        if (nlen > 0) {
+            gp->name = (char *) malloc(nlen);
+            (void) memcpy(gp->name, (char *) sel, nlen);
+            sel += nlen;
+        }
+
+        /*
+         * Get the bitmap.
+         */
+
+        switch (gl->bpp) {
+          case 1:
+            gp->bytes = ((gp->bbx.width + 7) >> 3) * gp->bbx.height;
+            break;
+          case 2:
+            gp->bytes = (((gp->bbx.width << 1) + 7) >> 3) * gp->bbx.height;
+            break;
+          case 4:
+            gp->bytes = (((gp->bbx.width << 2) + 7) >> 3) * gp->bbx.height;
+            break;
+          case 8:
+            gp->bytes = gp->bbx.width * gp->bbx.height;
+            break;
+        }
+
+        if (gp->bytes > 0) {
+            gp->bitmap = (unsigned char *) malloc(gp->bytes);
+            (void) memcpy((char *) gp->bitmap, (char *) sel, gp->bytes);
+            sel += gp->bytes;
+        }
+
+        /*
+         * Get the Unicode mappings.
+         */
+        gp->unicode.map_used = GETSHORT(sel);
+        sel += 2;
+        if (gp->unicode.map_used > 0) {
+            gp->unicode.map_size = ((gp->unicode.map_used >> 2) + 
+                                    ((gp->unicode.map_used & 3) ? 1 : 0)) << 2;
+            gp->unicode.map = (unsigned char *) malloc(gp->unicode.map_size);
+            (void) memcpy((char *) gp->unicode.map, (char *) sel,
+                          gp->unicode.map_used);
+            sel += gp->unicode.map_used;
+        }
+    }
+}
+
+/*
+ * This function assumes the fontgrid is realized so a GdkWindow exists.
+ */
+void
+fontgrid_copy_selection(Fontgrid *fw)
+{
+    GtkWidget *w;
+    GdkWindow *win;
+    guint32 bytes;
+    guchar *sel;
+
+    g_return_if_fail(fw != 0);
+
+    w = GTK_WIDGET(fw);
+
+    /*
+     * Make sure the widget owns the clipboard property.
+     */
+    if ((win = gdk_selection_owner_get(FONTGRID_CLIPBOARD)) == 0 ||
+        win != w->window)
+      gdk_selection_owner_set(w->window, FONTGRID_CLIPBOARD, GDK_CURRENT_TIME,
+                              FALSE);
+
+    /*
+     * Encode the selection as a byte stream for the clipboard.
+     */
+    if ((sel = fontgrid_encode_selection(fw, &bytes)) == 0)
+      return;
+
+    gdk_property_change(w->window, FONTGRID_CLIPBOARD, FONTGRID_GLYPHLIST,
+                        8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes);
+
+    /*
+     * Free the data because the property now has control over it.
+     */
+    g_free(sel);
+}
+
+void
+fontgrid_cut_selection(Fontgrid *fw)
+{
+    gint32 code, start, end;
+    bdf_glyph_t *gp;
+    FontgridInternalPageInfo *pi;
+    FontgridModificationInfo minfo;
+    FontgridSelectionInfo sinfo;
+
+    g_return_if_fail(fw != 0);
+
+    fontgrid_copy_selection(fw);
+
+    pi = (!fw->unencoded) ? &fw->npage : &fw->upage;
+    code = pi->sel_start;
+
+    if (bdf_delete_glyphs(fw->font, pi->sel_start, pi->sel_end,
+                          fw->unencoded)) {
+        start = pi->sel_start;
+        end = pi->sel_end;
+
+        fontgrid_deselect_all(fw);
+        Select(code, pi->selmap);
+        pi->sel_start = pi->sel_end = code;
+        fontgrid_draw_cells(GTK_WIDGET(fw), start, end, TRUE, TRUE);
+
+        /*
+         * Set up and emit the modified signal.
+         */
+        minfo.reason = FONTGRID_GLYPHS_DELETED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+
+        /*
+         * Set up and call the selection start signal.
+         */
+        gp = (!fw->unencoded) ?
+            fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                  code, TRUE) :
+            fontgrid_locate_glyph(fw->font->unencoded,
+                                  fw->font->unencoded_used, code, TRUE);
+        if (gp == 0) {
+            empty_glyph.encoding = code;
+            gp = &empty_glyph;
+        }
+
+        sinfo.glyphs = gp;
+        sinfo.num_glyphs = 1;
+        sinfo.start = sinfo.end = code;
+        sinfo.base = fw->base;
+        sinfo.unencoded = fw->unencoded;
+        sinfo.reason = FONTGRID_START_SELECTION;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                      &sinfo);
+    }
+}
+
+void
+fontgrid_paste_selection(Fontgrid *fw, FontgridPasteType paste_type)
+{
+    GtkWidget *w = GTK_WIDGET(fw);
+    GdkWindow *win;
+    GdkAtom atype;
+    gint afmt, nitems, unenc, doresize;
+    gint32 i;
+    unsigned int ng;
+    guchar *data;
+    bdf_font_t *font;
+    bdf_glyph_t *gp;
+    bdf_glyphlist_t *gl;
+    FontgridInternalPageInfo *pi;
+    bdf_glyphlist_t overflow;
+    FontgridModificationInfo minfo;
+    FontgridSelectionInfo sinfo;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(GTK_WIDGET_REALIZED(w));
+
+    if ((win = gdk_selection_owner_get(FONTGRID_CLIPBOARD)) == 0) {
+        gdk_selection_owner_set(w->window, FONTGRID_CLIPBOARD,
+                                GDK_CURRENT_TIME, FALSE);
+        /*
+         * Return here because there was no owner of the selection.
+         */
+        return;
+    }
+
+    doresize = 0;
+    unenc = fw->unencoded;
+
+    pi = (!unenc) ? &fw->npage : &fw->upage;
+
+    nitems = 0;
+    (void) gdk_property_get(win, FONTGRID_CLIPBOARD, FONTGRID_GLYPHLIST,
+                            0, 102400, FALSE, &atype, &afmt, &nitems, &data);
+
+    /*
+     * Attempt to own the clipboard after getting the value if this widget
+     * does not own it.
+     */
+    if (win != w->window)
+      gdk_selection_owner_set(w->window, FONTGRID_CLIPBOARD, GDK_CURRENT_TIME,
+                              FALSE);
+
+    if (nitems > 0) {
+        font = fw->font;
+        gl = &fw->clipboard;
+
+        /*
+         * Convert the encoded selection into a glyph list in the internal
+         * glyph list clipboard.
+         */
+        fontgrid_decode_selection(fw, data);
+
+        /*
+         * If the paste is occuring in the unencoded section, make sure the
+         * paste is appended as opposed to being inserted.  Also turn off
+         * the selected cell before doing the paste.
+         */
+        if (unenc) {
+            fontgrid_deselect_all(fw);
+            pi->sel_start = font->unencoded_used;
+            gl->start = 0;
+            gl->end = gl->glyphs_used - 1;
+        }
+
+        /*
+         * Set the end point of the selection.
+         */
+        pi->sel_end = pi->sel_start + (gl->end - gl->start);
+
+        /*
+         * First, check to see if pasting the glyphs will exceed the maximum
+         * encoding value of 0xffff.  If some of them do, then transfer the
+         * extra glyphs to the unencoded area before doing anything else.
+         * This means that a new glyph list needs to be constructed to do the
+         * insert into the unencoded area.
+         */
+        if (!unenc && pi->sel_end > 0xffff) {
+            /*
+             * Determine if any of the glyphs would actually get encoded after
+             * 0xffff or if those are all empty glyphs.
+             */
+            for (ng = 0, gp = gl->glyphs; ng < gl->glyphs_used; ng++, gp++) {
+                if (pi->sel_start + (gp->encoding - gl->start) > 0xffff)
+                  /*
+                   * The glyph list does contain glyphs that will overflow.
+                   */
+                  break;
+            }
+
+            if (ng < gl->glyphs_used) {
+                /*
+                 * Construct a new glyph list containing only the glyphs that
+                 * overflow the 0xffff boundary.  There is no need to
+                 * recalculate the bounding box for the new glyph list.  Any
+                 * resize will be handled correctly anyway.
+                 */
+                (void) memcpy((char *) &overflow.bbx, (char *) &gl->bbx,
+                              sizeof(bdf_bbx_t));
+                overflow.bpp = font->bpp;
+                overflow.glyphs_used = gl->glyphs_used - ng;
+                overflow.glyphs = gp;
+                overflow.start = 0;
+                overflow.end = overflow.glyphs_used - 1;
+
+                /*
+                 * Add the glyphs to the unencoded area.
+                 */
+                doresize = bdf_replace_glyphs(font, font->unencoded_used,
+                                              &overflow, 1);
+            }
+
+            /*
+             * Adjust the glyph list and selection to fit within the 0xffff
+             * limit before pasting the glyphs into the font.
+             */
+            gl->glyphs_used = ng;
+            gl->end -= pi->sel_end - 0xffff;
+            pi->sel_end = 0xffff;
+        }
+
+        /*
+         * If the grid is in insert mode, then determine if moving glyphs
+         * forward from the insert location would cause an overflow.
+         */
+        if (!unenc &&
+            (!fw->overwrite || paste_type == FONTGRID_INSERT_PASTE)) {
+            doresize += bdf_insert_glyphs(font, pi->sel_start, gl);
+            /*
+             * Force a page recalculation to be done so the application can
+             * update if needed.
+             */
+            fontgrid_goto_page(fw, fw->npage.pageno);
+        } else if (paste_type == FONTGRID_MERGE_PASTE)
+          doresize += bdf_merge_glyphs(font, pi->sel_start, gl, unenc);
+        else
+          doresize += bdf_replace_glyphs(font, pi->sel_start, gl, unenc);
+
+        /*
+         * If the paste has more than one glyph, make sure the whole
+         * range is selected.
+         */
+        for (i = pi->sel_start; i <= pi->sel_end; i++)
+          Select(i, pi->selmap);
+
+        /*
+         * If the incoming glyphs changed the font bounding box, then
+         * determine the new geometry and attempt a resize.
+         */
+        if (doresize) {
+            fontgrid_set_cell_geometry(fw);
+            fontgrid_set_rows_cols(fw, 0);
+            gtk_widget_queue_resize(w);
+        } else
+          fontgrid_draw_cells(w, pi->sel_start, pi->sel_end, TRUE, TRUE);
+
+        /*
+         * Update the number of pages used.
+         */
+        if (unenc) {
+            if (fw->noblanks) {
+                if (font->unencoded_used == 0)
+                  pi->maxpage = 0;
+                else {
+                    gp = font->unencoded + (font->unencoded_used - 1);
+                    pi->maxpage = gp->encoding / fw->pagesize;
+                }
+            }
+        } else {
+            if (fw->noblanks) {
+                if (font->glyphs_used == 0)
+                  pi->maxpage = 0;
+                else {
+                    gp = font->glyphs + (font->glyphs_used - 1);
+                    pi->maxpage = gp->encoding / fw->pagesize;
+                }
+            }
+        }
+
+        /*
+         * Set up and call the modified callback.
+         */
+        /*
+         * Set up and emit the modified signal.
+         */
+        minfo.reason = FONTGRID_GLYPHS_PASTED;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo);
+
+        if (pi->sel_start == pi->sel_end) {
+            /*
+             * Set up and call the selection start signal.
+             */
+            gp = (!fw->unencoded) ?
+                fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                      pi->sel_start, TRUE) :
+                fontgrid_locate_glyph(fw->font->unencoded,
+                                      fw->font->unencoded_used, pi->sel_start,
+                                      TRUE);
+            if (gp == 0) {
+                empty_glyph.encoding = pi->sel_start;
+                gp = &empty_glyph;
+            }
+
+            sinfo.glyphs = gp;
+            sinfo.num_glyphs = 1;
+        } else {
+            sinfo.glyphs = 0;
+            sinfo.num_glyphs = 0;
+        }
+        sinfo.start = pi->sel_start;
+        sinfo.end = pi->sel_end;
+        sinfo.base = fw->base;
+        sinfo.unencoded = fw->unencoded;
+        sinfo.reason = FONTGRID_START_SELECTION;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                      &sinfo);
+
+        /*
+         * And last, since the change of the selection owner caused the
+         * clipboard to lose its data, add the data to it again so
+         * it can be pasted in some other font editor.
+         */
+        gdk_property_change(w->window, FONTGRID_CLIPBOARD,
+                            FONTGRID_GLYPHLIST, 8, GDK_PROP_MODE_REPLACE,
+                            data, (gint) nitems);
+
+        g_free((char *) data);
+    }
+}
+
+void
+fontgrid_update_metrics(Fontgrid *fw, bdf_metrics_t *metrics)
+{
+    FontgridModificationInfo mi;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(fw->font != 0);
+
+    if (bdf_set_font_bbx(fw->font, metrics)) {
+        /*
+         * Need to resize.
+         */
+
+        /*
+         * Calculate the cell geometry and the rows and columns.
+         */
+        fontgrid_set_cell_geometry(fw);
+        fontgrid_set_rows_cols(fw, 0);
+
+        gtk_widget_queue_resize(GTK_WIDGET(fw));
+
+        mi.reason = FONTGRID_FONT_METRICS_MODIFIED;
+        mi.name = 0;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi);
+    }
+}
+
+void
+fontgrid_update_glyph(Fontgrid *fw, bdf_glyph_t *glyph, gboolean unencoded)
+{
+    FontgridInternalPageInfo *pi;
+    bdf_glyph_t *gp;
+    bdf_glyphlist_t gl;
+    FontgridModificationInfo mi;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(fw->font != 0);
+
+    gl.bpp = fw->font->bpp;
+    gl.start = gl.end = glyph->encoding;
+    gl.glyphs = glyph;
+    gl.glyphs_used = 1;
+    memcpy((char *) &gl.bbx, (char *) &glyph->bbx, sizeof(bdf_bbx_t));
+
+    if (bdf_replace_glyphs(fw->font, glyph->encoding, &gl, unencoded)) {
+        /*
+         * The font geometry was changed by the glyph being pasted.
+         * A resize will be needed.
+         */
+
+        /*
+         * Calculate the cell geometry and the rows and columns.
+         */
+        fontgrid_set_cell_geometry(fw);
+        fontgrid_set_rows_cols(fw, 0);
+
+        gtk_widget_queue_resize(GTK_WIDGET(fw));
+
+        mi.reason = FONTGRID_FONT_METRICS_MODIFIED;
+        mi.name = 0;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi);
+    } else
+      /*
+       * Simply redraw the cells that were modified.
+       */
+      fontgrid_draw_cells(GTK_WIDGET(fw), glyph->encoding, glyph->encoding,
+                          TRUE, TRUE);
+
+    pi = (fw->unencoded) ? &fw->upage : &fw->npage;
+    if (unencoded) {
+        if (fw->noblanks) {
+            if (fw->font->unencoded_used == 0)
+              pi->maxpage = 0;
+            else {
+                gp = fw->font->unencoded + (fw->font->unencoded_used - 1);
+                pi->maxpage = gp->encoding / fw->pagesize;
+            }
+        }
+    } else {
+        if (fw->noblanks) {
+            if (fw->font->glyphs_used == 0)
+              pi->maxpage = 0;
+            else {
+                gp = fw->font->glyphs + (fw->font->glyphs_used - 1);
+                pi->maxpage = gp->encoding / fw->pagesize;
+            }
+        }
+    }
+
+    mi.reason = FONTGRID_GLYPHS_MODIFIED;
+    mi.name = 0;
+    mi.start = mi.end = glyph->encoding;
+    mi.unencoded = fw->unencoded;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi);
+}
+
+void
+fontgrid_update_psf_mappings(Fontgrid *fw, gint32 encoding,
+                             bdf_psf_unimap_t *mappings)
+{
+    FontgridModificationInfo mi;
+
+    g_return_if_fail(fw != 0);
+    g_return_if_fail(fw->font != 0);
+
+    if (bdf_replace_mappings(fw->font, encoding, mappings, fw->unencoded)) {
+        mi.reason = FONTGRID_PSF_MAPPINGS_MODIFIED;
+        mi.name = 0;
+        mi.start = mi.end = encoding;
+        mi.unencoded = fw->unencoded;
+        g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi);
+    }
+}
+
+gboolean
+fontgrid_select_next_glyph(Fontgrid *fw, gint32 code)
+{
+    bdf_glyph_t *gp;
+    gint32 pageno;
+    guint32 count;
+    FontgridInternalPageInfo *pi;
+    FontgridSelectionInfo sinfo;
+
+    g_return_val_if_fail(fw != NULL, FALSE);
+    g_return_val_if_fail(IS_FONTGRID(fw), FALSE);
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        gp = (fw->font && fw->font->glyphs_used) ?
+            (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0;
+    } else {
+        pi = &fw->upage;
+        gp = (fw->font && fw->font->unencoded_used) ?
+            (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0;
+    }
+
+    if ((count = fw->count) == 0)
+      count = 1;
+
+    /*
+     * Make sure that when on the unencoded pages, the final glyph is
+     * the limit unlike the encoded pages where the max value is 0xffff.
+     */
+    if ((fw->unencoded &&
+         (gp == 0 || code == gp->encoding)) ||
+        code == 0xffff) {
+        gdk_beep();
+        return FALSE;
+    }
+
+    if (fw->orientation == GTK_ORIENTATION_VERTICAL)
+      code += (fw->cell_rows * count);
+    else
+      code += count;
+
+    if (fw->unencoded && code > gp->encoding)
+      code = gp->encoding;
+    else if (code > 0xffff)
+      code = 0xffff;
+
+    fontgrid_deselect_all(fw);
+
+    pageno = code / fw->pagesize;
+    if (pageno != pi->pageno) {
+        fw->no_sel_callback = TRUE;
+        fontgrid_goto_page(fw, pageno);
+    }
+
+    pi->sel_start = pi->sel_end = code;
+    Select(code, pi->selmap);
+    fontgrid_draw_cells(GTK_WIDGET(fw), code, code, FALSE, TRUE);
+
+    /*
+     * Reset the count.
+     */
+    fw->count = 0;
+
+    /*
+     * Set up and emit the selection start signal.
+     */
+    if (!fw->unencoded)
+      gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                 code, TRUE);
+    else
+      gp = fontgrid_locate_glyph(fw->font->unencoded, fw->font->unencoded_used,
+                                 code, TRUE);
+    if (gp == 0) {
+        empty_glyph.encoding = code;
+        gp = &empty_glyph;
+    }
+    sinfo.glyphs = gp;
+    sinfo.num_glyphs = 1;
+    sinfo.start = pi->sel_start;
+    sinfo.end = pi->sel_end;
+    sinfo.base = fw->base;
+    sinfo.unencoded = fw->unencoded;
+
+    sinfo.reason = FONTGRID_START_SELECTION;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                  &sinfo);
+    return TRUE;
+}
+
+gboolean
+fontgrid_select_previous_glyph(Fontgrid *fw, gint32 code)
+{
+    bdf_glyph_t *gp;
+    gint32 pageno;
+    guint32 count;
+    FontgridInternalPageInfo *pi;
+    FontgridSelectionInfo sinfo;
+
+    g_return_val_if_fail(fw != NULL, FALSE);
+    g_return_val_if_fail(IS_FONTGRID(fw), FALSE);
+
+    if (!fw->unencoded) {
+        pi = &fw->npage;
+        gp = (fw->font && fw->font->glyphs_used) ?
+            (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0;
+    } else {
+        pi = &fw->upage;
+        gp = (fw->font && fw->font->unencoded_used) ?
+            (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0;
+    }
+
+    if ((count = fw->count) == 0)
+      count = 1;
+
+    if (code == 0) {
+        gdk_beep();
+        return FALSE;
+    }
+
+    if (fw->orientation == GTK_ORIENTATION_VERTICAL)
+      code -= (fw->cell_rows * count);
+    else
+      code -= count;
+
+    if (code < 0)
+      code = 0;
+
+    fontgrid_deselect_all(fw);
+
+    pageno = code / fw->pagesize;
+    if (pageno != pi->pageno) {
+        fw->no_sel_callback = TRUE;
+        fontgrid_goto_page(fw, pageno);
+    }
+
+    pi->sel_start = pi->sel_end = code;
+    Select(code, pi->selmap);
+    fontgrid_draw_cells(GTK_WIDGET(fw), code, code, FALSE, TRUE);
+
+    /*
+     * Reset the count.
+     */
+    fw->count = 0;
+
+    /*
+     * Set up and emit the selection start signal.
+     */
+    if (!fw->unencoded)
+      gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used,
+                                 code, TRUE);
+    else
+      gp = fontgrid_locate_glyph(fw->font->unencoded, fw->font->unencoded_used,
+                                 code, TRUE);
+    if (gp == 0) {
+        empty_glyph.encoding = code;
+        gp = &empty_glyph;
+    }
+    sinfo.glyphs = gp;
+    sinfo.num_glyphs = 1;
+    sinfo.start = pi->sel_start;
+    sinfo.end = pi->sel_end;
+    sinfo.base = fw->base;
+    sinfo.unencoded = fw->unencoded;
+
+    sinfo.reason = FONTGRID_START_SELECTION;
+    g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0,
+                  &sinfo);
+    return TRUE;
+}
diff --git a/fontgrid.h b/fontgrid.h
new file mode 100644 (file)
index 0000000..cbabe3c
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_fontgrid
+#define _h_fontgrid
+
+#include <gdk/gdk.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtksignal.h>
+#include "bdfP.h"
+#include "gtkcompat.h"
+
+G_BEGIN_DECLS
+
+/*
+ * The macros for accessing various parts of the widget class.
+ */
+#define FONTGRID(o) \
+        (G_TYPE_CHECK_INSTANCE_CAST((o), fontgrid_get_type(), Fontgrid))
+
+#define FONTGRID_CLASS(c) \
+        (G_TYPE_CHECK_CLASS_CAST((c), fontgrid_get_type(), FontgridClass))
+
+#define IS_FONTGRID(o) G_TYPE_CHECK_INSTANCE_TYPE((o), fontgrid_get_type())
+
+#define IS_FONTGRID_CLASS(c) \
+        (G_TYPE_CHECK_CLASS_TYPE((c), fontgrid_get_type()))
+
+#define FONTGRID_GET_CLASS(o) \
+        (G_TYPE_INSTANCE_GET_CLASS((o), fontgrid_get_type(), FontgridClass))
+
+typedef struct _Fontgrid      Fontgrid;
+typedef struct _FontgridClass FontgridClass;
+
+typedef struct {
+    gint32 minpage;
+    gint32 maxpage;
+    gint32 npage;
+    gint32 ppage;
+
+    gint32 pageno;
+    gint32 bcode;
+    gint32 sel_start;
+    gint32 sel_end;
+
+    guint32 selmap[2048];
+} FontgridInternalPageInfo;
+
+struct _Fontgrid {
+    GtkWidget widget;
+
+    bdf_font_t *font;
+    guint base;
+    gboolean power2;
+    gboolean overwrite;
+    gboolean noblanks;
+    GtkOrientation orientation;
+    guint32 point_size;
+    gint32 spacing;
+    guint16 *colors;
+    gint32 initial_glyph;
+    gint32 bpp;
+    gint32 hres;
+    gint32 vres;
+
+    guint16 cell_rows;
+    guint16 cell_cols;
+    guint16 border;
+    guint16 hmargin;
+    guint16 vmargin;
+
+    /*
+     * Private variables.
+     */
+    gboolean init;
+
+    gboolean resizing;
+    gboolean from_keyboard;
+    gboolean no_sel_callback;
+
+    guint16 label_height;
+    guint16 cell_width;
+    guint16 cell_height;
+    guint16 pagesize;
+    gint16 xoff;
+    gint16 yoff;
+
+    gboolean unencoded;
+    gboolean debug;
+
+    GdkGC *xor_gc;
+
+    GdkPoint *points;
+    guint32 points_used;
+    guint32 points_size;
+
+    /*
+     * For creating RGB glyph images.
+     */
+    guchar *rgb;
+    guint32 rgb_used;
+    guint32 rgb_size;
+
+    /*
+     * Stuff related to the timer between clicks.
+     */
+    guint32 last_click;
+    guint32 mclick_time;
+
+    /*
+     * The count accumulated from pressing number keys.
+     */
+    guint32 count;
+
+    /*
+     * The clipboard used to store selections among other things.
+     */
+    bdf_glyphlist_t clipboard;
+
+    /*
+     * Page information necessary for paging an drawing.
+     */
+    FontgridInternalPageInfo npage;
+    FontgridInternalPageInfo upage;
+};
+
+struct _FontgridClass {
+    GtkWidgetClass parent_class;
+
+    void (*selection_start)(GtkWidget *, gpointer, gpointer);
+    void (*selection_extend)(GtkWidget *, gpointer, gpointer);
+    void (*selection_end)(GtkWidget *, gpointer, gpointer);
+    void (*page)(GtkWidget *, gpointer, gpointer);
+    void (*activate)(GtkWidget *, gpointer, gpointer);
+    void (*modified)(GtkWidget *, gpointer, gpointer);
+};
+
+/**************************************************************************
+ *
+ * Structures used for the API.
+ *
+ **************************************************************************/
+
+typedef struct {
+    gint32 previous_page;
+    gint32 current_page;
+    gint32 next_page;
+    gint32 encoded_glyphs;
+    gint32 unencoded_glyphs;
+    gboolean unencoded_page;
+} FontgridPageInfo;
+
+typedef struct {
+    gchar *name;
+    gchar *comments;
+    gchar *messages;
+    glong bits_per_pixel;
+    glong default_char;
+    guint16 monowidth;
+    guint16 spacing;
+    gulong font_ascent;
+    gulong font_descent;
+    gulong resolution_x;
+    gulong resolution_y;
+    bdf_bbx_t bbx;
+} FontgridFontInfo;
+
+/*
+ * Enum representing the callback reasons.
+ */
+typedef enum {
+    FONTGRID_START_SELECTION = 0,
+    FONTGRID_EXTEND_SELECTION,
+    FONTGRID_END_SELECTION,
+    FONTGRID_ACTIVATE,
+    FONTGRID_BASE_CHANGE
+} FontgridSelectionReason;
+
+typedef struct {
+    FontgridSelectionReason reason;
+    gint32 start;
+    gint32 end;
+    gint base;
+    bdf_glyph_t *glyphs;
+    guint32 num_glyphs;
+    gboolean unencoded;
+} FontgridSelectionInfo;
+
+typedef enum {
+    FONTGRID_MODIFIED = 0,
+    FONTGRID_GLYPH_NAMES_MODIFIED,
+    FONTGRID_NAME_MODIFIED,
+    FONTGRID_PROPERTIES_MODIFIED,
+    FONTGRID_COMMENTS_MODIFIED,
+    FONTGRID_DEVICE_WIDTH_MODIFIED,
+    FONTGRID_GLYPHS_MODIFIED,
+    FONTGRID_GLYPHS_DELETED,
+    FONTGRID_GLYPHS_PASTED,
+    FONTGRID_FONT_METRICS_MODIFIED,
+    FONTGRID_PSF_MAPPINGS_MODIFIED
+} FontgridModificationReason;
+
+typedef struct {
+    FontgridModificationReason reason;
+    gchar *name;
+    gint32 start;
+    gint32 end;
+    gboolean unencoded;
+} FontgridModificationInfo;
+
+/**************************************************************************
+ *
+ * General API
+ *
+ **************************************************************************/
+
+extern GType fontgrid_get_type(void);
+extern GtkWidget *fontgrid_new(const gchar *prop1, ...);
+extern GtkWidget *fontgrid_newv(bdf_font_t *font, guint32 pointSize,
+                                gint32 spacing, gboolean skipBlankPages,
+                                gboolean overwriteMode, gboolean powersOfTwo,
+                                guint16 *colorList,
+                                gint32 initialGlyph, guint codeBase,
+                                GtkOrientation orientation,
+                                gint32 bitsPerPixel,
+                                gint32 horizontalResolution,
+                                gint32 verticalResolution,
+                                FontgridPageInfo *initialPageInfo);
+
+/*
+ * A routine to force initialization before the widget is realized.  This is
+ * needed to get some fields filled in for apps using the widget.
+ */
+extern void fontgrid_force_init(Fontgrid *);
+
+/*
+ * Selection information.
+ */
+extern gboolean fontgrid_clipboard_empty(Fontgrid *);
+extern gboolean fontgrid_has_selection(Fontgrid *, FontgridSelectionInfo *);
+
+/*
+ * Getting and setting widget values.
+ */
+extern bdf_font_t *fontgrid_get_font(Fontgrid *);
+extern void fontgrid_set_font(Fontgrid *, bdf_font_t *, gint32);
+
+extern gchar *fontgrid_get_font_messages(Fontgrid *);
+
+extern GtkOrientation fontgrid_get_orientation(Fontgrid *);
+extern void fontgrid_set_orientation(Fontgrid *, GtkOrientation);
+
+extern gboolean fontgrid_viewing_unencoded(Fontgrid *);
+extern void fontgrid_switch_encoding_view(Fontgrid *);
+
+extern guint fontgrid_get_code_base(Fontgrid *);
+extern void fontgrid_set_code_base(Fontgrid *, guint);
+
+extern gchar *fontgrid_get_font_name(Fontgrid *);
+extern void fontgrid_set_font_name(Fontgrid *, gchar *);
+
+extern gboolean fontgrid_get_font_modified(Fontgrid *);
+extern void fontgrid_set_font_modified(Fontgrid *, gboolean);
+
+extern void fontgrid_set_unicode_glyph_names(Fontgrid *, FILE *);
+extern void fontgrid_set_adobe_glyph_names(Fontgrid *, FILE *);
+extern void fontgrid_set_code_glyph_names(Fontgrid *, gint);
+
+extern void fontgrid_set_font_property(Fontgrid *, bdf_property_t *);
+extern void fontgrid_delete_font_property(Fontgrid *, gchar *);
+
+extern guint32 fontgrid_get_font_comments(Fontgrid *, gchar **);
+extern void fontgrid_set_font_comments(Fontgrid *, gchar *);
+
+extern gint fontgrid_get_font_spacing(Fontgrid *);
+extern void fontgrid_set_font_spacing(Fontgrid *, gint);
+
+extern guint16 fontgrid_get_font_device_width(Fontgrid *);
+extern void fontgrid_set_font_device_width(Fontgrid *, guint16);
+
+extern void fontgrid_get_font_info(Fontgrid *, FontgridFontInfo *);
+extern void fontgrid_set_font_info(Fontgrid *, FontgridFontInfo *);
+
+/*
+ * Navigation and page information.
+ */
+extern void fontgrid_goto_page(Fontgrid *fw, gint32 pageno);
+extern void fontgrid_goto_code(Fontgrid *, gint32 pageno);
+extern void fontgrid_goto_first_page(Fontgrid *fw);
+extern void fontgrid_goto_last_page(Fontgrid *fw);
+extern void fontgrid_goto_next_page(Fontgrid *fw);
+extern void fontgrid_goto_previous_page(Fontgrid *fw);
+extern void fontgrid_get_page_info(Fontgrid *fw, FontgridPageInfo *pageinfo);
+extern gboolean fontgrid_select_next_glyph(Fontgrid *fw, gint32 code);
+extern gboolean fontgrid_select_previous_glyph(Fontgrid *fw, gint32 code);
+
+/*
+ * Font name functions.
+ */
+extern void fontgrid_make_xlfd_font_name(Fontgrid *);
+extern void fontgrid_update_font_name_from_properties(Fontgrid *);
+extern void fontgrid_update_properties_from_font_name(Fontgrid *);
+
+/*
+ * Graphical operations.
+ */
+extern void fontgrid_translate_glyphs(Fontgrid *fw, gint16 dx, gint16 dy,
+                                      gboolean all_glyphs);
+extern void fontgrid_rotate_glyphs(Fontgrid *fw, gint16 degrees,
+                                   gboolean all_glyphs);
+extern void fontgrid_shear_glyphs(Fontgrid *fw, gint16 degrees,
+                                  gboolean all_glyphs);
+extern void fontgrid_embolden_glyphs(Fontgrid *fw, gboolean all_glyphs);
+
+/*
+ * Clipboard operations.  MERGE and OVERLAY are the same operation.
+ */
+typedef enum {
+    FONTGRID_NORMAL_PASTE = 0,
+    FONTGRID_INSERT_PASTE,
+    FONTGRID_MERGE_PASTE,
+    FONTGRID_OVERLAY_PASTE
+} FontgridPasteType;
+
+extern void fontgrid_copy_selection(Fontgrid *fw);
+extern void fontgrid_cut_selection(Fontgrid *fw);
+extern void fontgrid_paste_selection(Fontgrid *fw,
+                                     FontgridPasteType paste_type);
+
+/*
+ * Metrics, glyph, and PSF mappings updates.
+ */
+extern void fontgrid_update_metrics(Fontgrid *fw, bdf_metrics_t *metrics);
+extern void fontgrid_update_glyph(Fontgrid *fw, bdf_glyph_t *glyph,
+                                  gboolean unencoded);
+
+extern void fontgrid_update_psf_mappings(Fontgrid *fw, gint32 encoding,
+                                         bdf_psf_unimap_t *mappings);
+G_END_DECLS
+
+#endif /* _h_fontgrid */
diff --git a/gbdfed.c b/gbdfed.c
new file mode 100644 (file)
index 0000000..d0343f7
--- /dev/null
+++ b/gbdfed.c
@@ -0,0 +1,2503 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+#include "labcon.h"
+
+/**************************************************************************
+ *
+ * Application globals.
+ *
+ **************************************************************************/
+
+gchar buffer1[BUFSIZ];
+gchar buffer2[BUFSIZ];
+gbdfed_options_t options;
+
+/*
+ * The list of editors that exist.
+ */
+gbdfed_editor_t *editors;
+guint num_editors;
+
+/**************************************************************************
+ *
+ * Forward declarations and local variables.
+ *
+ **************************************************************************/
+
+/*
+ * These are formats that can appear in the editor for importing/loading and
+ * exporting fonts.
+ */
+#define XMBDFED_BDF_FORMAT     1
+#define XMBDFED_CONSOLE_FORMAT 2
+#define XMBDFED_PKGF_FORMAT    3
+#define XMBDFED_FNT_FORMAT     4
+#define XMBDFED_HBF_FORMAT     5
+#define XMBDFED_TTF_FORMAT     6
+#define XMBDFED_PSF_FORMAT     7
+#define XMBDFED_HEX_FORMAT     8
+
+/**************************************************************************
+ *
+ * Application icons.
+ *
+ **************************************************************************/
+
+static const gchar *gbdfed_16x16[] = {
+/* width height num_colors chars_per_pixel */
+"    16    16        5            1",
+/* colors */
+". c #dcdcdc",
+"# c #ff0000",
+"a c #ffffff",
+"b c #000000",
+"c c #b2c0dc",
+/* pixels */
+"..#.............",
+"a.#.a.a.a.a.a.a.",
+"..#.............",
+"a.#bbba.a.a.a.b.",
+"..#.bbb......b..",
+"a.#.abbba.a.b.a.",
+"..#...bbb..b....",
+"a.#.a.abb.b.a.a.",
+"..#....b.bb.....",
+"a.#.a.b.abbba.a.",
+"..#..b....bbb...",
+"a.#.b.a.a.abbba.",
+"..#b........bbb.",
+"################",
+"..#.............",
+"c.#.c.c.c.c.c.c."
+};
+
+static const gchar *gbdfed_32x32[] = {
+/* width height num_colors chars_per_pixel */
+"    32    32        4            1",
+/* colors */
+". c #d9d9d9",
+"# c #ffffff",
+"a c #ff0000",
+"b c #000000",
+/* pixels */
+".##a##.##.##.##.##.##.##.##.##.#",
+".##a##.##.##.##.##.##.##.##.##.#",
+"...a............................",
+".##a##.##.##.##.##.##.##.##.##.#",
+".##a##.##.##.##.##.##.##.##.##.#",
+"...a............................",
+".##abb.bb.bb.##.##.##.##.##.bb.#",
+".##abb.bb.bb.##.##.##.##.##.bb.#",
+"...a............................",
+".##a##.bb.bb.bb.##.##.##.bb.##.#",
+".##a##.bb.bb.bb.##.##.##.bb.##.#",
+"...a............................",
+".##a##.##.bb.bb.bb.##.bb.##.##.#",
+".##a##.##.bb.bb.bb.##.bb.##.##.#",
+"...a............................",
+".##a##.##.##.bb.##.bb.##.##.##.#",
+".##a##.##.##.bb.##.bb.##.##.##.#",
+"...a............................",
+".##a##.##.bb.##.bb.bb.bb.##.##.#",
+".##a##.##.bb.##.bb.bb.bb.##.##.#",
+"...a............................",
+".##a##.bb.##.##.##.bb.bb.bb.##.#",
+".##a##.bb.##.##.##.bb.bb.bb.##.#",
+"...a............................",
+".##abb.##.##.##.##.##.bb.bb.bb.#",
+".##abb.##.##.##.##.##.bb.bb.bb.#",
+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+".##a##.##.##.##.##.##.##.##.##.#",
+".##a##.##.##.##.##.##.##.##.##.#",
+"...a............................",
+".##a##.##.##.##.##.##.##.##.##.#",
+".##a##.##.##.##.##.##.##.##.##.#"
+};
+
+static const gchar *gbdfed_48x48[] = {
+/* width height num_colors chars_per_pixel */
+"    48    48        4            1",
+/* colors */
+". c #dcdcdc",
+"# c #ff0000",
+"a c #ffffff",
+"b c #000000",
+/* pixels */
+"....#...........................................",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa..aa.aaa.aaa",
+"....#...........................................",
+".aaa#bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb",
+".aaa#bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb",
+".aaa#bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb",
+"....#...........................................",
+".aaa#aaa.bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.bbb.aaa",
+".aaa#aaa.bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.bbb.aaa",
+".aaa#aaa.bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.bbb.aaa",
+"....#...........................................",
+".aaa#aaa.aaa.bbb.bbb.bbb.aaa.aaa.aaa.bbb.aaa.aaa",
+".aaa#aaa.aaa.bbb.bbb.bbb.aaa.aaa.aaa.bbb.aaa.aaa",
+".aaa#aaa.aaa.bbb.bbb.bbb.aaa.aaa.aaa.bbb.aaa.aaa",
+"....#...........................................",
+".aaa#aaa.aaa.aaa.bbb.bbb.bbb.aaa.bbb.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.bbb.bbb.bbb.aaa.bbb.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.bbb.bbb.bbb.aaa.bbb.aaa.aaa.aaa",
+"....#...........................................",
+".aaa#aaa.aaa.aaa.aaa.bbb.aaa.bbb.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.bbb.aaa.bbb.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.bbb.aaa.bbb.aaa.aaa.aaa.aaa",
+"....#...........................................",
+".aaa#aaa.aaa.aaa.bbb.aaa.bbb.bbb.bbb.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.bbb.aaa.bbb.bbb.bbb.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.bbb.aaa.bbb.bbb.bbb.aaa.aaa.aaa",
+"....#...........................................",
+".aaa#aaa.aaa.bbb.aaa.aaa.aaa.bbb.bbb.bbb.aaa.aaa",
+".aaa#aaa.aaa.bbb.aaa.aaa.aaa.bbb.bbb.bbb.aaa.aaa",
+".aaa#aaa.aaa.bbb.aaa.aaa.aaa.bbb.bbb.bbb.aaa.aaa",
+"....#...........................................",
+".aaa#aaa.bbb.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb.aaa",
+".aaa#aaa.bbb.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb.aaa",
+".aaa#aaa.bbb.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb.aaa",
+"....#...........................................",
+".aaa#bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb",
+".aaa#bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb",
+".aaa#bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb",
+"################################################",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+"....#...........................................",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa",
+".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa"
+};
+
+static gboolean icons_set = FALSE;
+
+/**************************************************************************
+ *
+ * GTK signal handlers.
+ *
+ **************************************************************************/
+
+static gboolean
+quit_application(GtkWidget *w, GdkEvent *ev, gpointer data)
+{
+    guint i, modified;
+
+    for (i = modified = 0; i < num_editors; i++) {
+        if (fontgrid_get_font_modified(FONTGRID(editors[i].fgrid)))
+          modified++;
+    }
+
+    if (modified) {
+        if (modified == 1)
+          sprintf(buffer1, "Save Font: One font was modified.  Save?");
+        else
+          sprintf(buffer1, "Save Font: %d fonts were modified.  Save?",
+                  modified);
+        if (guiutil_yes_or_no(editors[0].shell, buffer1, TRUE)) {
+
+            /*
+             * Go through each editor and ask if the font should be saved if
+             * it has been modified.
+             */
+            for (i = 0; i < num_editors; i++) {
+                if (fontgrid_get_font_modified(FONTGRID(editors[i].fgrid))) {
+                    /*
+                     * Ask if this font should be saved.
+                     */
+                    if (editors[i].file)
+                      sprintf(buffer1, "Save Font: Save %s?", editors[i].file);
+                    else
+                      sprintf(buffer1, "Save Font: Save unnamed%d.bdf?", i);
+
+                    /*
+                     * Always ask this question using the shell window of the
+                     * first editor so the dialog box doesn't move around.
+                     */
+                    if (guiutil_yes_or_no(editors[0].shell, buffer1, TRUE))
+                      guifile_save_as_wait(w, GUINT_TO_POINTER(i));
+                }
+            }
+        }
+    }
+
+    /*
+     * Ask if the user really wants to exit if their preferences specify this
+     * question should be asked.
+     */
+    if (options.really_exit &&
+        !guiutil_yes_or_no(editors[0].shell, "Really Quit?", TRUE))
+      return TRUE;
+
+    /*
+     * Call all the cleanup routines in case something really needs to be
+     * deallocated.
+     */
+    guigedit_cleanup();
+    guiedit_preference_cleanup();
+    guiutil_cursor_cleanup();
+    guihelp_cleanup();
+
+    bdf_cleanup();
+    gtk_main_quit();
+    exit(0);
+}
+
+static void
+show_editor(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    /*
+     * Nothing seems to force the original window to the top of the stack
+     * on the screen, but this supposedly does everything necessary.
+     */
+    gtk_widget_show_all(ed->shell);
+    gtk_window_present(GTK_WINDOW(ed->shell));
+}
+
+static void
+goto_other_page(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    gint32 opage;
+    FontgridPageInfo pi;
+
+    fontgrid_get_page_info(FONTGRID(ed->fgrid), &pi);
+
+    if (!pi.unencoded_page) {
+        opage = ed->last_pageno;
+        ed->last_pageno = pi.current_page;
+    } else {
+        opage = ed->last_upageno;
+        ed->last_upageno = pi.current_page;
+    }
+
+    if (opage != -1 && opage != pi.current_page)
+      fontgrid_goto_page(FONTGRID(ed->fgrid), opage);
+}
+
+static void
+toggle_encoding_view(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    gchar *label;
+    GtkLabel *lw;
+
+    if (fontgrid_viewing_unencoded(FONTGRID(ed->fgrid)))
+      label = "Unencoded";
+    else
+      label = "Encoded";
+    lw = GTK_LABEL(GTK_BIN(ed->view_unencoded)->child);
+    gtk_label_set_text(lw, label);
+
+    fontgrid_switch_encoding_view(FONTGRID(ed->fgrid));
+}
+
+static void
+toggle_view_orientation(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    gchar *label;
+    GtkLabel *lw;
+
+    if (fontgrid_get_orientation(FONTGRID(ed->fgrid)) ==
+        GTK_ORIENTATION_VERTICAL) {
+        fontgrid_set_orientation(FONTGRID(ed->fgrid),
+                                 GTK_ORIENTATION_HORIZONTAL);
+        label = "Vertical View";
+    } else {
+        fontgrid_set_orientation(FONTGRID(ed->fgrid),
+                                 GTK_ORIENTATION_VERTICAL);
+        label = "Horizontal View";
+    }
+    lw = GTK_LABEL(GTK_BIN(ed->view_orientation)->child);
+    gtk_label_set_text(lw, label);
+}
+
+static void
+set_code_base(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    gint base;
+
+    if (w == ed->view_oct)
+      base = 8;
+    else if (w == ed->view_dec)
+      base = 10;
+    else
+      base = 16;
+
+    fontgrid_set_code_base(FONTGRID(ed->fgrid), base);
+
+    /*
+     * Make sure the font info editor is updated when the code base
+     * changes.
+     */
+    guiedit_update_code_base(ed);
+    guigedit_set_code_base(base);
+}
+
+static void
+page_change(GtkWidget *w, gpointer pinfo, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    FontgridPageInfo *pi = (FontgridPageInfo *) pinfo;
+    gchar *label;
+    GtkLabel *lw;
+
+    if (pi->previous_page < 0) {
+        gtk_widget_set_sensitive(ed->prev, FALSE);
+        gtk_widget_set_sensitive(ed->first, FALSE);
+    } else {
+        gtk_widget_set_sensitive(ed->prev, TRUE);
+        gtk_widget_set_sensitive(ed->first, TRUE);
+    }
+
+    if (pi->next_page < 0) {
+        gtk_widget_set_sensitive(ed->next, FALSE);
+        gtk_widget_set_sensitive(ed->last, FALSE);
+    } else {
+        gtk_widget_set_sensitive(ed->next, TRUE);
+        gtk_widget_set_sensitive(ed->last, TRUE);
+    }
+
+    /*
+     * Update the page number field with the current page.
+     */
+    sprintf(buffer1, "%d", pi->current_page);
+    gtk_entry_set_text(GTK_ENTRY(ed->pageno), buffer1);
+    gtk_editable_set_position(GTK_EDITABLE(ed->pageno), -1);
+
+    /*
+     * Finally, modify the label on the Encoded/Unencoded view menu item.
+     */
+    label = (pi->unencoded_page) ? "Encoded" : "Unencoded";
+    lw = GTK_LABEL(GTK_BIN(ed->view_unencoded)->child);
+    gtk_label_set_text(lw, label);
+}
+
+static void
+first_page(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+
+    fontgrid_goto_first_page(FONTGRID(ed->fgrid));
+
+    /*
+     * Force the focus back to the Fontgrid.
+     */
+    gtk_widget_grab_focus(ed->fgrid);
+}
+
+static void
+previous_page(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+
+    fontgrid_goto_previous_page(FONTGRID(ed->fgrid));
+
+    /*
+     * Force the focus back to the Fontgrid.
+     */
+    gtk_widget_grab_focus(ed->fgrid);
+}
+
+static void
+next_page(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+
+    fontgrid_goto_next_page(FONTGRID(ed->fgrid));
+
+    /*
+     * Force the focus back to the Fontgrid.
+     */
+    gtk_widget_grab_focus(ed->fgrid);
+}
+
+static void
+last_page(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+
+    fontgrid_goto_last_page(FONTGRID(ed->fgrid));
+
+    /*
+     * Force the focus back to the Fontgrid.
+     */
+    gtk_widget_grab_focus(ed->fgrid);
+}
+
+static void
+goto_page_or_code(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    const gchar *text;
+    gint32 code;
+    FontgridPageInfo pi;
+
+    fontgrid_get_page_info(FONTGRID(ed->fgrid), &pi);
+
+    if (!pi.unencoded_page)
+      ed->last_pageno = pi.current_page;
+    else
+      ed->last_upageno = pi.current_page;
+
+    if (w == ed->pageno) {
+        text = gtk_entry_get_text(GTK_ENTRY(ed->pageno));
+        fontgrid_goto_page(FONTGRID(ed->fgrid),
+                           _bdf_atol((char *) text, 0, 10));
+    } else {
+        text = gtk_entry_get_text(GTK_ENTRY(ed->charno));
+        code = _bdf_atol((char *) text, 0,
+                         fontgrid_get_code_base(FONTGRID(ed->fgrid)));
+        fontgrid_goto_code(FONTGRID(ed->fgrid), code);
+    }
+}
+
+static void
+update_selection_info(GtkWidget *w, gpointer sinfo, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    FontgridSelectionInfo *si = (FontgridSelectionInfo *) sinfo;
+    short as, ds, rt, lt;
+    gint32 start, end;
+    guint b1 = 0, b2 = 0, b3, b4;
+
+    as = ds = rt = lt = 0;
+    if (si->start == si->end) {
+        if (si->num_glyphs != 0 && si->glyphs != 0) {
+            b1 = (si->start >> 8) & 0xff;
+            b2 = si->start & 0xff;
+
+            as = si->glyphs->bbx.ascent;
+            ds = si->glyphs->bbx.descent;
+            lt = si->glyphs->bbx.x_offset;
+            rt = si->glyphs->bbx.width + lt;
+            if (si->glyphs->name != 0)
+              (void) strcpy(buffer1, si->glyphs->name);
+            else
+              sprintf(buffer1, "char%d", si->glyphs->encoding);
+
+            /*
+             * If the glyph test dialog is active, send it the glyph if this
+             * is an end selection event.
+             */
+            if (si->reason == FONTGRID_END_SELECTION && glyphtest != 0)
+              glyphtest_add_glyph(GLYPHTEST(glyphtest),
+                                  fontgrid_get_font(FONTGRID(ed->fgrid)),
+                                  si->glyphs);
+        } else
+          sprintf(buffer1, "char%d", si->start);
+
+        switch (si->base) {
+          case 8:
+            sprintf(buffer2, "\"%s\" %o (%o, %o)", buffer1,
+                    si->start, b1, b2);
+            break;
+          case 10:
+            sprintf(buffer2, "\"%s\" %d (%d, %d)", buffer1,
+                    si->start, b1, b2);
+            break;
+          case 16:
+            sprintf(buffer2, "\"%s\" %04X (%02X, %02X)", buffer1,
+                    si->start, b1, b2);
+            break;
+        }
+
+    } else {
+        /*
+         * A range of glyphs has been selected.
+         */
+        if (si->end < si->start) {
+            start = si->end;
+            end = si->start;
+        } else {
+            start = si->start;
+            end = si->end;
+        }
+        b1 = (start >> 8) & 0xff;
+        b2 = start & 0xff;
+        b3 = (end >> 8) & 0xff;
+        b4 = end & 0xff;
+
+        switch (si->base) {
+          case 8:
+            sprintf(buffer2, "Selection %o (%o, %o) - %o (%o, %o)",
+                    start, b1, b2, end, b3, b4);
+            break;
+          case 10:
+            sprintf(buffer2, "Selection %d (%d, %d) - %d (%d, %d)",
+                    start, b1, b2, end, b3, b4);
+            break;
+          case 16:
+            sprintf(buffer2, "Selection %04X (%02X, %02X) - %04X (%02X, %02X)",
+                    start, b1, b2, end, b3, b4);
+            break;
+        }
+    }
+
+    /*
+     * Update the glyph info label.
+     */
+    gtk_label_set_text(GTK_LABEL(ed->charinfo), buffer2);
+
+    /*
+     * Update the metrics label.
+     */
+    sprintf(buffer1, "ascent %hd descent %hd right %hd left %hd",
+            as, ds, rt, lt);
+    gtk_label_set_text(GTK_LABEL(ed->metrics), buffer1);
+
+    switch (si->reason) {
+      case FONTGRID_START_SELECTION:
+        break;
+      case FONTGRID_EXTEND_SELECTION:
+        break;
+      case FONTGRID_END_SELECTION:
+        break;
+      case FONTGRID_ACTIVATE:
+        guigedit_edit_glyph(ed, si);
+        break;
+      case FONTGRID_BASE_CHANGE:
+        break;
+    }
+}
+
+static void
+handle_modified_signal(GtkWidget *w, gpointer minfo, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    gchar *prgname = g_get_prgname();
+    FontgridModificationInfo *mi;
+
+    mi = (FontgridModificationInfo *) minfo;
+
+    /*
+     * Check to see what change was made so updates can be handled
+     * appropriately.
+     */
+    if (mi->reason == FONTGRID_NAME_MODIFIED)
+      gtk_entry_set_text(GTK_ENTRY(ed->fontname), mi->name);
+    else if (mi->reason == FONTGRID_PROPERTIES_MODIFIED)
+      /*
+       * Make sure the font info editing dialog knows that the list of
+       * font properties changed.
+       */
+      guiedit_update_font_properties(ed);
+    else if (mi->reason == FONTGRID_GLYPHS_DELETED ||
+             mi->reason == FONTGRID_GLYPHS_PASTED)
+      guiedit_update_font_details(ed);
+    else if ((mi->reason == FONTGRID_DEVICE_WIDTH_MODIFIED ||
+              mi->reason == FONTGRID_GLYPHS_MODIFIED) && glyphtest != 0)
+      /*
+       * Update the glyph test widget with the new device width.
+       */
+      glyphtest_update_device_width(GLYPHTEST(glyphtest),
+                                    fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+    /*
+     * Update the title.
+     */
+    if (ed->file == 0)
+      sprintf(buffer1, "%s - (unnamed%d) [modified]", prgname, ed->id);
+    else
+      sprintf(buffer1, "%s - %s [modified]", prgname, ed->file);
+
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+}
+
+static void
+view_font_messages(GtkWidget *w, gpointer editor)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor);
+    GtkWidget *dialog, *frame, *button, *label, *vbox, *sw;
+    GtkTextBuffer *text;
+    gchar *msgs;
+
+    if (ed->messages_dialog == 0) {
+        /*
+         * Special case of no messages and the menu item hasn't been
+         * checked for sensitivity yet.
+         */
+        if (fontgrid_get_font_messages(FONTGRID(ed->fgrid)) == 0)
+          return;
+
+        dialog = ed->messages_dialog = gtk_dialog_new();
+        (void) g_signal_connect(G_OBJECT(dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        frame = gtk_frame_new(0);
+        label = ed->messages_label = gtk_label_new("");
+        gtk_container_add(GTK_CONTAINER(frame), label);
+
+        vbox = GTK_DIALOG(dialog)->vbox;
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+        sw = gtk_scrolled_window_new(NULL, NULL);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_ALWAYS);
+        gtk_box_pack_start(GTK_BOX(vbox), sw, FALSE, FALSE, 0);
+
+        /*
+         * Add the text widget.
+         */
+        text = gtk_text_buffer_new(NULL);
+        ed->messages_text = gtk_text_view_new_with_buffer(text);
+        gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ed->messages_text),
+                                         FALSE);
+        gtk_widget_set_size_request(ed->messages_text, 500, 200);
+
+        gtk_text_view_set_editable(GTK_TEXT_VIEW(ed->messages_text), FALSE);
+        gtk_container_add(GTK_CONTAINER(sw), ed->messages_text);
+
+        button = gtk_button_new_with_label("Close");
+        (void) g_signal_connect_object(G_OBJECT(button), "clicked",
+                                       G_CALLBACK(gtk_widget_hide),
+                                       (gpointer) dialog,
+                                       G_CONNECT_SWAPPED);
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+                          button);
+
+        gtk_widget_show_all(GTK_DIALOG(ed->messages_dialog)->vbox);
+        gtk_widget_show_all(GTK_DIALOG(ed->messages_dialog)->action_area);
+    } else
+      text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->messages_text));
+
+    /*
+     * Update the label with the font name in case it changed since the last
+     * time the messages were shown.
+     */
+    if (ed->file != 0)
+      sprintf(buffer1, "%s: Messages", ed->file);
+    else
+      sprintf(buffer1, "unnamed%d.bdf: Messages", ed->id);
+    gtk_label_set_text(GTK_LABEL(ed->messages_label), buffer1);
+
+    /*
+     * Now change the text itself.
+     */
+    if ((msgs = fontgrid_get_font_messages(FONTGRID(ed->fgrid))) == 0)
+      msgs = "";
+    gtk_text_buffer_set_text(text, msgs, -1);
+
+    guiutil_show_dialog_centered(ed->messages_dialog, ed->shell);
+}
+
+/**************************************************************************
+ *
+ * Menu construction.
+ *
+ **************************************************************************/
+
+static GtkWidget *
+make_accel_menu_item(GtkWidget *menu, const gchar *text, const gchar *accel,
+                     GtkAccelGroup *ag)
+{
+    GtkWidget *mi;
+    guint key;
+    GdkModifierType mods;
+
+    mi = gtk_menu_item_new_with_mnemonic(text);
+
+    gtk_accelerator_parse(accel, &key, &mods);
+    gtk_widget_add_accelerator(mi, "activate", ag, key, mods,
+                               GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
+
+    return mi;
+}
+
+/*
+ * Handle all the menu checking needed to enable or disable menu items on
+ * all the menus.
+ */
+static gint
+menu_popup(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+    gbdfed_editor_t *ed;
+    bdf_font_t *font;
+    GtkWidget *mitem;
+    GList *kids, *label;
+    gboolean flag;
+    guint i;
+
+    /*
+     * Get a pointer to the editor.
+     */
+    ed = editors + GPOINTER_TO_UINT(data);
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    if (event->type == GDK_MAP) {
+        if (w == ed->file_menu) {
+            if (font && font->modified)
+              gtk_widget_set_sensitive(ed->file_save, TRUE);
+            else
+              gtk_widget_set_sensitive(ed->file_save, FALSE);
+
+            if (font && font->glyphs_used > 0)
+              gtk_widget_set_sensitive(ed->file_export, TRUE);
+            else
+              gtk_widget_set_sensitive(ed->file_export, FALSE);
+        } else if (w == ed->edit_menu) {
+            /*
+             * If the fontgrid clipboard is empty, then disable the paste,
+             * overlay, and insert menu options.
+             */
+            flag = fontgrid_clipboard_empty(FONTGRID(ed->fgrid)) ?
+                FALSE : TRUE;
+            gtk_widget_set_sensitive(ed->edit_paste, flag);
+            gtk_widget_set_sensitive(ed->edit_overlay, flag);
+            gtk_widget_set_sensitive(ed->edit_insert, flag);
+
+            /*
+             * If there is no selection, disable the cut and copy menu
+             * options.
+             */
+            flag = fontgrid_has_selection(FONTGRID(ed->fgrid), 0) ?
+                TRUE : FALSE;
+            gtk_widget_set_sensitive(ed->edit_cut, flag);
+            gtk_widget_set_sensitive(ed->edit_copy, flag);
+
+            flag = (font && font->glyphs_used > 0) ? TRUE : FALSE;
+
+            /*
+             * Disable glyph rename when viewing unecoded glyphs because their
+             * encoding values are not actual.
+             */
+            if (fontgrid_viewing_unencoded(FONTGRID(ed->fgrid)) || !flag)
+              gtk_widget_set_sensitive(ed->edit_rename_glyphs, FALSE);
+            else
+              gtk_widget_set_sensitive(ed->edit_rename_glyphs, TRUE);
+
+            gtk_widget_set_sensitive(ed->edit_test_glyphs, flag);
+        } else if (w == ed->edit_rename_menu) {
+            if (options.adobe_name_file != 0)
+              gtk_widget_set_sensitive(ed->edit_adobe_names, TRUE);
+            else
+              gtk_widget_set_sensitive(ed->edit_adobe_names, FALSE);
+            if (options.unicode_name_file != 0)
+              gtk_widget_set_sensitive(ed->edit_unicode_names, TRUE);
+            else
+              gtk_widget_set_sensitive(ed->edit_unicode_names, FALSE);
+        } else if (w == ed->name_submenu) {
+            flag = (!font || bdf_has_xlfd_name(font)) ? FALSE : TRUE;
+            gtk_widget_set_sensitive(ed->edit_make_xlfd, flag);
+        } else if (w == ed->view_menu) {
+            if (fontgrid_get_font_messages(FONTGRID(ed->fgrid)))
+              gtk_widget_set_sensitive(ed->view_messages, TRUE);
+            else
+              gtk_widget_set_sensitive(ed->view_messages, FALSE);
+        } else if (w == ed->win_menu) {
+            /*
+             * Go through and update the file names that might have changed
+             * since the last time this menu popped up.
+             */
+            for (i = 0, kids = GTK_MENU(w)->menu_shell.children; kids != 0;
+                 kids = kids->next, i++) {
+                if (editors[i].file == 0)
+                  sprintf(buffer1, "(unnamed%d)", i);
+                else
+                  strcpy(buffer1, editors[i].file);
+                label = gtk_container_get_children(GTK_CONTAINER(kids->data));
+                gtk_label_set_text(GTK_LABEL(label->data), buffer1);
+            } 
+
+            /*
+             * Add any new editors that were created since the last time this
+             * menu was shown.
+             */
+            for (; i < num_editors; i++) {
+                if (editors[i].file == 0)
+                  sprintf(buffer1, "(unnamed%d)", editors[i].id);
+                else
+                  strcpy(buffer1, editors[i].file);
+
+                mitem = gtk_menu_item_new_with_label(buffer1);
+                (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                                        G_CALLBACK(show_editor),
+                                        GUINT_TO_POINTER(i));
+                gtk_menu_shell_append(GTK_MENU_SHELL(ed->win_menu), mitem);
+                gtk_widget_show(mitem);
+            }
+
+            /*
+             * Disable the item for this editor.
+             */
+            gtk_widget_set_sensitive(ed->win_menu_item, FALSE);
+        }
+    } else if (event->type == GDK_UNMAP) {
+        /*
+         * Enable everything again, so it doesn't get forgotten.
+         */
+        gtk_widget_set_sensitive(ed->file_save, TRUE);
+        gtk_widget_set_sensitive(ed->file_export, TRUE);
+        gtk_widget_set_sensitive(ed->edit_paste, TRUE);
+        gtk_widget_set_sensitive(ed->edit_overlay, TRUE);
+        gtk_widget_set_sensitive(ed->edit_insert, TRUE);
+        gtk_widget_set_sensitive(ed->edit_cut, TRUE);
+        gtk_widget_set_sensitive(ed->edit_copy, TRUE);
+        gtk_widget_set_sensitive(ed->edit_rename_glyphs, TRUE);
+        gtk_widget_set_sensitive(ed->edit_adobe_names, TRUE);
+        gtk_widget_set_sensitive(ed->edit_unicode_names, TRUE);
+        gtk_widget_set_sensitive(ed->edit_test_glyphs, TRUE);
+        gtk_widget_set_sensitive(ed->edit_make_xlfd, TRUE);
+        gtk_widget_set_sensitive(ed->win_menu_item, TRUE);
+        gtk_widget_set_sensitive(ed->view_messages, TRUE);
+    }
+
+    return FALSE;
+}
+
+#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10)
+static void
+open_recent_font(GtkWidget *w, gpointer data)
+{
+    gchar *p, *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(w));
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (uri == NULL)
+      return;
+
+    /*
+     * Skip the URI prefix to get to the path. The URI's are strictly local
+     * at the moment.
+     */
+    for (p = uri; *p && *p != ':'; p++);
+    if (*p != ':')
+      return;
+    p++;
+    if (*p == '/' && *(p + 1) == '/')
+      p += 2;
+    guifile_load_bdf_font(ed, (const gchar *) p);
+    g_free(uri);
+}
+
+static GtkWidget *
+make_recent_menu(guint ed_id)
+{
+    GtkWidget *menu;
+    GtkRecentFilter *filter;
+    GtkRecentManager *manager;
+
+    manager = gtk_recent_manager_get_default();
+    menu = gtk_recent_chooser_menu_new_for_manager(manager);
+    gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(menu), 10);
+    gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(menu),
+                                     GTK_RECENT_SORT_MRU);
+    gtk_recent_chooser_set_show_tips(GTK_RECENT_CHOOSER(menu), TRUE);
+    gtk_recent_chooser_menu_set_show_numbers(GTK_RECENT_CHOOSER_MENU(menu),
+                                             TRUE);
+    gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(menu), TRUE);
+    filter = gtk_recent_filter_new();
+    gtk_recent_filter_add_pattern(filter, "*.[Bb][Dd][Ff]");
+    gtk_recent_chooser_set_filter(GTK_RECENT_CHOOSER(menu), filter);
+
+    (void) g_signal_connect(G_OBJECT(menu), "item_activated",
+                            G_CALLBACK(open_recent_font),
+                            GUINT_TO_POINTER(ed_id));
+
+    return menu;
+}
+#endif
+
+static GtkWidget *
+make_file_menu(gbdfed_editor_t *ed, GtkWidget *menubar)
+{
+    GtkWidget *file, *menu, *submenu, *mitem, *sep;
+
+    /*
+     * Create the File menu.
+     */
+    file = gtk_menu_item_new_with_mnemonic("_File");
+
+    ed->file_menu = menu = gtk_menu_new();
+    (void) g_signal_connect(G_OBJECT(menu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "_New", "<Control>N", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_new_editor), 0);
+
+    mitem = make_accel_menu_item(menu, "_Open", "<Control>O", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_bdf_font),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->file_save = make_accel_menu_item(menu, "_Save", "<Control>S", ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->file_save), "activate",
+                            G_CALLBACK(guifile_save),
+                            GUINT_TO_POINTER(ed->id));
+    mitem = make_accel_menu_item(menu, "Save _As...", "<Control>W", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_save_as),
+                            GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    /*
+     * Create the Import and Export menu items with their submenus.
+     */
+    mitem = gtk_menu_item_new_with_mnemonic("_Import");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+
+    submenu = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), submenu);
+
+    mitem = make_accel_menu_item(submenu, "_PK/GF Font", "<Control>K", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_pkgf_font),
+                            GUINT_TO_POINTER(ed->id));
+    mitem = make_accel_menu_item(submenu, "_Console Font", "<Control>L",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_console_font),
+                            GUINT_TO_POINTER(ed->id));
+#ifdef HAVE_HBF
+    mitem = make_accel_menu_item(submenu, "_HBF Font", "<Control>H",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_hbf_font),
+                            GUINT_TO_POINTER(ed->id));
+#endif
+
+    mitem = make_accel_menu_item(submenu, "_Windows Font", "<Control>B",
+                                 ed->ag);
+
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_windows_font),
+                            GUINT_TO_POINTER(ed->id));
+#ifdef HAVE_FREETYPE
+    mitem = make_accel_menu_item(submenu, "_OpenType Font", "<Control>Y",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_otf_font),
+                            GUINT_TO_POINTER(ed->id));
+#endif /* HAVE_FREETYPE */
+
+#ifdef HAVE_XLIB
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), sep);
+
+    mitem = make_accel_menu_item(submenu, "_X Server Font", "<Control>G",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_import_xserver_font),
+                            GUINT_TO_POINTER(ed->id));
+#endif
+
+    ed->file_export = gtk_menu_item_new_with_mnemonic("Ex_port");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), ed->file_export);
+
+    submenu = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(ed->file_export), submenu);
+
+    mitem = make_accel_menu_item(submenu, "_PSF Font", "<Control>F",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_export_psf_font),
+                            GUINT_TO_POINTER(ed->id));
+    mitem = gtk_menu_item_new_with_mnemonic("_HEX");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guifile_export_hex_font),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+#if (GTK_MAJOR_VERSION >=2 && GTK_MINOR_VERSION >= 10)
+    /*
+     * Only add the Recent Fonts menu if the GTK version supports it.
+     */
+    mitem = gtk_menu_item_new_with_mnemonic("_Recent Fonts");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), make_recent_menu(ed->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+#endif
+
+    if (ed->id == 0) {
+        mitem = make_accel_menu_item(menu, "E_xit", "<Control>F4", ed->ag);
+
+        (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                                G_CALLBACK(quit_application),
+                                GUINT_TO_POINTER(ed->id));
+    } else {
+        mitem = make_accel_menu_item(menu, "Clos_e", "<Control>F4", ed->ag);
+
+        (void) g_signal_connect_object(G_OBJECT(mitem), "activate",
+                                       G_CALLBACK(gtk_widget_hide),
+                                       (gpointer) ed->shell,
+                                       G_CONNECT_SWAPPED);
+    }
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), menu);
+
+    return file;
+}
+
+static void
+make_xlfd_name(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_make_xlfd_font_name(FONTGRID(ed->fgrid));
+}
+
+static void
+update_name_from_props(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_update_font_name_from_properties(FONTGRID(ed->fgrid));
+}
+
+static void
+update_props_from_name(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_update_properties_from_font_name(FONTGRID(ed->fgrid));
+}
+
+static GtkWidget *
+make_edit_menu(gbdfed_editor_t *ed, GtkWidget *menubar)
+{
+    GtkWidget *edit, *menu, *submenu, *mitem, *sep;
+
+    /*
+     * Create the Edit menu.
+     */
+    edit = gtk_menu_item_new_with_mnemonic("_Edit");
+
+    ed->edit_menu = menu = gtk_menu_new();
+    (void) g_signal_connect(G_OBJECT(menu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->edit_copy = make_accel_menu_item(menu, "_Copy", "<Control>C", ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->edit_copy), "activate",
+                            G_CALLBACK(guiedit_copy_selection),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->edit_cut = make_accel_menu_item(menu, "C_ut", "<Control>X", ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->edit_cut), "activate",
+                            G_CALLBACK(guiedit_cut_selection),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->edit_paste = make_accel_menu_item(menu, "_Paste", "<Control>V",
+                                          ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->edit_paste), "activate",
+                            G_CALLBACK(guiedit_paste_selection),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->edit_overlay = make_accel_menu_item(menu, "_Overlay",
+                                            "<Shift><Control>V", ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->edit_overlay), "activate",
+                            G_CALLBACK(guiedit_overlay_selection),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->edit_insert = make_accel_menu_item(menu, "_Insert",
+                                           "<Control><Alt>V", ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->edit_insert), "activate",
+                            G_CALLBACK(guiedit_insert_selection),
+                            GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = make_accel_menu_item(menu, "P_roperties", "<Control>P", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_show_font_properties),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "Co_mments", "<Control>M", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_show_font_comments),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "_Font Info", "<Control>I", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                              G_CALLBACK(guiedit_show_font_info),
+                              GUINT_TO_POINTER(ed->id));
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    /*
+     * Create the Font Name submenu.
+     */
+    mitem = gtk_menu_item_new_with_mnemonic("Font _Name");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+
+    ed->name_submenu = submenu = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), submenu);
+    (void) g_signal_connect(G_OBJECT(submenu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->edit_make_xlfd = gtk_menu_item_new_with_mnemonic("Make _XLFD Name");
+    (void) g_signal_connect(G_OBJECT(ed->edit_make_xlfd), "activate",
+                            G_CALLBACK(make_xlfd_name),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), ed->edit_make_xlfd);
+
+    mitem = gtk_menu_item_new_with_mnemonic("Update _Name From Properties");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(update_name_from_props),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    mitem = gtk_menu_item_new_with_mnemonic("Update _Properties From Name");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(update_props_from_name),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+    mitem = gtk_menu_item_new_with_mnemonic("Update _Average Width");
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    /*
+     * Make the glyph naming menus and submenus.
+     */
+    ed->edit_rename_glyphs = gtk_menu_item_new_with_mnemonic("Ren_ame Glyphs");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), ed->edit_rename_glyphs);
+
+    ed->edit_rename_menu = submenu = gtk_menu_new();
+    (void) g_signal_connect(G_OBJECT(submenu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(ed->edit_rename_glyphs), submenu);
+
+    ed->edit_unicode_names = gtk_menu_item_new_with_mnemonic("_Unicode Names");
+    (void) g_signal_connect(G_OBJECT(ed->edit_unicode_names), "activate",
+                            G_CALLBACK(guiedit_set_unicode_glyph_names),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), ed->edit_unicode_names);
+
+    ed->edit_adobe_names = gtk_menu_item_new_with_mnemonic("_Adobe Names");
+    (void) g_signal_connect(G_OBJECT(ed->edit_adobe_names), "activate",
+                            G_CALLBACK(guiedit_set_adobe_glyph_names),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), ed->edit_adobe_names);
+
+    mitem = gtk_menu_item_new_with_mnemonic("_Hexadecimal Values");
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+    submenu = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), submenu);
+
+    mitem = gtk_menu_item_new_with_mnemonic("_uniXXXX");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_set_uni_glyph_names),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    mitem = gtk_menu_item_new_with_mnemonic("0_xXXXX");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_set_zerox_glyph_names),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    mitem = gtk_menu_item_new_with_mnemonic("U_+XXXX");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_set_uplus_glyph_names),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    mitem = gtk_menu_item_new_with_mnemonic("_\\uXXXX");
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_set_bslashu_glyph_names),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+
+    ed->edit_test_glyphs = make_accel_menu_item(menu, "_Test Glyphs",
+                                                "<Control>Z", ed->ag);
+    (void) g_signal_connect(G_OBJECT(ed->edit_test_glyphs), "activate",
+                            G_CALLBACK(guiedit_show_glyphtest),
+                            GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = make_accel_menu_item(menu, "Pr_eferences", "<Control>T", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiedit_show_preferences), 0);
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit), menu);
+
+    return edit;
+}
+
+static GtkWidget *
+make_view_menu(gbdfed_editor_t *ed, GtkWidget *menubar)
+{
+    GtkWidget *view, *menu, *submenu, *mitem, *sep;
+    GSList *group;
+    GtkRadioMenuItem *ri;
+
+    /*
+     * Create the View menu.
+     */
+    view = gtk_menu_item_new_with_mnemonic("_View");
+
+    ed->view_menu = menu = gtk_menu_new();
+    (void) g_signal_connect(G_OBJECT(menu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->view_unencoded = mitem =
+        make_accel_menu_item(menu, "Unencoded", "<Control>E", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(toggle_encoding_view),
+                            GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Create the Code Base submenu.
+     */
+    mitem = gtk_menu_item_new_with_mnemonic("_Code Base");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+
+    submenu = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), submenu);
+
+    ed->view_oct = mitem = gtk_radio_menu_item_new_with_mnemonic(0, "_Octal");
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+    ri = GTK_RADIO_MENU_ITEM(mitem);
+    if (options.code_base == 8)
+      gtk_check_menu_item_set_active(&ri->check_menu_item, TRUE);
+
+    group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(mitem));
+    ed->view_dec = mitem =
+        gtk_radio_menu_item_new_with_mnemonic(group, "_Decimal");
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+    ri = GTK_RADIO_MENU_ITEM(mitem);
+    if (options.code_base == 10)
+      gtk_check_menu_item_set_active(&ri->check_menu_item, TRUE);
+
+    group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(mitem));
+    ed->view_hex = mitem =
+        gtk_radio_menu_item_new_with_mnemonic(group, "_Hexadecimal");
+    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), mitem);
+    ri = GTK_RADIO_MENU_ITEM(mitem);
+    if (options.code_base == 16)
+      gtk_check_menu_item_set_active(&ri->check_menu_item, TRUE);
+
+    /*
+     * Add the code base toggle handler to the three toggles.
+     */
+    (void) g_signal_connect(G_OBJECT(ed->view_oct), "toggled",
+                            G_CALLBACK(set_code_base),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->view_dec), "toggled",
+                            G_CALLBACK(set_code_base),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->view_hex), "toggled",
+                            G_CALLBACK(set_code_base),
+                            GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = make_accel_menu_item(menu, "_Other Page", "<Shift><Control>S",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(goto_other_page),
+                            GUINT_TO_POINTER(ed->id));
+
+    if (options.orientation == GTK_ORIENTATION_VERTICAL)
+      ed->view_orientation = mitem =
+          make_accel_menu_item(menu, "Horizontal View", "<Control>Q", ed->ag);
+    else
+      ed->view_orientation = mitem =
+          make_accel_menu_item(menu, "Vertical View", "<Control>Q", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(toggle_view_orientation),
+                            GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    /*
+     * Messages dialog.
+     */
+    ed->view_messages = mitem =
+        make_accel_menu_item(menu, "_Messages", "<Control>A", ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(view_font_messages),
+                            GUINT_TO_POINTER(ed->id));
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), menu);
+
+    return view;
+}
+
+static GtkWidget *
+make_ops_menu(gbdfed_editor_t *ed, GtkWidget *menubar)
+{
+    GtkWidget *ops, *menu, *mitem;
+
+    /*
+     * Create the Edit menu.
+     */
+    ops = gtk_menu_item_new_with_mnemonic("_Operations");
+
+    ed->ops_menu = menu = gtk_menu_new();
+    (void) g_signal_connect(G_OBJECT(menu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "_Translate Glyphs", "<Control>D",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiops_show_translate),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "_Rotate Glyphs", "<Control>R",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiops_show_rotate),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "_Shear Glyphs", "<Control>J",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiops_show_shear),
+                            GUINT_TO_POINTER(ed->id));
+
+    mitem = make_accel_menu_item(menu, "_Embolden Glyphs", "<Shift><Control>B",
+                                 ed->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guiops_show_embolden),
+                            GUINT_TO_POINTER(ed->id));
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(ops), menu);
+
+    return ops;
+}
+
+static GtkWidget *
+make_windows_menu(gbdfed_editor_t *ed, GtkWidget *menubar)
+{
+    GtkWidget *win, *menu, *mitem;
+    guint i;
+
+    win = gtk_menu_item_new_with_mnemonic("_Windows");
+
+    ed->win_menu = menu = gtk_menu_new();
+    (void) g_signal_connect(G_OBJECT(menu), "map_event",
+                            G_CALLBACK(menu_popup),
+                            GUINT_TO_POINTER(ed->id));
+
+    for (i = 0; i < num_editors; i++) {
+        if (editors[i].file == 0)
+          sprintf(buffer1, "(unnamed%d)", editors[i].id);
+        else
+          strcpy(buffer1, editors[i].file);
+
+        ed->win_menu_item = mitem = gtk_menu_item_new_with_label(buffer1);
+        (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                                G_CALLBACK(show_editor),
+                                GUINT_TO_POINTER(ed->id));
+        gtk_menu_shell_append(GTK_MENU_SHELL(ed->win_menu), mitem);
+    }
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(win), menu);
+
+    return win;
+}
+
+static GtkWidget *
+make_help_menu(gbdfed_editor_t *ed, GtkWidget *menubar)
+{
+    GtkWidget *help, *menu, *mitem, *sep;
+
+    /*
+     * Create the Edit menu.
+     */
+    help = gtk_menu_item_new_with_mnemonic("_Help");
+    gtk_menu_item_set_right_justified(GTK_MENU_ITEM(help), TRUE);
+
+    menu = gtk_menu_new();
+
+    mitem = gtk_menu_item_new_with_mnemonic("The _Program");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_PROGRAM));
+
+    mitem = gtk_menu_item_new_with_mnemonic("_Font Grid");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_FONTGRID));
+
+    mitem = gtk_menu_item_new_with_mnemonic("_Glyph Editor");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_GLYPH_EDITOR));
+
+    mitem = gtk_menu_item_new_with_mnemonic("_Configuration File");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_CONFIG_FILE));
+
+    mitem = gtk_menu_item_new_with_mnemonic("P_references Dialog");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_PREFERENCES));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = gtk_menu_item_new_with_mnemonic("_Windows Font Notes");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_FNT));
+
+    mitem = gtk_menu_item_new_with_mnemonic("_OpenType Font Notes");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_OTF));
+
+    mitem = gtk_menu_item_new_with_mnemonic("P_SF Font Notes");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_PSF));
+
+    mitem = gtk_menu_item_new_with_mnemonic("_HEX Font Notes");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_HEX));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = gtk_menu_item_new_with_mnemonic("C_olor Notes");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_COLOR));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = gtk_menu_item_new_with_mnemonic("T_ips");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_TIPS));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    sprintf(buffer1, "_About %s", g_get_prgname());
+    mitem = gtk_menu_item_new_with_mnemonic(buffer1);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(guihelp_show_help),
+                            GINT_TO_POINTER(GUIHELP_ABOUT));
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), menu);
+
+    return help;
+}
+
+#define UPDMSG "Font Name: XLFD name has been modified.\nDo you want to update the font properties from the name?"
+
+static void
+update_font_name(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    bdf_font_t *font;
+    gchar *name;
+
+    name = (gchar *) gtk_entry_get_text(GTK_ENTRY(ed->fontname));
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+    if (font && strcmp(name, font->name) == 0)
+      /*
+       * There was no actual change to the name.
+       */
+      return;
+
+    fontgrid_set_font_name(FONTGRID(ed->fgrid), name);
+
+    /*
+     * If the new name is an XLFD name, then offer to update the
+     * font properties from the name.
+     */
+    if (font && bdf_has_xlfd_name(font)) {
+        if (guiutil_yes_or_no(ed->shell, UPDMSG, TRUE)) {
+            fontgrid_update_properties_from_font_name(FONTGRID(ed->fgrid));
+
+            /*
+             * If the font info dialog is up, make sure it is updated with
+             * the font property changes.
+             */
+            guiedit_update_font_properties(ed);
+        }
+    }
+}
+
+guint
+gbdfed_make_editor(gchar *filename, gboolean cmdline)
+{
+    FILE *in;
+    GtkWidget *mb, *mitem, *table;
+    GtkWidget *vbox, *hbox, *bbox, *frame;
+    gbdfed_editor_t *ed;
+    bdf_font_t *font;
+    gchar *path;
+    GList *icon_list = 0;
+    FontgridPageInfo pageinfo;
+
+    font = 0;
+
+    /*
+     * Attempt to load the specified font before doing anything else.
+     */
+    if (filename != 0) {
+        /*
+         * Change code to put up an error dialog if the font won't load.
+         */
+        if ((in = fopen(filename, "r")) == 0) {
+            fprintf(stderr, "%s: unable to open BDF font '%s'.\n",
+                    g_get_prgname(), filename);
+            if (num_editors > 0 && !cmdline)
+              return ~0;
+            filename = 0;
+        } else {
+            font = bdf_load_font(in, &options.font_opts, 0, 0);
+            fclose(in);
+            if (font == 0)
+              fprintf(stderr, "%s: problem loading BDF font '%s'.\n",
+                      g_get_prgname(), filename);
+        }
+    }
+
+    /*
+     * Create the icon list if it hasn't already been created.
+     */
+    if (icons_set == FALSE) {
+        /*
+         * Create the 16x16 icon.
+         */
+        icon_list = g_list_append(icon_list,
+                                  gdk_pixbuf_new_from_xpm_data(gbdfed_48x48));
+        icon_list = g_list_append(icon_list,
+                                  gdk_pixbuf_new_from_xpm_data(gbdfed_32x32));
+        icon_list = g_list_append(icon_list,
+                                  gdk_pixbuf_new_from_xpm_data(gbdfed_16x16));
+
+        gtk_window_set_default_icon_list(icon_list);
+
+        icons_set = TRUE;
+    }
+
+    /*
+     * Create a new editor structure.
+     */
+    if (num_editors == 0)
+      editors = g_malloc(sizeof(gbdfed_editor_t));
+    else
+      editors = g_realloc(editors,
+                          sizeof(gbdfed_editor_t) * (num_editors + 1));
+
+    ed = editors + num_editors;
+    (void) memset(ed, 0, sizeof(gbdfed_editor_t));
+    ed->id = num_editors++;
+
+    /*
+     * Construct the path and filename from the one passed.  This makes copies
+     * of the strings.
+     */
+    if (font != 0 && filename != 0) {
+        if (filename[0] != G_DIR_SEPARATOR) {
+            path = g_get_current_dir();
+            sprintf(buffer1, "%s%c%s", path, G_DIR_SEPARATOR, filename);
+            g_free(path);
+        } else
+          strcpy(buffer1, filename);
+
+        ed->file = g_path_get_basename(buffer1);
+        ed->path = g_path_get_dirname(buffer1);
+    }
+
+    /*
+     * Create the top level window for the editor.
+     */
+    ed->shell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+    /*
+     * This is a bit risky, but it allows the shell to shrink to fit new
+     * fonts when they are added.
+     */
+    gtk_window_set_resizable(GTK_WINDOW(ed->shell), TRUE);
+
+    if (ed->id == 0) {
+        (void) g_signal_connect(G_OBJECT(ed->shell), "destroy_event",
+                                G_CALLBACK(quit_application),
+                                GUINT_TO_POINTER(ed->id));
+        (void) g_signal_connect(G_OBJECT(ed->shell), "delete_event",
+                                G_CALLBACK(quit_application),
+                                GUINT_TO_POINTER(ed->id));
+    } else {
+        (void) g_signal_connect(G_OBJECT(ed->shell), "destroy_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+        (void) g_signal_connect(G_OBJECT(ed->shell), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+    }
+
+    /*
+     * Determine the name for the window.
+     */
+    if (filename == 0)
+      sprintf(buffer1, "%s - (unnamed%d)", g_get_prgname(), ed->id);
+    else {
+        if (font && font->modified)
+          sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file);
+        else
+          sprintf(buffer1, "%s - %s", g_get_prgname(), ed->file);
+    }
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+    /*
+     * Create the vertical box that will contain the widgets.
+     */
+    table = gtk_table_new(5, 1, FALSE);
+    gtk_container_add(GTK_CONTAINER(ed->shell), table);
+
+    ed->ag = gtk_accel_group_new();
+
+    mb = gtk_menu_bar_new();
+    gtk_table_attach(GTK_TABLE(table), mb, 0, 1, 0, 1, GTK_FILL, GTK_FILL,
+                     0, 0);
+
+    /*
+     * Create the File menu.
+     */
+    mitem = make_file_menu(ed, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    /*
+     * Create the Edit menu.
+     */
+    mitem = make_edit_menu(ed, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    /*
+     * Create the View menu.
+     */
+    mitem = make_view_menu(ed, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    /*
+     * Create the Operations menu.
+     */
+    mitem = make_ops_menu(ed, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    /*
+     * Create the Windows menu.
+     */
+    mitem = make_windows_menu(ed, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    /*
+     * Create the Help menu.
+     */
+    mitem = make_help_menu(ed, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    /*
+     * Attach the accelerators to the editor.
+     */
+    gtk_window_add_accel_group(GTK_WINDOW(ed->shell), ed->ag);
+
+    /*
+     * 1. Add the font name widget.
+     */
+    frame = gtk_frame_new(0);
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+    gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 1, 2, GTK_FILL, GTK_FILL,
+                     0, 0);
+
+    ed->fontname = gtk_widget_new(gtk_entry_get_type(),
+                                  "max_length", 128, NULL);
+    mitem = labcon_new_label_defaults("Font:", ed->fontname, 0);
+    gtk_container_add(GTK_CONTAINER(frame), mitem);
+    g_signal_connect(G_OBJECT(ed->fontname), "activate",
+                     G_CALLBACK(update_font_name), GUINT_TO_POINTER(ed->id));
+
+    /*
+     * 2. Add the glyph information widgets.
+     */
+    frame = gtk_frame_new(0);
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+    gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 2, 3, GTK_FILL, GTK_FILL,
+                     0, 0);
+
+    vbox = gtk_vbox_new(TRUE, 0);
+    ed->charinfo = gtk_label_new("\"None\" (0000) (00,00)");
+    ed->metrics = gtk_label_new("ascent 0 descent 0 right 0 left 0");
+    gtk_misc_set_alignment(GTK_MISC(ed->charinfo), 0.0, 0.5);
+    gtk_misc_set_alignment(GTK_MISC(ed->metrics), 0.0, 0.5);
+    gtk_box_pack_start(GTK_BOX(vbox), ed->charinfo, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), ed->metrics, TRUE, TRUE, 0);
+
+    mitem = labcon_new_label_defaults("Glyph:", vbox, mitem);
+    gtk_container_add(GTK_CONTAINER(frame), mitem);
+
+    frame = gtk_frame_new(0);
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+    gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 3, 4, GTK_FILL, GTK_FILL,
+                     0, 0);
+
+    hbox = gtk_hbox_new(FALSE, 2);
+
+    bbox = gtk_hbox_new(FALSE, 0);
+
+    ed->first = gtk_button_new_with_label("First Page");
+    (void) g_signal_connect(G_OBJECT(ed->first), "released",
+                            G_CALLBACK(first_page),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->prev = gtk_button_new_with_label("Previous Page");
+    (void) g_signal_connect(G_OBJECT(ed->prev), "released",
+                            G_CALLBACK(previous_page),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->next = gtk_button_new_with_label("Next Page");
+    (void) g_signal_connect(G_OBJECT(ed->next), "released",
+                            G_CALLBACK(next_page),
+                            GUINT_TO_POINTER(ed->id));
+
+    ed->last = gtk_button_new_with_label("Last Page");
+    (void) g_signal_connect(G_OBJECT(ed->last), "released",
+                            G_CALLBACK(last_page),
+                            GUINT_TO_POINTER(ed->id));
+
+    gtk_box_pack_start(GTK_BOX(bbox), ed->first, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(bbox), ed->prev, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(bbox), ed->next, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(bbox), ed->last, FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
+
+    mitem = gtk_label_new("Page:");
+    ed->pageno = gtk_widget_new(gtk_entry_get_type(),
+                                "max_length", 6,
+                                "width-request", 70,
+                                NULL);
+    (void) g_signal_connect(G_OBJECT(ed->pageno), "activate",
+                            G_CALLBACK(goto_page_or_code),
+                            GUINT_TO_POINTER(ed->id));
+
+    gtk_box_pack_start(GTK_BOX(hbox), mitem, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), ed->pageno, FALSE, FALSE, 0);
+
+    mitem = gtk_label_new("Code:");
+    ed->charno = gtk_widget_new(gtk_entry_get_type(),
+                                "max_length", 6,
+                                "width-request", 70,
+                                NULL);
+    (void) g_signal_connect(G_OBJECT(ed->charno), "activate",
+                            G_CALLBACK(goto_page_or_code),
+                            GUINT_TO_POINTER(ed->id));
+
+    gtk_box_pack_start(GTK_BOX(hbox), mitem, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), ed->charno, FALSE, FALSE, 0);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    /*
+     * Now add the font grid itself.
+     */
+    ed->fgrid = fontgrid_newv(font,
+                              options.font_opts.point_size,
+                              options.font_opts.font_spacing,
+                              options.no_blanks,
+                              options.overwrite_mode,
+                              options.power2,
+                              options.colors,
+                              options.initial_glyph,
+                              options.code_base,
+                              options.orientation,
+                              options.font_opts.bits_per_pixel,
+                              options.font_opts.resolution_x,
+                              options.font_opts.resolution_y,
+                              &pageinfo);
+    (void) g_signal_connect(G_OBJECT(ed->fgrid), "turn_to_page",
+                            G_CALLBACK(page_change),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->fgrid), "selection-start",
+                            G_CALLBACK(update_selection_info),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->fgrid), "selection-extend",
+                            G_CALLBACK(update_selection_info),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->fgrid), "selection-end",
+                            G_CALLBACK(update_selection_info),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->fgrid), "activate",
+                            G_CALLBACK(update_selection_info),
+                            GUINT_TO_POINTER(ed->id));
+    (void) g_signal_connect(G_OBJECT(ed->fgrid), "modified",
+                            G_CALLBACK(handle_modified_signal),
+                            GUINT_TO_POINTER(ed->id));
+    gtk_table_attach(GTK_TABLE(table), ed->fgrid, 0, 1, 4, 5,
+                     GTK_FILL|GTK_EXPAND|GTK_SHRINK,
+                     GTK_FILL|GTK_EXPAND|GTK_SHRINK, 0, 0);
+
+    /*
+     * Set up the initial page information.
+     */
+    page_change(ed->fgrid, &pageinfo, GUINT_TO_POINTER(ed->id));
+
+    /*
+     * Show the editor if it the first one created or is not being created
+     * from the command line.
+     */
+    if (ed->id == 0)
+      gtk_widget_show_all(ed->shell);
+
+    {
+        /*
+         * Get the initial selection info to set up the glyph info labels.
+         *
+         * NOTE: This has to be done here because for some reason labels
+         * do not display properly if changed before the editor is fully
+         * realized.
+         */
+        FontgridSelectionInfo sinfo;
+        if (fontgrid_has_selection(FONTGRID(ed->fgrid), &sinfo) == TRUE)
+          update_selection_info(ed->fgrid, (gpointer) &sinfo,
+                                GUINT_TO_POINTER(ed->id));
+    }
+
+    /*
+     * Set the font name in the entry field if the font exists.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                       fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+
+    /*
+     * Change the focus to the Fontgrid.
+     */
+    gtk_widget_grab_focus(ed->fgrid);
+
+    return ed->id;
+}
+
+static int
+handle_unknown_options(bdf_options_t *opts, char **params,
+                       unsigned int nparams, void *client_data)
+{
+    gint idx;
+    gbdfed_options_t *op;
+
+    op = (gbdfed_options_t *) client_data;
+
+    if (nparams == 0)
+      return 0;
+
+    /*
+     * Handle the 2 bits per pixel color list.
+     */
+    if (strncmp(params[0], "2bpp_grays", 10) == 0) {
+        for (idx = 1; idx < 5; idx++)
+          op->colors[idx] = (unsigned short) _bdf_atos(params[idx], 0, 10);
+        return 1;
+    }
+
+    /*
+     * Handle the 4 bits per pixel color list.
+     */
+    if (strncmp(params[0], "4bpp_grays", 10) == 0) {
+        for (idx = 1; idx < 17; idx++)
+          op->colors[idx-1+4] = (unsigned short) _bdf_atos(params[idx], 0, 10);
+        return 1;
+    }
+
+    if (params[0][0] == 'a' &&
+        strncmp(params[0], "adobe_name_file", 15) == 0) {
+        if (op->adobe_name_file != 0)
+          g_free(op->adobe_name_file);
+        if (params[1] == 0 || params[1][0] == 0)
+          op->adobe_name_file = 0;
+        else
+          op->adobe_name_file = g_strdup(params[1]);
+        return 1;
+    }
+
+    if (params[0][0] == 'c') {
+        if (strncmp(params[0], "close_accelerator", 17) == 0) {
+            if (params[0][17] == 0) {
+                /*
+                 * We have the accelerator itself.  Add code to convert Xt key
+                 * bindings to GTK.
+                 */
+                if (op->accelerator != 0)
+                  g_free(op->accelerator);
+                if (params[1] == 0 || params[1][0] == 0)
+                  op->accelerator = 0;
+                else
+                  op->accelerator = g_strdup(params[1]);
+            }
+
+            /*
+             * Ignore instances of 'close_accelerator_text'.  GTK+ figures this
+             * out already.
+             */
+
+            return 1;
+        }
+
+        if (strncmp(params[0], "code_base", 9) == 0) {
+            switch (params[1][0]) {
+              case 'o': case 'O': op->code_base = 8; break;
+              case 'd': case 'D': op->code_base = 10; break;
+              case 'h': case 'H': op->code_base = 16; break;
+              default: op->code_base = 16;
+            }
+            return 1;
+        }
+
+        /*
+         * Ignore the old grayscale specification keywords.
+         */
+        if (strncmp(params[0], "color", 5) == 0) {
+            /*
+             * Quietly ignore the old color list.
+             */
+            return 1;
+        }
+    }
+
+    if (params[0][0] == 'f' &&
+        strncmp(params[0], "font_grid_horizontal", 20) == 0) {
+        switch (params[1][0]) {
+          case '0': case 'F': case 'f': case 'N': case 'n':
+            op->orientation = GTK_ORIENTATION_VERTICAL;
+            break;
+          case '1': case 'T': case 't': case 'Y': case 'y':
+            op->orientation = GTK_ORIENTATION_HORIZONTAL;
+            break;
+        }
+        return 1;
+    }        
+
+    if (params[0][0] == 'g') {
+        if (strncmp(params[0], "grid_overwrite_mode", 19) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->overwrite_mode = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->overwrite_mode = TRUE;
+                break;
+            }
+            return 1;
+        }
+
+        if (strncmp(params[0], "generate_sbit_metrics", 21) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->sbit = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->sbit = TRUE;
+                break;
+            }
+            return 1;
+        }
+    }
+
+    if (params[0][0] == 'm' && strncmp(params[0], "make_backups", 12) == 0) {
+        switch (params[1][0]) {
+          case '0': case 'F': case 'f': case 'N': case 'n':
+            op->backups = FALSE;
+            break;
+          case '1': case 'T': case 't': case 'Y': case 'y':
+            op->backups = TRUE;
+            break;
+        }
+        return 1;
+    }
+
+    if ((params[0][0] == 'n' && strncmp(params[0], "name_file", 9) == 0) ||
+        (params[0][0] == 'u' &&
+         strncmp(params[0], "unicode_name_file", 17) == 0)) {
+        if (op->unicode_name_file != 0)
+          g_free(op->unicode_name_file);
+        if (params[1] == 0 || params[1][0] == 0)
+          op->unicode_name_file = 0;
+        else
+          op->unicode_name_file = g_strdup(params[1]);
+        return 1;
+    }
+
+    if (params[0][0] == 'o' && strncmp(params[0], "orientation", 11) == 0) {
+        switch (params[1][0]) {
+          case 'H': case 'h':
+            op->orientation = GTK_ORIENTATION_HORIZONTAL;
+            break;
+          case 'V': case 'v':
+            op->orientation = GTK_ORIENTATION_VERTICAL;
+            break;
+        }
+        return 1;
+    }
+
+    if (params[0][0] == 'p') {
+        if (strncmp(params[0], "pixel_size", 10) == 0) {
+            op->pixel_size = _bdf_atoul(params[1], 0, 10);
+            if (op->pixel_size < 2)
+              op->pixel_size = 2;
+            else if (op->pixel_size > 20)
+              op->pixel_size = 20;
+            return 1;
+        }
+
+        if (strncmp(params[0], "power2", 6) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->power2 = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->power2 = TRUE;
+                break;
+            }
+            return 1;
+        }
+
+        if (strncmp(params[0], "percentage_only", 15) == 0)
+          /*
+           * Ignore this option.  No longer needed.
+           */
+          return 1;
+
+        if (strncmp(params[0], "progress_bar", 12) == 0)
+          /*
+           * The progress bar is no longer being used as of version 5.0.
+           */
+          return 1;
+    }
+
+    if (params[0][0] == 'r') {
+        if (strncmp(params[0], "resolution", 10) == 0) {
+            op->resolution = _bdf_atoul(params[1], 0, 10);
+            return 1;
+        }
+
+        if (strncmp(params[0], "really_exit", 11) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->really_exit = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->really_exit = TRUE;
+                break;
+            }
+            return 1;
+        }
+    }
+
+    if (params[0][0] == 's') {
+        if (strncmp(params[0], "skip_blank_pages", 16) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->no_blanks = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->no_blanks = TRUE;
+                break;
+            }
+            return 1;
+        }
+
+        if (strncmp(params[0], "show_cap_height", 15) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->show_cap_height = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->show_cap_height = TRUE;
+                break;
+            }
+            return 1;
+        }
+
+        if (strncmp(params[0], "show_x_height", 13) == 0) {
+            switch (params[1][0]) {
+              case '0': case 'F': case 'f': case 'N': case 'n':
+                op->show_x_height = FALSE;
+                break;
+              case '1': case 'T': case 't': case 'Y': case 'y':
+                op->show_x_height = TRUE;
+                break;
+            }
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void
+usage(void)
+{
+    fprintf(stderr, "Usage: %s [font1.bdf .....]\n", g_get_prgname());
+    exit(1);
+}
+
+static void
+editor_setup(int argc, char *argv[])
+{
+    int base = 0;
+    gchar *ap;
+    FILE *in;
+    gboolean need_editor;
+    GdkScreen *screen;
+
+    /*
+     * Get the default BDF options.
+     */
+    bdf_default_options(&options.font_opts);
+
+    options.accelerator = options.accelerator_text =
+        options.unicode_name_file = options.adobe_name_file = 0;
+    options.pixel_size = 10;
+    options.resolution = 0;
+    options.no_blanks = options.really_exit = 
+        options.overwrite_mode = options.power2 =
+        options.backups = TRUE;
+    options.show_cap_height = options.show_x_height =
+        options.sbit = options.gbdfed_opts = FALSE;
+    options.initial_glyph = -1;
+    options.code_base = 16;
+
+    options.orientation = GTK_ORIENTATION_HORIZONTAL;
+
+    /*
+     * Set the default colors for 2 bits per pixel.
+     */
+    options.colors[0] = 0;
+    options.colors[1] = 175;
+    options.colors[2] = 207;
+    options.colors[3] = 239;
+
+    /*
+     * Set the default colors for 4 bits per pixel.
+     */
+    options.colors[4] = 0;
+    options.colors[5] = 31;
+    options.colors[6] = 63;
+    options.colors[7] = 95;
+    options.colors[8] = 127;
+    options.colors[9] = 159;
+    options.colors[10] = 167;
+    options.colors[11] = 175;
+    options.colors[12] = 183;
+    options.colors[13] = 191;
+    options.colors[14] = 199;
+    options.colors[15] = 207;
+    options.colors[16] = 215;
+    options.colors[17] = 223;
+    options.colors[18] = 231;
+    options.colors[19] = 239;
+
+    /*
+     * Attempt to load the user config file.
+     */
+    if ((ap = getenv("HOME")) != 0) {
+        sprintf(buffer1, "%s/.gbdfedrc", ap);
+        if ((in = fopen(buffer1, "r")) != 0) {
+            bdf_load_options(in, &options.font_opts, handle_unknown_options,
+                             (void *) &options);
+            fclose(in);
+        } else {
+            /*
+             * Try reading the older ".xmbdfedrc".
+             */
+            sprintf(buffer1, "%s/.xmbdfedrc", ap);
+            if ((in = fopen(buffer1, "r")) != 0) {
+                bdf_load_options(in, &options.font_opts,
+                                 handle_unknown_options, (void *) &options);
+                fclose(in);
+            }
+        }
+    }
+
+    /*
+     * Determine the horizontal and vertical resolutions if they have not been
+     * set on the command line or from the config file.
+     */
+    if (options.resolution != 0)
+      options.font_opts.resolution_x = options.font_opts.resolution_y =
+          options.resolution;
+
+    screen = gdk_drawable_get_screen(GDK_DRAWABLE(gdk_get_default_root_window()));
+    if (options.font_opts.resolution_x == 0)
+      options.font_opts.resolution_x =
+          (unsigned int) ((((double) gdk_screen_get_width(screen)) * 25.4) /
+                           ((double) gdk_screen_get_width_mm(screen)) + 0.5);
+
+    if (options.font_opts.resolution_y == 0)
+      options.font_opts.resolution_y =
+          (unsigned int) ((((double) gdk_screen_get_height(screen)) * 25.4) /
+                           ((double) gdk_screen_get_height_mm(screen)) + 0.5);
+
+    /*
+     * Handle the case of no command line options here.
+     */
+    if (argc == 0) {
+        (void) gbdfed_make_editor(0, TRUE);
+        return;
+    }
+
+    /*
+     * If a filename is not present on the command line, make sure an
+     * editor is created after the command line is parsed.
+     */
+    need_editor = TRUE;
+
+    /*
+     * Now parse the options from the command line.
+     */
+    while (argc > 0) {
+        ap = *argv;
+        if (*ap == '-') {
+            if (strcmp(ap + 1, "nm") == 0)
+              options.font_opts.correct_metrics = 0;
+            else if (strcmp(ap + 1, "nu") == 0)
+              options.font_opts.keep_unencoded = 0;
+            else if (strcmp(ap + 1, "nc") == 0)
+              options.font_opts.keep_comments = 0;
+            else if (strcmp(ap + 1, "np") == 0)
+              options.font_opts.pad_cells = 0;
+            else if (strcmp(ap + 1, "bp") == 0)
+              options.no_blanks = FALSE;
+            else if (strcmp(ap + 1, "ed") == 0)
+              options.really_exit = FALSE;
+            else if (strcmp(ap + 1, "im") == 0)
+              options.overwrite_mode = FALSE;
+            else if (strcmp(ap + 1, "o") == 0) {
+                argc--;
+                argv++;
+                options.orientation =
+                    (argv[0][0] == 'v' || argv[0][0] == 'V') ?
+                    GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL;
+            } else if (strcmp(ap + 1, "ps") == 0) {
+                argc--;
+                argv++;
+                options.font_opts.point_size = _bdf_atoul(*argv, 0, 10);
+            } else if (strcmp(ap + 1, "res") == 0) {
+                argc--;
+                argv++;
+                options.resolution = _bdf_atoul(*argv, 0, 10);
+                options.font_opts.resolution_x =
+                    options.font_opts.resolution_y = options.resolution;
+            } else if (strcmp(ap + 1, "hres") == 0) {
+                argc--;
+                argv++;
+                options.font_opts.resolution_x = _bdf_atoul(*argv, 0, 10);
+            } else if (strcmp(ap + 1, "vres") == 0) {
+                argc--;
+                argv++;
+                options.font_opts.resolution_y = _bdf_atoul(*argv, 0, 10);
+            } else if (strcmp(ap + 1, "bpp") == 0) {
+                argc--;
+                argv++;
+                options.font_opts.bits_per_pixel = _bdf_atoul(*argv, 0, 10);
+            } else if (strcmp(ap + 1, "g") == 0) {
+                argc--;
+                argv++;
+                ap = *argv;
+                if (*ap == '\\') {
+                    switch (*(ap + 1)) {
+                      case 'u': case 'x': base = 16; ap += 2; break;
+                      case 'o': base = 8; ap += 2; break;
+                      case 'd': base = 10; ap += 2; break;
+                    }
+                } else if ((*ap == 'U' && (*(ap + 1) == '+' ||
+                                           *(ap + 1) == '-')) ||
+                           (*ap == '0' && (*(ap + 1) == 'x' ||
+                                           *(ap + 1) == 'X'))) {
+                    base = 16;
+                    ap += 2;
+                } else if (*ap == '0')
+                  base = 8;
+
+                options.initial_glyph = _bdf_atol(ap, 0, base);
+            } else if (strcmp(ap + 1, "sp") == 0) {
+                argc--;
+                argv++;
+                switch (argv[0][0]) {
+                  case 'c': case 'C':
+                    options.font_opts.font_spacing = BDF_CHARCELL; break;
+                  case 'm': case 'M':
+                    options.font_opts.font_spacing = BDF_MONOWIDTH; break;
+                  case 'p': case 'P':
+                    options.font_opts.font_spacing = BDF_PROPORTIONAL; break;
+                }
+            } else if (strcmp(ap + 1, "eol") == 0) {
+                argc--;
+                argv++;
+                switch (argv[0][0]) {
+                  case 'd': case 'D':
+                    options.font_opts.eol = BDF_DOS_EOL; break;
+                  case 'm': case 'M':
+                    options.font_opts.eol = BDF_MAC_EOL; break;
+                  case 'u': case 'U':
+                    options.font_opts.eol = BDF_UNIX_EOL; break;
+                }
+            } else if (strcmp(ap + 1, "cb") == 0) {
+                argc--;
+                argv++;
+                switch (argv[0][0]) {
+                  case 'd': case 'D':
+                    options.code_base = 10; break;
+                  case 'h': case 'H':
+                    options.code_base = 16; break;
+                  case 'o': case 'O':
+                    options.code_base = 8; break;
+                }
+            } else {
+                if (ap[1] != 'h')
+                  fprintf(stderr, "%s: unknown parameter '%s'.\n",
+                          g_get_prgname(), ap);
+                usage();
+            }
+        } else {
+            need_editor = FALSE;
+            (void) gbdfed_make_editor(argv[0], TRUE);
+        }
+
+        argc--;
+        argv++;
+    }
+
+    if (need_editor == TRUE)
+      /*
+       * If an editor was not created, make one here.
+       */
+      (void) gbdfed_make_editor(0, TRUE);
+}
+
+int 
+main(int argc, char* argv[])
+{
+    num_editors = 0;
+
+    /*
+     * Make sure the BDF library is initialized.
+     */
+    bdf_setup();
+
+    gtk_init(&argc, &argv);  
+
+    argc--;
+    argv++;
+
+    editor_setup(argc, argv);
+
+    gtk_main();
+
+    return 0;
+}
diff --git a/gbdfed.h b/gbdfed.h
new file mode 100644 (file)
index 0000000..0b521da
--- /dev/null
+++ b/gbdfed.h
@@ -0,0 +1,425 @@
+#ifndef _h_gbdfed
+#define _h_gbdfed
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "bdf.h"
+
+G_BEGIN_DECLS
+
+#define GBDFED_VERSION "1.6"
+
+/*************************************************************************
+ *
+ * Types.
+ *
+ *************************************************************************/
+
+typedef struct {
+    gboolean gbdfed_opts;
+
+    gchar *accelerator;
+    gchar *accelerator_text;
+    gchar *unicode_name_file;
+    gchar *adobe_name_file;
+    gboolean no_blanks;
+    gboolean really_exit;
+    gboolean overwrite_mode;
+    gint initial_glyph;
+    unsigned int pixel_size;
+    unsigned int resolution;
+    gboolean show_cap_height;
+    gboolean show_x_height;
+#if 0
+    gboolean vertical;
+#endif
+    gboolean power2;
+#if 0
+    gboolean colors_allocated;
+#endif
+    unsigned short colors[20];
+#if 0
+    guint32 pixels[20];
+#endif
+    gboolean sbit;
+    gboolean backups;
+    guint code_base;
+    GtkOrientation orientation;
+    bdf_options_t font_opts;
+} gbdfed_options_t;
+
+typedef struct {
+    /*
+     * Numeric ID for the editor.
+     */
+    guint id;
+
+    /*
+     * Path and filenames of the font in question.
+     */
+    gchar *path;
+    gchar *file;
+
+    GtkAccelGroup *ag;
+    GtkWidget *shell;
+
+    /*
+     * The Open/Save file selection dialogs that will be used for related
+     * operations as well as the original.
+     */
+    GtkWidget *open_dialog;
+    GtkWidget *save_dialog;
+
+    /*
+     * The menus that need to have item sensitivity checked on popup and
+     * popdown.
+     */
+    GtkWidget *file_menu;
+    GtkWidget *edit_menu;
+    GtkWidget *name_submenu;
+    GtkWidget *view_menu;
+    GtkWidget *ops_menu;
+    GtkWidget *win_menu;
+
+    /*
+     * This editor's item on the Windows menu.  Used to toggle sensitivity.
+     */
+    GtkWidget *win_menu_item;
+
+    /*
+     * The font name field.
+     */
+    GtkWidget *fontname;
+
+    /*
+     * The encoding and metrics labels.
+     */
+    GtkWidget *charinfo;
+    GtkWidget *metrics;
+
+    /*
+     * The paging buttons and page tracking variables.
+     */
+    GtkWidget *first;
+    GtkWidget *prev;
+    GtkWidget *next;
+    GtkWidget *last;
+    gint32 last_upageno;
+    gint32 last_pageno;
+
+    /*
+     * The Page number and character code input fields.
+     */
+    GtkWidget *pageno;
+    GtkWidget *charno;
+
+    /*
+     * The FontGrid widget.
+     */
+    GtkWidget *fgrid;
+
+    /*
+     * Widgets that may change sensitivity depending on the state of the font
+     * being edited.
+     */
+    GtkWidget *file_save;
+    GtkWidget *file_export;
+
+    GtkWidget *edit_cut;
+    GtkWidget *edit_copy;
+    GtkWidget *edit_paste;
+    GtkWidget *edit_overlay;
+    GtkWidget *edit_insert;
+    GtkWidget *edit_rename_glyphs;
+    GtkWidget *edit_rename_menu;
+    GtkWidget *edit_make_xlfd;
+    GtkWidget *edit_unicode_names;
+    GtkWidget *edit_adobe_names;
+    GtkWidget *edit_test_glyphs;
+
+    /*
+     * These are the toggle widgets that change the code base displayed
+     * in the Fontgrid to octal, decimal, or hex.
+     */
+    GtkWidget *view_oct;
+    GtkWidget *view_dec;
+    GtkWidget *view_hex;
+
+    /*
+     * The widget that toggles the view between the encoded and unencoded
+     * sections of the font.
+     */
+    GtkWidget *view_unencoded;
+
+    /*
+     * The orientation label which changes depending on the current
+     * orientation of the Fontgrid.
+     */
+    GtkWidget *view_orientation;
+
+    /*
+     * This is the font messages menu item that may or may not be
+     * enabled.
+     */
+    GtkWidget *view_messages;
+
+    /*
+     * These are the widgets that make up the font messages dialog which is
+     * used to record the font I/O and modification messages.
+     */
+    GtkWidget *messages_label;
+    GtkWidget *messages_text;
+    GtkWidget *messages_dialog;
+
+    /*
+     * These fields specify the font format being imported or exported.
+     */
+    guint import_format;
+    guint export_format;
+
+    /*
+     * These are the preference widgets.
+     */
+    GtkWidget *pref_dialog;
+
+    /*
+     * These are the widgets for editing information about the font.
+     */
+    GtkWidget *finfo_notebook;
+    GtkWidget *finfo_dialog;
+    GtkWidget *finfo_comments;
+    GtkWidget *finfo_erase_comments;
+    GtkWidget *finfo_apply_comments;
+    GtkWidget *finfo_font_props;
+    GtkWidget *finfo_all_props;
+    GtkWidget *finfo_prop_name;
+    GtkWidget *finfo_prop_value;
+    GtkWidget *finfo_prop_format[4];
+    GtkWidget *finfo_apply_prop;
+    GtkWidget *finfo_delete_prop;
+
+    GtkWidget *finfo_enc_count;
+    GtkWidget *finfo_unenc_count;
+
+    GtkWidget *finfo_default_char;
+
+    GtkWidget *finfo_spacing[4];
+
+    GtkWidget *finfo_hres;
+    GtkWidget *finfo_vres;
+    GtkWidget *finfo_bpp;
+
+    GtkWidget *finfo_dwidth;
+    GtkWidget *finfo_ascent;
+    GtkWidget *finfo_descent;
+
+    GtkWidget *finfo_apply_info;
+    gboolean finfo_xlfd_props_modified;
+
+    /*
+     * A flag to indicate if the font was just imported from a non-BDF format
+     * file.  It is set to FALSE whenever a save of any kind is done.
+     */
+    gboolean imported;
+
+} gbdfed_editor_t;
+
+/*************************************************************************
+ *
+ * Globals.
+ *
+ *************************************************************************/
+
+#include "fontgrid.h"
+#if 0
+#include "colorswatch.h"
+#endif
+#include "glyphtest.h"
+
+/*
+ * List of editors and their number.
+ */
+extern gbdfed_editor_t *editors;
+extern guint num_editors;
+
+/*
+ * Buffers used for making messages and filenames.
+ */
+extern gchar buffer1[];
+extern gchar buffer2[];
+
+/*
+ * Options for loading/saving fonts and other application options.
+ */
+extern gbdfed_options_t options;
+
+/*************************************************************************
+ *
+ * The glyph test widget used by all editors in one session.
+ *
+ *************************************************************************/
+
+extern GtkWidget *glyphtest;
+
+/*************************************************************************
+ *
+ * Other functions: gbdfed.c
+ *
+ *************************************************************************/
+
+extern guint gbdfed_make_editor(gchar *, gboolean);
+
+/*************************************************************************
+ *
+ * File menu functions: guifile.c
+ *
+ *************************************************************************/
+
+/*
+ * New editor creation callback.
+ */
+extern void guifile_new_editor(GtkWidget *, gpointer);
+
+/*
+ * File import callbacks.
+ */
+extern void guifile_import_bdf_font(GtkWidget *, gpointer);
+extern void guifile_import_console_font(GtkWidget *, gpointer);
+extern void guifile_import_pkgf_font(GtkWidget *, gpointer);
+extern void guifile_import_windows_font(GtkWidget *, gpointer);
+
+#ifdef HAVE_HBF
+extern void guifile_import_hbf_font(GtkWidget *, gpointer);
+#endif
+
+#ifdef HAVE_FREETYPE
+extern void guifile_import_otf_font(GtkWidget *, gpointer);
+#endif /* HAVE_FREETYPE */
+
+#ifdef HAVE_XLIB
+extern void guifile_import_xserver_font(GtkWidget *, gpointer);
+#endif
+
+/*
+ * File save callbacks.
+ */
+extern void guifile_save_as(GtkWidget *, gpointer);
+extern void guifile_save_as_wait(GtkWidget *, gpointer);
+extern void guifile_save(GtkWidget *, gpointer);
+extern void guifile_export_psf_font(GtkWidget *, gpointer);
+extern void guifile_export_hex_font(GtkWidget *, gpointer);
+
+/*
+ * Special direct BDF font load call for recent fonts menu.
+ */
+extern void guifile_load_bdf_font(gbdfed_editor_t *ed, const gchar *filename);
+
+/*************************************************************************
+ *
+ * Edit functions: guiedit.c
+ *
+ *************************************************************************/
+
+extern void guiedit_set_unicode_glyph_names(GtkWidget *, gpointer);
+extern void guiedit_set_adobe_glyph_names(GtkWidget *, gpointer);
+extern void guiedit_set_uni_glyph_names(GtkWidget *, gpointer);
+extern void guiedit_set_zerox_glyph_names(GtkWidget *, gpointer);
+extern void guiedit_set_uplus_glyph_names(GtkWidget *, gpointer);
+extern void guiedit_set_bslashu_glyph_names(GtkWidget *, gpointer);
+extern void guiedit_show_font_info(GtkWidget *, gpointer);
+extern void guiedit_show_font_comments(GtkWidget *, gpointer);
+extern void guiedit_show_font_properties(GtkWidget *, gpointer);
+extern void guiedit_update_font_info(gbdfed_editor_t *);
+extern void guiedit_update_font_properties(gbdfed_editor_t *);
+extern void guiedit_update_font_details(gbdfed_editor_t *);
+extern void guiedit_update_code_base(gbdfed_editor_t *);
+extern void guiedit_copy_selection(GtkWidget *, gpointer);
+extern void guiedit_cut_selection(GtkWidget *, gpointer);
+extern void guiedit_paste_selection(GtkWidget *, gpointer);
+extern void guiedit_overlay_selection(GtkWidget *, gpointer);
+extern void guiedit_insert_selection(GtkWidget *, gpointer);
+extern void guiedit_show_glyphtest(GtkWidget *, gpointer);
+
+/*************************************************************************
+ *
+ * Glyph edit functions: guigedit.c
+ *
+ *************************************************************************/
+
+extern void guigedit_edit_glyph(gbdfed_editor_t *ed,
+                                FontgridSelectionInfo *si);
+
+extern void guigedit_show_cap_height(gboolean show);
+extern void guigedit_show_x_height(gboolean show);
+extern void guigedit_set_pixel_size(guint pixel_size);
+extern void guigedit_set_font_spacing(gint spacing, guint16 monowidth);
+extern void guigedit_set_font_spacing(gint spacing, guint16 monowidth);
+extern void guigedit_set_code_base(gint code_base);
+
+extern void guigedit_cleanup(void);
+
+
+/*************************************************************************
+ *
+ * Preference functions: guipref.c
+ *
+ *************************************************************************/
+
+extern void guiedit_show_preferences(GtkWidget *, gpointer);
+extern void guiedit_preference_cleanup(void);
+
+/*************************************************************************
+ *
+ * Help functions: guihelp.c
+ *
+ *************************************************************************/
+
+/*
+ * Macros that specify the help text to be used.
+ */
+#define GUIHELP_ABOUT        0
+#define GUIHELP_PROGRAM      1
+#define GUIHELP_FONTGRID     2
+#define GUIHELP_GLYPH_EDITOR 3
+#define GUIHELP_CONFIG_FILE  4
+#define GUIHELP_PREFERENCES  5
+#define GUIHELP_FNT          6
+#define GUIHELP_OTF          7
+#define GUIHELP_PSF          8
+#define GUIHELP_HEX          9
+#define GUIHELP_COLOR        10
+#define GUIHELP_TIPS         11
+
+extern void guihelp_show_help(GtkWidget *w, gpointer data);
+
+extern void guigedit_cleanup(void);
+extern void guihelp_cleanup(void);
+
+/*************************************************************************
+ *
+ * Glyph operation functions: guiops.c
+ *
+ *************************************************************************/
+
+extern void guiops_show_translate(GtkWidget *, gpointer);
+extern void guiops_show_rotate(GtkWidget *, gpointer);
+extern void guiops_show_shear(GtkWidget *, gpointer);
+extern void guiops_show_embolden(GtkWidget *, gpointer);
+
+/*************************************************************************
+ *
+ * Util functions: guiutil.c
+ *
+ *************************************************************************/
+
+extern void guiutil_show_dialog_centered(GtkWidget *dialog, GtkWidget *parent);
+extern void guiutil_cursor_cleanup(void);
+extern void guiutil_busy_cursor(GtkWidget *w, gboolean on);
+extern void guiutil_error_message(GtkWidget *parent, gchar *text);
+extern gboolean guiutil_yes_or_no(GtkWidget *parent, gchar *text,
+                                  gboolean default_answer);
+extern void guiutil_util_set_tooltip(GtkWidget *, gchar *);
+
+G_END_DECLS
+
+#endif /* _h_gbdfed */
diff --git a/gbdfed.man b/gbdfed.man
new file mode 100644 (file)
index 0000000..a3e4839
--- /dev/null
@@ -0,0 +1,711 @@
+.TH GBDFED 1 "23 February 2010" "GTK2"
+.SH NAME 
+gbdfed \- GTK-based BDF font editor
+
+.SH SYNOPSIS
+.B gbdfed
+[\fIoptions\fP] [\fIfonts ...\fP]
+
+.SH DESCRIPTION
+.I gbdfed
+lets you interactively create new bitmap font files or modify
+existing ones.  It allows editing multiple fonts and multiple
+glyphs, it allows cut and paste operations between fonts and
+glyphs and editing font properties.
+.I gbdfed
+can import Metafont PK/GF fonts, Han Bitmap Font Format (HBF) fonts, Linux
+console fonts (PSF, CP, and EGA/VGA) fonts, Sun VF fonts, OpenType/TrueType
+(OTF/TTF) fonts, or grab a font from the X server (when running under X11).
+.I gbdfed
+can export PSF2 Linux console fonts and HEX fonts (see online help).
+
+.I gbdfed
+works on X Window System Version 11 (X11), Release 5 or 6, with GTK+ 2.6 or
+greater. It may work on Windows, but hasn't been tested yet.
+
+.SH OPTIONS
+.I gbdfed
+accepts the following command line arguments:
+
+.PP
+.TP 8
+.I -nc
+do not preserve comments (by default,
+.I gbdfed
+automatically collects comments that are saved with the font).
+.PP
+.TP 8
+.I -nu
+do not preserve unencoded glyphs (by default,
+.I gbdfed
+preserves the unencoded glyphs).
+.PP
+.TP 8
+.I -nm
+do not make metrics corrections (by default,
+.I gbdfed
+attempts to make metrics corrections automatically).
+.PP
+.TP 8
+.I -np
+do not pad character-cell bitmaps (by default,
+.I gbdfed
+pads character-cell bitmaps with 0's to the cell dimensions when the font is 
+saved).
+.PP
+.TP 8
+.I -bp
+allow blank pages (by default,
+.I gbdfed
+skips blank pages).
+.PP
+.TP 8
+.I -ed
+do not present the "Really Exit?" dialog (by default, this dialog always
+presented).
+.PP
+.TP 8
+.I -ps n
+set default point size (if unspecified, 
+.I gbdfed
+sets it to 12).
+.PP
+.TP 8
+.I -hres n
+set default horizontal resolution.
+.PP
+.TP 8
+.I -vres n
+set default vertical resolution.
+.PP
+.TP 8
+.I -res n
+set both default resolutions (if unspecified,
+.I gbdfed
+sets both horizontal and vertical resolution to that of display,
+(e.g. 90x90 dpi for Sun workstations).
+.PP
+.TP 8
+.I -sp s
+set the default font spacing ("p" for Proportional, "m" for Monowidth, or "c"
+for Character Cell).
+.PP
+.TP 8
+.I -eol e
+set the default end-of-line type ("u" for Unix LF, "d" for DOS/Windows CRLF,
+or "m" for Macintosh CR).
+CR)
+.PP
+.TP 8
+.I -g glyph-code
+specify the initial glyph code at startup.  The glyph code can be specified in
+decimal, octal, or hex.  Octal numbers must be prefixed with the digit 0, and
+hex numbers must be prefixed with one of: \fI0x, 0X, U+, U-, \\u\fP.
+.PP
+.TP 8
+.I -cb code-base
+specify the code base used to display the glyphs encodings (can be "octal",
+"decimal", or "hexadecimal").
+
+.SH FONT GRID
+
+At the top of each editor window there are some
+fields and buttons.  These are:
+.IP
+The "Font" text field is where the font name
+is set so it can be edited.
+
+The "Glyph" field is a label that provides
+some information about glyph name, encoding, and
+metrics when a glyph is selected.  When a range
+of glyphs are selected, this field displays the
+start and end codes of the range.
+
+The push buttons are used to navigate through the
+glyph pages.  The "Previous Page" and "Next Page"
+buttons normally skip glyph pages that are empty,
+but that can be changed using the "Preferences" dialog.
+
+The "Page" field indicates the current glyph page
+and also allows a specific page number to be entered.
+Once a page number is entered, pressing the Return
+key will cause the Font Grid to shift to that page.
+The page number entered is assumed to be a decimal
+number.
+
+The "Code" field is provided for situations where
+the page number is not known, but the encoding is
+known.  The encoding entered in this field must be
+in the base (8, 10, or 16) that is currently being
+used to display glyph encodings (see the "View"
+menu below).  Once the encoding is entered, pressing
+the Return key will cause the Font Grid to shift to
+the page containing the encoding.
+.PP
+The main window of each font editor is called the
+.I Font Grid.
+Each Font Grid has a clipboard used for passing glyphs around.
+This clipboard is called 
+.I FONTGRID_CLIPBOARD.
+The format of the data stored to this clipboard is not documented yet.
+.sp
+When a glyph has been modified either by the user or
+by automatic metrics corrections when the font is loaded,
+the glyph code above the glyph cell will be highlighted.
+
+.SH Font Grid Menus
+The 
+.I File
+menu has the following entries:
+.PP
+.TP 4
+.I New <Ctrl+N>
+This creates a new font using the current defaults for point size, horizontal
+and vertical resolution, and font spacing.
+.PP
+.TP 4
+.I Open <Ctrl+O>
+This opens a new font in the current Font Grid.  If the font in the grid has
+been modified, the option to save the font before loading a new one will be
+given.
+.PP
+.TP 4
+.I Save <Ctrl+S>
+Save the current font.  If the current font does not have a file name, a file
+selection dialog will pop up so a file name can be entered.
+.br
+When the font is saved, it will automatically generate a list of
+_XFREE86_GLYPH_RANGE properties containing a list of glyph codes available in
+the font.
+.PP
+.TP 4
+.I Save As <Ctrl+W>
+Save the current font with some other name.
+.br
+When the font is saved, it will automatically generate a list of
+_XFREE86_GLYPH_RANGE properties containing a list of glyph codes available in
+the font.
+.PP
+The
+.I Import
+submenu of the
+.I File
+menu has the following entries:
+.PP
+.TP 8
+.in 4
+.I PK/GF Font <Ctrl+K>
+Import a Metafont PK or GF font.
+.PP
+.TP 8
+.in 4
+.I Console Font <Ctrl+L>
+Import a binary console font used by Linux and Sun (PSF1, PSF2, CP, vfont, and
+other font formats).
+.PP
+.TP 8
+.in 4
+.I HBF Font <Ctrl+H>
+Import an HBF font. Only available if HBF support is compiled into gbdfed.
+.PP
+.TP 8
+.in 4
+.I Windows Font <Ctrl+B>
+Import a Windows FON/FNT font.  This will also import fonts from .EXE
+and .DLL files as well.
+.PP
+.TP 8
+.in 4
+.I OpenType/TrueType Font <Ctrl+Y>
+Import an OpenType/TrueType font (.otf or .ttf extension) or a TrueType
+collection (.ttc extension).
+.PP
+.TP 8
+.in 4
+.I Server Font <Ctrl+G>
+Import a font from the X server if running under the X Windowing System.
+.PP
+The \fIExport\fR submenu of the \fIFile\fR menu has the following entries:
+.PP
+.TP 8
+.in 4
+.I PSF <Ctrl+F>
+This will export the current BDF font or the currently selected glyphs to a
+PSF2 font.  Glyphs in PSF fonts are usually arranged in a specific way to make
+them work properly with the basic display driver.  Many of these fonts come
+with mapping tables attached that indicate which Unicode characters a glyph
+can be used for.  The mapping table allows the console to attempt to display
+Unicode text.
+.sp
+During the export, an option menu will let you select whether to:
+.TP 10
+.in 10
+Export Font with Mapping Table
+.br
+Export Font Only
+.br
+Export Mapping Table Only
+.TP 8
+.in 8
+Only the first 512 glyphs will be exported to the font.
+.PP
+.TP 8
+.in 4
+.I HEX
+.br
+This will export the current BDF font into the HEX format (see
+http://czyborra.com/unifont/).
+.PP
+.TP 4
+.I Exit/Close <Ctrl+F4>
+Exit the program if this is the primary Font Grid or simply hide (unmap) the
+current Font Grid window.
+.PP
+The 
+.I Edit
+menu has the following entries:
+.PP
+.TP 4
+.I Copy <Ctrl+C> or <Button3Down>
+This copies the current selection to the Font Grid clipboard.
+.PP
+.TP 4
+.I Cut <Ctrl+X> or <Key>Delete or <Key>BackSpace
+This copies the current selection to the Font Grid clipboard and
+then deletes the selection.
+.PP
+.TP 4
+.I Paste <Ctrl+V> or <Button2Down>
+This replaces the glyphs starting at the currently selected position with the
+Font Grid clipboard.
+.PP
+.TP 4
+.I Overlay <Ctrl+Shift+V> or Ctrl<Button2Down>
+This merges the glyphs on the Font Grid cliboard with the glyphs starting at
+the currently selected position.  This means that the bitmaps are actually
+combined together. The names of the modified glyphs are not
+changed.
+.PP
+.TP 4
+.I Insert <Ctrl+Meta+V> or Shift<Button2Down>
+This inserts the glyphs on the Font Grid clipboard in front of the currently
+selected position.
+.PP
+.TP 4
+.I Properties <Ctrl+P>
+This invokes the font property editor.
+.PP
+.TP 4
+.I Comments <Ctrl+M>
+This invokes the font comments editor.
+.PP
+.TP 4
+.I Font Info <Ctrl+I>
+This invokes a dialog that allows changes to some of the font information so
+these values do not have to be changed using the property editor.  These
+values include the default character, font device width (for monowidth and
+character cell fonts), font ascent and descent, font vertical and horizontal
+resolution, and the font spacing.
+.PP
+The 
+.I Font Name
+submenu of the
+.I Edit
+menu has the following four entries:
+.PP
+.TP 8
+.in 4
+.I Make XLFD Name
+If the font does not have an XLFD name, this
+will save the current font name in the
+.I _ORIGINAL_FONT_NAME
+font property and then generate an XLFD name
+for the font.
+.PP
+.TP 8
+.in 4
+.I Update Name From Properties
+This will update the XLFD font name fields from
+the font property list.
+.PP
+.TP 8
+.in 4
+.I Update Properties From Name
+This will update the font properties from the
+XLFD font name.
+.PP
+.TP 8
+.in 4
+.I Update Average Width
+This will update the average width field of the
+XLFD font name and will update the
+.I AVERAGE_WIDTH
+font property as a side effect.
+.PP
+.TP 8
+.I Name Glyphs
+.PP
+.TP 8
+.in 4
+.I Unicode Names
+This will rename all the glyphs using names taken from a file in the Unicode
+Character Database format.  This file can be set in the configuration file
+or set using the \fISetup\fR dialog.
+.PP
+.TP 8
+.in 4
+.I Unicode Values
+This will rename all the glyphs with a hexadecimal value prefixed by \fC0x\fR,
+\fCU+\fR, or \fC\\u\fR (example: 0x010D, U+010D, \\u010D).
+.PP
+.TP 8
+.I Test Glyphs <Ctrl+Z>
+This will toggle the glyph test dialog on or off for the editor.  When this is
+active, selecting a glyph from any Font Grid will also add it to the glyph
+test dialog.  When changes are made to a glyph or the font bounding box, the
+glyph test dialog will be updated accordingly.
+.sp
+The glyph test dialog provides a toggle to turn the baseline on or off and
+another toggle to draw from right to left instead of left to right.
+.PP
+.TP 8
+.I Setup <Ctrl+T>
+This will invoke the dialog to edit various settings
+used by the editor such as the default point size, resolution and font
+spacing.
+.PP
+The 
+.I View
+menu has the following entries:
+.PP
+.TP 4
+.I Unencoded <Ctrl+E>
+This will toggle between displaying the unencoded (glyphs with an
+.I ENCODING
+field of -1) and encoded glyphs.
+.PP
+.TP 4
+.I Code Base
+Selects displaying of glyph encoding.  Options are Octal
+(base 8), Decimal (base 10) or Hexadecimal (base 16).
+.PP
+.TP 4
+.I Other Page <Ctrl+Shift+S>
+This will toggle between the current page and the last page
+that was viewed.
+.PP
+.TP 4
+.I Vertical View <Ctrl+Q>
+This will toggle the FontGrid between showing the glyphs
+horizontally (default) and vertically.
+.PP
+.TP 4
+.I Messages <Ctrl+A>
+This will show messages generated when corrections to the font metrics are
+done or errors are encountered.
+.PP
+The
+.I Operations
+menu has the following entries:
+.PP
+.TP 4
+.I Translate <Ctrl+D>
+This will bring up the dialog for entering the X offset and Y offset used to
+translate the glyph to a new location.
+.sp
+The option of translating the selected glyphs or all of the glyphs is
+provided.
+.PP
+.TP 4
+.I Rotate <Ctrl+R>
+This will bring up the dialog for entering the rotation angle.  The rotation
+is limited to between plus or minus 1 and 359 degrees.
+.sp
+The option of rotating the selected glyphs or all of the glyphs is provided.
+.PP
+.TP 4
+.I Shear <Ctrl+J>
+This will bring up the dialog for entering theangle of the shear.  The shear
+is limited to plus or minus 45 degrees.
+.sp
+The option of rotating the selected glyphs or all of the glyphs is provided.
+.PP
+.TP 4
+.I Embolden <Ctrl+Shift+B>
+This will bring up the dialog for emboldening either the selected or all
+glyphs.
+.sp
+To \fIembolden\fP means to make bold.
+.PP
+The 
+.I Editors
+menu has the following entries:
+.PP
+.TP 4
+.I New <Ctrl+N>
+This will cause a new editor to be created using the point size, resolution,
+and bits per pixel set in the config file, from the command line or from the
+Setup dialog.
+.PP
+.TP 4
+.I [editor list]
+The remaining menu items are all the Font Grid's that have been
+created.  Choosing one will force that window to be made visible (mapped)
+and also put that window on top.
+
+.SH Font Grid Other Features
+Double clicking the mouse on one of the glyphs will start a Glyph Editor for
+that glyph.
+.sp
+The font name can be edited in the Font Grid and page switching can be done
+with the buttons on the Font Grid.
+
+.SH GLYPH EDITOR
+The
+.I Glyph Editor
+provides a simple bitmap editor
+designed to edit glyph bitmaps and other glyph
+information.  The Glyph Editors all use a special
+clipboard used to pass bitmaps between the Glyph
+Editors.  This clipboard is called
+.I GLYPHEDIT_CLIPBOARD.
+.sp
+The only limit on the number of Glyph Editors that
+can be open at one time is the amount of memory.
+
+.SH Glyph Editor Menus
+The
+.I File
+menu has the following entries:
+.PP
+.TP 4
+.I Update <Ctrl+S>
+This will update the Font Grid with the modified glyph.
+.br
+To the right of the Glyph Name field is a button that performs the same
+function.
+.PP
+.TP 4
+.I Update and Next <Ctrl+U>
+This will update the FontGrid with the modified glyph and move to the next
+glyph.
+.PP
+.TP 4
+.I Update and Previous <Ctrl+B>
+This will update the FontGrid with the modified glyph and move to the previous
+glyph.
+.PP
+.TP 4
+.I Close <Ctrl+F4>
+This will close the Glyph Editor.
+.PP
+The
+.I Edit
+menu has the following entries:
+.PP
+.TP 4
+.I Reload <Ctrl+L>
+This will reload the glyph and discard any changes made in the GlyphEditor.
+.PP
+.TP 4
+.I Copy <Ctrl+C>
+This will copy the currently selected portion of the bitmap to the Glyph
+Editor clipboard.
+.PP
+.TP 4
+.I Cut <Ctrl+X>
+This will copy the currently selected portion of the bitmap to the Glyph
+Editor clipboard and then delete the selection.
+.PP
+.TP 4
+.I Paste <Ctrl+V>
+This will paste the contents of the Glyph Editor clipboard into the current
+Glyph Editor with the top-left coordinate of the bitmap on the clipboard
+pasted at the location of the mouse.  If the bitmap is too big to fit if it is
+pasted at the mouse location, the bitmap will be shifted until it fits
+completely in the Glyph Editor.
+.PP
+.TP 4
+.I Select All <Ctrl+A>
+This will select the whole glyph bitmap.
+.PP
+.TP 4
+.I Next Glyph <Ctrl+N>
+This will move the Glyph Editor to the next glyph position in the Font Grid.
+If the current glyph has been modified, a save prompt will appear before
+moving to the next glyph.
+.br
+To the right of the Glyph Name field is a button that performs the same
+function.
+.PP
+.TP 4
+.I Previous Glyph <Ctrl+P>
+This will move the Glyph Editor to the previous glyph position in the Font
+Grid.  If the current glyph has been modified, a save prompt will appear
+before moving to the previous glyph.
+.br
+To the right of the Glyph Name field is a button that performs the same
+function.
+.PP
+The
+.I Operation
+menu has the following entries:
+.PP
+.TP 4
+.I Draw <Ctrl+D>
+Change the Glyph Editor into Draw mode.
+.PP
+.TP 4
+.I Move <Ctrl+M>
+Change the Glyph Editor into Move mode.  Move mode allows selecting a portion
+of the glyph bitmap and moving it to another location.
+.PP
+.TP 4
+.I Copy <Ctrl+Y>
+Change the Glyph Editor into Copy mode.  Copy mode allows copying a portion of
+the glyph bitmap and moving it to another location.
+.PP
+.TP 4
+.I Rotate <Ctrl+T>
+This will invoke the rotation dialog that allows the degrees of rotation
+to be specified.  Rotation can be between 1 and 359 degrees.
+.PP
+.TP 4
+.I Shear <Ctrl+E>
+This will invoke the shear dialog that allows the degrees of horizontal
+shear to be specified.  Other names for shearing are obliquing or slanting.
+Shearing is allowed between 1 and 45 degrees.
+.PP
+.TP 4
+.I Embolden <Ctrl+H>
+This will embolden the glyph in a simple manner.
+.PP
+.TP 4
+.I Resize BBX <Ctrl+R>
+This will allow changing the sizes of the glyph bounding box including the
+left/right bearings and the glyph ascent/descent.  If this change causes the
+glyph bounding box to be larger than the font bounding box, the font bounding
+box will be resized when the glyph is saved next.
+.PP
+.TP 4
+.I Edit PSF Unicode Mappings <Ctrl+F>
+This allows adding, deleting and editing of Unicode mappings for fonts that
+will be exported as PSF fonts. The code valued entered are expected to be
+in hexadecimal.
+
+.SH Glyph Editor Other Features
+When the mouse is used to shift the bitmap using one of the buttons, holding
+the mouse down will cause the activity to repeat.
+
+.SH PROPERTIES
+
+.SH "SEE ALSO"
+xmbdfed(1), xfed(1), bdftopcf(1), bdftosnf(1), psfaddtable(1), psfgettable(1), fontforge(1)
+.br
+\fIGlyph Bitmap Distribution Format (BDF) Specification\fP, Application
+Note 5005, Adobe System Inc, 1993
+.br
+\fIX Logical Font Description\fP, X Consortium
+
+.SH ACKNOWLEDGMENTS
+
+Ross Patterson for his HBF code.
+.br
+der Mouse for his "getbdf" code.
+.br
+K. Carothers and A. Korobka for their "fnt2bdf" code in Wine.
+.sp
+Mike Stroyan <mike_stroyan@fc.hp.com> for patches.
+.br
+Primoz Peterlin <primoz.peterlin@biofiz.mf.uni-lj.si> for this manual page.
+.br
+Danny Backx <u27113@kb.be> for the LessTif Imakefile.
+.br
+Donald Page <donaldp@sco.com> for patches.
+.br
+Michal Szymanski <msz@sirius.astrouw.edu.pl> for problem reports.
+.br
+Werner Lemberg <a7971428@unet.univie.ac.at> for problem reports.
+.br
+William F. Maton <wmaton@enterprise.ic.gc.ca> for problem reports.
+.br
+Ivan Nejgebauer <ian@uns.ns.ac.yu> for problem reports.
+.br
+Solofo <solofo@mpi-sb.mpg.de> for problem reports.
+.br
+Dave Bodenstab <imdave@mcs.net> for patches.
+.br
+W. Chao <wchao@HRZ.Uni-Bielefeld.DE> for Makefile changes and problem report.
+.br
+Andreas Reuter <ar205@bonzo.geowiss.nat.tu-bs.de> for problem reports.
+.br
+Leonard Dickens <leonard@saul.hipgraphics.com> for IRIX 6.3 Makefile changes.
+.br
+Markus Kuhn <Markus.Kuhn@cl.cam.ac.uk> for suggestions.
+.br
+Jim Knoble <jmknoble@pobox.com> for dialog geometry fixes.
+.br
+Darren Stuart Embry <dsembr01@ox.slug.louisville.edu> for HP/UX 10.20 X11R6
+Makefile additions.
+.br
+Vladimir Volovich <vvv@vvv.vsu.ru> for pointing out something I forgot to
+test.
+.br
+Ben Fry <fry@media.mit.edu> for IRIX 6.5.2 variables for the Makefile.
+.br
+J.H.M. Dassen (Ray) <jdassen@debian.org> for bug fixes.
+.br
+Robert Brady <rwb197@ecs.soton.ac.uk> for pointing out a problem.
+.br
+Stefan Monnier <monnier@cs.yale.edu> for a bug report.
+.br
+Humphrey Clerx <humphrey.clerx@eurocontrol.be> for a bug report.
+.br
+Rudolf Cejka <cejkar@dcse.fee.vutbr.cz> for bug fixes and a suggestion.
+.br
+Baruch Even <baruch@ev-en.org> for a bug fix.
+.br
+Sergey Vlasov <vsu@mivlgu.murom.ru> for bug fixes.
+.br
+Daniel Neuburger <daniel.neuburger@lmco.com> for bug fixes.
+.br
+Pierre HANSER <Pierre.Hanser@sxb.bsf.alcatel.fr> for a bug fix.
+.br
+Patrick Hagglund <patrik.hagglund@bredband.net> for FreeType 2 support.
+.br
+James Cloos <cloos@jhcloos.com> for pointing out problems.
+.br
+Ming Hua <minghua@rice.edu> for pointing out problems.
+.br
+Viktor Urban <viktor@icc-atcsolutions.com> for pointing out problems.
+.br
+Jiri "BlueBear" Dluhos <modry.medved@seznam.cz> for providing 64-bit fixes.
+.br
+Jan Engelhardt <jengelh@linux01.gwdg.de> help text improvements and missing
+prototype.
+.br
+Daniel Richard G. <skunk@iSKUNK.ORG> for help on 64-bit architectures.
+.br
+Baruch Even <baruch@ev-en.org> for help on 64-bit architectures.
+.br
+Ming Hua <minghua.debian@gmail.com> for an unsuspected warning.
+.br
+Ryan Hill <dirtyepic@gentoo.org> for import dialog crash report.
+.br
+Don Knuth (https://bugs.launchpad.net/ubuntu/+source/gbdfed/+bug/172836) for
+reporting spelling, gramatical and behavior problems.
+.br
+Tim Allen <screwtape@froup.com> for discovering glyph and font spacing bugs.
+.br
+Daniel Quarras <dqarras@yahoo.com> for discovering a PSF unicode map editing
+problem.
+.br
+Bertrand Janin <tamentis@neopulsar.org> for improving the GlyphEditor user
+interface.
+.br
+Peter Volkov <pva@gentoo.org> for fixing a name collision.
+.br
+Tom "spot" Callaway <tcallawa@redhat.com> for fixing a linking problem.
+
+.SH AUTHOR
+Mark Leisher <mleisher@gmail.com>
diff --git a/gectrl.c b/gectrl.c
new file mode 100644 (file)
index 0000000..96d4765
--- /dev/null
+++ b/gectrl.c
@@ -0,0 +1,1655 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <gtk/gtklabel.h>
+#include "gectrl.h"
+#include "gectrlbmaps.h"
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(s) dgettext(GETTEXT_PACKAGE,s)
+#else
+#define _(s) (s)
+#endif
+
+/*
+ * All the pixmaps are 16x16.
+ */
+#define BMAP_DIM 16
+
+#define GEC_TOGGLE_SIZE 38
+#define GEC_BUTTON_SIZE 33
+
+/*
+ * Properties of this widget.
+ */
+enum {
+    PROP_0 = 0,
+    TIP_LABEL,
+    GLYPH_IMAGE,
+    COLOR_LIST
+};
+
+/*
+ * Signals this widget emits.
+ */
+enum {
+    ACTIVATE = 0
+};
+
+static GtkDrawingAreaClass *parent_class = 0;
+static guint gecontrol_signals[ACTIVATE + 1];
+
+#define GEC_DRAW_TOGGLE     0
+#define GEC_MOVE_TOGGLE     1
+#define GEC_COPY_TOGGLE     2
+#define GEC_FLIPH_BUTTON    3
+#define GEC_FLIPV_BUTTON    4
+#define GEC_SHEAR_BUTTON    5
+#define GEC_RLEFT_BUTTON    6
+#define GEC_RRIGHT_BUTTON   7
+#define GEC_ROTATE_BUTTON   8
+#define GEC_ULEFT_BUTTON    9
+#define GEC_UP_BUTTON       10
+#define GEC_URIGHT_BUTTON   11
+#define GEC_LEFT_BUTTON     12
+#define GEC_RIGHT_BUTTON    13
+#define GEC_DLEFT_BUTTON    14
+#define GEC_DOWN_BUTTON     15
+#define GEC_DRIGHT_BUTTON   16
+#define GEC_GLYPH_IMAGE     17
+
+/*
+ * These are encoded in UTF-8.
+ */
+static guchar *help_strings[18] = {
+    (guchar *) "Draw",
+    (guchar *) "Move",
+    (guchar *) "Copy",
+    (guchar *) "Flip Horizontally",
+    (guchar *) "Flip Vertically",
+    (guchar *) "Shear Â±45°",
+    (guchar *) "Rotate -90°",
+    (guchar *) "Rotate +90°",
+    (guchar *) "Rotate Â±359°",
+    (guchar *) "Shift Up+Left",
+    (guchar *) "Shift Up",
+    (guchar *) "Shift Up+Right",
+    (guchar *) "Shift Left",
+    (guchar *) "Shift Right",
+    (guchar *) "Shift Down+Left",
+    (guchar *) "Shift Down",
+    (guchar *) "Shift Down+Right",
+    (guchar *) "Glyph Image",
+};
+
+/*
+ * Position all the buttons in the space provided.
+ */
+static void
+gecontrol_position_buttons(GtkWidget *w)
+{
+    GEControl *ge = GECONTROL(w);
+    gint x, y, sx, sy, ix, dx, i, j, v, wd, ht;
+    GdkPoint points[5];
+
+    dx = 0;
+
+    /*
+     * Determine the starting x and y coordinates, centered in the
+     * window. Modify later to make room for the color strip on the left side
+     * of the window.
+     */
+
+    sx = (w->allocation.width >> 1) - (((GEC_TOGGLE_SIZE * 3) + 6) >> 1);
+    v = (GEC_TOGGLE_SIZE + (GEC_BUTTON_SIZE * 5)) + 15;
+
+    if (ge->gimage != 0) {
+        /*
+         * The addition of 7 includes a space of 3 between the glyph image and
+         * the row of toggles, and 2 pixels on the top and bottom of the glyph
+         * image for a box that will be drawn around it and a single pixel
+         * between the edge of the box and the actual glyph image.
+         */
+        v += ge->gimage->height + 7;
+
+        /*
+         * Calculate a horizontal offset for the toggles and buttons in the
+         * case of 8 bits per pixel. The color selection square is 128x128 so
+         * everything else needs to move over to accomodate it.
+         */
+        if (ge->gimage->bpp == 8 && sx < 128 + 8)
+          dx = (128 + 8) - sx;
+    }
+
+    sx += dx;
+    sy = (w->allocation.height >> 1) - (v >> 1);
+
+    /*
+     * Position the glyph image first if one is present.
+     */
+    if (ge->gimage != 0) {
+        /*
+         * The addition of 2 is for the box and an empty row around the glyph
+         * image.
+         */
+        ix = ((w->allocation.width >> 1)-((ge->gimage->width + 2) >> 1)) + dx;
+
+        if (ge->buttons[GEC_GLYPH_IMAGE].region == NULL) {
+            /* Top left. */
+            points[0].x = points[4].x = ix;
+            points[0].y = points[4].y = sy;
+            /* Top right. */
+            points[1].x = ix + ge->gimage->width + 4;
+            points[1].y = points[0].y;
+            /* Bottom right. */
+            points[2].x = points[1].x;
+            points[2].y = sy + ge->gimage->height + 4;
+            /* Bottom left. */
+            points[3].x = points[0].x;
+            points[3].y = points[2].y;
+            ge->buttons[GEC_GLYPH_IMAGE].region =
+                gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+        } else
+          gdk_region_offset(ge->buttons[GEC_GLYPH_IMAGE].region,
+                            ix - ge->buttons[GEC_GLYPH_IMAGE].x,
+                            sy - ge->buttons[GEC_GLYPH_IMAGE].y);
+
+        ge->gimage->x = ge->buttons[GEC_GLYPH_IMAGE].x = ix;
+        ge->gimage->y = ge->buttons[GEC_GLYPH_IMAGE].y = sy;
+
+        sy += ge->gimage->height + 7;
+    }
+
+    x = sx;
+    y = sy;
+
+    /*
+     * Prep the points for creating regions for the toggle buttons.
+     */
+    points[0].x = points[4].x = x;
+    points[0].y = points[4].y = y + (GEC_TOGGLE_SIZE >> 1);
+    points[1].x = x + (GEC_TOGGLE_SIZE >> 1);
+    points[1].y = y;
+    points[2].x = x + GEC_TOGGLE_SIZE;
+    points[2].y = points[0].y;
+    points[3].x = points[1].x;
+    points[3].y = y + GEC_TOGGLE_SIZE;
+
+    /*
+     * Position the toggle buttons.
+     */
+    for (i = 0; i < GEC_FLIPH_BUTTON; i++) {
+        if (ge->buttons[i].region == NULL)
+          ge->buttons[i].region =
+              gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+        else
+          gdk_region_offset(ge->buttons[i].region,
+                            x - ge->buttons[i].x, y - ge->buttons[i].y);
+
+        ge->buttons[i].x = x;
+        ge->buttons[i].y = y;
+
+        x += GEC_TOGGLE_SIZE + 3;
+
+        for (j = 0; j < 5; j++)
+          points[j].x += GEC_TOGGLE_SIZE + 3;
+    }
+
+    /*
+     * Recalculate the starting x position based on the button size instead
+     * of the toggle size.
+     */
+    sx = ((w->allocation.width >> 1)-(((GEC_BUTTON_SIZE * 3) + 6) >> 1)) + dx;
+
+    y += GEC_TOGGLE_SIZE + 3;
+
+    /*
+     * Now set up the points for the buttons.
+     */
+    points[0].x = sx;
+    points[0].y = y;
+    points[1].x = sx + GEC_BUTTON_SIZE;
+    points[1].y = points[0].y;
+    points[2].x = points[1].x;
+    points[2].y = y + GEC_BUTTON_SIZE;
+    points[3].x = points[0].x;
+    points[3].y = points[2].y;
+
+    /*
+     * Position the first row of buttons.
+     */
+    for (x = sx; i < GEC_RLEFT_BUTTON; i++) {
+        if (ge->buttons[i].region == NULL)
+          ge->buttons[i].region =
+              gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+        else
+          gdk_region_offset(ge->buttons[i].region,
+                            x - ge->buttons[i].x, y - ge->buttons[i].y);
+        ge->buttons[i].x = x;
+        ge->buttons[i].y = y;
+
+        x += GEC_BUTTON_SIZE + 3;
+
+        for (j = 0; j < 4; j++)
+          points[j].x += GEC_BUTTON_SIZE + 3;
+    }
+
+    /*
+     * Reset the x coordinate for the regions.
+     */
+    points[0].x = points[3].x = sx;
+    points[1].x = points[2].x = sx + GEC_BUTTON_SIZE;
+
+    y += GEC_BUTTON_SIZE + 3;
+
+    for (j = 0; j < 4; j++)
+      points[j].y += GEC_BUTTON_SIZE + 3;
+
+    /*
+     * Position second row of buttons.
+     */
+    for (x = sx; i < GEC_ULEFT_BUTTON; i++) {
+        if (ge->buttons[i].region == NULL)
+          ge->buttons[i].region =
+              gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+        else
+          gdk_region_offset(ge->buttons[i].region,
+                            x - ge->buttons[i].x, y - ge->buttons[i].y);
+        ge->buttons[i].x = x;
+        ge->buttons[i].y = y;
+
+        x += GEC_BUTTON_SIZE + 3;
+
+        for (j = 0; j < 4; j++)
+          points[j].x += GEC_BUTTON_SIZE + 3;
+    }
+
+    /*
+     * Reset the x coordinate for the regions.
+     */
+    points[0].x = points[3].x = sx;
+    points[1].x = points[2].x = sx + GEC_BUTTON_SIZE;
+
+    y += GEC_BUTTON_SIZE + 3;
+
+    for (j = 0; j < 4; j++)
+      points[j].y += GEC_BUTTON_SIZE + 3;
+
+    /*
+     * Position third row of buttons.
+     */
+    for (x = sx; i < GEC_LEFT_BUTTON; i++) {
+        if (ge->buttons[i].region == NULL)
+          ge->buttons[i].region =
+              gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+        else
+          gdk_region_offset(ge->buttons[i].region,
+                            x - ge->buttons[i].x, y - ge->buttons[i].y);
+        ge->buttons[i].x = x;
+        ge->buttons[i].y = y;
+
+        x += GEC_BUTTON_SIZE + 3;
+
+        for (j = 0; j < 4; j++)
+          points[j].x += GEC_BUTTON_SIZE + 3;
+    }
+
+    /*
+     * Reset the x coordinate for the regions.
+     */
+    points[0].x = points[3].x = sx;
+    points[1].x = points[2].x = sx + GEC_BUTTON_SIZE;
+
+    x = sx;
+    y += GEC_BUTTON_SIZE + 3;
+
+    for (j = 0; j < 4; j++)
+      points[j].y += GEC_BUTTON_SIZE + 3;
+
+    /*
+     * Set the coordinates of the LEFT and RIGHT buttons.
+     */
+    if (ge->buttons[i].region == NULL)
+      ge->buttons[i].region =
+          gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+    else
+      gdk_region_offset(ge->buttons[i].region,
+                        x - ge->buttons[i].x, y - ge->buttons[i].y);
+    ge->buttons[i].x = x;
+    ge->buttons[i++].y = y;
+
+    x += (GEC_BUTTON_SIZE + 3) * 2;
+
+    for (j = 0; j < 4; j++)
+      points[j].x += (GEC_BUTTON_SIZE + 3) * 2;
+
+    if (ge->buttons[i].region == NULL)
+      ge->buttons[i].region =
+          gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+    else
+      gdk_region_offset(ge->buttons[i].region,
+                        x - ge->buttons[i].x, y - ge->buttons[i].y);
+    ge->buttons[i].x = x;
+    ge->buttons[i++].y = y;
+
+    /*
+     * Reset the x coordinate for the regions.
+     */
+    points[0].x = points[3].x = sx;
+    points[1].x = points[2].x = sx + GEC_BUTTON_SIZE;
+
+    y += GEC_BUTTON_SIZE + 3;
+
+    for (j = 0; j < 4; j++)
+      points[j].y += GEC_BUTTON_SIZE + 3;
+
+    for (x = sx; i < GEC_GLYPH_IMAGE; i++) {
+        if (ge->buttons[i].region == NULL)
+          ge->buttons[i].region =
+              gdk_region_polygon(points, 4, GDK_WINDING_RULE);
+        else
+          gdk_region_offset(ge->buttons[i].region,
+                            x - ge->buttons[i].x, y - ge->buttons[i].y);
+        ge->buttons[i].x = x;
+        ge->buttons[i].y = y;
+
+        x += GEC_BUTTON_SIZE + 3;
+
+        for (j = 0; j < 4; j++)
+          points[j].x += GEC_BUTTON_SIZE + 3;
+    }
+
+    /*
+     * Now position the color spots if they are needed.
+     */
+
+    if (ge->gimage && ge->gimage->bpp > 1) {
+        if (ge->gimage->bpp == 2 || ge->gimage->bpp == 4) {
+            /*
+             * The starting horizontal position is 1/2 way between the left
+             * edge of the window and the left edge of the buttons. The
+             * starting vertical position is centered on the left edge of the
+             * buttons.
+             */
+            sx = (sx >> 1) - (8 >> 1);
+            y = (8 * (1 << ge->gimage->bpp)) + ((1 << ge->gimage->bpp) - 1);
+            sy = ge->buttons[GEC_FLIPH_BUTTON].y +
+                ((w->allocation.height-ge->buttons[GEC_FLIPH_BUTTON].y)>>1) -
+                (y >> 1);
+            wd = 8;
+            ht = 8 * (1 << ge->gimage->bpp);
+        } else {
+            sx = (sx >> 1) - (128 >> 1);
+            sy = ge->buttons[GEC_FLIPH_BUTTON].y +
+                ((w->allocation.height-ge->buttons[GEC_FLIPH_BUTTON].y)>>1) -
+                (128 >> 1);
+            wd = ht = 128;
+        }
+
+        /*
+         * Initialize the points for the spot region.
+         */
+
+        /* Top left. */
+        points[0].x = points[4].x = sx;
+        points[0].y = points[4].y = sy;
+        /* Top right. */
+        points[1].x = points[0].x + wd;
+        points[1].y = points[0].y;
+        /* Bottom right. */
+        points[2].x = points[1].x;
+        points[2].y = points[1].y + ht;
+        /* Bottom left. */
+        points[3].x = points[0].x;
+        points[3].y = points[2].y;
+
+        if (ge->spot_region == NULL)
+          ge->spot_region = gdk_region_polygon(points, 4,
+                                               GDK_WINDING_RULE);
+        else
+          gdk_region_offset(ge->spot_region,
+                            sx - ge->spot.x, sy - ge->spot.y);
+
+        ge->spot.x = sx;
+        ge->spot.y = sy;
+        ge->spot.width = wd;
+        ge->spot.height = ht;
+    }
+}
+
+/**********************************************************************
+ *
+ * Initialization routines.
+ *
+ **********************************************************************/
+
+static void
+gecontrol_finalize(GObject *obj)
+{
+    gint i;
+    GEControl *ge;
+    GEControlClass *gwc;
+
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_GECONTROL(obj));
+
+    /*
+     * Destroy all the regions for the buttons.
+     */
+    ge = GECONTROL(obj);
+    for (i = 0; i < 18; i++) {
+        if (ge->buttons[i].region != 0)
+          gdk_region_destroy(ge->buttons[i].region);
+        ge->buttons[i].region = 0;
+    }
+
+    /*
+     * Make sure the image is removed if it exists.
+     */
+    if (ge->gimage != 0) {
+        if (ge->gimage->bytes > 0)
+          g_free(ge->gimage->bitmap);
+        g_free(ge->gimage);
+        ge->gimage = 0;
+    }
+
+    gwc = GECONTROL_GET_CLASS(obj);
+
+    if (gwc->selgc != 0)
+      g_object_unref(G_OBJECT(gwc->selgc));
+    gwc->selgc = 0;
+
+    /*
+     * Unreference all the pixbufs that were created.
+     */
+    if (gwc->draw != 0) {
+        g_object_unref(G_OBJECT(gwc->draw));
+        g_object_unref(G_OBJECT(gwc->move));
+        g_object_unref(G_OBJECT(gwc->copy));
+        g_object_unref(G_OBJECT(gwc->fliph));
+        g_object_unref(G_OBJECT(gwc->flipv));
+        g_object_unref(G_OBJECT(gwc->shear));
+        g_object_unref(G_OBJECT(gwc->rleft));
+        g_object_unref(G_OBJECT(gwc->rright));
+        g_object_unref(G_OBJECT(gwc->rotate));
+        g_object_unref(G_OBJECT(gwc->uleft));
+        g_object_unref(G_OBJECT(gwc->up));
+        g_object_unref(G_OBJECT(gwc->uright));
+        g_object_unref(G_OBJECT(gwc->left));
+        g_object_unref(G_OBJECT(gwc->right));
+        g_object_unref(G_OBJECT(gwc->dleft));
+        g_object_unref(G_OBJECT(gwc->down));
+        g_object_unref(G_OBJECT(gwc->dright));
+
+        gwc->draw = gwc->move = gwc->copy =
+            gwc->fliph = gwc->flipv = gwc->shear =
+            gwc->rleft = gwc->rright = gwc->rotate =
+            gwc->uleft = gwc->up = gwc->uright =
+            gwc->left = gwc->right =
+            gwc->dleft = gwc->down = gwc->dright = 0;
+    }
+}
+
+static void
+gecontrol_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
+{
+    GEControl *gw = GECONTROL(widget);
+    gint ht;
+
+    preferred->width = 50 + (3 * (GEC_TOGGLE_SIZE + 4)) + 4;
+    preferred->height = (GEC_TOGGLE_SIZE + 6) + ((5 * GEC_BUTTON_SIZE) + 8);
+
+    if (gw->gimage != 0) {
+        /*
+         * The addition of 10 includes a box around the glyph, a line of empty
+         * pixels between the box and the glyph image, and a space of 3 pixels
+         * above and below the glyph image.
+         */
+        preferred->height += gw->gimage->height + 10;
+
+        /*
+         * Determine the height of the color list. Each color spot is 8x8 and
+         * there is a buffer of two pixels on each side, and 2 pixels in
+         * between them vertically.
+         */
+        if (gw->gimage->bpp == 2 || gw->gimage->bpp == 4) {
+            preferred->width += 8 + 4;
+            ht = 8 * (1 << gw->gimage->bpp);
+            preferred->height = MAX(preferred->height, ht);
+        } else if (gw->gimage->bpp == 8) {
+            /*
+             * For 8 bits per pixel, the square is 8x8 spots with 16 spots per
+             * row and 16 rows. This gives 64 + 4 pixels which includes the 2
+             * empties on each side.
+             */
+            preferred->width += 128 + 4;
+            ht = 128 + 4;
+            preferred->height = MAX(preferred->height, ht);
+        }
+    }
+}
+
+static void
+gecontrol_actual_size(GtkWidget *widget, GtkAllocation *actual)
+{
+    widget->allocation = *actual;
+
+    gecontrol_position_buttons(widget);
+
+    if (GTK_WIDGET_REALIZED(widget))
+      gdk_window_move_resize(widget->window, actual->x, actual->y,
+                             actual->width, actual->height);
+}
+
+/*
+ * Use our own painting routines to get the look and behavior we want.
+ */
+static void
+gecontrol_paint_diamond(GtkStyle *style, GdkWindow *win, GtkStateType state,
+                        GdkRectangle *area, gint x, gint y,
+                        gint width, gint height)
+{
+    GdkSegment left[6], right[6];
+
+    /*
+     * Start the left segments at the top. Go from outer to inner.
+     */
+
+    /* Outer. */
+    left[0].x1 = x + (width >> 1);
+    left[0].y1 = y;
+    left[0].x2 = x;
+    left[0].y2 = y + (width >> 1);
+    left[1].x1 = left[0].x2;
+    left[1].y1 = left[0].y2;
+    left[1].x2 = x + (width >> 1);
+    left[1].y2 = y + height;
+
+    /* Middle. */
+    left[2].x1 = left[0].x1;
+    left[2].y1 = left[0].y1 + 1;
+    left[2].x2 = left[0].x2 + 1;
+    left[2].y2 = left[0].y2;
+    left[3].x1 = left[2].x2;
+    left[3].y1 = left[2].y2;
+    left[3].x2 = left[1].x2;
+    left[3].y2 = left[1].y2 - 1;
+
+    /* Inner. */
+    left[4].x1 = left[2].x1;
+    left[4].y1 = left[2].y1 + 1;
+    left[4].x2 = left[2].x2 + 1;
+    left[4].y2 = left[2].y2;
+    left[5].x1 = left[4].x2;
+    left[5].y1 = left[4].y2;
+    left[5].x2 = left[3].x2;
+    left[5].y2 = left[3].y2 - 1;
+
+    /* Outer. */
+    right[0].x1 = x + (width >> 1);
+    right[0].y1 = y;
+    right[0].x2 = x + width;
+    right[0].y2 = y + (width >> 1);
+    right[1].x1 = right[0].x2;
+    right[1].y1 = right[0].y2;
+    right[1].x2 = x + (width >> 1);
+    right[1].y2 = y + height;
+
+    /* Middle. */
+    right[2].x1 = right[0].x1;
+    right[2].y1 = right[0].y1 + 1;
+    right[2].x2 = right[0].x2 - 1;
+    right[2].y2 = right[0].y2;
+    right[3].x1 = right[2].x2;
+    right[3].y1 = right[2].y2;
+    right[3].x2 = right[1].x2;
+    right[3].y2 = right[1].y2 - 1;
+
+    /* Inner. */
+    right[4].x1 = right[2].x1;
+    right[4].y1 = right[2].y1 + 1;
+    right[4].x2 = right[2].x2 - 1;
+    right[4].y2 = right[2].y2;
+    right[5].x1 = right[4].x2;
+    right[5].y1 = right[4].y2;
+    right[5].x2 = right[3].x2;
+    right[5].y2 = right[3].y2 - 1;
+
+    if (area) {
+        gdk_gc_set_clip_rectangle(style->bg_gc[state], area);
+        gdk_gc_set_clip_rectangle(style->light_gc[state], area);
+        gdk_gc_set_clip_rectangle(style->dark_gc[state], area);
+        gdk_gc_set_clip_rectangle(style->black_gc, area);
+    }
+
+    if (state != GTK_STATE_ACTIVE) {
+        gdk_draw_segments(win, style->light_gc[state], left, 4);
+        gdk_draw_segments(win, style->bg_gc[state], &left[4], 2);
+        gdk_draw_segments(win, style->black_gc, right, 2);
+        gdk_draw_segments(win, style->dark_gc[state], &right[2], 4);
+    } else {
+        gdk_draw_segments(win, style->dark_gc[state], left, 4);
+        gdk_draw_segments(win, style->black_gc, &left[4], 2);
+        gdk_draw_segments(win, style->light_gc[state], right, 4);
+        gdk_draw_segments(win, style->bg_gc[state], &right[4], 2);
+    }
+
+    if (area) {
+        gdk_gc_set_clip_rectangle(style->bg_gc[state], NULL);
+        gdk_gc_set_clip_rectangle(style->light_gc[state], NULL);
+        gdk_gc_set_clip_rectangle(style->dark_gc[state], NULL);
+        gdk_gc_set_clip_rectangle(style->black_gc, NULL);
+    }
+}
+
+static void
+gecontrol_button_normal(GEControl *ge, gint button)
+{
+    gint v;
+    GtkWidget *w = GTK_WIDGET(ge);
+    GdkPoint points[4];
+
+    if (button == GEC_GLYPH_IMAGE)
+      return;
+
+    if (button < 3) {
+        gecontrol_paint_diamond(w->style, w->window, GTK_STATE_NORMAL, 0,
+                                ge->buttons[button].x, ge->buttons[button].y,
+                                GEC_TOGGLE_SIZE, GEC_TOGGLE_SIZE);
+
+        points[0].x = ge->buttons[button].x + (GEC_TOGGLE_SIZE >> 1);
+        points[0].y = ge->buttons[button].y + 3;
+        points[1].x = ge->buttons[button].x + 3;
+        points[1].y = ge->buttons[button].y + (GEC_TOGGLE_SIZE >> 1);
+        points[2].x = points[0].x;
+        points[2].y = ge->buttons[button].y + GEC_TOGGLE_SIZE - 3;
+        points[3].x = ge->buttons[button].x + GEC_TOGGLE_SIZE - 3;
+        points[3].y = points[1].y;
+
+        gdk_draw_polygon(w->window, w->style->bg_gc[GTK_STATE_NORMAL], TRUE,
+                         points, 4);
+
+        v = (GEC_TOGGLE_SIZE >> 1) - (BMAP_DIM >> 1);
+
+    } else {
+        gtk_paint_box(w->style, w->window, GTK_STATE_NORMAL,
+                      GTK_SHADOW_OUT, 0, GTK_WIDGET(ge), "gectrl",
+                      ge->buttons[button].x, ge->buttons[button].y,
+                      GEC_BUTTON_SIZE, GEC_BUTTON_SIZE);
+
+        v = (GEC_BUTTON_SIZE >> 1) - (BMAP_DIM >> 1);
+    }
+
+    gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)],
+                    ge->buttons[button].image, 0, 0,
+                    ge->buttons[button].x + v, ge->buttons[button].y + v,
+                    BMAP_DIM, BMAP_DIM, GDK_RGB_DITHER_NONE, 0, 0);
+}
+
+static void
+gecontrol_button_prelight(GEControl *ge, gint button)
+{
+    gint v;
+    GtkWidget *w = GTK_WIDGET(ge);
+    GdkPoint points[4];
+
+    if (button == GEC_GLYPH_IMAGE)
+      return;
+
+    if (button < 3) {
+        gecontrol_paint_diamond(w->style, w->window, GTK_STATE_PRELIGHT, 0,
+                                ge->buttons[button].x, ge->buttons[button].y,
+                                GEC_TOGGLE_SIZE, GEC_TOGGLE_SIZE);
+
+        points[0].x = ge->buttons[button].x + (GEC_TOGGLE_SIZE >> 1);
+        points[0].y = ge->buttons[button].y + 3;
+        points[1].x = ge->buttons[button].x + 3;
+        points[1].y = ge->buttons[button].y + (GEC_TOGGLE_SIZE >> 1);
+        points[2].x = points[0].x;
+        points[2].y = ge->buttons[button].y + GEC_TOGGLE_SIZE - 3;
+        points[3].x = ge->buttons[button].x + GEC_TOGGLE_SIZE - 3;
+        points[3].y = points[1].y;
+
+        gdk_draw_polygon(w->window, w->style->bg_gc[GTK_STATE_PRELIGHT], TRUE,
+                         points, 4);
+        v = (GEC_TOGGLE_SIZE >> 1) - (BMAP_DIM >> 1);
+    } else {
+        gtk_paint_box(w->style, w->window, GTK_STATE_PRELIGHT,
+                      GTK_SHADOW_OUT, 0, GTK_WIDGET(ge), "gectrl",
+                      ge->buttons[button].x, ge->buttons[button].y,
+                      GEC_BUTTON_SIZE, GEC_BUTTON_SIZE);
+        v = (GEC_BUTTON_SIZE >> 1) - (BMAP_DIM >> 1);
+    }
+
+    gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)],
+                    ge->buttons[button].image, 0, 0,
+                    ge->buttons[button].x + v, ge->buttons[button].y + v,
+                    BMAP_DIM, BMAP_DIM, GDK_RGB_DITHER_NONE, 0, 0);
+}
+
+static void
+gecontrol_button_active(GEControl *ge, gint button)
+{
+    gint v;
+    GtkWidget *w = GTK_WIDGET(ge);
+    GdkPoint points[4];
+
+    if (button == GEC_GLYPH_IMAGE)
+      return;
+
+    if (button < 3) {
+        gecontrol_paint_diamond(w->style, w->window, GTK_STATE_ACTIVE, 0,
+                                ge->buttons[button].x, ge->buttons[button].y,
+                                GEC_TOGGLE_SIZE, GEC_TOGGLE_SIZE);
+
+        points[0].x = ge->buttons[button].x + (GEC_TOGGLE_SIZE >> 1);
+        points[0].y = ge->buttons[button].y + 3;
+        points[1].x = ge->buttons[button].x + 3;
+        points[1].y = ge->buttons[button].y + (GEC_TOGGLE_SIZE >> 1);
+        points[2].x = points[0].x;
+        points[2].y = ge->buttons[button].y + GEC_TOGGLE_SIZE - 3;
+        points[3].x = ge->buttons[button].x + GEC_TOGGLE_SIZE - 3;
+        points[3].y = points[1].y;
+
+        gdk_draw_polygon(w->window, w->style->bg_gc[GTK_STATE_ACTIVE], TRUE,
+                         points, 4);
+        v = (GEC_TOGGLE_SIZE >> 1) - (BMAP_DIM >> 1);
+    } else {
+        gtk_paint_box(w->style, w->window, GTK_STATE_ACTIVE,
+                      GTK_SHADOW_IN, 0, GTK_WIDGET(ge), "gectrl",
+                      ge->buttons[button].x, ge->buttons[button].y,
+                      GEC_BUTTON_SIZE, GEC_BUTTON_SIZE);
+        v = (GEC_BUTTON_SIZE >> 1) - (BMAP_DIM >> 1);
+    }
+
+    gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)],
+                    ge->buttons[button].image, 0, 0,
+                    ge->buttons[button].x + v, ge->buttons[button].y + v,
+                    BMAP_DIM, BMAP_DIM, GDK_RGB_DITHER_NONE, 0, 0);
+}
+
+#if 0
+static void
+gecontrol_get_image_pixels(GEControl *ge, gint color)
+{
+    gint byte;
+    guint16 x, y, bpr, si, di, nx;
+    guchar *masks;
+    bdf_bitmap_t *im;
+
+    im = ge->gimage;
+    ge->points_used = 0;
+
+    di = 0;
+    masks = 0;
+    switch (im->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    bpr = ((im->width * im->bpp) + 7) >> 3;
+    for (y = 0; y < im->height; y++) {
+        for (nx = x = 0; x < im->width; x++, nx += im->bpp) {
+            si = (nx & 7) / im->bpp;
+
+            byte = im->bitmap[(y * bpr) + (nx >> 3)] & masks[si];
+            if (di > si)
+              byte >>= (di - si) * im->bpp;
+            if (byte == color) {
+                if (ge->points_used == ge->points_size) {
+                    if (ge->points_size == 0)
+                      ge->points =
+                          (GdkPoint *) g_malloc(sizeof(GdkPoint) * 64);
+                    else
+                      ge->points = (GdkPoint *)
+                          g_realloc(ge->points,
+                                    sizeof(GdkPoint) *
+                                    (ge->points_size + 64));;
+                    ge->points_size += 64;
+                }
+                ge->points[ge->points_used].x = x + im->x + 2;
+                ge->points[ge->points_used].y = y + im->y + 2;
+                ge->points_used++;
+            }
+        }
+    }
+}
+#endif
+
+static void
+gecontrol_make_rgb_glyph(GEControl *ge)
+{
+    GtkWidget *w = GTK_WIDGET(ge);
+    gint byte = 0;
+    guint16 x, y, bpr, rgb_bpr, si, di, nx;
+    guchar bg[4], pix[4], *masks, *img;
+    bdf_bitmap_t *im;
+
+    /*
+     * First, get the background color of the widget for the empty
+     * pixels.
+     */
+    bg[0] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].red;
+    bg[1] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].green;
+    bg[2] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].blue;
+
+    im = ge->gimage;
+
+    di = 0;
+    masks = 0;
+    switch (im->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    bpr = ((im->width * im->bpp) + 7) >> 3;
+
+    rgb_bpr = im->width * 3;
+    ge->rgb_used = rgb_bpr * im->height;
+
+    /*
+     * Make sure there is enough storage space for the image.
+     */
+    if (ge->rgb_size < ge->rgb_used) {
+        if (ge->rgb_size == 0)
+          ge->rgb = g_malloc(ge->rgb_used);
+        else
+          ge->rgb = g_realloc(ge->rgb, ge->rgb_used);
+        ge->rgb_size = ge->rgb_used;
+    }
+
+    img = ge->rgb;
+
+    for (y = 0; y < im->height; y++) {
+        for (nx = x = 0; x < im->width; x++, nx += im->bpp) {
+            si = (nx & 7) / im->bpp;
+
+            byte = im->bitmap[(y * bpr) + (nx >> 3)] & masks[si];
+            if (di > si)
+              byte >>= (di - si) * im->bpp;
+            if (byte) {
+                switch (im->bpp) {
+                  case 1: memset(pix, 0, 3); break;
+                  case 2: memset(pix, ge->colors[byte-1], 3); break;
+                  case 4: memset(pix, ge->colors[byte-1+4], 3); break;
+                  case 8: memset(pix, byte-1, 3); break;
+                }
+            } else
+              memcpy(pix, bg, 3);
+
+            memcpy(&ge->rgb[(y * rgb_bpr) + (x * 3)], pix, 3);
+        }
+    }
+}
+
+static void
+gecontrol_highlight_selected_spot(GEControl *ge)
+{
+    GtkWidget *w = GTK_WIDGET(ge);
+    gint x, y;
+    GEControlClass *gec = GECONTROL_GET_CLASS(ge);
+
+    if (!GTK_WIDGET_REALIZED(w) || ge->gimage == 0 || ge->gimage->bpp == 1)
+      return;
+
+    if (ge->gimage->bpp != 8) {
+        x = ge->spot.x;
+        y = ge->spot.y + (8 * ge->cidx);
+    } else {
+        x = ge->spot.x + ((ge->cidx % 16) * 8);
+        y = ge->spot.y + ((ge->cidx / 16) * 8);
+    }
+    gdk_draw_rectangle(GTK_WIDGET(ge)->window, gec->selgc, FALSE, x, y, 7, 7);
+}
+
+static void
+gecontrol_make_color_spots(GEControl *ge, gint bpp)
+{
+    gint i, j, c, wd, ht, bytes;
+
+    if (bpp < 2 || bpp > 8)
+      return;
+
+    bytes = wd = ht = 0;
+
+    switch (bpp) {
+      case 2:
+      case 4:
+        wd = 8;
+        ht = 8 * (1 << bpp);
+        break;
+      case 8:
+        wd = ht = 128;
+        break;
+    }
+    bytes = wd * ht;
+
+    if (ge->rgb_size < bytes) {
+        if (ge->rgb_size == 0)
+          ge->rgb = g_malloc(bytes);
+        else
+          ge->rgb = g_realloc(ge->rgb, bytes);
+        ge->rgb_size = bytes;
+    }
+    ge->rgb_used = bytes;
+
+    /*
+     * Now create the color spots image.
+     */
+    if (bpp != 8) {
+        for (i = 0; i < (1 << bpp); i++) {
+            if (bpp == 2)
+              memset(ge->rgb+(i*64), ge->colors[i], 64);
+            else
+              memset(ge->rgb+(i*64), ge->colors[i+4], 64);
+        }
+    } else {
+        for (c = i = 0; i < 128; i += 8) {
+            for (j = 0; j < 128; j += 8, c++)
+              memset(ge->rgb+((i*128)+j), c, 8);
+            memcpy(ge->rgb+((i+1)*128), ge->rgb+(i*128), 128);
+            memcpy(ge->rgb+((i+2)*128), ge->rgb+(i*128), 256);
+            memcpy(ge->rgb+((i+4)*128), ge->rgb+(i*128), 512);
+        }
+    }
+}
+
+static void
+gecontrol_draw_glyph_image(GEControl *ge)
+{
+    GtkWidget *w = GTK_WIDGET(ge);
+
+    if (ge->gimage == 0 || !GTK_WIDGET_REALIZED(w))
+      return;
+
+    /*
+     * 1. Draw the box around the image.
+     */
+    gdk_draw_rectangle(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)],
+                       FALSE, ge->gimage->x, ge->gimage->y,
+                       ge->gimage->width + 4, ge->gimage->height + 4);
+
+    /*
+     * 2. Clear the space inside the rectangle.
+     */
+    gdk_window_clear_area(w->window, ge->gimage->x + 1, ge->gimage->y + 1,
+                          ge->gimage->width - 2, ge->gimage->height - 2);
+
+    /*
+     * 3. Draw the points.
+     */
+    gecontrol_make_rgb_glyph(ge);
+    gdk_draw_rgb_image(w->window,
+                       w->style->bg_gc[GTK_WIDGET_STATE(w)],
+                       ge->gimage->x + 2, ge->gimage->y + 2,
+                       ge->gimage->width, ge->gimage->height,
+                       GDK_RGB_DITHER_NONE, ge->rgb,
+                       ge->gimage->width * 3);
+}
+
+static gboolean
+gecontrol_expose(GtkWidget *w, GdkEventExpose *ev)
+{
+    gint i;
+    GEControl *ge = GECONTROL(w);
+    GEControlClass *gec = GECONTROL_GET_CLASS(w);
+    GdkGCValues gcv;
+
+    /*
+     * Draw the glyph image if one was provided.
+     */
+    gecontrol_draw_glyph_image(ge);
+
+    for (i = 0; i < GEC_GLYPH_IMAGE; i++) {
+        if (ge->buttons[i].set)
+          gecontrol_button_active(ge, i);
+        else
+          gecontrol_button_normal(ge, i);
+    }
+
+    /*
+     * Draw the color spots if called for.
+     */
+    if (ge->gimage && ge->gimage->bpp > 1) {
+        /*
+         * Make sure the selection GC has been created.
+         */
+        if (gec->selgc == 0) {
+            gcv.foreground.pixel = w->style->fg[GTK_WIDGET_STATE(w)].pixel;
+            gcv.background.pixel = w->style->bg[GTK_WIDGET_STATE(w)].pixel;
+            gcv.foreground.pixel ^= gcv.background.pixel;
+            gcv.function = GDK_XOR;
+            gec->selgc = gdk_gc_new_with_values(w->window, &gcv,
+                                                GDK_GC_FOREGROUND|GDK_GC_BACKGROUND|GDK_GC_FUNCTION);
+        }
+
+        gecontrol_make_color_spots(ge, ge->gimage->bpp);
+
+        gdk_draw_gray_image(w->window,
+                            w->style->fg_gc[GTK_WIDGET_STATE(w)],
+                            ge->spot.x, ge->spot.y,
+                            ge->spot.width, ge->spot.height,
+                            GDK_RGB_DITHER_NONE, ge->rgb, ge->spot.width);
+
+        /*
+         * Draw the box around the active color.
+         */
+        gecontrol_highlight_selected_spot(ge);
+    }
+    return FALSE;
+}
+
+static gboolean
+gecontrol_motion_notify(GtkWidget *w, GdkEventMotion *ev)
+{
+    gint i, x, y;
+    GEControl *ge;
+    gchar buf[24];
+
+    ge = GECONTROL(w);
+    for (i = 0; i < 18; i++) {
+        if (ge->buttons[i].region != NULL &&
+            gdk_region_point_in(ge->buttons[i].region, ev->x, ev->y)) {
+            if (i != ge->last_button) {
+
+                /*
+                 * Turn of the prelight on the previous button.
+                 */
+                if (ge->last_button >= 0) {
+                    if (ge->buttons[ge->last_button].set == FALSE)
+                      gecontrol_button_normal(ge, ge->last_button);
+                }
+
+                if (ge->buttons[i].set == FALSE)
+                  gecontrol_button_prelight(ge, i);
+
+                if (ge->tip_label != 0)
+                  gtk_label_set_text(GTK_LABEL(ge->tip_label),
+                                     (gchar *) ge->buttons[i].help);
+                ge->last_button = i;
+            }
+            break;
+        }
+    }
+    if (i == 18) {
+        /*
+         * Now check to see if the pointer is in the color spot. Only
+         * if the tip label exists. No reason to know this info other than
+         * to inform the user.
+         */
+        if (ge->tip_label) {
+            if (ge->spot_region != NULL &&
+                gdk_region_point_in(ge->spot_region, ev->x, ev->y)) {
+                /*
+                 * Determine which color this is and it's value. Mask
+                 * the coordinates so they can't overflow the text buffer
+                 * if they somehow get too large.
+                 */
+                x = (((guint) ev->x) - ge->spot.x) & 0xff;
+                y = (((guint) ev->y) - ge->spot.y) & 0xff;
+                
+                if (ge->gimage->bpp == 2)
+                  sprintf(buf, "Color: %03d Gray: %03d", (y>>3)+1,
+                          ge->colors[y>>3]);
+                else if (ge->gimage->bpp == 4)
+                  sprintf(buf, "Color: %03d Gray: %03d", (y>>3)+1,
+                          ge->colors[(y>>3) + 4]);
+                else {
+                    /*
+                     * Divide x and y by 4 (spots are 4x4 in the 8bpp image)
+                     * to get row and column.
+                     */
+                    x >>= 3;
+                    y >>= 3;
+                    sprintf(buf, "Color: %03d Gray: %03d",
+                            ((y<<4)+x)+1, (y<<4)+x);
+                }
+                gtk_label_set_text(GTK_LABEL(ge->tip_label), buf);
+                return FALSE;
+            }
+        }
+
+        if (ge->tip_label != 0)
+          gtk_label_set_text(GTK_LABEL(ge->tip_label), "");
+        if (ge->last_button >= 0) {
+            if (ge->buttons[ge->last_button].set == FALSE)
+              gecontrol_button_normal(ge, ge->last_button);
+        }
+        ge->last_button = -1;
+    }
+    return FALSE;
+}
+
+static gboolean
+handle_timeout(gpointer data)
+{
+    GEControl *ge = GECONTROL(data);
+    GEControlActivateInfo ai;
+
+    if (ge->timer_button < 0 || ge->buttons[ge->timer_button].set == FALSE) {
+        ge->timer_button = -1;
+        return FALSE;
+    }
+
+    /*
+     * Emit the operation signal here.
+     */
+    ai.operation = (GEControlOperation) ge->timer_button;
+    g_signal_emit(G_OBJECT(ge), gecontrol_signals[ACTIVATE], 0, &ai);
+    ge->timer_count++;
+    return TRUE;
+}
+
+static gboolean
+gecontrol_button_press(GtkWidget *w, GdkEventButton *ev)
+{
+    gint i, o;
+    GEControl *ge = GECONTROL(w);
+
+    for (i = 0; i < 17; i++) {
+        if (ge->buttons[i].region != 0 &&
+            gdk_region_point_in(ge->buttons[i].region, ev->x, ev->y)) {
+            if (i < 3) {
+                if (ge->buttons[i].set == TRUE)
+                  /*
+                   * The toggle button is already set. Simply return.
+                   */
+                  return FALSE;
+
+                /*
+                 * Clear the button that is set.
+                 */
+                o = (ge->buttons[ge->buttons[i].other_toggles[0]].set) ?
+                    ge->buttons[i].other_toggles[0] :
+                    ge->buttons[i].other_toggles[1];
+                gecontrol_button_normal(ge, o);
+                ge->buttons[o].set = FALSE;
+            }
+            gecontrol_button_active(ge, i);
+            ge->buttons[i].set = TRUE;
+
+            /*
+             * If this is any of the shift buttons, add a timer so it
+             * will be handled multiple times.
+             */
+            if (i >= GEC_ULEFT_BUTTON && i <= GEC_DRIGHT_BUTTON) {
+                ge->timer_count = 0;
+                ge->timer_button = i;
+                ge->timer = g_timeout_add(100, handle_timeout,
+                                          (gpointer) ge);
+            }
+            break;
+        }
+    }
+
+    return FALSE;
+}
+
+static gboolean
+gecontrol_button_release(GtkWidget *w, GdkEventButton *ev)
+{
+    gint i, x, y;
+    GEControl *ge = GECONTROL(w);
+    GEControlActivateInfo ai;
+
+    for (i = 0; i < 17; i++) {
+        if (ge->buttons[i].region != 0 &&
+            gdk_region_point_in(ge->buttons[i].region, ev->x, ev->y)) {
+            if (i >= 3) {
+                gecontrol_button_prelight(ge, i);
+                ge->buttons[i].set = FALSE;
+            }
+            if (ge->timer_count == 0) {
+                ai.operation = (GEControlOperation) i;
+                g_signal_emit(G_OBJECT(ge), gecontrol_signals[ACTIVATE], 0,
+                              &ai);
+            } else
+              /*
+               * Simply reset the timer count because the signal was emitted
+               * in the timeout handler, probably more than once.
+               */
+              ge->timer_count = 0;
+            break;
+        }
+    }
+    if (i == 17) {
+        /*
+         * Check to see if one of the colors was selected.
+         */
+        if (ge->gimage || ge->gimage->bpp > 1) {
+            if (ge->spot_region != NULL &&
+                gdk_region_point_in(ge->spot_region, ev->x, ev->y)) {
+                x = (((guint) ev->x) - ge->spot.x) & 0xff;
+                y = (((guint) ev->y) - ge->spot.y) & 0xff;
+                if (ge->gimage->bpp != 8)
+                  i = y >> 3;
+                else {
+                    x >>= 3;
+                    y >>= 3;
+                    i = (y << 4) + x;
+                }
+                gecontrol_highlight_selected_spot(ge);
+                ge->cidx = i;
+                gecontrol_highlight_selected_spot(ge);
+
+                ai.operation = GECONTROL_COLOR_CHANGE;
+                ai.color = ge->cidx + 1;
+                g_signal_emit(G_OBJECT(ge), gecontrol_signals[ACTIVATE],
+                              0, &ai);
+            }
+        }
+    }
+    return FALSE;
+}
+
+static void
+gecontrol_set_property(GObject *obj, guint prop_id, const GValue *value,
+                       GParamSpec *pspec)
+{
+    GEControl *ge;
+
+    ge = GECONTROL(obj);
+
+    switch (prop_id) {
+      case TIP_LABEL:
+        ge->tip_label = (GtkWidget *) g_value_get_object(value);
+        break;
+      case GLYPH_IMAGE:
+        gecontrol_set_glyph_image(ge,
+                                  (bdf_bitmap_t *) g_value_get_pointer(value));
+        break;
+      case COLOR_LIST:
+        gecontrol_set_color_list(ge, (guint16 *) g_value_get_pointer(value));
+        break;
+    }
+}
+
+static void
+gecontrol_get_property(GObject *obj, guint prop_id, GValue *value,
+                       GParamSpec *pspec)
+{
+    GEControl *ge;
+
+    ge = GECONTROL(obj);
+
+    switch (prop_id) {
+      case TIP_LABEL:
+        g_value_set_object(value, ge->tip_label);
+        break;
+      case GLYPH_IMAGE:
+        g_value_set_pointer(value, ge->gimage);
+        break;
+      case COLOR_LIST:
+        g_value_set_pointer(value, ge->colors);
+        break;
+    }
+}
+
+static void
+gecontrol_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass *goc = G_OBJECT_CLASS(g_class);
+    GtkWidgetClass *wc = GTK_WIDGET_CLASS(g_class);
+    GEControlClass *gc = GECONTROL_CLASS(g_class);
+
+    goc->set_property = gecontrol_set_property;
+    goc->get_property = gecontrol_get_property;
+    goc->finalize = gecontrol_finalize;
+
+    wc->size_request = gecontrol_preferred_size;
+    wc->size_allocate = gecontrol_actual_size;
+    wc->expose_event = gecontrol_expose;
+    wc->motion_notify_event = gecontrol_motion_notify;
+    wc->button_press_event = gecontrol_button_press;
+    wc->button_release_event = gecontrol_button_release;
+
+    g_object_class_install_property(goc, TIP_LABEL,
+                                    g_param_spec_object("tipLabel",
+                                                        _("Tip Label"),
+                                                        _("A GtkLabel widget where tips are shown when the mouse moves."),
+                                                        GTK_TYPE_WIDGET,
+                                                        G_PARAM_READWRITE));
+
+    g_object_class_install_property(goc, GLYPH_IMAGE,
+                                    g_param_spec_pointer("glyphImage",
+                                                       _("Glyph Image"),
+                                                       _("The bitmap image of a glyph."),
+                                                       G_PARAM_READWRITE));
+
+    g_object_class_install_property(goc, COLOR_LIST,
+                                    g_param_spec_pointer("colorList",
+                                                         _("Color list"),
+                                                         _("Colors to be used for glyphs having bits-per-pixel > 1."),
+                                                       G_PARAM_READWRITE));
+
+    gecontrol_signals[ACTIVATE] =
+        g_signal_new("activate",
+                     G_TYPE_FROM_CLASS(goc),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GEControlClass, activate),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    /*
+     * Initialize all the pixbufs.
+     */
+    gc->draw = gdk_pixbuf_new_from_xpm_data(draw_xpm);
+    gc->move = gdk_pixbuf_new_from_xpm_data(move_xpm);
+    gc->copy = gdk_pixbuf_new_from_xpm_data(copy_xpm);
+
+    gc->fliph = gdk_pixbuf_new_from_xpm_data(fliph_xpm);
+    gc->flipv = gdk_pixbuf_new_from_xpm_data(flipv_xpm);
+    gc->shear = gdk_pixbuf_new_from_xpm_data(shear_xpm);
+
+    gc->rleft = gdk_pixbuf_new_from_xpm_data(rleft_xpm);
+    gc->rright = gdk_pixbuf_new_from_xpm_data(rright_xpm);
+    gc->rotate = gdk_pixbuf_new_from_xpm_data(rotate_xpm);
+
+    gc->uleft = gdk_pixbuf_new_from_xpm_data(uleft_xpm);
+    gc->up = gdk_pixbuf_new_from_xpm_data(up_xpm);
+    gc->uright = gdk_pixbuf_new_from_xpm_data(uright_xpm);
+
+    gc->left = gdk_pixbuf_new_from_xpm_data(left_xpm);
+    gc->right = gdk_pixbuf_new_from_xpm_data(right_xpm);
+
+    gc->dleft = gdk_pixbuf_new_from_xpm_data(dleft_xpm);
+    gc->down = gdk_pixbuf_new_from_xpm_data(down_xpm);
+    gc->dright = gdk_pixbuf_new_from_xpm_data(dright_xpm);
+
+    parent_class = g_type_class_peek_parent(gc);
+}
+
+#define GEC_EVMASK (GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|\
+                    GDK_BUTTON_RELEASE_MASK)
+
+static void
+gecontrol_init(GTypeInstance *instance, gpointer g_class)
+{
+    gint i;
+    GEControl *gw = GECONTROL(instance);
+    GEControlClass *gc = GECONTROL_CLASS(g_class);
+
+    gw->gimage = 0;
+
+    gw->last_button = gw->timer_button = -1;
+    gw->timer_count = 0;
+
+    /*
+     * Enable the button press, release, and motion events.
+     */
+    gtk_widget_add_events(GTK_WIDGET(gw), GEC_EVMASK);
+
+    gw->points_used = gw->points_size = 0;
+
+    gw->buttons[GEC_DRAW_TOGGLE].image = gc->draw;
+    gw->buttons[GEC_MOVE_TOGGLE].image = gc->move;
+    gw->buttons[GEC_COPY_TOGGLE].image = gc->copy;
+
+    gw->buttons[GEC_FLIPH_BUTTON].image = gc->fliph;
+    gw->buttons[GEC_FLIPV_BUTTON].image = gc->flipv;
+    gw->buttons[GEC_SHEAR_BUTTON].image = gc->shear;
+
+    gw->buttons[GEC_RLEFT_BUTTON].image = gc->rleft;
+    gw->buttons[GEC_RRIGHT_BUTTON].image = gc->rright;
+    gw->buttons[GEC_ROTATE_BUTTON].image = gc->rotate;
+
+    gw->buttons[GEC_ULEFT_BUTTON].image = gc->uleft;
+    gw->buttons[GEC_UP_BUTTON].image = gc->up;
+    gw->buttons[GEC_URIGHT_BUTTON].image = gc->uright;
+
+    gw->buttons[GEC_LEFT_BUTTON].image = gc->left;
+    gw->buttons[GEC_RIGHT_BUTTON].image = gc->right;
+
+    gw->buttons[GEC_DLEFT_BUTTON].image = gc->dleft;
+    gw->buttons[GEC_DOWN_BUTTON].image = gc->down;
+    gw->buttons[GEC_DRIGHT_BUTTON].image = gc->dright;
+
+    for (i = 0; i < 18; i++) {
+        gw->buttons[i].help = help_strings[i];
+        gw->buttons[i].region = NULL;
+        gw->buttons[i].x = gw->buttons[i].y = 0;
+        gw->buttons[i].set = gw->buttons[i].toggle = FALSE;
+
+        /*
+         * At initialization time, the Draw toggle is always set by
+         * default.
+         */
+        switch (i) {
+          case GEC_DRAW_TOGGLE:
+            gw->buttons[i].set = TRUE;
+            gw->buttons[i].toggle = TRUE;
+            gw->buttons[i].other_toggles[0] = GEC_MOVE_TOGGLE;
+            gw->buttons[i].other_toggles[1] = GEC_COPY_TOGGLE;
+            break;
+          case GEC_MOVE_TOGGLE:
+            gw->buttons[i].toggle = TRUE;
+            gw->buttons[i].other_toggles[0] = GEC_DRAW_TOGGLE;
+            gw->buttons[i].other_toggles[1] = GEC_COPY_TOGGLE;
+            break;
+          case GEC_COPY_TOGGLE:
+            gw->buttons[i].toggle = TRUE;
+            gw->buttons[i].other_toggles[0] = GEC_DRAW_TOGGLE;
+            gw->buttons[i].other_toggles[1] = GEC_MOVE_TOGGLE;
+            break;
+        }
+    }
+
+    gw->cidx = 0;
+    gw->spot_region = 0;
+    gw->spot.x = gw->spot.y = gw->spot.width = gw->spot.height = 0;
+}
+
+/**********************************************************************
+ *
+ * API functions.
+ *
+ **********************************************************************/
+
+GType
+gecontrol_get_type(void)
+{
+    static GType gecontrol_type = 0;
+  
+    if (!gecontrol_type) {
+        static const GTypeInfo gecontrol_info = {
+            sizeof (GEControlClass),           /* class_size           */
+            0,                                 /* base_init            */
+            0,                                 /* base_finalize        */
+            gecontrol_class_init,              /* class_init           */
+            0,                                 /* class_finalize       */
+            0,                                 /* class_data           */
+            sizeof(GEControl),                 /* instance_size        */
+            0,                                 /* n_preallocs          */
+            gecontrol_init,                    /* instance_init        */
+            0,                                 /* value_table          */
+        };
+
+        gecontrol_type = g_type_register_static(GTK_TYPE_DRAWING_AREA,
+                                                "GEControl",
+                                                &gecontrol_info, 0);
+    }
+  
+    return gecontrol_type;
+}
+
+GtkWidget *
+gecontrol_new(const gchar *prop1, ...)
+{
+    GtkWidget *w;
+    va_list var_args;
+
+    va_start(var_args, prop1);
+    w = GTK_WIDGET(g_object_new_valist(gecontrol_get_type(), prop1, var_args));
+    va_end(var_args);
+
+    return w;
+}
+
+GtkWidget *
+gecontrol_newv(GtkWidget *tip_label, bdf_bitmap_t *image, guint16 *colors)
+{
+    GEControl *ge = g_object_new(gecontrol_get_type(),
+                                 "tipLabel", tip_label,
+                                 "glyphImage", image,
+                                 "colorList", colors,
+                                 NULL);
+
+    return GTK_WIDGET(ge);
+}
+
+void
+gecontrol_update_glyph_image(GEControl *ge, bdf_bitmap_t *image)
+{
+    if (ge->gimage) {
+        if (ge->gimage->bytes > 0)
+          g_free(ge->gimage->bitmap);
+        g_free(ge->gimage);
+        ge->gimage = 0;
+    }
+    if (image != 0) {
+        ge->gimage = (bdf_bitmap_t *) g_malloc(sizeof(bdf_bitmap_t));
+        memcpy(ge->gimage, image, sizeof(bdf_bitmap_t));
+        if (ge->gimage->bytes > 0) {
+            ge->gimage->bitmap = g_malloc(ge->gimage->bytes);
+            memcpy(ge->gimage->bitmap, image->bitmap, image->bytes);
+        }
+        ge->gimage->x = ge->buttons[GEC_GLYPH_IMAGE].x;
+        ge->gimage->y = ge->buttons[GEC_GLYPH_IMAGE].y;
+        gecontrol_draw_glyph_image(ge);
+    } else
+      gtk_widget_queue_draw(GTK_WIDGET(ge));
+}
+
+void
+gecontrol_set_glyph_image(GEControl *ge, bdf_bitmap_t *image)
+{
+    g_return_if_fail(ge != NULL);
+    g_return_if_fail(IS_GECONTROL(ge));
+
+    if (ge->gimage) {
+        if (ge->gimage->bytes > 0)
+          g_free(ge->gimage->bitmap);
+        g_free(ge->gimage);
+        ge->gimage = 0;
+    }
+    if (image != 0) {
+        ge->gimage = (bdf_bitmap_t *) g_malloc(sizeof(bdf_bitmap_t));
+        memcpy(ge->gimage, image, sizeof(bdf_bitmap_t));
+        if (ge->gimage->bytes > 0) {
+            ge->gimage->bitmap = g_malloc(ge->gimage->bytes);
+            memcpy(ge->gimage->bitmap, image->bitmap, image->bytes);
+        }
+    }
+
+    /*
+     * Delete any spot region to force a new one to be created. This is
+     * because the sizes change depending on the bits per pixel.
+     */
+    if (ge->spot_region != 0) {
+        gdk_region_destroy(ge->spot_region);
+        ge->spot_region = 0;
+    }
+
+    /*
+     * Always make sure the color index is reset in this case.
+     */
+    ge->cidx = 0;
+
+    /*
+     * Always queue a resize to at least force a redraw of the widget.
+     */
+    gtk_widget_queue_resize(GTK_WIDGET(ge));
+}
+
+void
+gecontrol_set_color_list(GEControl *ge, guint16 *colors)
+{
+    g_return_if_fail(ge != NULL);
+    g_return_if_fail(IS_GECONTROL(ge));
+
+    ge->colors = colors;
+    gtk_widget_queue_draw(GTK_WIDGET(ge));
+}
+
+void
+gecontrol_change_operation(GEControl *ge, GEControlOperation op)
+{
+    gint b, i;
+
+    g_return_if_fail(ge != NULL);
+    g_return_if_fail(IS_GECONTROL(ge));
+
+    b = -1;
+    if (op == GECONTROL_DRAW)
+      b = GEC_DRAW_TOGGLE;
+    else if (op == GECONTROL_MOVE)
+      b = GEC_MOVE_TOGGLE;
+    else if (op == GECONTROL_COPY)
+      b = GEC_COPY_TOGGLE;
+
+    if (b < 0 || ge->buttons[b].set == TRUE)
+      return;
+
+    for (i = 0; i < 3; i++) {
+        if (i != b && ge->buttons[i].set == TRUE) {
+            ge->buttons[i].set = FALSE;
+            gecontrol_button_normal(ge, i);
+            break;
+        }
+    }
+
+    gecontrol_button_active(ge, b);
+    ge->buttons[b].set = TRUE;
+}
+
+void
+gecontrol_change_color(GEControl *ge, gint cidx)
+{
+    g_return_if_fail(ge != NULL);
+    g_return_if_fail(IS_GECONTROL(ge));
+
+    /*
+     * No point in setting a color if this is a one bit per pixel image or
+     * there is no image.
+     */
+    if (!ge->gimage || ge->gimage->bpp == 1)
+      return;
+
+    /*
+     * If the index is out of bounds, then wrap it around the other side.
+     */
+    cidx--;
+    if (cidx >= (1 << ge->gimage->bpp))
+      cidx = 0;
+    else if (cidx < 0)
+      cidx = (1 << ge->gimage->bpp) - 1;
+
+    gecontrol_highlight_selected_spot(ge);
+    ge->cidx = cidx;
+    gecontrol_highlight_selected_spot(ge);
+}
diff --git a/gectrl.h b/gectrl.h
new file mode 100644 (file)
index 0000000..6c2634e
--- /dev/null
+++ b/gectrl.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_gectrl
+#define _h_gectrl
+
+#include <gtk/gtkdrawingarea.h>
+#include "bdfP.h"
+#include "gtkcompat.h"
+
+G_BEGIN_DECLS
+
+#define GECONTROL(obj) \
+      (G_TYPE_CHECK_INSTANCE_CAST(obj, gecontrol_get_type(), GEControl))
+#define GECONTROL_CLASS(klass) \
+      (G_TYPE_CHECK_CLASS_CAST(klass, gecontrol_get_type(), GEControlClass))
+
+#define IS_GECONTROL(obj) \
+      (G_TYPE_CHECK_INSTANCE_TYPE(obj, gecontrol_get_type()))
+#define IS_GECONTROL_CLASS(klass) \
+      (G_TYPE_CHECK_CLASS_TYPE(klass, gecontrol_get_type()))
+
+#define GECONTROL_GET_CLASS(obj) \
+      (G_TYPE_INSTANCE_GET_CLASS(obj, gecontrol_get_type(), GEControlClass))
+
+typedef struct _GEControl      GEControl;
+typedef struct _GEControlClass GEControlClass;
+
+typedef enum {
+    GECONTROL_DRAW = 0,
+    GECONTROL_MOVE,
+    GECONTROL_COPY,
+    GECONTROL_FLIP_HORIZONTAL,
+    GECONTROL_FLIP_VERTICAL,
+    GECONTROL_SHEAR,
+    GECONTROL_ROTATE_LEFT_90,
+    GECONTROL_ROTATE_RIGHT_90,
+    GECONTROL_ROTATE,
+    GECONTROL_SHIFT_UP_LEFT,
+    GECONTROL_SHIFT_UP,
+    GECONTROL_SHIFT_UP_RIGHT,
+    GECONTROL_SHIFT_LEFT,
+    GECONTROL_SHIFT_RIGHT,
+    GECONTROL_SHIFT_DOWN_LEFT,
+    GECONTROL_SHIFT_DOWN,
+    GECONTROL_SHIFT_DOWN_RIGHT,
+    GECONTROL_COLOR_CHANGE
+} GEControlOperation;
+
+/*
+ * Structure passed to the "activate" signal handler.
+ */
+typedef struct {
+    GEControlOperation operation;
+    gint color;
+} GEControlActivateInfo;
+
+typedef struct {
+    guchar *help;
+    gint x;
+    gint y;
+    GdkPixbuf *image;
+    GdkRegion *region;
+    gint other_toggles[2];
+    gboolean set;
+    gboolean toggle;
+} GEControlButton;
+
+typedef struct {
+    gint x;
+    gint y;
+    GdkRegion *region;
+    gboolean set;
+} GEControlColor;
+
+struct _GEControl {
+    GtkDrawingArea da;
+
+    /*
+     * Public fields.
+     */
+
+    /*
+     * The glyph image.
+     */
+    bdf_bitmap_t *gimage;
+
+    /*
+     * An application provided label widget where the help
+     * messages are set.
+     */
+    GtkWidget *tip_label;
+
+    /*
+     * The list of colors to use.
+     */
+    guint16 *colors;
+
+    /*
+     * Private fields.
+     */
+    gint last_button;
+
+    /*
+     * The current color index.
+     */
+    gint cidx;
+
+    /*
+     * 16 color spots. Used to track mouse position and update the tip label.
+     */
+    GdkRectangle spot;
+    GdkRegion *spot_region;
+
+    /*
+     * Timer stuff for holding down the buttons.
+     */
+    gint timer_count;
+    gint timer_button;
+    guint timer;
+
+    GdkPoint *points;
+    gint points_used;
+    gint points_size;
+
+    /*
+     * Buffer for building a grayscale glyph image.
+     */
+    guchar *rgb;
+    guint rgb_used;
+    guint rgb_size;
+
+    GEControlButton buttons[18];
+};
+
+struct _GEControlClass {
+    GtkDrawingAreaClass parent_class;
+
+    /*
+     * A GC for drawing the color selection rectangle.
+     */
+    GdkGC *selgc;
+
+    GdkPixbuf *draw;
+    GdkPixbuf *move;
+    GdkPixbuf *copy;
+
+    GdkPixbuf *fliph;
+    GdkPixbuf *flipv;
+    GdkPixbuf *shear;
+
+    GdkPixbuf *rleft;
+    GdkPixbuf *rright;
+    GdkPixbuf *rotate;
+
+    GdkPixbuf *uleft;
+    GdkPixbuf *up;
+    GdkPixbuf *uright;
+
+    GdkPixbuf *left;
+    GdkPixbuf *right;
+
+    GdkPixbuf *dleft;
+    GdkPixbuf *down;
+    GdkPixbuf *dright;
+
+    /*
+     * Signal handlers.
+     */
+    void (*activate)(GtkWidget *, gpointer, gpointer);
+};
+
+extern GType gecontrol_get_type(void);
+
+extern GtkWidget *gecontrol_new(const gchar *prop1, ...);
+
+extern GtkWidget *gecontrol_newv(GtkWidget *tips_label, bdf_bitmap_t *image,
+                                 guint16 *colors);
+
+extern void gecontrol_set_glyph_image(GEControl *ge, bdf_bitmap_t *image);
+
+extern void gecontrol_update_glyph_image(GEControl *ge, bdf_bitmap_t *image);
+
+extern void gecontrol_set_color_list(GEControl *ge, guint16 *colors);
+
+extern void gecontrol_change_operation(GEControl *ge, GEControlOperation op);
+
+extern void gecontrol_change_color(GEControl *ge, gint cidx);
+
+G_END_DECLS
+
+#endif /* _h_gectrl */
diff --git a/gectrlbmaps.h b/gectrlbmaps.h
new file mode 100644 (file)
index 0000000..75e90eb
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_gectrlbmaps
+#define _h_gectrlbmaps
+
+static const gchar *copy_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"..............  ",
+".            .  ",
+". ..............",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+". .          . .",
+".............. .",
+"  .            .",
+"  .............."
+};
+
+static const gchar *dleft_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"        .       ",
+"       ...      ",
+"      .....     ",
+"  .. .......    ",
+"  ...........   ",
+"  ............  ",
+"  ...........   ",
+"  ..........    ",
+"  .........     ",
+"  ........      ",
+"  .........     ",
+"  .........     ",
+"                ",
+"                "
+};
+
+static const gchar *down_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"     ......     ",
+"     ......     ",
+"     ......     ",
+"     ......     ",
+" .............. ",
+" .............. ",
+"  ............  ",
+"   ..........   ",
+"    ........    ",
+"     ......     ",
+"      ....      ",
+"       ..       ",
+"                ",
+"                "
+};
+
+static const gchar *draw_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"         ...    ",
+"         ....   ",
+"        .  .    ",
+"        .  .    ",
+"       .  .     ",
+"       .  .     ",
+"      .  .      ",
+"      .  .      ",
+"     .  .       ",
+"     .  .       ",
+"    .  .        ",
+"    .  .        ",
+"   .  .         ",
+"   .  .         ",
+"   ...          ",
+"   ..           "
+};
+
+static const gchar *dright_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"       .        ",
+"      ...       ",
+"     .....      ",
+"    ....... ..  ",
+"   ...........  ",
+"  ............  ",
+"   ...........  ",
+"    ..........  ",
+"     .........  ",
+"      ........  ",
+"     .........  ",
+"     .........  ",
+"                ",
+"                "
+};
+
+static const gchar *fliph_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"       ..       ",
+"       ..       ",
+"       ..       ",
+" ........ .. .. ",
+" .     ..     . ",
+" .     ..       ",
+" .     ..     . ",
+" .     ..     . ",
+" .     ..       ",
+" .     ..     . ",
+" ........ .. .. ",
+"       ..       ",
+"       ..       ",
+"       ..       ",
+"                "
+};
+
+static const gchar *flipv_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"    ........    ",
+"    .      .    ",
+"    .      .    ",
+"    .      .    ",
+"    .      .    ",
+"    .      .    ",
+" .............. ",
+" .............. ",
+"                ",
+"    .      .    ",
+"    .      .    ",
+"                ",
+"    .      .    ",
+"    .. .. ..    ",
+"                "
+};
+
+static const gchar *left_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"        ..      ",
+"       ...      ",
+"      ....      ",
+"     .....      ",
+"    ..........  ",
+"   ...........  ",
+"  ............  ",
+"  ............  ",
+"   ...........  ",
+"    ..........  ",
+"     .....      ",
+"      ....      ",
+"       ...      ",
+"        ..      ",
+"                "
+};
+
+static const gchar *move_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"..  ..  ..  ..  ",
+".            .  ",
+"  ..............",
+"  .            .",
+". .          . .",
+". .          . .",
+"  .            .",
+"  .            .",
+". .          . .",
+". .          . .",
+"  .            .",
+"  .            .",
+". .          . .",
+"... ..  ..  .. .",
+"  .            .",
+"  .............."
+};
+
+static const gchar *right_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"      ..        ",
+"      ...       ",
+"      ....      ",
+"      .....     ",
+"  ..........    ",
+"  ...........   ",
+"  ............  ",
+"  ............  ",
+"  ...........   ",
+"  ..........    ",
+"      .....     ",
+"      ....      ",
+"      ...       ",
+"      ..        ",
+"                "
+};
+
+static const gchar *rleft_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"     ..         ",
+"    ...         ",
+"   ....         ",
+"  ............  ",
+" .............. ",
+" .............. ",
+"  ............. ",
+"   ....    .... ",
+"    ...    .... ",
+"     ..    .... ",
+"           .... ",
+"           .... ",
+"                ",
+"                "
+};
+
+static const gchar *rotate_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"     ......     ",
+"   ..........   ",
+"   ..........   ",
+"  ....    ....  ",
+"  ...      ...  ",
+"  ...      ...  ",
+"  ...   .  ...  ",
+"  ...   ......  ",
+"  ...   ......  ",
+"  ....  .....   ",
+"   .... .....   ",
+"    ... ......  ",
+"                ",
+"                "
+};
+
+static const gchar *rright_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"         ..     ",
+"         ...    ",
+"         ....   ",
+"  ............  ",
+" .............. ",
+" .............. ",
+" .............  ",
+" ....    ....   ",
+" ....    ...    ",
+" ....    ..     ",
+" ....           ",
+" ....           ",
+"                ",
+"                "
+};
+
+static const gchar *shear_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"   .            ",
+"   .            ",
+"   ....       . ",
+"   .   . .   .  ",
+"   .    ..  .   ",
+"   .   ... .    ",
+"   .      .     ",
+"   .     .      ",
+"   .    .       ",
+"   .   .        ",
+"   .  .         ",
+" .............. ",
+"   .            ",
+"   .            ",
+"                "
+};
+
+static const gchar *uleft_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"  .........     ",
+"  .........     ",
+"  ........      ",
+"  .........     ",
+"  ..........    ",
+"  ...........   ",
+"  ............  ",
+"  ...........   ",
+"  .. .......    ",
+"      .....     ",
+"       ...      ",
+"        .       ",
+"                ",
+"                "
+};
+
+static const gchar *up_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"       ..       ",
+"      ....      ",
+"     ......     ",
+"    ........    ",
+"   ..........   ",
+"  ............  ",
+" .............. ",
+" .............. ",
+"     ......     ",
+"     ......     ",
+"     ......     ",
+"     ......     ",
+"                ",
+"                "
+};
+
+static const gchar *uright_xpm[] = {
+"16 16 2 1",
+"  c None",
+". c black",
+"                ",
+"                ",
+"     .........  ",
+"     .........  ",
+"      ........  ",
+"     .........  ",
+"    ..........  ",
+"   ...........  ",
+"  ............  ",
+"   ...........  ",
+"    ....... ..  ",
+"     .....      ",
+"      ...       ",
+"       .        ",
+"                ",
+"                "
+};
+
+#endif /* _h_gectrlbmaps */
diff --git a/glyphedit.c b/glyphedit.c
new file mode 100644 (file)
index 0000000..8e453e7
--- /dev/null
@@ -0,0 +1,2549 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "glyphedit.h"
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkselection.h>
+
+#ifdef HAVE_XLIB
+#include <gdk/gdkx.h>
+#endif
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(s) dgettext(GETTEXT_PACKAGE,s)
+#else
+#define _(s) (s)
+#endif
+
+/*
+ * Each pixel will be displayed by a square with this number of pixels
+ * on each side.
+ */
+#define MIN_PIXEL_SIZE     2
+#define MAX_PIXEL_SIZE     20
+#define DEFAULT_PIXEL_SIZE 10
+
+#define HMARGINS(gw) ((gw)->hmargin << 1)
+#define VMARGINS(gw) ((gw)->vmargin << 1)
+
+/*
+ * Crosshair cursor.
+ */
+static const gchar *cross_xpm[] = {
+"13 13 2 1",
+"      c None",
+".     c #000000",
+"      .      ",
+"      .      ",
+"      .      ",
+"      .      ",
+"      .      ",
+"             ",
+".....   .....",
+"             ",
+"      .      ",
+"      .      ",
+"      .      ",
+"      .      ",
+"      .      "
+};
+
+/*
+ * Macros that represent the properties used by this type of object.
+ */
+#define GLYPHEDIT_CLIPBOARD gdk_atom_intern("GLYPHEDIT_CLIPBOARD", FALSE)
+#define GLYPHEDIT_BDF_CHAR gdk_atom_intern("GLYPHEDIT_BDF_CHAR", FALSE)
+#define GLYPHEDIT_BITMAP gdk_atom_intern("GLYPHEDIT_BITMAP", FALSE)
+#define GLYPHEDIT_GLYPH gdk_atom_intern("GLYPHEDIT_GLYPH", FALSE)
+
+/*
+ * Set default values.
+ */
+
+/*
+ * Enums used for identifying properties.
+ */
+enum {
+    PROP_0 = 0,
+    GLYPH_GRID,
+    BASELINE_COLOR,
+    SELECTION_COLOR,
+    CURSOR_COLOR,
+    PIXEL_SIZE,
+    SHOW_X_HEIGHT,
+    SHOW_CAP_HEIGHT,
+    COLOR_LIST,
+    OPERATION
+};
+
+/*
+ * The list of signals emitted by these objects.
+ */
+enum {
+    GLYPH_MODIFIED = 0,
+    POINTER_MOVED,
+    OPERATION_CHANGE,
+    COLOR_CHANGE
+};
+
+/**************************************************************************
+ *
+ * Local variables.
+ *
+ **************************************************************************/
+
+static GtkWidgetClass *parent_class = 0;
+static guint glyphedit_signals[OPERATION_CHANGE + 1];
+
+/**************************************************************************
+ *
+ * Class member functions.
+ *
+ **************************************************************************/
+
+static void
+glyphedit_set_property(GObject *obj, guint prop_id, const GValue *value,
+                       GParamSpec *pspec)
+{
+    GtkWidget *widget;
+    Glyphedit *gw;
+
+    widget = GTK_WIDGET(obj);
+    gw = GLYPHEDIT(obj);
+
+    switch (prop_id) {
+      case GLYPH_GRID:
+        glyphedit_set_grid(gw,
+                           (bdf_glyph_grid_t *) g_value_get_pointer(value));
+        break;
+      case BASELINE_COLOR:
+        break;
+      case SELECTION_COLOR:
+        break;
+      case CURSOR_COLOR:
+        break;
+      case PIXEL_SIZE:
+        glyphedit_set_pixel_size(gw, g_value_get_uint(value));
+        break;
+      case SHOW_X_HEIGHT:
+        glyphedit_set_show_x_height(gw, g_value_get_boolean(value));
+        break;
+      case SHOW_CAP_HEIGHT:
+        glyphedit_set_show_cap_height(gw, g_value_get_boolean(value));
+        break;
+      case COLOR_LIST:
+        gw->colors = (guint16 *) g_value_get_pointer(value);
+        gtk_widget_queue_draw(widget);
+        break;
+      case OPERATION:
+        glyphedit_set_operation(gw,
+                                (GlypheditOperation) g_value_get_uint(value));
+        break;
+    }
+}
+
+static void
+glyphedit_get_property(GObject *obj, guint prop_id, GValue *value,
+                       GParamSpec *pspec)
+{
+    GtkWidget *widget;
+    Glyphedit *gw;
+
+    widget = GTK_WIDGET(obj);
+    gw = GLYPHEDIT(obj);
+
+    switch (prop_id) {
+      case GLYPH_GRID:
+        g_value_set_pointer(value, gw->grid);
+        break;
+      case BASELINE_COLOR:
+        break;
+      case SELECTION_COLOR:
+        break;
+      case CURSOR_COLOR:
+        break;
+      case PIXEL_SIZE:
+        g_value_set_uint(value, gw->pixel_size);
+        break;
+      case SHOW_X_HEIGHT:
+        g_value_set_boolean(value, gw->show_x_height);
+        break;
+      case SHOW_CAP_HEIGHT:
+        g_value_set_boolean(value, gw->show_cap_height);
+        break;
+      case COLOR_LIST:
+        g_value_set_pointer(value, gw->colors);
+        break;
+      case OPERATION:
+        g_value_set_uint(value, (guint) gw->op);
+        break;
+    }
+}
+
+static void
+glyphedit_destroy(GtkObject *obj)
+{
+    Glyphedit *gw;
+    GlypheditClass *gwc;
+
+    /*
+     * Do some checks to make sure the incoming object exists and is the right
+     * kind.
+     */
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_GLYPHEDIT(obj));
+
+    gw = GLYPHEDIT(obj);
+    gwc = GLYPHEDIT_GET_CLASS(obj);
+
+    /*
+     * Unreference objects used class-wide so they get deallocated properly
+     * when no longer used. The unreference only needs to happen the first
+     * time since the objects are created at class initialization time.
+     */
+    if (gwc->cursor != 0)
+      gdk_cursor_unref(gwc->cursor);
+
+    if (gwc->gridgc != 0)
+      g_object_unref(G_OBJECT(gwc->gridgc));
+    if (gwc->bbxgc != 0)
+      g_object_unref(G_OBJECT(gwc->bbxgc));
+    if (gwc->pixgc != 0)
+      g_object_unref(G_OBJECT(gwc->pixgc));
+    if (gwc->selgc != 0)
+      g_object_unref(G_OBJECT(gwc->selgc));
+
+    /*
+     * Free up any colors allocated.
+     */
+    if (gw->baselineColor.pixel != 0)
+      gdk_colormap_free_colors(gw->widget.style->colormap,
+                               &gw->baselineColor, 1);
+
+    gwc->cursor = 0;
+    gwc->gridgc = gwc->bbxgc = gwc->pixgc = gwc->selgc = 0;
+
+    /*
+     * Free up the grid info.
+     */
+    if (gw->grid != 0) {
+        bdf_free_glyph_grid(gw->grid);
+        gw->grid = 0;
+    }
+
+    if (gw->spot_size > 0) {
+        g_free(gw->spot);
+        gw->spot_size = gw->spot_used = 0;
+    }
+
+    GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+glyphedit_finalize(GObject *obj)
+{
+    /*
+     * Do some checks to make sure the incoming object exists and is the right
+     * kind.
+     */
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_GLYPHEDIT(obj));
+
+    /*
+     * Follow the class chain back up to free up resources allocated in the
+     * parent classes.
+     */
+    G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+glyphedit_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
+{
+    Glyphedit *gw;
+    GdkScreen *screen;
+    guint16 dht, margin;
+
+    gw = GLYPHEDIT(widget);
+
+    screen = gdk_display_get_default_screen(gdk_display_get_default());
+    dht = gdk_screen_get_height(screen);
+
+    /*
+     * This little bit of code quietly forces the glyph grid to be
+     * at most 1/2 the height of the screen being used to help avoid taking
+     * up too much space on the desktop.
+     */
+    margin = VMARGINS(gw);
+    preferred->height = margin +
+        ((gw->pixel_size + 4) * gw->grid->grid_height);
+    if (preferred->height > (dht >> 1)) {
+        while (gw->pixel_size > 2) {
+            preferred->height = margin +
+                ((gw->pixel_size + 4) * gw->grid->grid_height);
+            if (preferred->height < (dht >> 1))
+              break;
+            gw->pixel_size--;
+        }
+    }
+
+    preferred->width = HMARGINS(gw) +
+        ((gw->pixel_size + 4) * gw->grid->grid_width);
+}
+
+static void
+glyphedit_actual_size(GtkWidget *widget, GtkAllocation *actual)
+{
+    widget->allocation = *actual;
+
+    if (GTK_WIDGET_REALIZED(widget))
+      gdk_window_move_resize(widget->window, actual->x, actual->y,
+                             actual->width, actual->height);
+}
+
+static void
+glyphedit_draw_focus(GtkWidget *widget, GdkRectangle *area)
+{
+    GdkGC *gc;
+    gint x, y, wd, ht, fwidth, fpad;
+
+    /*
+     * Do something with this later to make sure the focus line width
+     * is set in the GC's.
+     */
+    gtk_widget_style_get(widget,
+                         "focus-line-width", &fwidth,
+                         "focus-padding", &fpad, NULL);
+
+    gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
+
+    x = (widget->style->xthickness + fwidth + fpad) - 1;
+    y = (widget->style->ythickness + fwidth + fpad) - 1;
+    wd = (widget->allocation.width - (x * 2));
+    ht = (widget->allocation.height - (y * 2));
+
+    if (GTK_WIDGET_HAS_FOCUS(widget))
+      gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget),
+                      area, widget, "glyphedit", x, y, wd, ht);
+    else {
+        gdk_gc_set_clip_rectangle(gc, area);
+        gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1);
+        gdk_gc_set_clip_rectangle(gc, 0);
+    }
+}
+
+static void
+glyphedit_draw_pixel(Glyphedit *gw, gint16 x, gint16 y, gboolean sel)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    GlypheditClass *gwc;
+    gint16 bpr, set, dx, dy, di, si;
+    guchar *masks, *bmap;
+    GdkRectangle pix;
+
+    if (!GTK_WIDGET_REALIZED(w) || gw->grid == 0)
+      return;
+
+    gwc = GLYPHEDIT_GET_CLASS(gw);
+
+    di = 0;
+    masks = 0;
+    switch (gw->grid->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    dx = (gw->pixel_size + 4) * gw->grid->grid_width;
+    dy = (gw->pixel_size + 4) * gw->grid->grid_height;
+
+    pix.x = (gw->widget.allocation.width >> 1) - (dx >> 1) +
+        ((gw->pixel_size + 4) * x) + 2;
+    pix.y = (gw->widget.allocation.height >> 1) - (dy >> 1) +
+        ((gw->pixel_size + 4) * y) + 2;
+    pix.width = pix.height = gw->pixel_size + 1;
+
+    if (sel == TRUE && gw->grid->sel.width != 0) {
+        bpr = ((gw->grid->sel.width * gw->grid->bpp) + 7) >> 3;
+        dy = y - gw->grid->sel.y;
+        dx = (x - gw->grid->sel.x) * gw->grid->bpp;
+        bmap = gw->grid->sel.bitmap;
+    } else {
+        bpr = ((gw->grid->grid_width * gw->grid->bpp) + 7) >> 3;
+        dy = y;
+        dx = x * gw->grid->bpp;
+        bmap = gw->grid->bitmap;
+    }
+    si = (dx & 7) / gw->grid->bpp;
+    set = bmap[(dy * bpr) + (dx >> 3)] & masks[si];
+    if (di > si)
+      set >>= (di - si) * gw->grid->bpp;
+
+    if (set) {
+        if (gw->grid->bpp > 1) {
+            switch (gw->grid->bpp) {
+              case 2:
+                memset(gw->spot, gw->colors[set-1], gw->spot_used);
+                break;
+              case 4:
+                memset(gw->spot, gw->colors[set-1+4], gw->spot_used);
+                break;
+              case 8:
+                memset(gw->spot, set, gw->spot_used);
+                break;
+            }
+            gdk_draw_gray_image(GTK_WIDGET(gw)->window, gwc->pixgc,
+                                pix.x, pix.y, pix.width, pix.height,
+                                GDK_RGB_DITHER_NONE, gw->spot, pix.width);
+        } else
+          gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->pixgc, TRUE,
+                             pix.x, pix.y, pix.width, pix.height);
+    } else
+      gdk_window_clear_area(GTK_WIDGET(gw)->window, pix.x, pix.y,
+                            pix.width, pix.height);
+    if (sel == TRUE)
+      gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->selgc, TRUE,
+                         pix.x + 1, pix.y + 1,
+                         pix.width - 2, pix.height - 2);
+}
+
+static void
+glyphedit_draw_glyph(Glyphedit *gw)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    gint16 x, y;
+    gboolean sel;
+
+    if (!GTK_WIDGET_REALIZED(w) || gw->grid == 0)
+      return;
+
+    for (y = 0; y < gw->grid->grid_height; y++) {
+        for (x = 0; x < gw->grid->grid_width; x++) {
+            sel = (bdf_in_selection(gw->grid, x, y, 0) ? TRUE : FALSE);
+            glyphedit_draw_pixel(gw, x, y, sel);
+        }
+    }
+}
+
+static void
+glyphedit_draw_font_bbx(Glyphedit *gw)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    GlypheditClass *gwc;
+    gint16 xoff, yoff, fxoff, fyoff, psize;
+    GdkRectangle frame;
+
+    if (!GTK_WIDGET_REALIZED(w))
+      return;
+
+    gwc = GLYPHEDIT_GET_CLASS(gw);
+
+    psize = gw->pixel_size + 4;
+    frame.width = psize * gw->grid->font_bbx.width;
+    frame.height = psize *
+        (gw->grid->font_bbx.ascent + gw->grid->font_bbx.descent);
+
+    fxoff = psize * gw->grid->grid_width;
+    fyoff = psize * gw->grid->grid_height;
+    frame.x = (gw->widget.allocation.width >> 1) - (fxoff >> 1);
+    frame.y = (gw->widget.allocation.height >> 1) - (fyoff >> 1);
+
+    if (gw->grid->font_bbx.x_offset < 0)
+      fxoff = psize * (gw->grid->base_x + gw->grid->font_bbx.x_offset);
+    else
+      fxoff = psize * gw->grid->base_x;
+
+    fyoff = psize * (gw->grid->base_y - gw->grid->font_bbx.ascent);
+
+    /*
+     * Due to some odd behavior, the box has to be drawn with the y point off
+     * by one because the top of the rectangle does not get drawn otherwise.
+     * Even calling gdk_draw_line() specifically doesn't work.
+     *
+     * This may have been fixed in later versions of GDK.
+     */
+    gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->bbxgc, FALSE,
+                       frame.x + fxoff, frame.y + fyoff + 1,
+                       frame.width, frame.height);
+
+    /*
+     * Draw vertical baseline.
+     */
+    xoff = (gw->pixel_size + 4) * gw->grid->base_x;
+    yoff = (gw->pixel_size + 4) * gw->grid->base_y;
+
+    gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
+                  frame.x + xoff, frame.y + fyoff,
+                  frame.x + xoff, frame.y + fyoff + frame.height);
+
+    /*
+     * Draw horizontal baseline.
+     */
+    gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
+                  frame.x + fxoff, frame.y + yoff,
+                  frame.x + fxoff + frame.width, frame.y + yoff);
+
+    /*
+     * Draw the CAP_HEIGHT if indicated and exists.
+     */
+    if (gw->grid && gw->grid->cap_height != 0) {
+        yoff = (gw->pixel_size + 4) *
+            (gw->grid->base_y - gw->grid->cap_height);
+        if (gw->show_cap_height == TRUE)
+          gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
+                        frame.x + fxoff, frame.y + yoff,
+                        frame.x + fxoff + frame.width, frame.y + yoff);
+        else {
+            gdk_window_clear_area(GTK_WIDGET(gw)->window, frame.x + fxoff,
+                                  frame.y + yoff, frame.width, 1);
+            gdk_draw_line(GTK_WIDGET(gw)->window, gwc->gridgc,
+                          frame.x + fxoff, frame.y + yoff,
+                          frame.x + fxoff + frame.width, frame.y + yoff);
+        }
+    }
+
+    /*
+     * Draw the X_HEIGHT if indicated and exists.
+     */
+    if (gw->grid && gw->grid->x_height != 0) {
+        yoff = (gw->pixel_size + 4) * (gw->grid->base_y - gw->grid->x_height);
+        if (gw->show_x_height == TRUE)
+          gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
+                        frame.x + fxoff, frame.y + yoff,
+                        frame.x + fxoff + frame.width, frame.y + yoff);
+        else {
+            gdk_window_clear_area(GTK_WIDGET(gw)->window, frame.x + fxoff,
+                                  frame.y + yoff, frame.width, 1);
+            gdk_draw_line(GTK_WIDGET(gw)->window, gwc->gridgc,
+                          frame.x + fxoff, frame.y + yoff,
+                          frame.x + fxoff + frame.width, frame.y + yoff);
+        }
+    }
+}
+
+static void
+glyphedit_draw(GtkWidget *widget, GdkRegion *region)
+{
+    Glyphedit *gw;
+    gint x, y, limit, unit, wd, ht;
+    GlypheditClass *gwc;
+    GdkRectangle frame;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(widget));
+
+    gw = GLYPHEDIT(widget);
+    gwc = GLYPHEDIT_GET_CLASS(widget);
+
+    wd = gw->grid->grid_width;
+    ht = gw->grid->grid_height;
+
+    frame.width = (gw->pixel_size + 4) * wd;
+    frame.height = (gw->pixel_size + 4) * ht;
+
+    /*
+     * Adjust the frame horizontal and vertical positions so it
+     * always appears centered on the window.
+     */
+    frame.x = (widget->allocation.width >> 1) - (frame.width >> 1);
+    frame.y = (widget->allocation.height >> 1) - (frame.height >> 1);
+
+    /*
+     * Limit the drawing area to the clip region.
+     */
+    if (region != 0)
+      gdk_gc_set_clip_region(gwc->gridgc, region);
+
+    /*
+     * Draw the outside frame.
+     */
+    gdk_draw_rectangle(widget->window, gwc->gridgc, FALSE,
+                       frame.x, frame.y, frame.width, frame.height);
+
+    /*
+     * Draw the vertical grid lines.
+     */
+    limit = frame.x + frame.width;
+    unit = gw->pixel_size + 4;
+    for (x = frame.x + unit, y = frame.y; x < limit; x += unit)
+      gdk_draw_line(widget->window, gwc->gridgc, x, y, x, y + frame.height);
+
+    /*
+     * Draw the horizontal grid lines.
+     */
+    limit = frame.y + frame.height;
+    for (x = frame.x, y = frame.y + unit; y < limit; y += unit)
+      gdk_draw_line(widget->window, gwc->gridgc, x, y, x + frame.width, y);
+
+    if (region != 0)
+      gdk_gc_set_clip_region(gwc->gridgc, 0);
+
+    glyphedit_draw_font_bbx(gw);
+    glyphedit_draw_glyph(gw);
+}
+
+static void
+glyphedit_create_gcs(GtkWidget *widget, gboolean force)
+{
+    Glyphedit *gw;
+    GlypheditClass *gwc;
+    GdkGCValuesMask gcm;
+    GdkGCValues gcv;
+    gint8 dashes[2] = {1, 1};
+
+    gw = GLYPHEDIT(widget);
+    gwc = GLYPHEDIT_GET_CLASS(G_OBJECT(widget));
+
+    gcm = GDK_GC_FOREGROUND|GDK_GC_BACKGROUND|GDK_GC_FUNCTION;
+
+    if (gwc->gridgc == 0 || force == TRUE) {
+        if (gwc->gridgc != 0)
+          g_object_unref(G_OBJECT(gwc->gridgc));
+        gcv.foreground.pixel =
+            widget->style->fg[GTK_WIDGET_STATE(widget)].pixel;
+        gcv.background.pixel =
+            widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
+        gcv.function = GDK_COPY;
+        gcv.line_style = GDK_LINE_ON_OFF_DASH;
+        gwc->gridgc = gdk_gc_new_with_values(widget->window, &gcv,
+                                             gcm|GDK_GC_LINE_STYLE);
+
+        /*
+         * Now set the dash lengths since they can't be set in the GC values.
+         */
+        gdk_gc_set_dashes(gwc->gridgc, 0, dashes, 2);
+    }
+
+    if (gwc->bbxgc == 0 || force == TRUE) {
+        if (gwc->bbxgc != 0)
+          g_object_unref(G_OBJECT(gwc->bbxgc));
+
+        if (gw->baselineColor.pixel == 0)
+          /*
+           * Default to red.
+           */
+          gdk_colormap_alloc_color(gw->widget.style->colormap,
+                                   &gw->baselineColor, FALSE, TRUE);
+
+        gcv.foreground.pixel = gw->baselineColor.pixel;
+        gcv.function = GDK_COPY;
+        gwc->bbxgc = gdk_gc_new_with_values(widget->window, &gcv,
+                                            GDK_GC_FOREGROUND|GDK_GC_FUNCTION);
+    }
+
+    if (gwc->selgc == 0 || force == TRUE) {
+        if (gwc->selgc != 0)
+          g_object_unref(G_OBJECT(gwc->selgc));
+
+        gcv.foreground.pixel =
+            widget->style->fg[GTK_WIDGET_STATE(widget)].pixel;
+        gcv.background.pixel =
+            widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
+        gcv.foreground.pixel ^= gcv.background.pixel;
+        gcv.function = GDK_XOR;
+        gwc->selgc = gdk_gc_new_with_values(widget->window, &gcv, gcm);
+    }
+
+    if (gwc->pixgc == 0 || force == TRUE) {
+        if (gwc->pixgc != 0)
+          g_object_unref(G_OBJECT(gwc->pixgc));
+
+        gcv.foreground.pixel =
+            widget->style->fg[GTK_WIDGET_STATE(widget)].pixel;
+        gcv.background.pixel =
+            widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
+        gcv.function = GDK_COPY;
+        gwc->pixgc = gdk_gc_new_with_values(widget->window, &gcv, gcm);
+    }
+}
+
+static void
+glyphedit_realize(GtkWidget *widget)
+{
+    Glyphedit *gw;
+    GlypheditClass *gwc;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+    GdkPixbuf *cb;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(widget));
+
+    gwc = GLYPHEDIT_GET_CLASS(widget);
+    gw = GLYPHEDIT(widget);
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK|
+                              GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK|
+                              GDK_POINTER_MOTION_MASK|
+                              GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|
+                              GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK|
+                              GDK_PROPERTY_CHANGE_MASK);
+
+    attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP;
+
+    widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
+                                    &attributes, attributes_mask);
+    gdk_window_set_user_data(widget->window, widget);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
+
+    /*
+     * Create the crosshair cursor.
+     */
+    if (gwc->cursor == 0) {
+        gwc = GLYPHEDIT_GET_CLASS(widget);
+        cb = gdk_pixbuf_new_from_xpm_data(cross_xpm);
+        gwc->cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
+                                                 cb, 7, 7);
+        g_object_unref(G_OBJECT(cb));
+    }
+
+    glyphedit_create_gcs(widget, FALSE);
+
+    gdk_window_set_cursor(widget->window, gwc->cursor);
+}
+
+static gboolean
+glyphedit_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+    /*
+     * Paint the shadow first.
+     */
+    if (GTK_WIDGET_DRAWABLE(widget))
+      gtk_paint_shadow(widget->style, widget->window,
+                       GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
+                       &event->area, widget, "glyphedit",
+                       0, 0,
+                       widget->allocation.width,
+                       widget->allocation.height);
+
+    glyphedit_draw(widget, event->region);
+
+    glyphedit_draw_focus(widget, &event->area);
+
+    return FALSE;
+}
+
+static gint
+glyphedit_focus_in(GtkWidget *widget, GdkEventFocus *event)
+{
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_GLYPHEDIT(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+    glyphedit_draw_focus(widget, 0);
+
+    return FALSE;
+}
+
+static gint
+glyphedit_focus_out(GtkWidget *widget, GdkEventFocus *event)
+{
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_GLYPHEDIT(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
+    glyphedit_draw_focus(widget, 0);
+
+    return FALSE;
+}
+
+/**************************************************************************
+ *
+ * Class and object initialization routines.
+ *
+ **************************************************************************/
+
+static GType
+glyphedit_get_operation_type(void)
+{
+    static GType etype = 0;
+    if (etype == 0) {
+        static const GEnumValue values[] = {
+            {GLYPHEDIT_NONE, "GLYPHEDIT_NONE", "none"},
+            {GLYPHEDIT_SELECT, "GLYPHEDIT_SELECT", "select"},
+            {GLYPHEDIT_DRAW, "GLYPHEDIT_DRAW", "draw"},
+            {GLYPHEDIT_MOVE, "GLYPHEDIT_MOVE", "move"},
+            {GLYPHEDIT_COPY, "GLYPHEDIT_COPY", "copy"},
+            {GLYPHEDIT_FLIP_HORIZONTAL,
+             "GLYPHEDIT_FLIP_HORIZONTAL",
+             "flip-horizontal"},
+            {GLYPHEDIT_FLIP_VERTICAL,
+             "GLYPHEDIT_FLIP_VERTICAL",
+             "flip-verticalal"},
+            {GLYPHEDIT_SHEAR, "GLYPHEDIT_SHEAR", "shear"},
+            {GLYPHEDIT_ROTATE_LEFT,
+             "GLYPHEDIT_ROTATE_LEFT",
+             "rotate-left"},
+            {GLYPHEDIT_ROTATE_RIGHT,
+             "GLYPHEDIT_ROTATE_RIGHT",
+             "rotate-right"},
+            {GLYPHEDIT_ROTATE,
+             "GLYPHEDIT_ROTATE",
+             "rotate"},
+            {GLYPHEDIT_SHIFT_UP_LEFT,
+             "GLYPHEDIT_SHIFT_UP_LEFT",
+             "shift-up-left"},
+            {GLYPHEDIT_SHIFT_UP,
+             "GLYPHEDIT_SHIFT_UP",
+             "shift-up"},
+            {GLYPHEDIT_SHIFT_UP_RIGHT,
+             "GLYPHEDIT_SHIFT_UP_RIGHT",
+             "shift-up-right"},
+            {GLYPHEDIT_SHIFT_LEFT,
+             "GLYPHEDIT_SHIFT_LEFT",
+             "shift-left"},
+            {GLYPHEDIT_SHIFT_RIGHT,
+             "GLYPHEDIT_SHIFT_RIGHT",
+             "shift-right"},
+            {GLYPHEDIT_SHIFT_DOWN_LEFT,
+             "GLYPHEDIT_SHIFT_DOWN_LEFT",
+             "shift-down-left"},
+            {GLYPHEDIT_SHIFT_DOWN,
+             "GLYPHEDIT_SHIFT_DOWN",
+             "shift-down"},
+            {GLYPHEDIT_SHIFT_DOWN_RIGHT,
+             "GLYPHEDIT_SHIFT_DOWN_RIGHT",
+             "shift-down-right"},
+            {0, 0, 0}
+        };
+        etype = g_enum_register_static("GlypheditOperation", values);
+    }
+    return etype;
+}
+
+static void
+glyphedit_init(GTypeInstance *obj, gpointer g_class)
+{
+    Glyphedit *gw = GLYPHEDIT(obj);
+    GlypheditClass *gwc = GLYPHEDIT_CLASS(g_class);
+    gint fwidth, fpad;
+
+    GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS);
+
+    gwc->gridgc = gwc->bbxgc = gwc->pixgc = gwc->selgc = 0;
+
+    gw->default_pixel_size = gw->pixel_size = DEFAULT_PIXEL_SIZE;
+
+    /*
+     * Make sure the spot is the right size.
+     */
+    fpad = (gw->pixel_size + 1) * (gw->pixel_size + 1);
+    if (gw->spot_size < fpad) {
+        if (gw->spot_size == 0)
+          gw->spot = g_malloc(fpad);
+        else
+          gw->spot = g_realloc(gw->spot, fpad);
+        gw->spot_size = fpad;
+    }
+    gw->spot_used = fpad;
+
+    gw->owns_clipboard = FALSE;
+
+    gw->grid = 0;
+
+    gw->last_x = gw->last_y = 0;
+
+    memset((char *) &gw->sel_start, 0, sizeof(GdkPoint));
+    memset((char *) &gw->sel_end, 0, sizeof(GdkPoint));
+
+    /*
+     * Always initialize to the first color.
+     */
+    gw->cidx = 1;
+
+    /*
+     * Initialize the last color seen.
+     */
+    gw->lcolor = 0;
+
+    gtk_widget_style_get(GTK_WIDGET(gw),
+                         "focus-line-width", &fwidth,
+                         "focus-padding", &fpad,
+                         NULL);
+
+    /*
+     * Padding that will appear before and after the focus rectangle.
+     * Hardcode this for now.
+     */
+    gw->border = 4;
+
+    gw->hmargin = gw->widget.style->xthickness + fwidth + fpad + gw->border;
+    gw->vmargin = gw->widget.style->ythickness + fwidth + fpad + gw->border;
+
+    gw->baselineColor.pixel = gw->selectionColor.pixel =
+        gw->cursorColor.pixel = 0;
+
+    gw->baselineColor.red = 0xffff;
+    gw->baselineColor.green = gw->baselineColor.blue = 0;
+
+    gw->op = GLYPHEDIT_DRAW;
+}
+
+/*
+ * A convenience function for calling the GLYPH_MODIFIED signal because
+ * so many functions depend on it.
+ */
+static void
+glyphedit_signal_glyph_change(Glyphedit *gw)
+{
+    bdf_bitmap_t image;
+    bdf_metrics_t metrics;
+    GlypheditSignalInfo si;
+
+    if (gw->grid == 0)
+      return;
+
+    glyphedit_get_glyph_metrics(gw, &metrics);
+    bdf_grid_image(gw->grid, &image);
+    si.reason = GLYPHEDIT_GLYPH_MODIFIED;
+    si.metrics = &metrics;
+    si.image = &image;
+    si.color = gw->cidx;
+
+    g_signal_emit(G_OBJECT(gw), glyphedit_signals[GLYPH_MODIFIED], 0, &si);
+    if (image.bytes > 0)
+      free(image.bitmap);
+}
+
+/**************************************************************************
+ *
+ * API functions.
+ *
+ **************************************************************************/
+
+GtkWidget *
+glyphedit_new(const gchar *prop1, ...)
+{
+    GtkWidget *w;
+    va_list var_args;
+
+    va_start(var_args, prop1);
+    w = GTK_WIDGET(g_object_new_valist(glyphedit_get_type(), prop1, var_args));
+    va_end(var_args);
+
+    return w;
+}
+
+GtkWidget *
+glyphedit_newv(bdf_glyph_grid_t *grid, guint16 default_pixel_size,
+               gboolean show_x_height, gboolean show_cap_height,
+               guint16 *colors)
+{
+    Glyphedit *ge = g_object_new(glyphedit_get_type(),
+                                 "glyphGrid", grid,
+                                 "pixelSize", default_pixel_size,
+                                 "showXHeight", show_x_height,
+                                 "showCapHeight", show_cap_height,
+                                 "colorList", colors,
+                                 NULL);
+
+    return GTK_WIDGET(ge);
+}
+
+gint32
+glyphedit_get_encoding(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, -1);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), -1);
+
+    return (gw->grid) ? gw->grid->encoding : -1;
+}
+
+void
+glyphedit_get_glyph_metrics(Glyphedit *gw, bdf_metrics_t *metrics)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(metrics != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (!gw->grid)
+      memset(metrics, 0, sizeof(bdf_metrics_t));
+    else {
+        metrics->font_spacing = gw->grid->spacing;
+        metrics->swidth = gw->grid->swidth;
+        metrics->dwidth = gw->grid->dwidth;
+        metrics->width = gw->grid->glyph_bbx.width;
+        metrics->height = gw->grid->glyph_bbx.height;
+        metrics->x_offset = gw->grid->glyph_bbx.x_offset;
+        metrics->y_offset = gw->grid->glyph_bbx.y_offset;
+        metrics->ascent = gw->grid->glyph_bbx.ascent;
+        metrics->descent = gw->grid->glyph_bbx.descent;
+    }
+}
+
+void
+glyphedit_get_font_metrics(Glyphedit *gw, bdf_metrics_t *metrics)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(metrics != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (!gw->grid)
+      memset(metrics, 0, sizeof(bdf_metrics_t));
+    else {
+        metrics->font_spacing = gw->grid->spacing;
+        metrics->swidth = gw->grid->swidth;
+        metrics->dwidth = gw->grid->dwidth;
+        metrics->width = gw->grid->font_bbx.width;
+        metrics->height = gw->grid->font_bbx.height;
+        metrics->x_offset = gw->grid->font_bbx.x_offset;
+        metrics->y_offset = gw->grid->font_bbx.y_offset;
+        metrics->ascent = gw->grid->font_bbx.ascent;
+        metrics->descent = gw->grid->font_bbx.descent;
+    }
+}
+
+bdf_psf_unimap_t *
+glyphedit_get_psf_mappings(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, 0);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), 0);
+
+    return (gw->grid) ? &gw->grid->unicode : 0;
+}
+
+/*
+ * Can set both font and glyph metrics.
+ */
+void
+glyphedit_set_metrics(Glyphedit *gw, bdf_metrics_t *metrics)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(metrics != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid == 0)
+      return;
+
+    if (bdf_grid_resize(gw->grid, metrics)) {
+        glyphedit_signal_glyph_change(gw);
+        gtk_widget_queue_resize(GTK_WIDGET(gw));
+    } else if (GTK_WIDGET_REALIZED(w))
+      /*
+       * The size didn't change, but we need to redraw if the widget
+       * has been realized.
+       */
+      gtk_widget_queue_draw(GTK_WIDGET(gw));
+}
+
+gint
+glyphedit_get_spacing(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, -1);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), -1);
+    g_return_val_if_fail(gw->grid != NULL, -1);
+
+    return gw->grid->spacing;
+}
+
+void
+glyphedit_set_spacing(Glyphedit *gw, gint spacing, guint16 monowidth)
+{
+    bdf_metrics_t metrics;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(gw->grid != NULL);
+
+    gw->grid->spacing = spacing;
+    if (spacing != BDF_PROPORTIONAL) {
+        glyphedit_get_font_metrics(gw, &metrics);
+        metrics.dwidth = metrics.width = monowidth;
+        glyphedit_set_metrics(gw, &metrics);
+    }
+}
+
+void
+glyphedit_set_grid(Glyphedit *gw, bdf_glyph_grid_t *grid)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    bdf_free_glyph_grid(gw->grid);
+    gw->grid = grid;
+
+    if (grid) {
+      gw->last_x = grid->base_x;
+      gw->last_y = grid->base_y;
+    } else
+      gw->last_x = gw->last_y = 0;
+
+    /*
+     * If the widget is in Move or Copy mode, change back to Select mode.
+     */
+    if (gw->op == GLYPHEDIT_MOVE || gw->op == GLYPHEDIT_COPY) {
+        gw->pending_op = gw->op;
+        gw->op = GLYPHEDIT_SELECT;
+    }
+
+    gw->cidx = 1;
+    gw->lcolor = 0;
+
+    gtk_widget_queue_resize(GTK_WIDGET(gw));
+}
+
+gboolean
+glyphedit_get_modified(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, FALSE);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), FALSE);
+
+    return (gw->grid) ? gw->grid->modified : FALSE;
+}
+
+void
+glyphedit_set_modified(Glyphedit *gw, gboolean modified)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid)
+      gw->grid->modified = ((modified == TRUE) ? 1 : 0);
+}
+
+void
+glyphedit_signal_modified(Glyphedit *gw)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    glyphedit_signal_glyph_change(gw);
+}
+
+gboolean
+glyphedit_get_selecting(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, FALSE);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), FALSE);
+    g_return_val_if_fail(gw->grid != NULL, FALSE);
+
+    return bdf_has_selection(gw->grid, 0, 0, 0, 0) ? TRUE : FALSE;
+}
+
+gboolean
+glyphedit_clipboard_empty(Glyphedit *gw)
+{
+    GdkWindow *owner;
+    gboolean empty = TRUE;
+    GdkAtom atype;
+    gint aformat, nitems;
+    guchar *data;
+
+    g_return_val_if_fail(gw != NULL, empty);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), empty);
+
+    if ((owner = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0)
+      return empty;
+
+    /*
+     * Check to see if the clipboard contents are empty or not.
+     *
+     * This is handled specially to allow determination of this without
+     * using up what might be a lot of memory to get the whole contents.  It
+     * will have to be changed for Windows.
+     */
+    if (gdk_property_get(owner, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
+                         0, 16, FALSE, &atype, &aformat, &nitems, &data)) {
+        if (nitems > 0) {
+            empty = FALSE;
+            free((char *) data);
+        }
+    }
+
+    return empty;
+}
+
+void
+glyphedit_get_image(Glyphedit *gw, bdf_bitmap_t *image)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(image != NULL);
+
+    if (gw->grid)
+      bdf_grid_image(gw->grid, image);
+    else
+      memset(image, 0, sizeof(bdf_bitmap_t));
+}
+
+bdf_glyph_grid_t *
+glyphedit_get_grid(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, 0);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), 0);
+
+    return gw->grid;
+}
+
+bdf_glyph_t *
+glyphedit_get_glyph(Glyphedit *gw, gboolean *unencoded)
+{
+    g_return_val_if_fail(gw != NULL, 0);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), 0);
+
+    if (gw->grid) {
+        if (unencoded)
+          *unencoded = (gw->grid->unencoded == 0) ? FALSE : TRUE;
+        return bdf_grid_glyph(gw->grid);
+    }
+    if (unencoded)
+      *unencoded = FALSE;
+    return 0;
+}
+
+void
+glyphedit_set_show_cap_height(Glyphedit *gw, gboolean show)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    gw->show_cap_height = show;
+
+    /*
+     * Redraw the bounding box.
+     */
+    glyphedit_draw_font_bbx(gw);
+}
+
+void
+glyphedit_set_show_x_height(Glyphedit *gw, gboolean show)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    gw->show_x_height = show;
+
+    /*
+     * Redraw the bounding box.
+     */
+    glyphedit_draw_font_bbx(gw);
+}
+
+void
+glyphedit_crop_glyph(Glyphedit *gw)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid && bdf_grid_crop(gw->grid, 1))
+      glyphedit_signal_glyph_change(gw);
+
+    glyphedit_draw_glyph(gw);
+}
+
+void
+glyphedit_shift_glyph(Glyphedit *gw, gint16 xcount, gint16 ycount)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid && bdf_grid_shift(gw->grid, xcount, ycount))
+      glyphedit_signal_glyph_change(gw);
+
+    glyphedit_draw_glyph(gw);
+}
+
+void
+glyphedit_rotate_glyph(Glyphedit *gw, gint16 degrees)
+{
+    gint resize = 0;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid && bdf_grid_rotate(gw->grid, degrees, &resize)) {
+        glyphedit_signal_glyph_change(gw);
+        if (resize)
+          gtk_widget_queue_resize(GTK_WIDGET(gw));
+        else
+          glyphedit_draw_glyph(gw);
+    }
+}
+
+void
+glyphedit_shear_glyph(Glyphedit *gw, gint16 degrees)
+{
+    gint resize = 0;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid && bdf_grid_shear(gw->grid, degrees, &resize)) {
+        glyphedit_signal_glyph_change(gw);
+        if (resize)
+          gtk_widget_queue_resize(GTK_WIDGET(gw));
+        else
+          glyphedit_draw_glyph(gw);
+    }
+}
+
+void
+glyphedit_embolden_glyph(Glyphedit *gw)
+{
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (gw->grid && bdf_grid_embolden(gw->grid)) {
+        glyphedit_signal_glyph_change(gw);
+
+        /*
+         * Simply redraw the glyph because the size didn't change,
+         * only the bitmap.
+         */
+        glyphedit_draw_glyph(gw);
+    }
+}
+
+void
+glyphedit_flip_glyph(Glyphedit *gw, GtkOrientation direction)
+{
+    gint flipped;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(gw->grid != NULL);
+
+    flipped = (direction == GTK_ORIENTATION_HORIZONTAL) ?
+        bdf_grid_flip(gw->grid, -1) : bdf_grid_flip(gw->grid, 1);
+
+    if (flipped) {
+        glyphedit_signal_glyph_change(gw);
+
+        /*
+         * Simply redraw the glyph because the size didn't change,
+         * only the bitmap.
+         */
+        glyphedit_draw_glyph(gw);
+    }
+}
+
+void
+glyphedit_set_pixel_size(Glyphedit *gw, guint pixel_size)
+{
+    gint bytes;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if (pixel_size < MIN_PIXEL_SIZE || pixel_size > MAX_PIXEL_SIZE)
+      return;
+
+    /*
+     * Queue up a resize to force the resize and redraw.
+     */
+    gw->pixel_size = pixel_size;
+
+    /*
+     * Make sure the spot is the right size.
+     */
+    bytes = (pixel_size + 1) * (pixel_size + 1);
+    if (gw->spot_size < bytes) {
+        if (gw->spot_size == 0)
+          gw->spot = g_malloc(bytes);
+        else
+          gw->spot = g_realloc(gw->spot, bytes);
+        gw->spot_size = bytes;
+    }
+    gw->spot_used = bytes;
+
+    gtk_widget_queue_resize(GTK_WIDGET(gw));
+}
+
+guint
+glyphedit_get_pixel_size(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, GLYPHEDIT_NONE);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), GLYPHEDIT_NONE);
+    g_return_val_if_fail(gw->grid != NULL, GLYPHEDIT_NONE);
+
+    return gw->pixel_size;
+}
+
+GlypheditOperation
+glyphedit_get_operation(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, GLYPHEDIT_NONE);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), GLYPHEDIT_NONE);
+    g_return_val_if_fail(gw->grid != NULL, GLYPHEDIT_NONE);
+
+    return gw->op;
+}
+
+void
+glyphedit_set_operation(Glyphedit *gw, GlypheditOperation op)
+{
+    gint16 sx, sy, x, y, wd, ht;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(gw->grid != NULL);
+
+    if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
+        if (op == GLYPHEDIT_MOVE)
+          bdf_detach_selection(gw->grid);
+        else if (op == GLYPHEDIT_COPY)
+          bdf_attach_selection(gw->grid);
+        else {
+            if (op == GLYPHEDIT_DRAW) {
+                /*
+                 * Attach the selected part of the bitmap.
+                 */
+                bdf_attach_selection(gw->grid);
+
+                /*
+                 * Erase the selected rectangle.
+                 */
+                for (sy = y; sy < y + ht; sy++) {
+                    for (sx = x; sx < x + wd; sx++)
+                      glyphedit_draw_pixel(gw, sx, sy, FALSE);
+                }
+                bdf_lose_selection(gw->grid);
+            }
+
+            gw->op = op;
+        }
+        gw->pending_op = GLYPHEDIT_NONE;
+
+        glyphedit_signal_glyph_change(gw);
+    } else {
+        if (op == GLYPHEDIT_MOVE || op == GLYPHEDIT_COPY) {
+            gw->op = GLYPHEDIT_SELECT;
+            gw->pending_op = op;
+        } else {
+            gw->op = op;
+            gw->pending_op = GLYPHEDIT_NONE;
+        }
+    }
+}
+
+void
+glyphedit_insert_bitmap(Glyphedit *gw, bdf_bitmap_t *bitmap)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    GdkWindow *win;
+    gint16 sx, sy, x, y, wd, ht;
+    bdf_metrics_t metrics;
+    GlypheditSignalInfo si;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    if ((win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) {
+        gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
+                                GDK_CURRENT_TIME, FALSE);
+        win = w->window;
+    } else if (win != w->window)
+      gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
+                              GDK_CURRENT_TIME, FALSE);
+
+    if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
+        /*
+         * This widget already has a selection, so release it.
+         */
+        if (gw->op != GLYPHEDIT_SELECT)
+          bdf_attach_selection(gw->grid);
+
+        for (sy = y; sy < y + ht; sy++) {
+            for (sx = x; sx < x + wd; sx++)
+              glyphedit_draw_pixel(gw, sx, sy, FALSE);
+        }
+        bdf_lose_selection(gw->grid);
+    }
+
+    bitmap->x = gw->last_x;
+    bitmap->y = gw->last_y;
+
+    glyphedit_get_font_metrics(gw, &metrics);
+    if (bitmap->width > metrics.width || bitmap->height > metrics.height) {
+        /*
+         * Adjust the insert position on the X axis if necessary.
+         */
+        if (bitmap->width > metrics.width)
+          bitmap->x = gw->grid->base_x + gw->grid->font_bbx.x_offset;
+        /*
+         * Adjust the insert position on the Y axis and the ascent if
+         * necessary.
+         */
+        if (bitmap->height > metrics.height) {
+            bitmap->y = 0;
+            metrics.ascent = bitmap->height - gw->grid->font_bbx.descent;
+        }
+        metrics.width = bitmap->width;
+        metrics.height = bitmap->height;
+        glyphedit_set_metrics(gw, &metrics);
+    }
+
+    /*
+     * Set the selection in the grid.
+     */
+    bdf_add_selection(gw->grid, bitmap);
+
+    /*
+     * Now update the grid.
+     */
+    if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
+        for (sy = y; sy < y + ht; sy++) {
+            for (sx = x; sx < x + wd; sx++)
+              glyphedit_draw_pixel(gw, sx, sy, TRUE);
+        }
+    }
+
+    /*
+     * Set up and call the operation change signal.
+     */
+    si.reason = GLYPHEDIT_OPERATION_CHANGE;
+    si.operation = GLYPHEDIT_MOVE;
+    g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE], 0,
+                  &si);
+
+    /*
+     * Set up and call the modified signal.
+     */
+    glyphedit_signal_glyph_change(gw);
+
+    /*
+     * Make sure the widget goes into MOVE mode at this point.
+     * This allows the user to position what was pasted without
+     * destroying the glyph bitmap that was already there.
+     */
+    if (gw->op != GLYPHEDIT_MOVE) {
+        gw->op = GLYPHEDIT_MOVE;
+        gw->pending_op = GLYPHEDIT_NONE;
+    }
+
+    glyphedit_copy_selection(gw);
+}
+
+static void
+glyphedit_own_clipboard(Glyphedit *gw)
+{
+    GtkWidget *w;
+    GdkWindow *win;
+
+    w = GTK_WIDGET(gw);
+    if (!GTK_WIDGET_REALIZED(w) || gw->owns_clipboard == TRUE)
+      return;
+
+    win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD);
+    gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
+                            GDK_CURRENT_TIME, FALSE);
+
+    gw->owns_clipboard =
+        (gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD) == w->window) ? TRUE : FALSE;
+
+    /*
+     * The Intrinsics may need to have a SelectionClear notice sent. Probably
+     * won't be necessary on Windows.
+     */
+}
+
+static guchar *
+glyphedit_encode_selection(Glyphedit *gw, gint *bytes)
+{
+    gint bcount, size;
+    gint16 wd, ht;
+    guchar *bmap, *bp;
+
+    *bytes = 0;
+    if (!bdf_has_selection(gw->grid, 0, 0, &wd, &ht))
+      return 0;
+
+    size = bcount = (gint) gw->grid->sel.bytes >> 1;
+    size += sizeof(guint16) * 3;
+    bp = bmap = (guchar *) g_malloc(size);
+
+    /*
+     * Encode the width and height in Most Significant Byte order assuming
+     * the width and height types are 16-bit values.
+     */
+    if (!bdf_little_endian()) {
+        *bp++ = (gw->grid->bpp >> 8) & 0xff;
+        *bp++ = gw->grid->bpp & 0xff;
+        *bp++ = (wd >> 8) & 0xff;
+        *bp++ = wd & 0xff;
+        *bp++ = (ht >> 8) & 0xff;
+        *bp++ = ht & 0xff;
+    } else {
+        *bp++ = gw->grid->bpp & 0xff;
+        *bp++ = (gw->grid->bpp >> 8) & 0xff;
+        *bp++ = wd & 0xff;
+        *bp++ = (wd >> 8) & 0xff;
+        *bp++ = ht & 0xff;
+        *bp++ = (ht >> 8) & 0xff;
+    }
+
+    (void) memcpy((char *) bp, (char *) gw->grid->sel.bitmap, bcount);
+
+    *bytes = size;
+    return bmap;
+}
+
+void
+glyphedit_copy_selection(Glyphedit *gw)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    guchar *sel;
+    gint bytes;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    /*
+     * If the widget has no selection, then this routine will return 0.
+     */
+    if ((sel = glyphedit_encode_selection(gw, &bytes)) == 0)
+      return;
+
+    /*
+     * Go ahead and actually write the data to the clipboard and then free the
+     * buffer.
+     */
+    gdk_property_change(w->window, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
+                        8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes);
+
+    g_free(sel);
+}
+
+void
+glyphedit_cut_selection(Glyphedit *gw)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    guchar *sel;
+    gint bytes;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    /*
+     * If the widget has no selection, then this routine will return 0.
+     */
+    if ((sel = glyphedit_encode_selection(gw, &bytes)) == 0)
+      return;
+
+    /*
+     * Go ahead and actually write the data to the clipboard and then free the
+     * buffer.
+     */
+    gdk_property_change(w->window, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
+                        8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes);
+
+    g_free(sel);
+
+    /*
+     * Now actually delete the selection and update the glyph.
+     */
+    bdf_delete_selection(gw->grid);
+    bdf_lose_selection(gw->grid);
+    if (gw->op != GLYPHEDIT_DRAW) {
+        gw->pending_op = gw->op;
+        gw->op = GLYPHEDIT_SELECT;
+    }
+    glyphedit_draw_glyph(gw);
+    glyphedit_signal_glyph_change(gw);
+}
+
+void
+glyphedit_change_operation(Glyphedit *gw, GlypheditOperation op)
+{
+    gboolean call_modify;
+    gint16 sx, sy, x, y, wd, ht;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+
+    call_modify = TRUE;
+
+    /*
+     * Special handling is needed for move and copy operations.  If a
+     * selection does not exist yet, then make the move/copy a pending
+     * operation and set the operation to select.  Once the selection is made,
+     * the operation will be changed to the pending move/copy operation.  If a
+     * selection exists, then set the move/copy operation and detach/attach
+     * the selection accordingly.
+     */
+    if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
+        if (op == GLYPHEDIT_MOVE)
+          bdf_detach_selection(gw->grid);
+        else if (op == GLYPHEDIT_COPY)
+          bdf_attach_selection(gw->grid);
+        else {
+            if (op == GLYPHEDIT_DRAW) {
+                /*
+                 * Attach the selected part of the bitmap.
+                 */
+                bdf_attach_selection(gw->grid);
+
+                /*
+                 * Erase the selected rectangle.
+                 */
+                for (sy = y; sy < y + ht; sy++) {
+                    for (sx = x; sx < x + wd; sx++)
+                      glyphedit_draw_pixel(gw, sx, sy, FALSE);
+                }
+                bdf_lose_selection(gw->grid);
+
+            } else
+              call_modify = FALSE;
+            gw->op = op;
+        }
+        gw->pending_op = GLYPHEDIT_NONE;
+        glyphedit_signal_glyph_change(gw);
+    } else {
+        if (op == GLYPHEDIT_MOVE || op == GLYPHEDIT_COPY) {
+            gw->op = GLYPHEDIT_SELECT;
+            gw->pending_op = op;
+        } else {
+            gw->op = op;
+            gw->pending_op = GLYPHEDIT_NONE;
+        }
+    }
+}
+
+void
+glyphedit_set_color(Glyphedit *gw, gint idx)
+{
+    GlypheditSignalInfo si;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(gw->grid != 0);
+
+    if (gw->grid) {
+        if (idx <= 0)
+          idx = (1 << gw->grid->bpp);
+        else if (idx > (1 << gw->grid->bpp))
+          idx = 1;
+    } else
+      idx = 1;
+
+    if (idx != gw->cidx) {
+        si.reason = GLYPHEDIT_COLOR_CHANGE;
+        si.color = idx;
+        g_signal_emit(G_OBJECT(gw), glyphedit_signals[COLOR_CHANGE], 0, &si);
+    }
+
+    gw->cidx = idx;
+}
+
+void
+glyphedit_paste_selection(Glyphedit *gw)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    GdkWindow *win;
+    GdkAtom atype;
+    gint aformat, nitems;
+    guchar *data, *bp;
+    gint16 sx, sy, x, y, wd, ht;
+    bdf_metrics_t metrics;
+    bdf_bitmap_t image;
+    GlypheditSignalInfo si;
+    
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(gw->grid != NULL);
+
+    if ((win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) {
+        gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
+                                GDK_CURRENT_TIME, FALSE);
+        win = w->window;
+    }
+
+    nitems = 0;
+    gdk_property_get(win, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
+                     0, 10240, FALSE, &atype, &aformat, &nitems, &data);
+
+    if (win != w->window)
+      gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
+                              GDK_CURRENT_TIME, FALSE);
+
+    if (nitems > 0) {
+        /*
+         * Got a bitmap.
+         */
+
+        if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
+            /*
+             * This widget already has a selection, so release it.
+             */
+            if (gw->op != GLYPHEDIT_SELECT)
+              bdf_attach_selection(gw->grid);
+
+            for (sy = y; sy < y + ht; sy++) {
+                for (sx = x; sx < x + wd; sx++)
+                  glyphedit_draw_pixel(gw, sx, sy, FALSE);
+            }
+            bdf_lose_selection(gw->grid);
+        }
+
+        bp = data;
+
+        if (!bdf_little_endian()) {
+            image.bpp = (*bp++ << 8) & 0xff00;
+            image.bpp |= *bp++;
+            image.width = (*bp++ << 8) & 0xff00;
+            image.width |= *bp++;
+            image.height = (*bp++ << 8) & 0xff00;
+            image.height |= *bp++;
+        } else {
+            image.bpp = *bp++ & 0xff;
+            image.bpp |= (*bp++ << 8) & 0xff00;
+            image.width = *bp++ & 0xff;
+            image.width |= (*bp++ << 8) & 0xff00;
+            image.height = *bp++ & 0xff;
+            image.height |= (*bp++ << 8) & 0xff00;
+        }
+
+        image.bytes = (((image.width * image.bpp) + 7) >> 3) * image.height;
+        image.bitmap = bp;
+
+        image.x = gw->last_x;
+        image.y = gw->last_y;
+
+        /*
+         * If the bitmap being pasted is larger than the current grid, then
+         * resize the grid before doing anything else.
+         */
+        glyphedit_get_font_metrics(gw, &metrics);
+        if (image.width > metrics.width || image.height > metrics.height) {
+            /*
+             * Adjust the insert position on the X axis if necessary.
+             */
+            if (image.width > metrics.width)
+              image.x = gw->grid->base_x +
+                  gw->grid->font_bbx.x_offset;
+            /*
+             * Adjust the insert position on the Y axis and the ascent if
+             * necessary.
+             */
+            if (image.height > metrics.height) {
+                image.y = 0;
+                metrics.ascent = image.height - gw->grid->font_bbx.descent;
+            }
+            metrics.width = image.width;
+            metrics.height = image.height;
+            glyphedit_set_metrics(gw, &metrics);
+        }
+
+        /*
+         * Set the selection in the grid.
+         */
+        bdf_add_selection(gw->grid, &image);
+
+        /*
+         * Now update the grid.
+         */
+        if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
+            for (sy = y; sy < y + ht; sy++) {
+                for (sx = x; sx < x + wd; sx++)
+                  glyphedit_draw_pixel(gw, sx, sy, TRUE);
+            }
+        }
+
+        /*
+         * Set up and call the image update.
+         */
+        glyphedit_signal_glyph_change(gw);
+
+        /*
+         * Free up the original value passed.
+         */
+        g_free(data);
+
+        /*
+         * Alert the client that the widget is changing to the MOVE
+         * operation.
+         */
+        si.reason = GLYPHEDIT_OPERATION_CHANGE;
+        si.operation = GLYPHEDIT_MOVE;
+        g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE],
+                      0, &si);
+
+        /*
+         * Make sure the widget goes into MOVE mode at this point.
+         * This allows the user to position what was pasted without
+         * destroying the glyph bitmap that was already there.
+         */
+        if (gw->op != GLYPHEDIT_MOVE) {
+            gw->op = GLYPHEDIT_MOVE;
+            gw->pending_op = GLYPHEDIT_NONE;
+        }
+
+        /*
+         * Last, recopy the selection to the clipboard because changing owners
+         * causes the data to be lost.
+         */
+        glyphedit_copy_selection(gw);
+    }
+}
+
+void
+glyphedit_select_all(Glyphedit *gw)
+{
+    gint16 tx, ty, sx, sy, wd, ht;
+    GlypheditSignalInfo si;
+
+    g_return_if_fail(gw != NULL);
+    g_return_if_fail(IS_GLYPHEDIT(gw));
+    g_return_if_fail(gw->grid != NULL);
+
+    /*
+     * If a selection already exists, clear it.
+     */
+    if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) {
+        if (gw->op != GLYPHEDIT_SELECT)
+          bdf_attach_selection(gw->grid);
+
+        for (ty = sy; ty < sy + ht; ty++) {
+            for (tx = sx; tx < sx + wd; tx++)
+              glyphedit_draw_pixel(gw, tx, ty, FALSE);
+        }
+        bdf_lose_selection(gw->grid);
+    }
+
+    wd = gw->grid->glyph_bbx.width;
+    ht = gw->grid->glyph_bbx.height;
+
+    sx = gw->sel_start.x = gw->grid->glyph_x;
+    sy = gw->sel_start.y = gw->grid->glyph_y;
+    gw->sel_end.x = gw->grid->glyph_x + wd;
+    gw->sel_end.y = gw->grid->glyph_y + ht;
+
+    /*
+     * Gain control of the GLYPHEDIT_CLIPBOARD atom.
+     */
+    glyphedit_own_clipboard(gw);
+
+    bdf_set_selection(gw->grid, sx, sy, wd, ht);
+    bdf_detach_selection(gw->grid);
+
+    for (ty = sy; ty < sy + ht; ty++) {
+        for (tx = sx; tx < sx + wd; tx++)
+          glyphedit_draw_pixel(gw, tx, ty, TRUE);
+    }
+
+    /*
+     * Alert the client that the widget is changing to the MOVE
+     * operation.
+     */
+    si.reason = GLYPHEDIT_OPERATION_CHANGE;
+    si.operation = GLYPHEDIT_MOVE;
+    g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE],
+                  0, &si);
+
+    /*
+     * Make sure the widget goes into MOVE mode at this point.
+     * This allows the user to position what was pasted without
+     * destroying the glyph bitmap that was already there.
+     */
+    if (gw->op != GLYPHEDIT_MOVE) {
+        gw->op = GLYPHEDIT_MOVE;
+        gw->pending_op = GLYPHEDIT_NONE;
+    }
+}
+
+gint32
+glyphedit_encoding(Glyphedit *gw)
+{
+    g_return_val_if_fail(gw != NULL, -1);
+    g_return_val_if_fail(IS_GLYPHEDIT(gw), -1);
+    g_return_val_if_fail(gw->grid != NULL, -1);
+
+    return (gw->grid->unencoded) ? -1 : gw->grid->encoding;
+}
+
+static void
+glyphedit_get_pointer_coord(Glyphedit *gw, gint16 ex, gint16 ey,
+                            gint16 *px, gint16 *py)
+{
+    GtkWidget *w = GTK_WIDGET(gw);
+    gint16 x, y, wd, ht;
+
+    wd = (gw->pixel_size + 4) * gw->grid->grid_width;
+    ht = (gw->pixel_size + 4) * gw->grid->grid_height;
+
+    /*
+     * Need the plus 1 to account for the outer rectangle.
+     */
+    x = (w->allocation.width >> 1) - (wd >> 1) + 1;
+    y = (w->allocation.height >> 1) - (ht >> 1) + 1;
+
+    if (ex < x || ex > x + wd)
+      *px = -1;
+    else
+      *px = (ex - x) / (gw->pixel_size + 4);
+
+    if (ey < y || ey > y + ht)
+      *py = -1;
+    else
+      *py = (ey - y) / (gw->pixel_size + 4);
+
+    /*
+     * Adjust for a possible overrun off the edges of the grid.
+     */
+    if (*px >= gw->grid->grid_width)
+      *px = gw->grid->grid_width - 1;
+    if (*py >= gw->grid->grid_height)
+      *py = gw->grid->grid_height - 1;
+}
+
+static gboolean
+glyphedit_in_selection(Glyphedit *gw, gint16 x, gint16 y)
+{
+    return (((gw->sel_start.y <= y && y <= gw->sel_end.y) ||
+             (gw->sel_end.y <= y && y <= gw->sel_start.y)) &&
+            ((gw->sel_start.x <= x && x <= gw->sel_end.x) ||
+             (gw->sel_end.x <= x && x <= gw->sel_start.x)))
+        ? TRUE : FALSE;
+}
+
+static gboolean
+glyphedit_in_intersection(Glyphedit *gw, gint16 ix, gint16 iy,
+                          gint16 x, gint16 y)
+{
+    return (((gw->sel_start.y <= y && y <= iy) ||
+             (iy <= y && y <= gw->sel_start.y)) &&
+            ((gw->sel_start.x <= x && x <= ix) ||
+             (ix <= x && x <= gw->sel_start.x))) ? TRUE : FALSE;
+}
+
+static void
+glyphedit_update_selection(Glyphedit *gw, gint16 x, gint16 y, gboolean set)
+{
+    gint16 wd, ht;
+
+    for (ht = 0; ht < gw->grid->grid_height; ht++) {
+        for (wd = 0; wd < gw->grid->grid_width; wd++) {
+            if (glyphedit_in_intersection(gw, x, y, wd, ht) == FALSE &&
+                glyphedit_in_selection(gw, wd, ht) == TRUE)
+              /*
+               * Clear or set the pixel.
+               */
+              glyphedit_draw_pixel(gw, wd, ht, set);
+        }
+    }
+}
+
+static gboolean
+glyphedit_button_press(GtkWidget *w, GdkEventButton *event)
+{
+    Glyphedit *gw;
+    gint16 x, y, sx, sy, tx, ty, wd, ht;
+    gboolean changed;
+
+    gw = GLYPHEDIT(w);
+
+    glyphedit_get_pointer_coord(gw, (gint16) event->x, (gint16) event->y,
+                                &x, &y);
+
+    if (event->button == 2 && (event->state & GDK_SHIFT_MASK)) {
+        /*
+         * Paste.
+         */
+        glyphedit_paste_selection(gw);
+        return FALSE;
+    }
+
+    changed = FALSE;
+    if (gw->op == GLYPHEDIT_DRAW) {
+        switch (event->button) {
+          case 1:
+            if ((changed = bdf_grid_set_pixel(gw->grid, x, y, gw->cidx)))
+              glyphedit_draw_pixel(gw, x, y, FALSE);
+            break;
+          case 2:
+            if ((changed = bdf_grid_invert_pixel(gw->grid, x, y, gw->cidx)))
+              glyphedit_draw_pixel(gw, x, y, FALSE);
+            break;
+          case 3:
+            if ((changed = bdf_grid_clear_pixel(gw->grid, x, y)))
+              glyphedit_draw_pixel(gw, x, y, FALSE);
+            break;
+        }
+        if (changed == TRUE)
+          glyphedit_signal_glyph_change(gw);
+    } else if (gw->op == GLYPHEDIT_SELECT) {
+        /*
+         * If a selection already exists, clear it.
+         */
+        if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) {
+            if (gw->pending_op != GLYPHEDIT_NONE)
+              bdf_attach_selection(gw->grid);
+
+            for (ty = sy; ty < sy + ht; ty++) {
+                for (tx = sx; tx < sx + wd; tx++)
+                  glyphedit_draw_pixel(gw, tx, ty, FALSE);
+            }
+            bdf_lose_selection(gw->grid);
+        }
+
+        /*
+         * Select the pixel at the point and initialize the selection
+         * rectangle.
+         */
+        glyphedit_draw_pixel(gw, x, y, TRUE);
+
+        gw->sel_start.x = gw->sel_end.x = x;
+        gw->sel_start.y = gw->sel_end.y = y;
+    } else {
+        /*
+         * Check to see if this is Button3 and a selection exists.  If so,
+         * then copy the selection to the clipboard and return.
+         */
+        if (event->button == 3 &&
+            bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) {
+            glyphedit_copy_selection(gw);
+            gw->last_x = x;
+            gw->last_y = y;
+            return FALSE;
+        }
+
+        /*
+         * The operation is one of move or copy.  If the button is clicked
+         * outside the selection, remove the selection and start over.
+         */
+        if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht) &&
+            !bdf_in_selection(gw->grid, x, y, 0)) {
+
+            if (gw->op != GLYPHEDIT_SELECT)
+              bdf_attach_selection(gw->grid);
+
+            for (ty = sy; ty < sy + ht; ty++) {
+                for (tx = sx; tx < sx + wd; tx++)
+                  glyphedit_draw_pixel(gw, tx, ty, FALSE);
+            }
+            bdf_lose_selection(gw->grid);
+
+            gw->pending_op = gw->op;
+            gw->op = GLYPHEDIT_SELECT;
+
+            /*
+             * Select the pixel at the point and initialize the selection
+             * rectangle.
+             */
+            glyphedit_draw_pixel(gw, x, y, TRUE);
+
+            gw->sel_start.x = gw->sel_end.x = x;
+            gw->sel_start.y = gw->sel_end.y = y;
+        }
+    }
+
+    /*
+     * Set the last coordinate to the point just handled.
+     */
+    gw->last_x = x;
+    gw->last_y = y;
+
+    return FALSE;
+}
+
+static gboolean
+glyphedit_button_release(GtkWidget *w, GdkEventButton *event)
+{
+    Glyphedit *gw;
+    gint16 sx, sy, ex, ey;
+
+    /*
+     * Button releases on a widget without the focus is ignored.
+     */
+    if (!GTK_WIDGET_HAS_FOCUS(w))
+      return FALSE;
+
+    gw = GLYPHEDIT(w);
+
+    sx = MIN(gw->sel_start.x, gw->sel_end.x);
+    ex = MAX(gw->sel_start.x, gw->sel_end.x);
+    sy = MIN(gw->sel_start.y, gw->sel_end.y);
+    ey = MAX(gw->sel_start.y, gw->sel_end.y);
+
+    if (gw->op == GLYPHEDIT_SELECT) {
+        if (sx == ex && sy == ey)
+          glyphedit_draw_pixel(gw, gw->sel_start.x, gw->sel_start.y, FALSE);
+        else {
+            /*
+             * Gain control of the GLYPHEDIT_CLIPBOARD atom.
+             */
+            glyphedit_own_clipboard(gw);
+
+            bdf_set_selection(gw->grid, sx, sy, (ex - sx) + 1, (ey - sy) + 1);
+
+            /*
+             * Switch to a move/copy operations if necessary.
+             */
+            if (gw->pending_op != GLYPHEDIT_NONE) {
+                gw->op = gw->pending_op;
+                gw->pending_op = GLYPHEDIT_NONE;
+                /*
+                 * If the pending operation is a move, then make sure the
+                 * selection is detached.
+                 */
+                if (gw->op == GLYPHEDIT_MOVE)
+                  bdf_detach_selection(gw->grid);
+            }
+        }
+    }
+    return FALSE;
+}
+
+static gboolean
+glyphedit_motion_notify(GtkWidget *w, GdkEventMotion *event)
+{
+    Glyphedit *gw;
+    gboolean changed;
+    gint16 x, y, ix, iy;
+    GlypheditSignalInfo si;
+
+    gw = GLYPHEDIT(w);
+
+    glyphedit_get_pointer_coord(gw, (gint16) event->x, (gint16) event->y,
+                                &x, &y);
+
+    /*
+     * Return if the mouse is off the edges of the grid or the mouse is still
+     * on the same point as the last one.
+     */
+    if (x < 0 || y < 0 || (x == gw->last_x && y == gw->last_y))
+      return FALSE;
+
+    si.reason = GLYPHEDIT_POINTER_MOVED;
+    si.x = x - gw->grid->base_x;
+    si.y = -(y - gw->grid->base_y) - 1;
+    si.color = bdf_grid_color_at(gw->grid, x, y);
+    g_signal_emit(G_OBJECT(gw), glyphedit_signals[POINTER_MOVED],
+                  0, &si);
+
+    ix = gw->last_x;
+    iy = gw->last_y;
+
+    gw->last_x = x;
+    gw->last_y = y;
+
+    /*
+     * If the event is a simple motion event with no button being pressed,
+     * then simply return at this point.
+     */
+    if (!GTK_WIDGET_HAS_FOCUS(w) ||
+        !(event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)))
+      return FALSE;
+
+    changed = FALSE;
+    if (gw->op == GLYPHEDIT_DRAW) {
+        /*
+         * Drawing.
+         */
+        if (event->state & GDK_BUTTON1_MASK) {
+            if ((changed = bdf_grid_set_pixel(gw->grid, x, y, gw->cidx)))
+              glyphedit_draw_pixel(gw, x, y, FALSE);
+        } else if (event->state & GDK_BUTTON2_MASK) {
+            if ((changed = bdf_grid_invert_pixel(gw->grid, x, y, gw->cidx)))
+              glyphedit_draw_pixel(gw, x, y, FALSE);
+        } else if (event->state & GDK_BUTTON3_MASK) {
+            if ((changed = bdf_grid_clear_pixel(gw->grid, x, y)))
+              glyphedit_draw_pixel(gw, x, y, FALSE);
+        }
+
+        /*
+         * If one of the pixels changed, then call the callback.
+         */
+        if (changed)
+          glyphedit_signal_glyph_change(gw);
+    } else if (gw->op == GLYPHEDIT_SELECT) {
+        /*
+         * Determine the other point on the intersection rectangle.
+         */
+        ix = gw->sel_start.x;
+        iy = gw->sel_start.y;
+
+        if (x > ix)
+          ix = MIN(gw->sel_end.x, x);
+        else if (x < ix)
+          ix = MAX(gw->sel_end.x, x);
+
+        if (y > iy)
+          iy = MIN(gw->sel_end.y, y);
+        else if (y < iy)
+          iy = MAX(gw->sel_end.y, y);
+
+        /*
+         * Clear the pixels outside the intersection of the old selection
+         * rectangle and the new selection rectangle.
+         */
+        glyphedit_update_selection(gw, ix, iy, FALSE);
+
+        /*
+         * Set the new endpoint of the selection rectangle.
+         */
+        gw->sel_end.x = x;
+        gw->sel_end.y = y;
+
+        /*
+         * Set all pixels outside the intersection of the old selection
+         * rectangle and the new selection rectangle, but inside the new
+         * selection rectangle.
+         */
+        glyphedit_update_selection(gw, ix, iy, TRUE);
+    } else {
+        /*
+         * A move or copy is in progress.
+         */
+        if (bdf_has_selection(gw->grid, 0, 0, 0, 0) &&
+            bdf_grid_shift(gw->grid, x - ix, y - iy)) {
+            glyphedit_draw_glyph(gw);
+            glyphedit_signal_glyph_change(gw);
+        }
+    }
+
+    return FALSE;
+}
+
+static gboolean
+glyphedit_key_press(GtkWidget *w, GdkEventKey *event)
+{
+    gboolean ret = FALSE;
+
+    switch (event->keyval) {
+      case GDK_Left:
+      case GDK_KP_Left:
+        glyphedit_shift_glyph(GLYPHEDIT(w), -1, 0);
+        break;
+      case GDK_Right:
+      case GDK_KP_Right:
+        glyphedit_shift_glyph(GLYPHEDIT(w), 1, 0);
+        break;
+      case GDK_Up:
+      case GDK_KP_Up:
+        /*
+         * For some reason, the Up arrow causes the focus to change to
+         * other widgets. Returning TRUE insures that the up arrow works
+         * as expected.
+         */
+        glyphedit_shift_glyph(GLYPHEDIT(w), 0, -1);
+        ret = TRUE;
+        break;
+      case GDK_Down:
+      case GDK_KP_Down:
+        glyphedit_shift_glyph(GLYPHEDIT(w), 0, 1);
+        break;
+      case GDK_Delete:
+      case GDK_BackSpace:
+        glyphedit_cut_selection(GLYPHEDIT(w));
+        break;
+      case GDK_9:
+      case GDK_KP_9:
+        glyphedit_rotate_glyph(GLYPHEDIT(w), -90);
+      case GDK_0:
+      case GDK_KP_0:
+        glyphedit_rotate_glyph(GLYPHEDIT(w), 90);
+        break;
+      case GDK_minus:
+      case GDK_KP_Subtract:
+        glyphedit_flip_glyph(GLYPHEDIT(w), GTK_ORIENTATION_HORIZONTAL);
+        break;
+      case GDK_equal:
+      case GDK_KP_Equal:
+        glyphedit_flip_glyph(GLYPHEDIT(w), GTK_ORIENTATION_VERTICAL);
+        break;
+      case GDK_comma:
+      case GDK_Z:
+      case GDK_z:
+        /* Change to a lighter color. */
+        glyphedit_set_color(GLYPHEDIT(w), GLYPHEDIT(w)->cidx - 1);
+        break;
+      case GDK_period:
+      case GDK_X:
+      case GDK_x:
+        /* Change to a darker color. */
+        glyphedit_set_color(GLYPHEDIT(w), GLYPHEDIT(w)->cidx + 1);
+        break;
+    }
+
+    return ret;
+}
+
+static gboolean
+glyphedit_key_release(GtkWidget *w, GdkEventKey *event)
+{
+    return FALSE;
+}
+
+static void
+glyphedit_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass *gocp = G_OBJECT_CLASS(g_class);
+    GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class);
+    GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class);
+
+    /*
+     * Set the class global variables.
+     */
+    parent_class = g_type_class_peek_parent(g_class);
+
+    ocp->destroy = glyphedit_destroy;
+
+    gocp->set_property = glyphedit_set_property;
+    gocp->get_property = glyphedit_get_property;
+    gocp->finalize = glyphedit_finalize;
+
+    /*
+     * Add argument (a.k.a. resource) types.
+     */
+    g_object_class_install_property(gocp, GLYPH_GRID,
+                                    g_param_spec_pointer("glyphGrid",
+                                                       _("Glyph Grid"),
+                                                       _("The glyph in a grid structure."),
+                                                       G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PIXEL_SIZE,
+                                    g_param_spec_uint("pixelSize",
+                                                      _("Pixel Size"),
+                                                      _("The number of pixels to use to draw one grid pixel."),
+                                                      1,
+                                                      20,
+                                                      10,
+                                                      G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, SHOW_X_HEIGHT,
+                                    g_param_spec_boolean("showXHeight",
+                                                         _("Show X Height"),
+                                                         _("Draw a line at the x height."),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, SHOW_CAP_HEIGHT,
+                                    g_param_spec_boolean("showCapHeight",
+                                                         _("Show Cap Height"),
+                                                         _("Draw a line at the cap height."),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, OPERATION,
+                                    g_param_spec_enum("operation",
+                                                      _("Edit Operation"),
+                                                      _("Glyph edit operation."),
+                                                      glyphedit_get_operation_type(),
+                                                      GLYPHEDIT_DRAW,
+                                                      G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, COLOR_LIST,
+                                    g_param_spec_pointer("colorList",
+                                                         _("Color list"),
+                                                         _("Colors to be used for glyphs having bits-per-pixel > 1."),
+                                                         G_PARAM_READWRITE));
+
+
+    /*
+     * Add the signals these objects emit.
+     */
+    glyphedit_signals[GLYPH_MODIFIED] =
+        g_signal_new("glyph-modified",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GlypheditClass, glyph_modified),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    glyphedit_signals[POINTER_MOVED] =
+        g_signal_new("pointer-moved",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GlypheditClass, pointer_moved),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    glyphedit_signals[OPERATION_CHANGE] =
+        g_signal_new("operation-change",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GlypheditClass, operation_change),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    glyphedit_signals[COLOR_CHANGE] =
+        g_signal_new("color-change",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GlypheditClass, color_change),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__POINTER,
+                     G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    /*
+     * Set all the functions for handling events for objects of this class.
+     */
+    wcp->size_request = glyphedit_preferred_size;
+    wcp->size_allocate = glyphedit_actual_size;
+    wcp->realize = glyphedit_realize;
+    wcp->expose_event = glyphedit_expose;
+    wcp->focus_in_event = glyphedit_focus_in;
+    wcp->focus_out_event = glyphedit_focus_out;
+    wcp->button_press_event = glyphedit_button_press;
+    wcp->button_release_event = glyphedit_button_release;
+    wcp->motion_notify_event = glyphedit_motion_notify;
+    wcp->key_press_event = glyphedit_key_press;
+    wcp->key_release_event = glyphedit_key_release;
+}
+
+GType
+glyphedit_get_type(void)
+{
+    static GType glyphedit_type = 0;
+
+    if (!glyphedit_type) {
+        static const GTypeInfo glyphedit_info = {
+            sizeof(GlypheditClass),
+            0,
+            0,
+            glyphedit_class_init,
+            0,
+            0,
+            sizeof(Glyphedit),
+            0,
+            glyphedit_init,
+            0,
+        };
+
+        glyphedit_type = g_type_register_static(GTK_TYPE_WIDGET, "Glyphedit",
+                                                &glyphedit_info, 0);
+    }
+
+    return glyphedit_type;
+}
diff --git a/glyphedit.h b/glyphedit.h
new file mode 100644 (file)
index 0000000..ecc0d73
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_glyphedit
+#define _h_glyphedit
+
+#include <gdk/gdk.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtksignal.h>
+#include "bdfP.h"
+#include "gtkcompat.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+    GLYPHEDIT_NONE = 0,
+    GLYPHEDIT_SELECT,
+    GLYPHEDIT_DRAW,
+    GLYPHEDIT_MOVE,
+    GLYPHEDIT_COPY,
+    GLYPHEDIT_FLIP_HORIZONTAL,
+    GLYPHEDIT_FLIP_VERTICAL,
+    GLYPHEDIT_SHEAR,
+    GLYPHEDIT_ROTATE_LEFT,
+    GLYPHEDIT_ROTATE_RIGHT,
+    GLYPHEDIT_ROTATE,
+    GLYPHEDIT_SHIFT_UP_LEFT,
+    GLYPHEDIT_SHIFT_UP,
+    GLYPHEDIT_SHIFT_UP_RIGHT,
+    GLYPHEDIT_SHIFT_LEFT,
+    GLYPHEDIT_SHIFT_RIGHT,
+    GLYPHEDIT_SHIFT_DOWN_LEFT,
+    GLYPHEDIT_SHIFT_DOWN,
+    GLYPHEDIT_SHIFT_DOWN_RIGHT
+} GlypheditOperation;
+
+/*
+ * The macros for accessing various parts of the widget class.
+ */
+#define GLYPHEDIT(o) \
+        (G_TYPE_CHECK_INSTANCE_CAST((o), glyphedit_get_type(), Glyphedit))
+
+#define GLYPHEDIT_CLASS(c) \
+        (G_TYPE_CHECK_CLASS_CAST((c), glyphedit_get_type(), GlypheditClass))
+
+#define IS_GLYPHEDIT(o) G_TYPE_CHECK_INSTANCE_TYPE((o), glyphedit_get_type())
+
+#define IS_GLYPHEDIT_CLASS(c) \
+        (G_TYPE_CHECK_CLASS_TYPE((c), glyphedit_get_type()))
+
+#define GLYPHEDIT_GET_CLASS(o) \
+        (G_TYPE_INSTANCE_GET_CLASS((o), glyphedit_get_type(), GlypheditClass))
+
+typedef struct _Glyphedit      Glyphedit;
+typedef struct _GlypheditClass GlypheditClass;
+
+struct _Glyphedit {
+    GtkWidget widget;
+
+    bdf_glyph_grid_t *grid;
+    gboolean show_cap_height;
+    gboolean show_x_height;
+
+    GdkColor baselineColor;
+    GdkColor selectionColor;
+    GdkColor cursorColor;
+
+    guint16 *colors;
+    /*
+     * Buffer for drawing grayscale pixels and color spots.
+     */
+    guchar *spot;
+    guint spot_used;
+    guint spot_size;
+
+    gboolean owns_clipboard;
+
+    GlypheditOperation op;
+    GlypheditOperation pending_op;
+
+    GdkPoint sel_start;
+    GdkPoint sel_end;
+
+    gint last_x;
+    gint last_y;
+
+    gint lcolor;
+    gint cidx;
+
+    guint16 default_pixel_size;
+    guint16 pixel_size;
+
+    guint16 vmargin;
+    guint16 hmargin;
+    guint16 border;
+};
+
+struct _GlypheditClass {
+    GtkWidgetClass parent_class;
+
+    /*
+     * Cursor.
+     */
+    GdkCursor *cursor;
+
+    /*
+     * GC's used for drawing.
+     */
+    GdkGC *gridgc;
+    GdkGC *bbxgc;
+    GdkGC *pixgc;
+    GdkGC *cleargc;
+    GdkGC *selgc;
+
+    /*
+     * Signal handlers.
+     */
+    void (*glyph_modified)(GtkWidget *, gpointer, gpointer);
+    void (*pointer_moved)(GtkWidget *, gpointer, gpointer);
+    void (*operation_change)(GtkWidget *, gpointer, gpointer);
+    void (*color_change)(GtkWidget *, gpointer, gpointer);
+};
+
+/**************************************************************************
+ *
+ * Structures used for the API.
+ *
+ **************************************************************************/
+
+/*
+ * List of callback reasons.
+ */
+enum {
+    GLYPHEDIT_GLYPH_MODIFIED = 0,
+    GLYPHEDIT_POINTER_MOVED,
+    GLYPHEDIT_OPERATION_CHANGE,
+    GLYPHEDIT_COLOR_CHANGE
+};
+
+/*
+ * The structure passed back in the signals.
+ */
+typedef struct {
+    gint reason;
+    bdf_bitmap_t *image;
+    bdf_metrics_t *metrics;
+    GlypheditOperation operation;
+    gint x;
+    gint y;
+    gint color;
+} GlypheditSignalInfo;
+
+/**************************************************************************
+ *
+ * General API
+ *
+ **************************************************************************/
+
+extern GType glyphedit_get_type(void);
+extern GtkWidget *glyphedit_new(const gchar *prop1, ...);
+extern GtkWidget *glyphedit_newv(bdf_glyph_grid_t *grid,
+                                 guint16 default_pixel_size,
+                                 gboolean show_x_height,
+                                 gboolean show_cap_height,
+                                 guint16 *colors);
+
+/*
+ * Get the encoding of the current glyph.
+ */
+extern gint32 glyphedit_get_encoding(Glyphedit *gw);
+
+/*
+ * Get the current glyph metrics or the current font metrics.
+ */
+extern void glyphedit_get_glyph_metrics(Glyphedit *gw, bdf_metrics_t *metrics);
+extern void glyphedit_get_font_metrics(Glyphedit *gw, bdf_metrics_t *metrics);
+
+/*
+ * Get the PSF Unicode mappings.
+ */
+extern bdf_psf_unimap_t *glyphedit_get_psf_mappings(Glyphedit *gw);
+
+/*
+ * Changes device width, width, and height values from the metrics supplied.
+ */
+extern void glyphedit_set_metrics(Glyphedit *gw, bdf_metrics_t *metrics);
+
+/*
+ * Get the glyph spacing.
+ */
+extern gint glyphedit_get_spacing(Glyphedit *gw);
+
+/*
+ * Changes the font spacing and the mono width if necessary.
+ */
+extern void glyphedit_set_spacing(Glyphedit *gw, gint spacing,
+                                  guint16 monowidth);
+
+/*
+ * Get and set the operation.
+ */
+extern void glyphedit_set_operation(Glyphedit *gw, GlypheditOperation op);
+extern GlypheditOperation glyphedit_get_operation(Glyphedit *gw);
+
+extern void glyphedit_set_pixel_size(Glyphedit *gw, guint pixel_size);
+extern guint glyphedit_get_pixel_size(Glyphedit *gw);
+
+/*
+ * Sets the glyph grid.
+ */
+extern void glyphedit_set_grid(Glyphedit *gw, bdf_glyph_grid_t *grid);
+
+/*
+ * Check to see if the glyph or associated info has been modified.
+ */
+extern gboolean glyphedit_get_modified(Glyphedit *gw);
+extern void glyphedit_set_modified(Glyphedit *gw, gboolean modified);
+extern void glyphedit_signal_modified(Glyphedit *gw);
+
+/*
+ * Determine if a selection is in progress.
+ */
+extern gboolean glyphedit_get_selecting(Glyphedit *gw);
+
+/*
+ * Check to see if the glyph editor clipboard is empty or not.
+ */
+extern gboolean glyphedit_clipboard_empty(Glyphedit *gw);
+
+/*
+ * Get the glyph image from the editor.
+ */
+extern void glyphedit_get_image(Glyphedit *gw, bdf_bitmap_t *image);
+
+/*
+ * Retrieve the glyph grid.
+ */
+extern bdf_glyph_grid_t *glyphedit_get_grid(Glyphedit *gw);
+
+/*
+ * Get the glyph itself.
+ */
+extern bdf_glyph_t *glyphedit_get_glyph(Glyphedit *gw, gboolean *unencoded);
+
+/*
+ * Show or hide the cap height.
+ */
+extern void glyphedit_set_show_cap_height(Glyphedit *gw, gboolean show);
+
+/*
+ * Show or hide the x height.
+ */
+extern void glyphedit_set_show_x_height(Glyphedit *gw, gboolean show);
+
+/*
+ * Crop the glyph bitmap to get rid of empty rows and columns around the
+ * glyph.
+ */
+extern void glyphedit_crop_glyph(Glyphedit *gw);
+
+/*
+ * Shift the bitmap horizontally, vertically or a combination of both.
+ */
+extern void glyphedit_shift_glyph(Glyphedit *gw, gint16 xcount, gint16 ycount);
+
+/*
+ * Rotate the bitmap clockwise (positive count) or counter-clockwise
+ * (negative count).
+ */
+extern void glyphedit_rotate_glyph(Glyphedit *w, gint16 degrees);
+
+/*
+ * Shear the bitmap clockwise (positive count) or counter-clockwise
+ * (negative count).  Limited to the range of [-20,20] degrees.
+ */
+extern void glyphedit_shear_glyph(Glyphedit *gw, gint16 degrees);
+
+/*
+ * Make the glyph bold.
+ */
+extern void glyphedit_embolden_glyph(Glyphedit *gw);
+
+/*
+ * Flip the bitmap horizontally or vertically.
+ */
+extern void glyphedit_flip_glyph(Glyphedit *gw, GtkOrientation direction);
+
+/*
+ * Change to the draw, select, move, or copy operation.
+ */
+extern void glyphedit_change_operation(Glyphedit *gw, GlypheditOperation op);
+
+/*
+ * Change the current color index.
+ */
+extern void glyphedit_set_color(Glyphedit *gw, gint idx);
+
+/*
+ * Insert a bitmap from some outside source.
+ */
+extern void glyphedit_insert_bitmap(Glyphedit *gw, bdf_bitmap_t *bitmap);
+
+/*
+ * Functions explicitly for importing and exporting XBM bitmaps.
+ */
+extern int glyphedit_import_xbm(Glyphedit *gw, gchar *filename);
+extern int glyphedit_export_xbm(Glyphedit *gw, gchar *filename);
+
+/*
+ * Functions dealing with the selection.
+ */
+extern void glyphedit_copy_selection(Glyphedit *gw);
+extern void glyphedit_cut_selection(Glyphedit *gw);
+extern void glyphedit_paste_selection(Glyphedit *gw);
+extern void glyphedit_select_all(Glyphedit *gw);
+
+G_END_DECLS
+
+#endif /* _h_glyphedit */
diff --git a/glyphtest.c b/glyphtest.c
new file mode 100644 (file)
index 0000000..f3d8392
--- /dev/null
@@ -0,0 +1,828 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "glyphtest.h"
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(s) dgettext(GETTEXT_PACKAGE,s)
+#else
+#define _(s) (s)
+#endif
+
+#define HMARGINS(fw) ((fw)->hmargin << 1)
+#define VMARGINS(fw) ((fw)->vmargin << 1)
+
+/*
+ * Argument types.
+ */
+enum {
+    PROP_0 = 0,
+    PROP_BASELINE,
+    PROP_DIRECTION
+};
+
+enum {
+    ADD_GLYPH = 0,
+    NUM_SIGNALS
+};
+
+static GtkWidgetClass *parent_class = 0;
+static guint glyphtest_signals[NUM_SIGNALS];
+
+#define GTESTMAX(h,i) ((h) > (i) ? (h) : (i))
+
+static gboolean
+_glyphtest_set_line_size(Glyphtest *gw)
+{
+    GtkWidget *w;
+    gboolean changed = FALSE;
+    guint32 i;
+    guint16 wd, wwidth;
+    GlyphtestLine *lp;
+    bdf_bbx_t bbx;
+
+    w = GTK_WIDGET(gw);
+
+    lp = &gw->line;
+    (void) memset((char *) &bbx, 0, sizeof(bdf_bbx_t));
+
+    wwidth = w->allocation.width - (HMARGINS(gw) + 4);
+
+    for (wd = 0, i = 0; i < lp->glyphs_used; i++) {
+        bbx.ascent = GTESTMAX(bbx.ascent, lp->glyphs[i].font->bbx.ascent);
+        bbx.descent = GTESTMAX(bbx.descent, lp->glyphs[i].font->bbx.descent);
+        bbx.width = GTESTMAX(bbx.width, lp->glyphs[i].font->bbx.width);
+        wd += (lp->glyphs[i].font->spacing == BDF_PROPORTIONAL) ?
+            lp->glyphs[i].glyph->dwidth : lp->glyphs[i].font->monowidth;
+    }
+
+    if (lp->glyphs_used == 0) {
+        /*
+         * If no glyphs are present, then set the overall bounding box
+         * to some simple default.
+         */
+        bbx.ascent = 12;
+        bbx.descent = 3;
+        bbx.width = 10;
+        wd = bbx.width << 3;
+    }
+
+    /*
+     * If the actual line width changed, set the indicator.
+     */
+    if (wd != lp->width) {
+        lp->width = wd;
+
+        /*
+         * If the line width overflows the window width, set the changed flag.
+         */
+        if (wd > wwidth)
+          changed = TRUE;
+    }
+
+    /*
+     * If the new bounding box is not the same as the current line bounding
+     * box, then make the new one the current line bounding box.
+     */
+    if (bbx.ascent != lp->bbx.ascent || bbx.descent != lp->bbx.descent ||
+        bbx.width != lp->bbx.width) {
+        (void) memcpy((char *) &lp->bbx, (char *) &bbx, sizeof(bdf_bbx_t));
+        changed = TRUE;
+    }
+
+    /*
+     * Now set the line size.
+     */
+    lp->height = lp->bbx.ascent + lp->bbx.descent;
+    lp->cpoint.y = (VMARGINS(gw) >> 1) + 2 + lp->bbx.ascent;
+
+    return changed;
+}
+
+/**************************************************************************
+ *
+ * Class functions.
+ *
+ **************************************************************************/
+
+static void
+glyphtest_set_property(GObject *obj, guint prop_id, const GValue *value,
+                       GParamSpec *pspec)
+{
+    Glyphtest *gw;
+
+    gw = GLYPHTEST(obj);
+
+    switch (prop_id) {
+      case PROP_BASELINE:
+        glyphtest_show_baseline(gw, g_value_get_boolean(value));
+        break;
+      case PROP_DIRECTION:
+        glyphtest_change_direction(gw, g_value_get_int(value));
+        break;
+    }
+}
+
+static void
+glyphtest_get_property(GObject *obj, guint prop_id, GValue *value,
+                       GParamSpec *pspec)
+{
+    Glyphtest *gw;
+
+    gw = GLYPHTEST(obj);
+
+    switch (prop_id) {
+      case PROP_BASELINE:
+        g_value_set_boolean(value, gw->show_baseline);
+        break;
+      case PROP_DIRECTION:
+        g_value_set_int(value, gw->dir);
+        break;
+    }
+}
+
+static void
+glyphtest_destroy(GtkObject *obj)
+{
+    Glyphtest *gw;
+
+    /*
+     * Do some checks to make sure the incoming object exists and is the right
+     * kind.
+     */
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_GLYPHTEST(obj));
+
+    gw = GLYPHTEST(obj);
+
+    /*
+     * Delete the line structure if it was allocated.
+     */
+    if (gw->line.glyphs_size > 0)
+      g_free(gw->line.glyphs);
+    gw->line.glyphs_used = gw->line.glyphs_size = 0;
+
+    /*
+     * Free up any points that have been allocated.
+     */
+    if (gw->image_size > 0)
+      g_free(gw->image);
+    gw->image_size = gw->image_used = 0;
+
+    /*
+     * Follow the class chain back up to free up resources allocated in the
+     * parent classes.
+     */
+    GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+glyphtest_finalize(GObject *obj)
+{
+    /*
+     * Do some checks to make sure the incoming object exists and is the right
+     * kind.
+     */
+    g_return_if_fail(obj != 0);
+    g_return_if_fail(IS_GLYPHTEST(obj));
+
+    /*
+     * Follow the class chain back up to free up resources allocated in the
+     * parent classes.
+     */
+    G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+glyphtest_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
+{
+    Glyphtest *gw;
+
+    gw = GLYPHTEST(widget);
+
+    preferred->width = gw->line.width + 4 + HMARGINS(gw);
+    preferred->height = gw->line.height + 4 + VMARGINS(gw);
+}
+
+static void
+glyphtest_actual_size(GtkWidget *widget, GtkAllocation *actual)
+{
+    widget->allocation = *actual;
+
+    if (GTK_WIDGET_REALIZED(widget))
+      gdk_window_move_resize(widget->window, actual->x, actual->y,
+                             actual->width, actual->height);
+}
+
+static void
+glyphtest_draw_focus(GtkWidget *widget, GdkRectangle *area)
+{
+    GdkGC *gc;
+    gint x, y, wd, ht, fwidth, fpad;
+
+    /*
+     * Do something with this later to make sure the focus line width
+     * is set in the GC's.
+     */
+    gtk_widget_style_get(widget,
+                         "focus-line-width", &fwidth,
+                         "focus-padding", &fpad, NULL);
+
+    gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
+
+    x = (widget->style->xthickness + fwidth + fpad) - 1;
+    y = (widget->style->ythickness + fwidth + fpad) - 1;
+    wd = (widget->allocation.width - (x * 2));
+    ht = (widget->allocation.height - (y * 2));
+
+    if (GTK_WIDGET_HAS_FOCUS(widget))
+      gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget),
+                      area, widget, "glyphtest", x, y, wd, ht);
+    else {
+        gdk_gc_set_clip_rectangle(gc, area);
+        gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1);
+        gdk_gc_set_clip_rectangle(gc, 0);
+    }
+}
+
+static void
+_glyphtest_get_pixels(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font,
+                      gint16 x, gint y)
+{
+    gint byte;
+    guint16 i, j, bpr, si, di, nx;
+    guchar *masks;
+
+    di = 0;
+    masks = 0;
+    gw->image_used = 0;
+
+    switch (font->bpp) {
+      case 1: masks = bdf_onebpp; di = 7; break;
+      case 2: masks = bdf_twobpp; di = 3; break;
+      case 4: masks = bdf_fourbpp; di = 1; break;
+      case 8: masks = bdf_eightbpp; di = 0; break;
+    }
+
+    bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3;
+    for (i = 0; i < glyph->bbx.height; i++) {
+        for (nx = j = 0; j < glyph->bbx.width; j++, nx += font->bpp) {
+            si = (nx & 7) / font->bpp;
+
+            byte = glyph->bitmap[(i * bpr) + (nx >> 3)] & masks[si];
+            if (di > si)
+              byte >>= (di - si) * font->bpp;
+
+            if (byte) {
+                if (gw->image_used == gw->image_size) {
+                    if (gw->image_size == 0)
+                      gw->image =
+                          (GdkPoint *) g_malloc(sizeof(GdkPoint) * 64);
+                    else
+                      gw->image = (GdkPoint *)
+                          g_realloc(gw->image,
+                                    sizeof(GdkPoint) *
+                                    (gw->image_size + 64));;
+                    gw->image_size += 64;
+                }
+                gw->image[gw->image_used].x =
+                    x + glyph->bbx.x_offset + j;
+                gw->image[gw->image_used].y =
+                    (y - glyph->bbx.ascent) + i;
+                gw->image_used++;
+            }
+        }
+    }
+}
+
+static void
+_glyphtest_draw_glyph(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font)
+{
+    GtkWidget *w;
+    gint16 rx, ry;
+
+    w = GTK_WIDGET(gw);
+
+    if (!GTK_WIDGET_REALIZED(w))
+      return;
+
+    ry = gw->line.cpoint.y;
+    rx = gw->line.cpoint.x;
+    if (gw->dir != GLYPHTEST_LEFT_TO_RIGHT)
+      rx -= glyph->bbx.width;
+
+    _glyphtest_get_pixels(gw, glyph, font, rx, ry);
+    if (gw->image_used > 0)
+      gdk_draw_points(w->window, w->style->fg_gc[GTK_STATE_NORMAL],
+                      gw->image, gw->image_used);
+}
+
+static void
+_glyphtest_redraw_glyphs(Glyphtest *gw)
+{
+    GtkWidget *w;
+    guint32 i;
+    guint16 dwidth;
+    GlyphtestLine *lp;
+    GlyphtestGlyph *gp;
+
+    w = GTK_WIDGET(gw);
+
+    if (!GTK_WIDGET_REALIZED(w))
+      return;
+
+    lp = &gw->line;
+
+    lp->width = 0;
+    if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
+      lp->cpoint.x = (HMARGINS(gw) >> 1) + 2;
+    else
+      lp->cpoint.x = w->allocation.width - ((HMARGINS(gw) >> 1) + 2);
+
+    for (i = 0, gp = lp->glyphs; i < lp->glyphs_used; i++, gp++) {
+
+        /*
+         * Handle the special cases of the first glyph in case the normal
+         * drawing position is going to put part of the glyph off the edge of
+         * the window.
+         */
+        if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) {
+            if (i == 0 && gp->glyph->bbx.x_offset < 0)
+              lp->cpoint.x += -gp->glyph->bbx.x_offset;
+        } else {
+            if (i == 0 && gp->glyph->bbx.x_offset > 0 &&
+                gp->glyph->bbx.x_offset > gp->glyph->bbx.width)
+              lp->cpoint.x -= gp->glyph->bbx.width - gp->glyph->bbx.x_offset;
+        }
+        _glyphtest_draw_glyph(gw, gp->glyph, gp->font);
+
+        dwidth = (gp->font->spacing == BDF_PROPORTIONAL) ?
+            gp->glyph->dwidth : gp->font->monowidth;
+        if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
+          lp->cpoint.x += dwidth;
+        else
+          lp->cpoint.x -= dwidth;
+        lp->width += dwidth;
+    }
+}
+
+static void
+glyphtest_draw(GtkWidget *widget, GdkRectangle *area)
+{
+    Glyphtest *gw;
+    GdkPoint s, e;
+    GdkRectangle clear;
+
+    if (!GTK_WIDGET_REALIZED(widget))
+      return;
+
+    gw = GLYPHTEST(widget);
+
+    /*
+     * Erase the window.
+     */
+    clear.x = clear.y = (HMARGINS(gw) >> 1);
+    clear.width = widget->allocation.width - (clear.x << 1);
+    clear.height = widget->allocation.height - (clear.y << 1);
+    gdk_window_clear_area(widget->window, clear.x, clear.y,
+                          clear.width, clear.height);
+
+    /*
+     * Redraw the glyphs.
+     */
+    _glyphtest_redraw_glyphs(gw);
+
+    /*
+     * Draw the baseline if indicated.
+     */
+    if (gw->show_baseline == TRUE) {
+        s.x = (HMARGINS(gw) >> 1) + 2;
+        e.x = widget->allocation.width - s.x;
+        s.y = e.y = gw->line.cpoint.y;
+
+        gdk_draw_line(widget->window,
+                      widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+                      s.x, s.y, e.x, e.y);
+    }
+}
+
+static gboolean
+glyphtest_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+    /*
+     * Paint the shadow first.
+     */
+    if (GTK_WIDGET_DRAWABLE(widget))
+      gtk_paint_shadow(widget->style, widget->window,
+                       GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
+                       &event->area,
+                       widget, "glyphtest",
+                       0, 0,
+                       widget->allocation.width,
+                       widget->allocation.height);
+
+    glyphtest_draw(widget, 0);
+
+    glyphtest_draw_focus(widget, &event->area);
+
+    return FALSE;
+}
+
+static void
+glyphtest_realize(GtkWidget *widget)
+{
+    Glyphtest *gw;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_GLYPHTEST(widget));
+
+    gw = GLYPHTEST(widget);
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_ENTER_NOTIFY_MASK|
+                              GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK);
+
+    attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP;
+
+    widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
+                                    &attributes, attributes_mask);
+    gdk_window_set_user_data(widget->window, widget);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+glyphtest_unrealize(GtkWidget *widget)
+{
+    Glyphtest *gw;
+
+    gw = GLYPHTEST(widget);
+}
+
+static gint
+glyphtest_focus_in(GtkWidget *widget, GdkEventFocus *event)
+{
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_GLYPHTEST(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+    (void) glyphtest_draw_focus(widget, NULL);
+
+    return FALSE;
+}
+
+static gint
+glyphtest_focus_out(GtkWidget *widget, GdkEventFocus *event)
+{
+    g_return_val_if_fail(widget != NULL, FALSE);
+    g_return_val_if_fail(IS_GLYPHTEST(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
+
+    GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
+    (void) glyphtest_draw_focus(widget, NULL);
+
+    return FALSE;
+}
+
+/**************************************************************************
+ *
+ * Class and object initialization routines.
+ *
+ **************************************************************************/
+
+static void
+glyphtest_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass *gocp = G_OBJECT_CLASS(g_class);
+    GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class);
+    GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class);
+
+    /*
+     * Set the class global variables.
+     */
+    parent_class = g_type_class_peek_parent(g_class);
+
+    ocp->destroy = glyphtest_destroy;
+    gocp->set_property = glyphtest_set_property;
+    gocp->get_property = glyphtest_get_property;
+    gocp->finalize = glyphtest_finalize;
+
+    /*
+     * Add argument (a.k.a. resource) types.
+     */
+    g_object_class_install_property(gocp, PROP_BASELINE,
+                                    g_param_spec_boolean("showBaseline",
+                                                         _("Show baseline"),
+                                                         _("Draw the baseline."),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+
+    g_object_class_install_property(gocp, PROP_DIRECTION,
+                                    g_param_spec_uint("direction",
+                                                      _("Direction"),
+                                                      _("Override for the drawing direction of the glyphs."),
+                                                      GLYPHTEST_LEFT_TO_RIGHT,
+                                                      GLYPHTEST_RIGHT_TO_LEFT,
+                                                      GLYPHTEST_LEFT_TO_RIGHT,
+                                                      G_PARAM_READWRITE));
+
+    /*
+     * Add the signals these objects emit.
+     */
+    glyphtest_signals[ADD_GLYPH] =
+        g_signal_new("add_glyph",
+                     G_TYPE_FROM_CLASS(gocp),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GlyphtestClass, glyph_added),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE, 0);
+
+    /*
+     * Set all the functions for handling events for objects of this class.
+     */
+    wcp->size_request = glyphtest_preferred_size;
+    wcp->size_allocate = glyphtest_actual_size;
+    wcp->realize = glyphtest_realize;
+    wcp->unrealize = glyphtest_unrealize;
+    wcp->expose_event = glyphtest_expose;
+    wcp->focus_in_event = glyphtest_focus_in;
+    wcp->focus_out_event = glyphtest_focus_out;
+}
+
+static void
+glyphtest_init(GTypeInstance *obj, gpointer g_class)
+{
+    Glyphtest *gw = GLYPHTEST(obj);
+    gint fwidth;
+
+    GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS);
+
+    (void) memset((char *) &gw->line, 0, sizeof(GlyphtestLine));
+
+    gw->dir = GLYPHTEST_LEFT_TO_RIGHT;
+    gw->show_baseline = TRUE;
+
+    gw->image_used = gw->image_size = 0;
+
+    /*
+     * Determine the space that will be needed for drawing the shadow and the
+     * focus rectangle.
+     */
+    gtk_widget_style_get(GTK_WIDGET(gw),
+                         "focus-line-width", &fwidth,
+                         NULL);
+
+    /*
+     * Padding that will appear before and after the focus rectangle.
+     * Hardcode this for now.
+     */
+    gw->focus_thickness = 3;
+    gw->hmargin =
+        gw->widget.style->xthickness + fwidth + (gw->focus_thickness * 2);
+    gw->vmargin = 
+        gw->widget.style->ythickness + fwidth + (gw->focus_thickness * 2);
+
+    /*
+     * Call the line size function to set the initial size.
+     */
+    (void) _glyphtest_set_line_size(gw);
+}
+
+/**************************************************************************
+ *
+ * API functions.
+ *
+ **************************************************************************/
+
+static const GTypeInfo glyphtest_info = {
+    sizeof(GlyphtestClass),
+    NULL,
+    NULL,
+    glyphtest_class_init,
+    NULL,
+    NULL,
+    sizeof(Glyphtest),
+    0,
+    glyphtest_init,
+    NULL,
+};
+
+GType
+glyphtest_get_type(void)
+{
+    static GType glyphtest_type = 0;
+
+    if (!glyphtest_type)
+      glyphtest_type = g_type_register_static(GTK_TYPE_WIDGET,
+                                              "Glyphtest", &glyphtest_info, 0);
+
+    return glyphtest_type;
+}
+
+GtkWidget *
+glyphtest_new(void)
+{
+    return GTK_WIDGET(g_object_new(glyphtest_get_type(), NULL));
+}
+
+void
+glyphtest_add_glyph(Glyphtest *gw, bdf_font_t *font, bdf_glyph_t *glyph)
+{
+    GlyphtestLine *lp;
+    GlyphtestGlyph *gp;
+
+    g_return_if_fail(gw != 0);
+    g_return_if_fail(font != 0);
+    g_return_if_fail(glyph != 0);
+
+    lp = &gw->line;
+
+    if (lp->glyphs_used == lp->glyphs_size) {
+        if (lp->glyphs_size == 0)
+          lp->glyphs = (GlyphtestGlyph *)
+              g_malloc(sizeof(GlyphtestGlyph) << 3);
+        else
+          lp->glyphs = (GlyphtestGlyph *)
+              g_realloc(lp->glyphs,
+                        sizeof(GlyphtestGlyph) * (lp->glyphs_size + 8));
+        lp->glyphs_size += 8;
+    }
+    gp = lp->glyphs + lp->glyphs_used++;
+    gp->font = font;
+    gp->glyph = glyph;
+
+    if (_glyphtest_set_line_size(gw))
+      gtk_widget_queue_resize(GTK_WIDGET(gw));
+    else {
+        /*
+         * Just draw the glyph.
+         */
+        /*
+         * If the first glyph would be drawn off the edge of the window, make
+         * sure the initial position is adjusted to display the first glyph at
+         * the edge.
+         */
+        if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) {
+            if (gw->line.glyphs_used == 1 && glyph->bbx.x_offset < 0)
+              lp->cpoint.x += -glyph->bbx.x_offset;
+        } else {
+            if (gw->line.glyphs_used == 1 && glyph->bbx.x_offset > 0 &&
+                glyph->bbx.x_offset > glyph->bbx.width)
+              lp->cpoint.x -= glyph->bbx.width - glyph->bbx.x_offset;
+        }
+
+        _glyphtest_draw_glyph(gw, glyph, font);
+
+        if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
+          lp->cpoint.x += (font->spacing == BDF_PROPORTIONAL) ?
+              glyph->dwidth : font->monowidth;
+        else
+          lp->cpoint.x -= (font->spacing == BDF_PROPORTIONAL) ?
+              glyph->dwidth : font->monowidth;
+    }
+
+    /*
+     * Call the signal that indicates that a glyph has been added.
+     */
+    g_signal_emit(GTK_OBJECT(gw), glyphtest_signals[ADD_GLYPH], 0);
+}
+
+void
+glyphtest_remove_font(Glyphtest *gw, bdf_font_t *font)
+{
+    GtkWidget *w;
+    guint32 i, j;
+    gboolean redo;
+
+    g_return_if_fail(gw != 0);
+    g_return_if_fail(font != 0);
+
+    w = GTK_WIDGET(gw);
+
+    for (redo = FALSE, i = j = 0; i < gw->line.glyphs_used; i++) {
+        if (gw->line.glyphs[i].font != font) {
+            gw->line.glyphs[j].font = gw->line.glyphs[i].font;
+            gw->line.glyphs[j].glyph = gw->line.glyphs[i].glyph;
+            j++;
+        }
+    }
+    if (gw->line.glyphs_used != j) {
+        redo = TRUE;
+        gw->line.glyphs_used = j;
+    }
+
+    if (redo) {
+        if (_glyphtest_set_line_size(gw))
+          gtk_widget_queue_resize(w);
+        else
+          glyphtest_draw(w, 0);
+    }
+}
+
+void
+glyphtest_update_device_width(Glyphtest *gw, bdf_font_t *font)
+{
+    GtkWidget *w;
+    guint32 i;
+    gboolean redraw;
+
+    g_return_if_fail(gw != 0);
+
+    w = GTK_WIDGET(gw);
+
+    /*
+     * Determine if the device width change will cause a redraw.
+     */
+    redraw = FALSE;
+
+    for (i = 0; redraw == FALSE && i < gw->line.glyphs_used; i++) {
+        if (gw->line.glyphs[i].font == font)
+          redraw = TRUE;
+    }
+
+    if (redraw) {
+        /*
+         * Determine if a resize is in order or just a redraw.
+         */
+        if (_glyphtest_set_line_size(gw))
+          gtk_widget_queue_resize(w);
+        else
+          glyphtest_draw(w, 0);
+    }
+}
+
+void
+glyphtest_change_direction(Glyphtest *gw, gint direction)
+{
+    g_return_if_fail(gw != 0);
+
+    /*
+     * Return if the direction is invalid or the same as the current
+     * direction.
+     */
+    if (direction < GLYPHTEST_LEFT_TO_RIGHT ||
+        direction > GLYPHTEST_RIGHT_TO_LEFT ||
+        direction == gw->dir)
+      return;
+
+    gw->dir = direction;
+    glyphtest_draw(GTK_WIDGET(gw), 0);
+}
+
+void
+glyphtest_show_baseline(Glyphtest *gw, gboolean baseline)
+{
+    g_return_if_fail(gw != 0);
+
+    if (gw->show_baseline == baseline)
+      return;
+
+    gw->show_baseline = baseline;
+    glyphtest_draw(GTK_WIDGET(gw), 0);
+}
+
+void
+glyphtest_erase(Glyphtest *gw)
+{
+    g_return_if_fail(gw != 0);
+
+    /*
+     * May change later to shrink the widget.
+     */
+    gw->line.glyphs_used = 0;
+    glyphtest_draw(GTK_WIDGET(gw), 0);
+}
diff --git a/glyphtest.h b/glyphtest.h
new file mode 100644 (file)
index 0000000..2101d0d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_glyphtest
+#define _h_glyphtest
+
+#include <gdk/gdk.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtksignal.h>
+#include "bdfP.h"
+#include "gtkcompat.h"
+
+G_BEGIN_DECLS
+
+/*
+ * The macros for accessing various parts of the widget class.
+ */
+#define GLYPHTEST(o) \
+        (G_TYPE_CHECK_INSTANCE_CAST((o), glyphtest_get_type(), Glyphtest))
+
+#define GLYPHTEST_CLASS(c) \
+        (G_TYPE_CHECK_CLASS_CAST((c), glyphtest_get_type(), GlyphtestClass))
+
+#define IS_GLYPHTEST(o) G_TYPE_CHECK_INSTANCE_TYPE((o), glyphtest_get_type())
+
+#define IS_GLYPHTEST_CLASS(c) \
+        (G_TYPE_CHECK_CLASS_TYPE((c), glyphtest_get_type()))
+
+#define GLYPHTEST_GET_CLASS(o) \
+        (G_TYPE_INSTANCE_GET_CLASS((o), glyphtest_get_type(), GlyphtestClass))
+
+typedef struct _Glyphtest      Glyphtest;
+typedef struct _GlyphtestClass GlyphtestClass;
+
+typedef struct {
+    bdf_font_t *font;
+    bdf_glyph_t *glyph;
+} GlyphtestGlyph;
+
+typedef struct {
+    GlyphtestGlyph *glyphs;
+    guint32 glyphs_used;
+    guint32 glyphs_size;
+    GdkPoint cpoint;
+    guint16 width;
+    guint16 height;
+    bdf_bbx_t bbx;
+} GlyphtestLine;
+
+struct _Glyphtest {
+    GtkWidget widget;
+
+    /*
+     * Public members.
+     */
+    guint dir;
+    gboolean show_baseline;
+#if 0
+    /* ENABLE WHEN COLORS ARE WORKING. */
+    gulong *colors;
+#endif
+
+    /*
+     * Private members.
+     */
+    GlyphtestLine line;
+
+    /*
+     * The points used to draw the glyphs.
+     */
+    GdkPoint *image;
+    guint32 image_used;
+    guint32 image_size;
+
+    guint16 vmargin;
+    guint16 hmargin;
+    guint16 focus_thickness;
+};
+
+struct _GlyphtestClass {
+    GtkWidgetClass parent_class;
+
+    void (*glyph_added)(GtkWidget *, gpointer);
+};
+
+/**************************************************************************
+ *
+ * General API
+ *
+ **************************************************************************/
+
+extern GType glyphtest_get_type(void);
+extern GtkWidget *glyphtest_new(void);
+
+/*
+ * Direction values for the change_direction call.
+ */
+#define GLYPHTEST_LEFT_TO_RIGHT 0
+#define GLYPHTEST_RIGHT_TO_LEFT 1
+
+extern void glyphtest_add_glyph(Glyphtest *, bdf_font_t *, bdf_glyph_t *);
+extern void glyphtest_remove_font(Glyphtest *, bdf_font_t *);
+extern void glyphtest_update_device_width(Glyphtest *, bdf_font_t *);
+extern void glyphtest_change_direction(Glyphtest *, gint direction);
+extern void glyphtest_show_baseline(Glyphtest *, gboolean baseline);
+extern void glyphtest_erase(Glyphtest *);
+
+G_END_DECLS
+
+#endif /* _h_glyphtest */
diff --git a/grayswatch.c b/grayswatch.c
new file mode 100644 (file)
index 0000000..1fc5d49
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <gtk/gtkdrawingarea.h>
+#include <gtk/gtkspinbutton.h>
+#include "grayswatch.h"
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(s) dgettext(GETTEXT_PACKAGE,s)
+#else
+#define _(s) (s)
+#endif
+
+static GtkVBoxClass *parent_class = 0;
+
+#define SWATCH_MIN_WIDTH  20
+#define SWATCH_MIN_HEIGHT 100
+
+/*
+ * Argument types.
+ */
+enum {
+    PROP_0 = 0,
+    PROP_GRAYLEVEL
+};
+
+/*
+ * Signal names.
+ */
+enum {
+    VALUE_CHANGED = 0
+};
+
+static guint grayswatch_signals[VALUE_CHANGED + 1];
+
+/**************************************************************************
+ *
+ * Class functions.
+ *
+ **************************************************************************/
+
+static void
+value_changed(GtkSpinButton *b, gpointer data)
+{
+    gint v;
+    Grayswatch *gs = GRAYSWATCH(data);
+    GtkWidget *sw = gs->swatch;
+
+    v = gtk_spin_button_get_value_as_int(b);
+
+    gs->gray = v;
+
+    memset(gs->image, v, gs->image_size);
+
+    if (GTK_WIDGET_DRAWABLE(sw))
+      gdk_draw_gray_image(sw->window,
+                          sw->style->fg_gc[GTK_WIDGET_STATE(sw)],
+                          GTK_CONTAINER(gs)->border_width,
+                          GTK_CONTAINER(gs)->border_width,
+                          sw->allocation.width, sw->allocation.height,
+                          GDK_RGB_DITHER_NONE, gs->image,
+                          sw->allocation.width);
+
+    if (gs->signal_blocked == FALSE)
+      /*
+       * Now we emit the value_changed signal for this widget.
+       */
+      g_signal_emit(G_OBJECT(data), grayswatch_signals[VALUE_CHANGED], 0, v);
+}
+
+/**************************************************************************
+ *
+ * Class functions.
+ *
+ **************************************************************************/
+
+static gboolean
+grayswatch_configure(GtkWidget *widget, GdkEventConfigure *event,
+                     gpointer data)
+{
+    Grayswatch *gs = GRAYSWATCH(data);
+    gint nbytes;
+
+    nbytes = gs->swatch->allocation.width *
+        gs->swatch->allocation.height;
+    if (nbytes > gs->image_size) {
+        if (gs->image_size == 0)
+          gs->image = (guchar *) g_malloc(nbytes);
+        else
+          gs->image = (guchar *) g_realloc(gs->image, nbytes);
+        gs->image_size = nbytes;
+    }
+    memset(gs->image, gs->gray, gs->image_size);
+
+    return FALSE;
+}
+
+static gboolean
+grayswatch_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+    Grayswatch *gs = GRAYSWATCH(data);
+
+    if (gs->image_size > 0)
+      gdk_draw_gray_image(widget->window,
+                          widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+                          GTK_CONTAINER(gs)->border_width,
+                          GTK_CONTAINER(gs)->border_width,
+                          widget->allocation.width,
+                          widget->allocation.height,
+                          GDK_RGB_DITHER_NONE, gs->image,
+                          widget->allocation.width);
+
+    return FALSE;
+}
+
+static void
+grayswatch_get_property(GObject *obj, guint propid, GValue *val,
+                        GParamSpec *pspec)
+{
+    Grayswatch *gs;
+
+    gs = GRAYSWATCH(obj);
+
+    if (propid == PROP_GRAYLEVEL)
+      g_value_set_uint(val, gs->gray);
+}
+
+static void
+grayswatch_set_property(GObject *obj, guint propid, const GValue *val,
+                        GParamSpec *pspec)
+{
+    Grayswatch *gs;
+    guint gray;
+
+    gs = GRAYSWATCH(obj);
+
+    if (propid == PROP_GRAYLEVEL) {
+        gray = g_value_get_uint(val);
+        if (gray > 255)
+          gray = 255;
+        if (gray != gs->gray)
+          gtk_spin_button_set_value(GTK_SPIN_BUTTON(gs->value),
+                                    (gdouble) gray);
+    }
+}
+
+static void
+grayswatch_add(GtkContainer *container, GtkWidget *child)
+{
+    /*
+     * This is here as a placeholder as it seems the container doesn't
+     * work quite right without it for some reason.
+     */
+}
+
+static void
+grayswatch_remove(GtkContainer *container, GtkWidget *child)
+{
+    /*
+     * This is here as a placeholder as it seems the container doesn't
+     * work quite right without it for some reason.
+     */
+}
+
+static void
+grayswatch_foreach(GtkContainer *container, gboolean int_kids,
+                   GtkCallback callback, gpointer callback_data)
+{
+    Grayswatch *gs = GRAYSWATCH(container);
+
+    if (callback != 0) {
+        (*callback)(gs->swatch, callback_data);
+        (*callback)(gs->value, callback_data);
+    }
+}
+
+/**************************************************************************
+ *
+ * Class and object initialization routines.
+ *
+ **************************************************************************/
+
+static void
+grayswatch_class_init(gpointer g_class, gpointer class_data)
+{
+    GtkContainerClass *cc = GTK_CONTAINER_CLASS(g_class);
+    GObjectClass *oc = G_OBJECT_CLASS(g_class);
+
+    cc->forall = grayswatch_foreach;
+    cc->add = grayswatch_add;
+    cc->remove = grayswatch_remove;
+
+    oc->set_property = grayswatch_set_property;
+    oc->get_property = grayswatch_get_property;
+
+    g_object_class_install_property(oc, PROP_GRAYLEVEL,
+                                    g_param_spec_uint("grayLevel",
+                                                      _("GrayLevel"),
+                                                      _("The gray level."),
+                                                      0, 255, 0,
+                                                      G_PARAM_READWRITE));
+
+    grayswatch_signals[VALUE_CHANGED] =
+        g_signal_new("value-changed",
+                     G_TYPE_FROM_CLASS(oc),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GrayswatchClass, value_changed),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__INT,
+                     G_TYPE_NONE, 1, G_TYPE_INT);
+
+    parent_class = g_type_class_peek_parent(g_class);
+}
+
+static void
+grayswatch_init(GTypeInstance *obj, gpointer klass)
+{
+    Grayswatch *gs = GRAYSWATCH(obj);
+
+    GTK_WIDGET_SET_FLAGS(obj, GTK_NO_WINDOW);
+    gtk_widget_set_redraw_on_allocate(GTK_WIDGET(obj), FALSE);
+
+    gs->swatch = gtk_drawing_area_new();
+    g_signal_connect(G_OBJECT(gs->swatch), "configure_event",
+                     G_CALLBACK(grayswatch_configure), (gpointer) gs);
+    g_signal_connect(G_OBJECT(gs->swatch), "expose_event",
+                     G_CALLBACK(grayswatch_expose), (gpointer) gs);
+    gtk_box_pack_start(GTK_BOX(obj), gs->swatch, TRUE, TRUE, 0);
+    gtk_widget_show(gs->swatch);
+
+    gs->value = gtk_spin_button_new_with_range(0.0, 255.0, 1.0);
+    g_signal_connect(G_OBJECT(gs->value), "value-changed",
+                     G_CALLBACK(value_changed), (gpointer) gs);
+
+    gtk_box_pack_start(GTK_BOX(obj), gs->value, FALSE, FALSE, 0);
+    gtk_widget_show(gs->value);
+
+    gs->gray = 0;
+    gs->image_size = 0;
+    gs->image = NULL;
+}
+
+/**************************************************************************
+ *
+ * API functions.
+ *
+ **************************************************************************/
+
+static const GTypeInfo grayswatch_info = {
+    sizeof(GrayswatchClass),
+    NULL,
+    NULL,
+    grayswatch_class_init,
+    NULL,
+    NULL,
+    sizeof(Grayswatch),
+    0,
+    grayswatch_init,
+};
+
+GType
+grayswatch_get_type(void)
+{
+    static GType grayswatch_type = 0;
+
+    if (!grayswatch_type)
+      grayswatch_type = g_type_register_static(GTK_TYPE_VBOX,
+                                               "Grayswatch",
+                                               &grayswatch_info, 0);
+
+    return grayswatch_type;
+}
+
+GtkWidget *
+grayswatch_new(guint gray)
+{
+    return gtk_widget_new(grayswatch_get_type(),
+                          "grayLevel", gray, NULL);
+}
+
+void
+grayswatch_set_gray(Grayswatch *gs, guint gray)
+{
+    g_return_if_fail(gs != 0);
+    g_return_if_fail(IS_GRAYSWATCH(gs));
+
+    if (gray > 255)
+      gray = 255;
+
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(gs->value), gray);
+}
+
+guint
+grayswatch_get_gray(Grayswatch *gs)
+{
+    g_return_val_if_fail(gs != 0, 256);
+    g_return_val_if_fail(IS_GRAYSWATCH(gs), 256);
+
+    return gs->gray;
+}
+
+void
+grayswatch_block_signal(Grayswatch *gs, gboolean block)
+{
+    g_return_if_fail(gs != 0);
+    g_return_if_fail(IS_GRAYSWATCH(gs));
+
+    gs->signal_blocked = block;
+}
diff --git a/grayswatch.h b/grayswatch.h
new file mode 100644 (file)
index 0000000..a44826a
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_grayswatch
+#define _h_grayswatch
+
+#include <gtk/gtkvbox.h>
+#include "gtkcompat.h"
+
+G_BEGIN_DECLS
+
+/*
+ * The macros for accessing various parts of the widget class.
+ */
+#define GRAYSWATCH(obj)(G_TYPE_CHECK_INSTANCE_CAST(obj, grayswatch_get_type(), Grayswatch))
+#define GRAYSWATCH_CLASS(klass) \
+        (G_TYPE_CHECK_CLASS_CAST(klass, grayswatch_get_type(), GrayswatchClass))
+#define IS_GRAYSWATCH(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, grayswatch_get_type())
+#define IS_GRAYSWATCH_CLASS(klass) \
+        (G_TYPE_CHECK_CLASS_TYPE(klass, grayswatch_get_type()))
+#define GRAYSWATCH_GET_CLASS(obj) \
+        (G_TYPE_INSTANCE_GET_CLASS(obj, grayswatch_get_type(), GrayswatchClass))
+
+typedef struct _Grayswatch      Grayswatch;
+typedef struct _GrayswatchClass GrayswatchClass;
+
+struct _Grayswatch {
+    GtkVBox vbox;
+
+    /*
+     * Public members.
+     */
+    guint gray;
+
+    /*
+     * Private members.
+     */
+    GtkWidget *swatch;
+    GtkWidget *value;
+
+    /*
+     * The grayscale image used to draw the swatch color.
+     */
+    guchar *image;
+    guint image_size;
+
+    /*
+     * Flag indicating whether the signal should be blocked or not.
+     */
+    gboolean signal_blocked;
+};
+
+struct _GrayswatchClass {
+    GtkVBoxClass parent_class;
+
+    /*
+     * Signal handlers.
+     */
+    void (*value_changed)(GtkWidget *, gint, gpointer);
+};
+
+/**************************************************************************
+ *
+ * General API
+ *
+ **************************************************************************/
+
+extern GType grayswatch_get_type(void);
+
+extern GtkWidget *grayswatch_new(guint gray);
+
+/*
+ * The gray value is between 0 and 255.
+ */
+extern void grayswatch_set_gray(Grayswatch *gs, guint gray);
+
+/*
+ * Anything over 255 returned by this routine means an error.
+ */
+extern guint grayswatch_get_gray(Grayswatch *gs);
+
+/*
+ * Block the signal from being emitted while setting the gray level.
+ * This is for a specific application and may not be useful anywhere else.
+ * No error checking is done to make sure blocking and unblocking are
+ * done in pairs.
+ */
+extern void grayswatch_block_signal(Grayswatch *gs, gboolean block);
+
+G_END_DECLS
+
+#endif /* _h_grayswatch */
diff --git a/gtkcompat.h b/gtkcompat.h
new file mode 100644 (file)
index 0000000..e50ceb4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_gtkcompat
+#define _h_gtkcompat
+
+#include <gtk/gtkversion.h>
+
+#if GTK_MAJOR_VERSION >= (2) && GTK_MINOR_VERSION >= (20)
+#define GTK_WIDGET_REALIZED gtk_widget_get_realized
+#define GTK_WIDGET_STATE gtk_widget_get_state
+#define GTK_WIDGET_HAS_FOCUS gtk_widget_has_focus
+#define GTK_WIDGET_DRAWABLE gtk_widget_is_drawable
+#define GTK_WIDGET_IS_SENSITIVE gtk_widget_is_sensitive
+#define GTK_WIDGET_SENSITIVE gtk_widget_get_sensitive
+#define GTK_WIDGET_VISIBLE gtk_widget_get_visible
+#endif
+
+#endif /* _h_gtkcompat */
diff --git a/guiedit.c b/guiedit.c
new file mode 100644 (file)
index 0000000..0f57853
--- /dev/null
+++ b/guiedit.c
@@ -0,0 +1,1371 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+#include "labcon.h"
+
+/*
+ * The global glyphtest widget.
+ */
+GtkWidget *glyphtest = 0;
+static GtkWidget *gtest_dialog = 0;
+static GtkWidget *gtest_erase = 0;
+
+static void
+guiedit_glyphtest_baseline(GtkWidget *w, gpointer data)
+{
+    GtkToggleButton *tb;
+
+    tb = GTK_TOGGLE_BUTTON(w);
+    glyphtest_show_baseline(GLYPHTEST(glyphtest),
+                            gtk_toggle_button_get_active(tb));
+}
+
+static void
+guiedit_glyphtest_direction(GtkWidget *w, gpointer data)
+{
+    GtkToggleButton *tb;
+    gint dir;
+
+    tb = GTK_TOGGLE_BUTTON(w);
+    dir = gtk_toggle_button_get_active(tb) ?
+        GLYPHTEST_RIGHT_TO_LEFT : GLYPHTEST_LEFT_TO_RIGHT;
+    glyphtest_change_direction(GLYPHTEST(glyphtest), dir);
+}
+
+static void
+guiedit_glyphtest_erase(GtkWidget *w, gpointer data)
+{
+    glyphtest_erase(GLYPHTEST(glyphtest));
+
+    /*
+     * Disable the clear button until a glyph is added.
+     */
+    gtk_widget_set_sensitive(gtest_erase, FALSE);
+}
+
+static void
+guiedit_glyphtest_enable_erase(GtkWidget *w, gpointer data)
+{
+    /*
+     * Enable the clear button if a glyph is added.
+     */
+    gtk_widget_set_sensitive(gtest_erase, TRUE);
+}
+
+void
+guiedit_show_glyphtest(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    GtkWidget *hbox, *vbox, *frame, *cb;
+
+    if (gtest_dialog == 0) {
+        gtest_dialog = gtk_dialog_new();
+        (void) g_signal_connect(G_OBJECT(gtest_dialog),
+                                "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        vbox = gtk_vbox_new(FALSE, 0);
+
+        frame = gtk_frame_new("Test Glyphs");
+        gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+        glyphtest = glyphtest_new();
+        (void) g_signal_connect(G_OBJECT(glyphtest), "add_glyph",
+                                G_CALLBACK(guiedit_glyphtest_enable_erase),
+                                0);
+        gtk_container_add(GTK_CONTAINER(frame), glyphtest);
+
+        gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, FALSE, 0);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        cb = gtk_check_button_new_with_label("Show Baseline");
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
+        (void) g_signal_connect(G_OBJECT(cb), "toggled",
+                                G_CALLBACK(guiedit_glyphtest_baseline),
+                                0);
+        gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 2);
+        cb = gtk_check_button_new_with_label("Draw Right To Left");
+        (void) g_signal_connect(G_OBJECT(cb), "toggled",
+                                G_CALLBACK(guiedit_glyphtest_direction),
+                                0);
+        gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 2);
+
+        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(gtest_dialog)->vbox),
+                          vbox);
+
+        /*
+         * Add the buttons.
+         */
+        hbox = GTK_DIALOG(gtest_dialog)->action_area;
+
+        gtest_erase = gtk_button_new_with_label("Erase");
+        gtk_widget_set_sensitive(gtest_erase, FALSE);
+        (void) g_signal_connect(G_OBJECT(gtest_erase), "clicked",
+                                G_CALLBACK(guiedit_glyphtest_erase), 0);
+        gtk_container_add(GTK_CONTAINER(hbox), gtest_erase);
+
+        cb = gtk_button_new_with_label("Close");
+        (void) g_signal_connect_object(G_OBJECT(cb), "clicked",
+                                       G_CALLBACK(gtk_widget_hide),
+                                       (gpointer) gtest_dialog,
+                                       G_CONNECT_SWAPPED);
+        gtk_container_add(GTK_CONTAINER(hbox), cb);
+        gtk_widget_show_all(GTK_DIALOG(gtest_dialog)->vbox);
+        gtk_widget_show_all(GTK_DIALOG(gtest_dialog)->action_area);
+    }
+
+    guiutil_show_dialog_centered(gtest_dialog, ed->shell);
+}
+
+void
+guiedit_set_unicode_glyph_names(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    FILE *in;
+
+    if (options.unicode_name_file == 0) {
+        guiutil_error_message(ed->shell,
+                              "Unicode Glyph Names: No file provided.");
+        return;
+    }
+
+    if ((in = fopen(options.unicode_name_file, "r")) == 0) {
+        sprintf(buffer1, "Unicode Glyph Names: Unable to open %s.",
+                options.unicode_name_file);
+        guiutil_error_message(ed->shell, buffer1);
+    }
+
+    fontgrid_set_unicode_glyph_names(FONTGRID(ed->fgrid), in);
+
+    fclose(in);
+}
+
+void
+guiedit_set_adobe_glyph_names(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    FILE *in;
+
+    if (options.adobe_name_file == 0) {
+        guiutil_error_message(ed->shell,
+                              "Adobe Glyph Names: No file provided.");
+        return;
+    }
+
+    if ((in = fopen(options.adobe_name_file, "r")) == 0) {
+        sprintf(buffer1, "Adobe Glyph Names: Unable to open %s.",
+                options.adobe_name_file);
+        guiutil_error_message(ed->shell, buffer1);
+    }
+
+    fontgrid_set_adobe_glyph_names(FONTGRID(ed->fgrid), in);
+
+    fclose(in);
+}
+
+void
+guiedit_set_uni_glyph_names(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), 'u');
+}
+
+void
+guiedit_set_zerox_glyph_names(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), 'x');
+}
+
+void
+guiedit_set_uplus_glyph_names(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), '+');
+}
+
+void
+guiedit_set_bslashu_glyph_names(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), '\\');
+}
+
+static void
+guiedit_select_property(GtkTreeSelection *selection, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    gchar *prop_name;
+    bdf_font_t *font;
+    bdf_property_t *prop;
+    GtkToggleButton *tb;
+    gboolean from_font = TRUE;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GValue val;
+
+    /*
+     * This is called after the list is cleared as well, so return if there is
+     * no selection.
+     */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    (void) memset((char *) &val, 0, sizeof(GValue));
+
+    gtk_tree_model_get_value(model, &iter, 0, &val);
+    prop_name = (gchar *) g_value_get_string(&val);
+
+    if ((prop = bdf_get_font_property(font, prop_name)) == 0) {
+        from_font = FALSE;
+        prop = bdf_get_property(prop_name);
+    }
+
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_name), prop_name);
+
+    g_value_unset(&val);
+
+    /*
+     * Always clear the property value field in case of ATOM properties with
+     * no actual value.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), "");
+
+    if (from_font) {
+        switch (prop->format) {
+          case BDF_ATOM:
+            if (prop->value.atom != 0)
+              gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value),
+                                 prop->value.atom);
+            break;
+          case BDF_INTEGER:
+            sprintf(buffer1, "%d", prop->value.int32);
+            gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), buffer1);
+            break;
+          case BDF_CARDINAL:
+            sprintf(buffer1, "%d", prop->value.card32);
+            gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), buffer1);
+            break;
+        }
+    }
+
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[prop->format]);
+    gtk_toggle_button_set_active(tb, TRUE);
+
+    /*
+     * Change the sensitivity of the Delete button depending on whether the
+     * property was defined in the font or not.  If the property came from the
+     * font and is one of FONT_ASCENT or FONT_DESCENT, do not allow them to be
+     * deleted.
+     */
+    if (from_font && (strncmp(prop_name, "FONT_ASCENT", 11) == 0 ||
+                      strncmp(prop_name, "FONT_DESCENT", 12) == 0))
+      from_font = FALSE;
+    gtk_widget_set_sensitive(ed->finfo_delete_prop, from_font);
+
+    /*
+     * Always change the sensitivity of the Apply button to False when a
+     * property is selected.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_prop, FALSE);
+}
+
+/*
+ * Called whenever a new font is loaded into this editor.
+ */
+void
+guiedit_update_font_info(gbdfed_editor_t *ed)
+{
+    GtkToggleButton *tb;
+    GtkTextBuffer *text;
+    bdf_font_t *font;
+    guint32 i, nprops;
+    bdf_property_t *props;
+    GtkListStore *store;
+    GtkTreeIter iter;
+
+    /*
+     * Simply return if the editor's info dialog doesn't exist yet.
+     */
+    if (ed->finfo_dialog == 0)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    /*
+     * Set the dialog title.
+     */
+    if (ed->file == 0)
+      sprintf(buffer1, "(unnamed%d) Info Edit", ed->id);
+    else
+      sprintf(buffer1, "%s Info Edit", ed->file);
+    gtk_window_set_title(GTK_WINDOW(ed->finfo_dialog), buffer1);
+
+    /*
+     * Update the font properties.
+     */
+    store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ed->finfo_font_props)));
+    gtk_list_store_clear(store);
+
+    if ((nprops = bdf_font_property_list(font, &props)) > 0) {
+        for (i = 0; i < nprops; i++) {
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, props[i].name, -1);
+        }
+        free((char *) props);
+    }
+
+    /*
+     * Update the defined properties.
+     */
+    store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ed->finfo_all_props)));
+    gtk_list_store_clear(store);
+
+    if ((nprops = bdf_property_list(&props)) > 0) {
+        for (i = 0; i < nprops; i++) {
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, props[i].name, -1);
+        }
+        free((char *) props);
+    }
+
+    /*
+     * Clear the property name and value fields.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_name), "");
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), "");
+
+    /*
+     * Turn off the property apply button until something gets edited.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_prop, FALSE);
+
+    /*
+     * Update the font comments.
+     */
+    text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->finfo_comments));
+    if (font && font->comments_len > 0)
+      gtk_text_buffer_set_text(text, font->comments, font->comments_len);
+    else
+      gtk_text_buffer_set_text(text, "", 0);
+
+    gtk_widget_set_sensitive(ed->finfo_apply_comments, FALSE);
+
+    /*
+     * Update the font spacing.
+     */
+    if (font)
+      tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[font->spacing >> 4]);
+    else
+      tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[options.font_opts.font_spacing >> 4]);
+    gtk_toggle_button_set_active(tb, TRUE);
+
+    /*
+     * Update the device width value.
+     */
+    gtk_widget_set_sensitive(ed->finfo_dwidth, TRUE);
+    if (font && font->spacing != BDF_PROPORTIONAL)
+      sprintf(buffer1, "%hd", font->monowidth);
+    else
+      (void) strcpy(buffer1, "0");
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_dwidth), buffer1);
+
+    if (font)
+      sprintf(buffer1, "%d", font->font_ascent);
+    else
+      sprintf(buffer1, "0");
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_ascent), buffer1);
+
+    if (font)
+      sprintf(buffer1, "%d", font->font_descent);
+    else
+      sprintf(buffer1, "0");
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_descent), buffer1);
+
+    if ((!font && options.font_opts.font_spacing == BDF_PROPORTIONAL) ||
+        font->spacing == BDF_PROPORTIONAL)
+      gtk_widget_set_sensitive(ed->finfo_dwidth, FALSE);
+
+    /*
+     * Finally, update the remaining information.
+     */
+    if (font)
+      sprintf(buffer1, "%d", font->glyphs_used);
+    else
+      sprintf(buffer1, "0");
+    gtk_label_set_text(GTK_LABEL(ed->finfo_enc_count), buffer1);
+
+    if (font)
+      sprintf(buffer1, "%d", font->unencoded_used);
+    else
+      sprintf(buffer1, "0");
+    gtk_label_set_text(GTK_LABEL(ed->finfo_unenc_count), buffer1);
+
+    if (font) {
+        gtk_spin_button_set_value(GTK_SPIN_BUTTON(ed->finfo_hres),
+                                  (gfloat) font->resolution_x);
+        gtk_spin_button_set_value(GTK_SPIN_BUTTON(ed->finfo_vres),
+                                  (gfloat) font->resolution_y);
+    }
+
+    if (font) {
+        switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) {
+          case 8: sprintf(buffer1, "%o", font->default_glyph); break;
+          case 10: sprintf(buffer1, "%d", font->default_glyph); break;
+          case 16: sprintf(buffer1, "%04x", font->default_glyph); break;
+        }
+        gtk_entry_set_text(GTK_ENTRY(ed->finfo_default_char), buffer1);
+    }
+
+    if (font)
+      sprintf(buffer1, "%hd", font->bpp);
+    else
+      sprintf(buffer1, "%d", options.font_opts.bits_per_pixel);
+    gtk_label_set_text(GTK_LABEL(ed->finfo_bpp), buffer1);
+
+    gtk_widget_set_sensitive(ed->finfo_apply_info, FALSE);
+}
+
+void
+guiedit_update_code_base(gbdfed_editor_t *ed)
+{
+    bdf_font_t *font;
+
+    if (ed->finfo_dialog == 0)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) {
+      case 8: sprintf(buffer1, "%o", font->default_glyph); break;
+      case 10: sprintf(buffer1, "%d", font->default_glyph); break;
+      case 16: sprintf(buffer1, "%04x", font->default_glyph); break;
+    }
+    gtk_entry_set_text(GTK_ENTRY(ed->finfo_default_char), buffer1);
+}
+
+void
+guiedit_update_font_details(gbdfed_editor_t *ed)
+{
+    bdf_font_t *font;
+
+    if (ed->finfo_dialog == 0)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    sprintf(buffer1, "%d", font->glyphs_used);
+    gtk_label_set_text(GTK_LABEL(ed->finfo_enc_count), buffer1);
+    sprintf(buffer1, "%d", font->unencoded_used);
+    gtk_label_set_text(GTK_LABEL(ed->finfo_unenc_count), buffer1);
+}
+
+static void
+enable_apply(GtkWidget *w, gpointer data)
+{
+    gtk_widget_set_sensitive(w, TRUE);
+}
+
+static void
+guiedit_enable_comment_buttons(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    gtk_widget_set_sensitive(ed->finfo_erase_comments, TRUE);
+    gtk_widget_set_sensitive(ed->finfo_apply_comments, TRUE);
+}
+
+static void
+guiedit_apply_property(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    GtkToggleButton *tb;
+    gchar *v;
+    bdf_property_t prop;
+
+    /*
+     * Get the property info.
+     */
+    prop.name = (char *) gtk_entry_get_text(GTK_ENTRY(ed->finfo_prop_name));
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[BDF_ATOM]);
+    if (gtk_toggle_button_get_active(tb))
+      prop.format = BDF_ATOM;
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[BDF_INTEGER]);
+    if (gtk_toggle_button_get_active(tb))
+      prop.format = BDF_INTEGER;
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[BDF_CARDINAL]);
+    if (gtk_toggle_button_get_active(tb))
+      prop.format = BDF_CARDINAL;
+
+    v = (char *) gtk_entry_get_text(GTK_ENTRY(ed->finfo_prop_value));
+    switch (prop.format) {
+      case BDF_ATOM: prop.value.atom = v; break;
+      case BDF_INTEGER: prop.value.int32 = _bdf_atol(v, 0, 10); break;
+      case BDF_CARDINAL: prop.value.card32 = _bdf_atoul(v, 0, 10); break;
+    }
+
+    fontgrid_set_font_property(FONTGRID(ed->fgrid), &prop);
+
+    if (bdf_is_xlfd_property(prop.name))
+      ed->finfo_xlfd_props_modified = TRUE;
+
+    /*
+     * Turn the apply button back off.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_prop, FALSE);
+}
+
+static void
+guiedit_delete_property(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    gchar *name;
+
+    name = (char *) gtk_entry_get_text(GTK_ENTRY(ed->finfo_prop_name));
+
+    fontgrid_delete_font_property(FONTGRID(ed->fgrid), name);
+
+    /*
+     * Make sure the Apply button is activated in case the delete was
+     * unintentional so the user can apply it again.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_prop, TRUE);
+}
+
+static void
+guiedit_erase_comments(GtkWidget *w, gpointer data)
+{
+    GtkTextBuffer *text;
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    gchar *comments;
+
+    fontgrid_set_font_comments(FONTGRID(ed->fgrid), 0);
+
+    /*
+     * Clear out the comments text widget.
+     */
+    text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->finfo_comments));
+    gtk_text_buffer_set_text(text, "", 0);
+
+    /*
+     * Disable the Erase button until the next change in the comment text.
+     */
+    gtk_widget_set_sensitive(ed->finfo_erase_comments, FALSE);
+
+    /*
+     * If the font contains no comments either, disable the Apply button.  If
+     * it has comments, then enable the Apply button so that the existing
+     * comments can be removed if wanted.
+     */
+    if (fontgrid_get_font_comments(FONTGRID(ed->fgrid), &comments) == 0)
+      gtk_widget_set_sensitive(ed->finfo_apply_comments, FALSE);
+    else
+      gtk_widget_set_sensitive(ed->finfo_apply_comments, TRUE);
+
+    /*
+     * Force the focus back to the comments widget.
+     */
+    gtk_widget_grab_focus(ed->finfo_comments);
+}
+
+static void
+guiedit_apply_comments(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    gchar *comments;
+    GtkTextBuffer *text;
+    GtkTextIter start, end;
+
+    text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->finfo_comments));
+    gtk_text_buffer_get_bounds(text, &start, &end);
+    comments = gtk_text_buffer_get_text(text, &start, &end, TRUE);
+
+    fontgrid_set_font_comments(FONTGRID(ed->fgrid), comments);
+
+    /*
+     * Disable the Apply button until the next change in the comment text.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_comments, FALSE);
+
+    /*
+     * Force the focus back to the comments widget.
+     */
+    gtk_widget_grab_focus(ed->finfo_comments);
+}
+
+static void
+guiedit_change_spacing(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    /*
+     * Enable the device width input field depending on which radio button was
+     * toggled.
+     */
+    if (w == ed->finfo_spacing[BDF_PROPORTIONAL >> 4])
+      gtk_widget_set_sensitive(ed->finfo_dwidth, FALSE);
+    else
+      gtk_widget_set_sensitive(ed->finfo_dwidth, TRUE);
+
+    /*
+     * Enable the Apply button.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_info, TRUE);
+}
+
+static void
+guiedit_apply_details(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    GtkToggleButton *tb;
+    const gchar *v;
+    bdf_property_t sp;
+    FontgridFontInfo finfo;
+
+    v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_dwidth));
+    finfo.monowidth = (guint16) _bdf_atos((char *) v, 0, 10);
+    
+    /*
+     * Get the current spacing value.
+     */
+    finfo.spacing = 0;
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[BDF_PROPORTIONAL >> 4]);
+    if (gtk_toggle_button_get_active(tb))
+      finfo.spacing = BDF_PROPORTIONAL;
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[BDF_MONOWIDTH >> 4]);
+    if (gtk_toggle_button_get_active(tb))
+      finfo.spacing = BDF_MONOWIDTH;
+    tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[BDF_CHARCELL >> 4]);
+    if (gtk_toggle_button_get_active(tb))
+      finfo.spacing = BDF_CHARCELL;
+
+    /*
+     * Make sure a property gets added to the font as well so it
+     * gets saved in the event there is no XLFD name.
+     */
+    sp.name = "SPACING";
+    sp.format = BDF_ATOM;
+    switch (finfo.spacing) {
+      case BDF_PROPORTIONAL: sp.value.atom = "P"; break;
+      case BDF_MONOWIDTH: sp.value.atom = "M"; break;
+      case BDF_CHARCELL: sp.value.atom = "C"; break;
+    }
+    bdf_add_font_property(fontgrid_get_font(FONTGRID(ed->fgrid)), &sp);
+
+    /*
+     * Set the font spacing values on all of the visible glyph editors.
+     */
+    guigedit_set_font_spacing(finfo.spacing, finfo.monowidth);
+
+    /*
+     * Get the default character.
+     */
+    v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_default_char));
+    finfo.default_char =
+        (glong) _bdf_atol((char *) v, 0,
+                          fontgrid_get_code_base(FONTGRID(ed->fgrid)));
+
+    finfo.resolution_x = (glong)
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ed->finfo_hres));
+    finfo.resolution_y = (glong)
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ed->finfo_vres));
+
+    v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_ascent));
+    finfo.font_ascent = (glong) _bdf_atol((char *) v, 0, 10);
+    v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_descent));
+    finfo.font_descent = (glong) _bdf_atol((char *) v, 0, 10);
+
+    /*
+     * Turn off the Apply button until something else changes.
+     */
+    gtk_widget_set_sensitive(ed->finfo_apply_info, FALSE);
+}
+
+static void
+notebook_switch_page(GtkNotebook *nb, GtkNotebookPage *nbp, gint pageno,
+                     gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    /*
+     * Whenever the comments page is shown, force the focus on the text
+     * widget.
+     */
+    if (pageno == 2)
+      gtk_widget_grab_focus(ed->finfo_comments);
+}
+
+#define CLINFOMSG "Font Info: Some XLFD properties were modified.\nDo you wish to update the font name with these changes?"
+
+static void
+close_finfo(GtkWidget *w, gint response, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (ed->finfo_xlfd_props_modified == TRUE) {
+        if (guiutil_yes_or_no(ed->finfo_dialog, CLINFOMSG, TRUE))
+          fontgrid_update_font_name_from_properties(FONTGRID(ed->fgrid));
+    }
+    ed->finfo_xlfd_props_modified = FALSE;
+
+    gtk_widget_hide(ed->finfo_dialog);
+}
+
+static void
+finfo_sync_res(GtkWidget *w, GdkEventFocus *ev, gpointer data)
+{
+    gfloat v;
+    GtkSpinButton *b;
+
+    b = GTK_SPIN_BUTTON(data);
+    v = (gfloat) gtk_spin_button_get_value(b);
+
+    if (v != (gfloat) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))) {
+        gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+
+#if 0
+        /*
+         * Enable the apply button.
+         */
+        gtk_widget_set_sensitive(pref_apply, TRUE);
+#endif
+    }
+}
+
+void
+guiedit_show_font_info(GtkWidget *w, gpointer data)
+{
+    GtkWidget *hbox, *vbox, *frame, *label, *button, *table;
+    GtkWidget *nb, *vbox1, *hbox1, *swin, *image;
+    GtkTextBuffer *text;
+    GtkRadioButton *rb;
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    bdf_font_t *font;
+    GtkListStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *cell_renderer;
+    GtkTreeSelection *sel;
+    GtkAdjustment *adj;
+    gint idx;
+
+    if (ed->finfo_dialog == 0) {
+        font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+        ed->finfo_dialog = gtk_dialog_new();
+
+        (void) g_signal_connect(G_OBJECT(ed->finfo_dialog),
+                                "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        nb = ed->finfo_notebook = gtk_notebook_new();
+
+        (void) g_signal_connect(G_OBJECT(nb), "switch_page",
+                                G_CALLBACK(notebook_switch_page),
+                                GUINT_TO_POINTER(ed->id));
+
+        /*
+         * Create the font info page.
+         */
+
+        /*
+         * Create the Apply button first so it can be used in callbacks
+         * for the data fields.
+         */
+        ed->finfo_apply_info = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+        (void) g_signal_connect(G_OBJECT(ed->finfo_apply_info), "clicked",
+                                G_CALLBACK(guiedit_apply_details),
+                                GUINT_TO_POINTER(ed->id));
+
+        
+        vbox = gtk_vbox_new(FALSE, 0);
+
+        frame = gtk_frame_new("Glyph Counts");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+        ed->finfo_enc_count = gtk_label_new("0");
+        gtk_misc_set_alignment(GTK_MISC(ed->finfo_enc_count), 0.0, 0.5);
+        label = labcon_new_label_defaults("Encoded:",
+                                          ed->finfo_enc_count, 0);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+        ed->finfo_unenc_count = gtk_label_new("0");
+        gtk_misc_set_alignment(GTK_MISC(ed->finfo_unenc_count), 0.0, 0.5);
+        label = labcon_new_label_defaults("Unencoded:",
+                                          ed->finfo_unenc_count, 0);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+        gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+        frame = gtk_frame_new("Default Character");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+        ed->finfo_default_char = gtk_widget_new(gtk_entry_get_type(),
+                                                "max_length", 4, NULL);
+        gtk_widget_set_size_request(ed->finfo_default_char, 75, -1);
+        g_signal_connect_object(G_OBJECT(ed->finfo_default_char), "changed",
+                                G_CALLBACK(enable_apply),
+                                (gpointer) ed->finfo_apply_info,
+                                G_CONNECT_SWAPPED);
+        gtk_box_pack_start(GTK_BOX(hbox), ed->finfo_default_char,
+                           FALSE, FALSE, 0);
+        gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+        frame = gtk_frame_new("Font Spacing");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+        idx = BDF_PROPORTIONAL >> 4;
+
+        button = ed->finfo_spacing[idx] =
+            gtk_radio_button_new_with_label(0, "Proportional");
+        g_signal_connect(G_OBJECT(ed->finfo_spacing[idx]), "toggled",
+                         G_CALLBACK(guiedit_change_spacing),
+                         GUINT_TO_POINTER(ed->id));
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+        idx++;
+        rb = GTK_RADIO_BUTTON(button);
+        button = ed->finfo_spacing[idx] =
+            gtk_radio_button_new_with_label_from_widget(rb, "Monowidth");
+        g_signal_connect(G_OBJECT(ed->finfo_spacing[idx]), "toggled",
+                         G_CALLBACK(guiedit_change_spacing),
+                         GUINT_TO_POINTER(ed->id));
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+        idx++;
+        rb = GTK_RADIO_BUTTON(button);
+        button = ed->finfo_spacing[idx] =
+            gtk_radio_button_new_with_label_from_widget(rb, "Character Cell");
+        g_signal_connect(G_OBJECT(ed->finfo_spacing[idx]), "toggled",
+                         G_CALLBACK(guiedit_change_spacing),
+                         GUINT_TO_POINTER(ed->id));
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+        gtk_container_add(GTK_CONTAINER(frame), hbox);
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+        frame = gtk_frame_new("Font Resolution");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+        adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0,
+                                                   1.0, 10.0, 0.0);
+        ed->finfo_hres = gtk_spin_button_new(adj, 1.0, 0);
+        gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(ed->finfo_hres), TRUE);
+        gtk_widget_set_size_request(ed->finfo_hres, 75, -1);
+        g_signal_connect_object(G_OBJECT(ed->finfo_hres), "value-changed",
+                                G_CALLBACK(enable_apply),
+                                (gpointer) ed->finfo_apply_info,
+                                G_CONNECT_SWAPPED);
+        label = labcon_new_label_defaults("Horizontal:", ed->finfo_hres, 0);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+        adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0,
+                                                   1.0, 10.0, 0.0);
+        ed->finfo_vres = gtk_spin_button_new(adj, 1.0, 0);
+        gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(ed->finfo_vres), TRUE);
+        gtk_widget_set_size_request(ed->finfo_vres, 75, -1);
+        g_signal_connect_object(G_OBJECT(ed->finfo_vres), "value-changed",
+                                G_CALLBACK(enable_apply),
+                                (gpointer) ed->finfo_apply_info,
+                                G_CONNECT_SWAPPED);
+        g_signal_connect(G_OBJECT(ed->finfo_vres), "focus-in-event",
+                         G_CALLBACK(finfo_sync_res),
+                         (gpointer) ed->finfo_hres);
+
+        label = labcon_new_label_defaults("Vertical:", ed->finfo_vres, label);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+        ed->finfo_bpp = gtk_label_new("1");
+        label = labcon_new_label_defaults("Bits Per Pixel:", ed->finfo_bpp,
+                                          label);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+        gtk_container_add(GTK_CONTAINER(frame), hbox);
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+        frame = gtk_frame_new("Font Metrics");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+        ed->finfo_dwidth = gtk_widget_new(gtk_entry_get_type(),
+                                          "max_length", 4, NULL);
+        gtk_widget_set_size_request(ed->finfo_dwidth, 75, -1);
+        g_signal_connect_object(G_OBJECT(ed->finfo_dwidth), "changed",
+                                G_CALLBACK(enable_apply),
+                                (gpointer) ed->finfo_apply_info,
+                                G_CONNECT_SWAPPED);
+        label = labcon_new_label_defaults("Device Width:",
+                                          ed->finfo_dwidth, label);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+        ed->finfo_ascent = gtk_widget_new(gtk_entry_get_type(),
+                                          "max_length", 4, NULL);
+        gtk_widget_set_size_request(ed->finfo_ascent, 75, -1);
+        g_signal_connect_object(G_OBJECT(ed->finfo_ascent), "changed",
+                                G_CALLBACK(enable_apply),
+                                (gpointer) ed->finfo_apply_info,
+                                G_CONNECT_SWAPPED);
+        label = labcon_new_label_defaults("Font Ascent:", ed->finfo_ascent,
+                                          label);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+        ed->finfo_descent = gtk_widget_new(gtk_entry_get_type(),
+                                           "max_length", 4, NULL);
+        gtk_widget_set_size_request(ed->finfo_descent, 75, -1);
+        g_signal_connect_object(G_OBJECT(ed->finfo_descent), "changed",
+                                G_CALLBACK(enable_apply),
+                                (gpointer) ed->finfo_apply_info,
+                                G_CONNECT_SWAPPED);
+        label = labcon_new_label_defaults("Font Descent:",
+                                          ed->finfo_descent, label);
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+        gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+        /*
+         * Finally, add the Apply button.
+         */
+        gtk_box_pack_end(GTK_BOX(vbox), ed->finfo_apply_info, FALSE, FALSE, 5);
+
+        label = gtk_label_new("Font Info");
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox, label);
+
+        /*
+         * Create the font properties editor.
+         */
+        vbox = gtk_vbox_new(FALSE, 5);
+
+        table = gtk_table_new(1, 2, TRUE);
+
+        swin = gtk_scrolled_window_new(0, 0);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+        gtk_container_set_border_width(GTK_CONTAINER(swin), 3);
+
+        store = gtk_list_store_new(1, G_TYPE_STRING);
+        ed->finfo_font_props =
+            gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+        g_object_unref(store);
+
+        gtk_widget_set_size_request(ed->finfo_font_props, -1, 200);
+        gtk_container_add(GTK_CONTAINER(swin), ed->finfo_font_props);
+
+        cell_renderer = gtk_cell_renderer_text_new();
+        column = gtk_tree_view_column_new_with_attributes("Font Properties",
+                                                          cell_renderer,
+                                                          "text", 0,
+                                                          NULL);
+
+        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column(GTK_TREE_VIEW(ed->finfo_font_props),
+                                    column);
+        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ed->finfo_font_props));
+
+        (void) g_signal_connect(G_OBJECT(sel), "changed",
+                                G_CALLBACK(guiedit_select_property),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_table_attach(GTK_TABLE(table), swin, 0, 1, 0, 1,
+                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+        swin = gtk_scrolled_window_new(0, 0);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+        gtk_container_set_border_width(GTK_CONTAINER(swin), 3);
+
+        store = gtk_list_store_new(1, G_TYPE_STRING);
+        ed->finfo_all_props =
+            gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+        g_object_unref(store);
+
+        gtk_widget_set_size_request(ed->finfo_all_props, -1, 200);
+        gtk_container_add(GTK_CONTAINER(swin), ed->finfo_all_props);
+
+        cell_renderer = gtk_cell_renderer_text_new();
+        column = gtk_tree_view_column_new_with_attributes("All Properties",
+                                                          cell_renderer,
+                                                          "text", 0,
+                                                          NULL);
+
+        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column(GTK_TREE_VIEW(ed->finfo_all_props),
+                                    column);
+        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ed->finfo_all_props));
+
+        (void) g_signal_connect(G_OBJECT(sel), "changed",
+                                G_CALLBACK(guiedit_select_property),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_table_attach(GTK_TABLE(table), swin, 1, 2, 0, 1,
+                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+        gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 2);
+
+        frame = gtk_frame_new("Property");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        vbox1 = gtk_vbox_new(FALSE, 5);
+
+        table = gtk_table_new(3, 2, FALSE);
+
+        label = gtk_label_new("Name:");
+        gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+                         GTK_FILL, GTK_FILL, 5, 0);
+
+        label = gtk_label_new("Value:");
+        gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+                         GTK_FILL, GTK_FILL, 5, 0);
+
+        label = gtk_label_new("Type:");
+        gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
+                         GTK_FILL, GTK_FILL, 5, 0);
+
+        ed->finfo_prop_name = gtk_entry_new();
+        gtk_table_attach(GTK_TABLE(table), ed->finfo_prop_name, 1, 2, 0, 1,
+                         GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5);
+
+        ed->finfo_prop_value = gtk_entry_new();
+        gtk_table_attach(GTK_TABLE(table), ed->finfo_prop_value, 1, 2, 1, 2,
+                         GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5);
+
+        hbox = gtk_hbox_new(FALSE, 2);
+        button = ed->finfo_prop_format[BDF_ATOM] =
+            gtk_radio_button_new_with_label(0, "String");
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+        rb = GTK_RADIO_BUTTON(button);
+        button = ed->finfo_prop_format[BDF_INTEGER] =
+            gtk_radio_button_new_with_label_from_widget(rb, "Integer");
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+        rb = GTK_RADIO_BUTTON(button);
+        button = ed->finfo_prop_format[BDF_CARDINAL] =
+            gtk_radio_button_new_with_label_from_widget(rb, "Cardinal");
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+        gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 2, 3,
+                         GTK_FILL, GTK_FILL, 0, 0);
+
+        gtk_box_pack_start(GTK_BOX(vbox1), table, FALSE, FALSE, 0);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+
+        hbox1 = gtk_hbox_new(FALSE, 0);
+        label = gtk_label_new_with_mnemonic("_Apply To Font Properties");
+        image = gtk_image_new_from_stock(GTK_STOCK_APPLY,
+                                         GTK_ICON_SIZE_BUTTON);
+        gtk_box_pack_start(GTK_BOX(hbox1), image, FALSE, FALSE, 0);
+        gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
+
+        button = ed->finfo_apply_prop = gtk_button_new();
+        gtk_container_add(GTK_CONTAINER(button), hbox1);
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(guiedit_apply_property),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+        (void) g_signal_connect_object(G_OBJECT(ed->finfo_prop_value),
+                                       "changed",
+                                       G_CALLBACK(enable_apply),
+                                       (gpointer) button,
+                                       G_CONNECT_SWAPPED);
+
+        button = ed->finfo_prop_format[BDF_ATOM];
+        (void) g_signal_connect_object(G_OBJECT(button),
+                                       "toggled",
+                                       G_CALLBACK(enable_apply),
+                                       (gpointer) ed->finfo_apply_prop,
+                                       G_CONNECT_SWAPPED);
+
+        button = ed->finfo_prop_format[BDF_INTEGER];
+        (void) g_signal_connect_object(G_OBJECT(button),
+                                       "toggled",
+                                       G_CALLBACK(enable_apply),
+                                       (gpointer) ed->finfo_apply_prop,
+                                       G_CONNECT_SWAPPED);
+
+        button = ed->finfo_prop_format[BDF_CARDINAL];
+        (void) g_signal_connect_object(G_OBJECT(button),
+                                       "toggled",
+                                       G_CALLBACK(enable_apply),
+                                       (gpointer) ed->finfo_apply_prop,
+                                       G_CONNECT_SWAPPED);
+
+        hbox1 = gtk_hbox_new(FALSE, 0);
+        label = gtk_label_new_with_mnemonic("_Delete From Font Properties");
+        image = gtk_image_new_from_stock(GTK_STOCK_DELETE,
+                                         GTK_ICON_SIZE_BUTTON);
+        gtk_box_pack_start(GTK_BOX(hbox1), image, FALSE, FALSE, 0);
+        gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
+
+        button = ed->finfo_delete_prop = gtk_button_new();
+        gtk_container_add(GTK_CONTAINER(button), hbox1);
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(guiedit_delete_property),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+        gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 2);
+
+        gtk_container_add(GTK_CONTAINER(frame), vbox1);
+
+        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
+
+        label = gtk_label_new("Font Properties");
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox, label);
+
+        /*
+         * Create the font comment editor.
+         */
+        frame = gtk_frame_new("Comments");
+        gtk_container_set_border_width(GTK_CONTAINER(frame), 3);
+
+        table = gtk_table_new(2, 2, FALSE);
+
+        text = gtk_text_buffer_new(NULL);
+        ed->finfo_comments = gtk_text_view_new_with_buffer(text);
+        gtk_widget_set_size_request(ed->finfo_comments, 400, 200);
+
+        swin = gtk_scrolled_window_new(NULL, NULL);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_ALWAYS);
+        gtk_container_add(GTK_CONTAINER(swin), ed->finfo_comments);
+        gtk_table_attach(GTK_TABLE(table), swin, 0, 1, 0, 1,
+                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 5);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        button = ed->finfo_apply_comments =
+            gtk_button_new_from_stock(GTK_STOCK_APPLY);
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(guiedit_apply_comments),
+                                GUINT_TO_POINTER(ed->id));
+        (void) g_signal_connect_data((gpointer) text,
+                                     "changed",
+                                     G_CALLBACK(guiedit_enable_comment_buttons),
+                                     GUINT_TO_POINTER(ed->id), NULL, 0);
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = ed->finfo_erase_comments =
+            gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(guiedit_erase_comments),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+        gtk_table_attach(GTK_TABLE(table), hbox, 0, 2, 1, 2,
+                         GTK_FILL, GTK_FILL, 0, 5);
+        gtk_container_add(GTK_CONTAINER(frame), table);
+
+        label = gtk_label_new("Font Comments");
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), frame, label);
+
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(ed->finfo_dialog)->vbox),
+                          nb);
+
+        /*
+         * Add the buttons.
+         */
+        gtk_dialog_add_button(GTK_DIALOG(ed->finfo_dialog),
+                              GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
+
+        g_signal_connect(G_OBJECT(ed->finfo_dialog), "response",
+                         G_CALLBACK(close_finfo), GUINT_TO_POINTER(ed->id));
+
+        guiedit_update_font_info(ed);
+
+        gtk_widget_show_all(GTK_DIALOG(ed->finfo_dialog)->vbox);
+        gtk_widget_show_all(GTK_DIALOG(ed->finfo_dialog)->action_area);
+    }
+
+    /*
+     * Center the dialog and show it.
+     */
+    guiutil_show_dialog_centered(ed->finfo_dialog, ed->shell);
+}
+
+void
+guiedit_show_font_properties(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    guiedit_show_font_info(w, data);
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->finfo_notebook), 1);
+}
+
+void
+guiedit_show_font_comments(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    guiedit_show_font_info(w, data);
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->finfo_notebook), 2);
+}
+
+/*
+ * Called when the font name is used to update the properties so the font info
+ * editor list of properties is updated.
+ */
+void
+guiedit_update_font_properties(gbdfed_editor_t *ed)
+{
+    gchar *prop;
+    guint32 i, nprops;
+    bdf_font_t *font;
+    bdf_property_t *props;
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GtkTreePath *row;
+    GtkTreeView *tview;
+    GtkTreeIter iter;
+    GValue val;
+
+    /*
+     * Update the font properties list if the font info dialog has been
+     * created.
+     */
+    if (ed->finfo_dialog == 0)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    /*
+     * This will be the name of the currently selected property, if one
+     * is selected.
+     */
+    prop = 0;
+
+    selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(ed->finfo_font_props));
+    if (gtk_tree_selection_get_selected(selection, &model, &iter) != FALSE) {
+        memset((char *) &val, 0, sizeof(GValue));
+        gtk_tree_model_get_value(model, &iter, 0, &val);
+        prop = (gchar *) g_value_get_string(&val);
+    }
+
+    /*
+     * This will track the row that needs to be reselected once the list has
+     * been updated.
+     */
+    row = 0;
+
+    gtk_list_store_clear(GTK_LIST_STORE(model));
+
+    if ((nprops = bdf_font_property_list(font, &props)) != 0) {
+        for (i = 0; i < nprops; i++) {
+            if (prop && strcmp(prop, props[i].name) == 0)
+              row = gtk_tree_path_new_from_indices(i, -1);
+            gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+            gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+                               0, props[i].name, -1);
+        }
+    }
+
+    if (row != 0) {
+        gtk_tree_selection_select_path(selection, row);
+
+        /*
+         * Make sure the selected property is made visible.
+         */
+        tview = gtk_tree_selection_get_tree_view(selection);
+        gtk_tree_view_scroll_to_cell(tview, row, NULL, TRUE, 0.5, 0.5);
+    }
+
+    /*
+     * Make sure memory is deallocated when necessary.
+     */
+    if (prop != 0)
+      g_value_unset(&val);
+}
+
+void
+guiedit_copy_selection(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_copy_selection(FONTGRID(ed->fgrid));
+}
+
+void
+guiedit_cut_selection(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_cut_selection(FONTGRID(ed->fgrid));
+}
+
+void
+guiedit_paste_selection(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_paste_selection(FONTGRID(ed->fgrid), FONTGRID_NORMAL_PASTE);
+}
+
+void
+guiedit_overlay_selection(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_paste_selection(FONTGRID(ed->fgrid), FONTGRID_OVERLAY_PASTE);
+}
+
+void
+guiedit_insert_selection(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    fontgrid_paste_selection(FONTGRID(ed->fgrid), FONTGRID_INSERT_PASTE);
+}
diff --git a/guifile.c b/guifile.c
new file mode 100644 (file)
index 0000000..a9f21e3
--- /dev/null
+++ b/guifile.c
@@ -0,0 +1,3009 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+#include "labcon.h"
+
+#ifdef HAVE_XLIB
+#include <gdk/gdkx.h>
+#endif
+
+/*
+ * These are formats that can appear in the editor for importing/loading and
+ * exporting fonts.
+ */
+#define BDF_FORMAT     1
+#define CONSOLE_FORMAT 2
+#define PKGF_FORMAT    3
+#define FNT_FORMAT     4
+#define HBF_FORMAT     5
+#define OTF_FORMAT     6
+#define HEX_FORMAT     7
+#define PSF_FORMAT     8
+#define PSFUNI_FORMAT  9
+
+/*
+ * An array of filters used for the open/import and save dialogs.
+ */
+static GtkFileFilter *filename_filters[10];
+
+/*
+ * This variable is used to track whether the save dialog has been closed
+ * so the guifile_save_as_wait() routine knows when to return to the main
+ * application.
+ */
+static gboolean save_dialog_done;
+
+#ifdef HAVE_FREETYPE
+
+#include FT_GLYPH_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_TABLES_H
+
+/*
+ * Globals used for FreeType.
+ */
+static FT_Library library;
+static FT_Face face;
+
+/*
+ * Globals used for importing OpenType fonts.
+ */
+static gboolean ftinit = FALSE;
+static gboolean otf_collection;
+static gboolean otf_face_open;
+static gint otf_select_done = 0;
+static gchar *otf_fullpath;
+
+/*
+ * These are the widgets that will be needed for importing OpenType fonts.
+ */
+static GtkWidget *otf_dialog;
+static GtkWidget *otf_faces;
+static GtkWidget *otf_platforms;
+static GtkWidget *otf_encodings;
+static GtkWidget *otf_point_size;
+static GtkWidget *otf_hres;
+static GtkWidget *otf_vres;
+
+/*
+ * List of platform IDs seen that is used when platforms are selected
+ * from OpenType fonts.
+ */
+static gint16 platforms[32];
+static gint nplatforms;
+
+/*
+ * List of encoding IDs seen that is used when encodings are selected
+ * from OpenType fonts.
+ */
+static gint16 encodings[34];
+static gint nencodings;
+
+/*
+ * Variables to hold the selected platform and encoding ID's.
+ */
+static gint16 otf_pid_pos;
+static gint16 otf_eid_pos;
+
+#endif /* HAVE_FREETYPE */
+
+#ifdef HAVE_XLIB
+
+/*
+ * These are for importing fonts from the X server.
+ */
+#define _XSRV_MAX_FONTS 32767
+#define _XSRV_DEFAULT_FILTER "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+
+static GtkWidget *xsrv_dialog;
+static GtkWidget *xsrv_filter_text;
+static GtkWidget *xsrv_selection_text;
+static GtkWidget *xsrv_font_list;
+static GtkWidget *xsrv_import;
+
+/*
+ * Because the grab dialog is shared amongst the editors, this tracks which
+ * editor has control of the font list.
+ */
+static guint xsrv_active_editor;
+
+#endif /* HAVE_XLIB */
+
+/*
+ * Widgets for dealing with exporting PSF fonts.
+ */
+static GtkWidget *psf_export_frame;
+static GtkWidget *psf_export_options;
+
+/*
+ * Widgets for selecting fonts from a Windows font archive.
+ */
+static GtkWidget *fnt_dialog;
+static GtkWidget *fnt_font_list;
+static GtkWidget *fnt_load_button;
+
+/*
+ * This is a list of Windows fonts that have been selected.  It assumes that
+ * the font file will never contain more than 32 fonts.
+ */
+static gint fnt_selected[32];
+static gint fnt_selected_count;
+
+/*
+ * A structure used to pass data to the load and cancel callbacks when dealing
+ * with FON/FNT fonts.
+ */
+typedef struct {
+    gchar *file;
+    gchar *dir;
+    gchar *dot;
+    bdffnt_font_t font;
+} _bdffnt_callback_data_t;
+
+/*
+ * This is used in a couple of cases to point at the active editor.
+ */
+static gbdfed_editor_t *active_editor;
+
+static void
+make_file_chooser_filters(void)
+{
+    int i;
+
+    if (filename_filters[0] != NULL)
+      return;
+
+    filename_filters[BDF_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[BDF_FORMAT],
+                                "*.[Bb][Dd][Ff]");
+
+    filename_filters[CONSOLE_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[CONSOLE_FORMAT], "*");
+
+    filename_filters[PKGF_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[PKGF_FORMAT],
+                                "*[PpGg][KkFf]");
+
+    filename_filters[FNT_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[FNT_FORMAT],
+                                "*.[FfEeDd][OoNnXxLl][NnTtEeLl]");
+
+    filename_filters[HEX_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[HEX_FORMAT],
+                                "*.[Hh][Ee][Xx]");
+
+    filename_filters[PSF_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[PSF_FORMAT],
+                                "*.[Ps][Ss][Ff]*");
+
+    /*
+     * This one is basically for exporting unimap files that belong to PSF
+     * fonts.
+     */
+    filename_filters[PSFUNI_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[PSFUNI_FORMAT],
+                                "*.[Uu][Nn][Ii]");
+
+#ifdef HAVE_HBF
+    filename_filters[HBF_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[HBF_FORMAT],
+                                "*.[Hh][Bb][Ff]");
+#endif
+
+#ifdef HAVE_FREETYPE
+    filename_filters[OTF_FORMAT] = gtk_file_filter_new();
+    gtk_file_filter_add_pattern(filename_filters[OTF_FORMAT],
+                                "*.[OoTt][Tt][FfCcEe]");
+#endif /* HAVE_FREETYPE */
+
+    filename_filters[0] = (GtkFileFilter *) 1;
+
+    /*
+     * Add a reference to all the filters so they don't cause a delayed crash
+     * when popping up the import dialog multiple times.
+     */
+    for (i = 1; i < 10; i++) {
+        if (filename_filters[i] != NULL)
+          g_object_ref(filename_filters[i]);
+    }
+}
+
+static gboolean
+export_font(gchar *filename, gbdfed_editor_t *ed, gboolean copy_filename)
+{
+    FILE *out;
+    bdf_font_t *font;
+    gboolean local_font = FALSE;
+    bdf_property_t vanity;
+    FontgridSelectionInfo sinfo;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    /*
+     * First, attempt to make a backup if they are specified.
+     */
+    if (options.backups) {
+        out = fopen(filename, "rb");
+        if (out != 0) {
+            fclose(out);
+
+            /*
+             * Attempt to make a backup.
+             */
+            sprintf(buffer2, "%s.bak", filename);
+
+            /*
+             * %PLATFORM_CHECK%
+             *
+             * Don't return here because we want to save the font even if a
+             * backup can't be made.
+             */
+            if (rename(filename, buffer2))
+              guiutil_error_message(ed->shell,
+                                    "Backups: Unable to make a backup.");
+        }
+    }
+
+    /*
+     * Try to open the file for writing. Only PSF needs binary.
+     */
+    out = (ed->export_format != PSF_FORMAT) ?
+        fopen(filename, "w") : fopen(filename, "wb");
+
+    if (out == 0) {
+        if (ed->export_format == BDF_FORMAT)
+          sprintf(buffer2, "Save Font: Unable to write to %s.", filename);
+        else
+          sprintf(buffer2, "Export Font: Unable to write to %s.", filename);
+        guiutil_error_message(ed->shell, buffer2);
+        return FALSE;
+    }
+
+    switch (ed->export_format) {
+      case BDF_FORMAT:
+        if (!font) {
+            /*
+             * We need to create a font with the default options so it
+             * can be written out as a skeleton.
+             */
+            font = bdf_new_font("unnamed",
+                                options.font_opts.point_size,
+                                options.font_opts.resolution_x,
+                                options.font_opts.resolution_y,
+                                options.font_opts.font_spacing,
+                                options.font_opts.bits_per_pixel);
+            local_font = TRUE;
+        }
+
+        /*
+         * Add a custom property if the font has been
+         */
+        if (font->modified || local_font == TRUE) {
+            sprintf(buffer2, "Edited with gbdfed %s.", GBDFED_VERSION);
+            vanity.name = "_GBDFED_INFO";
+            vanity.format = BDF_ATOM;
+            vanity.value.atom = buffer2;
+            bdf_add_font_property(font, &vanity);
+        }
+        bdf_save_font(out, font, &options.font_opts, 0, 0);
+        if (local_font == TRUE)
+          bdf_free_font(font);
+        break;
+      case HEX_FORMAT:
+        bdf_export_hex(out, font, &options.font_opts, 0, 0);
+        break;
+      case PSF_FORMAT:
+        sinfo.start = sinfo.end = 0;
+        (void) fontgrid_has_selection(FONTGRID(ed->fgrid), &sinfo);
+        if (sinfo.start == sinfo.end) {
+            sinfo.start = font->glyphs[0].encoding;
+            sinfo.end = font->glyphs[font->glyphs_used - 1].encoding;
+        }
+        switch (bdf_export_psf(out, font, &options.font_opts,
+                               sinfo.start, sinfo.end)) {
+          case BDF_OK:
+            buffer1[0] = 0;
+            break;
+          case BDF_BAD_RANGE:
+            sprintf(buffer1, "Export PSF: Invalid range %d-%d.\n",
+                    sinfo.start, sinfo.end);
+            break;
+          case BDF_PSF_CORRUPT_UTF8:
+            strcpy(buffer1,
+                   "Export PSF: Bad UTF-8 encountered in the mappings.");
+            break;
+        }
+        if (buffer1[0] != 0)
+          /*
+           * Something went wrong during the PSF export.
+           */
+          guiutil_error_message(ed->shell, buffer1);
+    }
+
+    fclose(out);
+
+    /*
+     * The rest of this only applies to BDF fonts and not PSF or HEX fonts.
+     * PSF and HEX fonts have their own extensions in the save dialog, but
+     * that does not affect the actual file name in the editor.
+     */
+    if (ed->export_format == BDF_FORMAT) {
+
+        /*
+         * Copy the path and filename into the editor if specified.
+         */
+        if (copy_filename) {
+            if (ed->path)
+              g_free(ed->path);
+            if (ed->file)
+              g_free(ed->file);
+            ed->path = ed->file = 0;
+            ed->file = g_path_get_basename(filename);
+            ed->path = g_path_get_dirname(filename);
+        }
+
+        /*
+         * Mark the font as being unmodified.
+         */
+        fontgrid_set_font_modified(FONTGRID(ed->fgrid), FALSE);
+
+        /*
+         * Update the window title accordingly.
+         */
+        if (ed->file)
+          sprintf(buffer1, "%s - %s", g_get_prgname(), ed->file);
+        else
+          sprintf(buffer1, "%s - (unnamed%d)", g_get_prgname(), ed->id);
+
+        gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+        /*
+         * Since the font was saved as BDF, it is no longer marked as being
+         * imported.
+         */
+        ed->imported = FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+really_save_font(guint ed_id)
+{
+    gbdfed_editor_t *ed = editors + ed_id;
+    gchar *fname;
+    FILE *have;
+#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10)
+    GtkRecentManager *recent;
+#endif
+
+    fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(ed->save_dialog));
+
+    have = fopen(fname, "rb");
+    if (have != 0) {
+        fclose(have);
+
+        /*
+         * Check to see if the user wishes to overwrite the existing font.
+         */
+        sprintf(buffer2, "Save Font: %s exists.\nDo you wish to overwrite?",
+                fname);
+        if (guiutil_yes_or_no(ed->shell, buffer2, TRUE) == FALSE) {
+            g_free(fname);
+            return;
+        }
+    }
+
+    /*
+     * If the write was successful, hide the dialog.
+     */
+    if (export_font(fname, ed, TRUE)) {
+        save_dialog_done = TRUE;
+        gtk_widget_hide(ed->save_dialog);
+#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10)
+        recent = gtk_recent_manager_get_default();
+        sprintf(buffer1, "file://%s", fname);
+        if (gtk_recent_manager_has_item(recent,
+                                        (const gchar *) buffer1) == FALSE)
+          gtk_recent_manager_add_item(recent,
+                                      (const gchar *) buffer1);
+#endif
+    }
+    g_free(fname);
+}
+
+/*
+ * This callback routine handles errors and updating the progress bar if
+ * one is being used.
+ */
+static void
+handle_import_messages(bdf_callback_struct_t *call_data, void *client_data)
+{
+    if (call_data->reason == BDF_ERROR) {
+        sprintf(buffer1, "Import Font:%d: error: See the font messages.",
+                call_data->errlineno);
+        guiutil_error_message(GTK_WIDGET(client_data), buffer1);
+    }
+}
+
+/**************************************************************************
+ *
+ * BDF section.
+ *
+ **************************************************************************/
+
+static void
+load_bdf_font(gbdfed_editor_t *ed, const gchar *fullpath, const gchar *dir,
+              const gchar *file)
+{
+    FILE *in;
+    bdf_font_t *font;
+
+    /*
+     * Check to see if the file can be opened.
+     */
+    if ((in = fopen(fullpath, "rb")) == 0) {
+        sprintf(buffer1, "Import Font: Unable to open %s.", file);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    if (ed->open_dialog != NULL)
+      guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+    font = bdf_load_font(in, &options.font_opts,
+                         handle_import_messages, (void *) ed->shell);
+
+    guiutil_busy_cursor(ed->shell, FALSE);
+    if (ed->open_dialog != NULL)
+      guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+    if (font == 0) {
+        fclose(in);
+        sprintf(buffer1, "Import Font: Unable to load %s.", file);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    fclose(in);
+    if (ed->open_dialog != NULL)
+      gtk_widget_hide(ed->open_dialog);
+
+    /*
+     * Delete the file and path names so they can be updated.
+     */
+    if (ed->file != 0)
+      g_free(ed->file);
+    if (ed->path != 0)
+      g_free(ed->path);
+
+    ed->file = ed->path = 0;
+
+    ed->file = strdup(file);
+    ed->path = strdup(dir);
+
+    /*
+     * Update the window title.
+     */
+    if (font->modified)
+      sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file);
+    else
+      sprintf(buffer1, "%s - %s", g_get_prgname(), ed->file);
+
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+    /*
+     * Tell the glyphtest widget to remove references to the current font if
+     * it has any, and redraw.
+     */
+    if (glyphtest != 0)
+      glyphtest_remove_font(GLYPHTEST(glyphtest),
+                            fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+    fontgrid_set_font(FONTGRID(ed->fgrid), font, -1);
+
+    /*
+     * Finally, update the font name field.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                       fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+
+    /*
+     * Make sure the imported flag is cleared in this case.
+     */
+    ed->imported = FALSE;
+}
+
+/**************************************************************************
+ *
+ * Console section.
+ *
+ **************************************************************************/
+
+static void
+load_console_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot,
+                  gchar *dir, gchar *file)
+{
+    FILE *in;
+    gbdfed_editor_t *ep;
+    gint i, j, nfonts, len;
+    gchar *np;
+    bdf_font_t *fonts[3];
+
+    /*
+     * Check to see if the file can be opened.
+     */
+    if ((in = fopen(fullpath, "rb")) == 0) {
+        sprintf(buffer1, "Import Font: Unable to open %s.", fullpath);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+    i = bdf_load_console_font(in, &options.font_opts, 0, 0, fonts, &nfonts);
+
+    guiutil_busy_cursor(ed->shell, FALSE);
+    guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+    fclose(in);
+
+    if (i != BDF_OK) {
+        /*
+         * Free up any font structures that happened to be loaded.
+         */
+        for (j = 0; j < nfonts; j++)
+          bdf_free_font(fonts[j]);
+
+        sprintf(buffer1, "Import Font: %s not a console font.", fullpath);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    gtk_widget_hide(ed->open_dialog);
+
+    /*
+     * Handle creation of the editors.  In the case of some console fonts,
+     * there are three different sizes contained in the font.
+     */
+    for (i = 0; i < nfonts; i++) {
+        if (i)
+          ep = editors + gbdfed_make_editor(0, FALSE);
+        else {
+            ep = ed;
+
+            /*
+             * Erase the existing file and directory name in the "root"
+             * editor.
+             */
+            if (ep->file)
+              g_free(ep->file);
+            if (ep->path)
+              g_free(ep->path);
+            ep->file = ep->path = 0;
+
+            /*
+             * Tell the glyphtest widget to remove references to the current
+             * font, if it has any, and redraw.
+             */
+            if (glyphtest != 0)
+              glyphtest_remove_font(GLYPHTEST(glyphtest),
+                                    fontgrid_get_font(FONTGRID(ep->fgrid)));
+        }
+
+        /*
+         * Make an XLFD name for the font using the filename.  Run through the
+         * file name and change all occurences of '-' to '_' to avoid problems
+         * with '-' being the XLFD field separator.
+         */
+        for (j = 0, np = file; np < dot; np++, j++)
+          buffer2[j] = (*np != '-') ? *np : '_';
+        buffer2[j] = 0;
+
+        fonts[i]->name =
+            bdf_make_xlfd_name(fonts[i], "Unknown", buffer2);
+        bdf_update_properties_from_name(fonts[i]);
+
+        len = (gint) (dot - file);
+
+        /*
+         * Create the default name for the font file.
+         */
+        if (nfonts == 3) {
+            switch (i) {
+              case 0:
+                sprintf(buffer1, "%.*s-16.bdf", len, file);
+                break;
+              case 1:
+                sprintf(buffer1, "%.*s-14.bdf", len, file);
+                break;
+              case 2:
+                sprintf(buffer1, "%.*s-08.bdf", len, file);
+                break;
+            }
+        } else
+          sprintf(buffer1, "%.*s.bdf", len, file);
+
+        /*
+         * Set the filename for the editor.
+         */
+        ep->file = strdup(buffer1);
+        ep->path = strdup(dir);
+
+        /*
+         * Set the new editor title.
+         */
+        sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ep->file);
+        gtk_window_set_title(GTK_WINDOW(ep->shell), buffer1);
+
+        /*
+         * Change the font in the editor.
+         */
+        fontgrid_set_font(FONTGRID(ep->fgrid), fonts[i], -1);
+
+        /*
+         * Indicate the font was imported.
+         */
+        ed->imported = TRUE;
+
+        /*
+         * Update the XLFD name.
+         */
+        gtk_entry_set_text(GTK_ENTRY(ep->fontname),
+                           fontgrid_get_font_name(FONTGRID(ep->fgrid)));
+    }
+}
+
+/**************************************************************************
+ *
+ * PK/GF section.
+ *
+ **************************************************************************/
+
+static void
+load_pkgf_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot,
+               gchar *dir, gchar *file)
+{
+    FILE *in;
+    gint i;
+    gchar *np;
+    bdf_font_t *font;
+
+    /*
+     * Check to see if the file can be opened.
+     */
+    if ((in = fopen(fullpath, "rb")) == 0) {
+        sprintf(buffer1, "Import Font: Unable to open %s.", file);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+    i = bdf_load_mf_font(in, &options.font_opts, 0, 0, &font);
+
+    guiutil_busy_cursor(ed->shell, FALSE);
+    guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+    fclose(in);
+
+    if (i != BDF_OK) {
+        sprintf(buffer1, "Import Font: %s not a PK or GF font.", fullpath);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    gtk_widget_hide(ed->open_dialog);
+
+    /*
+     * Make an XLFD name for the font using the filename.  Run through the
+     * file name and change all occurences of '-' to '_' to avoid problems
+     * with '-' being the XLFD field separator.
+     */
+    for (i = 0, np = file; np < dot; np++, i++)
+      buffer2[i] = (*np != '-') ? *np : '_';
+    buffer2[i] = 0;
+
+    font->name = bdf_make_xlfd_name(font, "Unknown", buffer2);
+    bdf_update_properties_from_name(font);
+
+    /*
+     * Now set up a file name.
+     */
+    sprintf(buffer1, "%.*s.bdf", (int) (dot - file), file);
+
+    /*
+     * Delete the file and path names so they can be updated.
+     */
+    if (ed->file != 0)
+      g_free(ed->file);
+    if (ed->path != 0)
+      g_free(ed->path);
+
+    ed->file = strdup(buffer1);
+    ed->path = strdup(dir);
+
+    /*
+     * Update the window title.
+     */
+    sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file);
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+    /*
+     * Tell the glyphtest widget to remove references to the current font if
+     * it has any, and redraw.
+     */
+    if (glyphtest != 0)
+      glyphtest_remove_font(GLYPHTEST(glyphtest),
+                            fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+    fontgrid_set_font(FONTGRID(ed->fgrid), font, -1);
+
+    /*
+     * Indicate the font was imported.
+     */
+    ed->imported = TRUE;
+
+    /*
+     * Finally, update the font name field.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                       fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+}
+
+/**************************************************************************
+ *
+ * FNT section.
+ *
+ **************************************************************************/
+
+/*
+ * Toggles the "Ok" button on or off depending if there was a selection or
+ * not.
+ */
+static void
+fnt_check_load_button(GtkWidget *w, gpointer data)
+{
+    GtkTreeSelection *sel = GTK_TREE_SELECTION(data);
+
+    if (gtk_tree_selection_count_selected_rows(sel) == 0)
+      gtk_widget_set_sensitive(fnt_load_button, FALSE);
+    else
+      gtk_widget_set_sensitive(fnt_load_button, TRUE);
+}
+
+static void
+fnt_unselect_all(GtkWidget *w, gpointer data)
+{
+    GtkTreeSelection *sel = GTK_TREE_SELECTION(data);
+
+    gtk_tree_selection_unselect_all(sel);
+
+    /*
+     * Disable the Ok button since everything is unselected.
+     */
+    gtk_widget_set_sensitive(fnt_load_button, FALSE);
+}
+
+static void
+fnt_select_all(GtkWidget *w, gpointer data)
+{
+    GtkTreeSelection *sel = GTK_TREE_SELECTION(data);
+
+    gtk_tree_selection_select_all(sel);
+
+    /*
+     * Enable the Ok button since everything is unselected.
+     */
+    gtk_widget_set_sensitive(fnt_load_button, TRUE);
+}
+
+static void
+fnt_foreach_selected(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+                     gpointer data)
+{
+    gint *id;
+
+    id = gtk_tree_path_get_indices(path);
+    fnt_selected[fnt_selected_count++] = *id;
+}
+
+static void
+fnt_load_selected_fonts(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ep, *ed = editors + GPOINTER_TO_UINT(data);
+    GtkTreeSelection *sel =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(fnt_font_list));
+    gint i, nfonts;
+    gboolean loaded;
+    bdf_font_t **fonts;
+    _bdffnt_callback_data_t *cdata;
+
+    fnt_selected_count = 0;
+
+    if ((cdata = g_object_get_data(G_OBJECT(w),
+                                   "bdffnt_callback_data")) == NULL) {
+        /*
+         * Big problem.  Should never happen.
+         */
+        guiutil_error_message(editors[0].shell,
+                              "BIG PROBLEM PASSING OPEN FON/FNT FONT!!!!");
+        return;
+    }
+
+    /*
+     * This collects all the selected indices from the list and puts them in
+     * the global fnt_selected array.
+     */
+    gtk_tree_selection_selected_foreach(sel, fnt_foreach_selected, NULL);
+
+    /*
+     * CHANGE - maybe.
+     */
+    if (fnt_selected_count == 0)
+      return;
+
+    /*
+     * Hide the dialog that allowed selection of the fonts in the file.
+     */
+    gtk_widget_hide(fnt_dialog);
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+    fonts = (bdf_font_t **)
+        g_malloc(sizeof(bdf_font_t *) * fnt_selected_count);
+    for (loaded = TRUE, nfonts = 0;
+         nfonts < fnt_selected_count && loaded == TRUE;
+         nfonts++) {
+        /*
+         * If the current font can't be loaded, then assume the rest are
+         * not available either.
+         */
+        if (bdffnt_load_font(cdata->font, fnt_selected[nfonts],
+                             0, 0, &fonts[nfonts]) != 0) {
+            /*
+             * It is easier to get the font name from the font than it is
+             * from the list store.
+             */
+            (void) bdffnt_get_facename(cdata->font, fnt_selected[nfonts], 0,
+                                       (unsigned char *) buffer1);
+            sprintf(buffer2, "Import Font: Unable to load %s from %s.",
+                    buffer1, cdata->file);
+            guiutil_error_message(ed->shell, buffer2);
+
+            guiutil_busy_cursor(ed->shell, FALSE);
+            guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+            loaded = FALSE;
+        }
+    }
+
+    guiutil_busy_cursor(ed->shell, FALSE);
+    guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+    /*
+     * If no fonts were loaded, then simply return with the open dialog still
+     * up, giving the user a chance to load another font.
+     */
+    if (nfonts == 0) {
+        g_free(fonts);
+        return;
+    }
+
+    /*
+     * Hide the open dialog.
+     */
+    gtk_widget_hide(ed->open_dialog);
+
+    /*
+     * Create the editors for the fonts that did get loaded.
+     */
+    for (i = 0; i < nfonts; i++) {
+        if (i)
+          ep = editors + gbdfed_make_editor(0, FALSE);
+        else {
+            ep = ed;
+
+            /*
+             * Erase the existing file and directory name in the "root"
+             * editor.
+             */
+            if (ep->file)
+              g_free(ep->file);
+            if (ep->path)
+              g_free(ep->path);
+            ep->file = ep->path = 0;
+
+            /*
+             * Tell the glyphtest widget to remove references to the current
+             * font, if it has any, and redraw.
+             */
+            if (glyphtest != 0)
+              glyphtest_remove_font(GLYPHTEST(glyphtest),
+                                    fontgrid_get_font(FONTGRID(ep->fgrid)));
+        }
+
+        /*
+         * Make the BDF file name for the font.
+         */
+        sprintf(buffer1, "%.*s%d.bdf", (int) (cdata->dot - cdata->file),
+                cdata->file, fonts[i]->point_size);
+
+        ep->file = strdup(buffer1);
+        ep->path = strdup(cdata->dir);
+
+        /*
+         * Set the new editor title.
+         */
+        sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ep->file);
+        gtk_window_set_title(GTK_WINDOW(ep->shell), buffer1);
+
+        /*
+         * Change the font in the editor.
+         */
+        fontgrid_set_font(FONTGRID(ep->fgrid), fonts[i], -1);
+
+        /*
+         * Indicate the font was imported.
+         */
+        ep->imported = TRUE;
+
+        /*
+         * Update the XLFD name.
+         */
+        gtk_entry_set_text(GTK_ENTRY(ep->fontname),
+                           fontgrid_get_font_name(FONTGRID(ep->fgrid)));
+    }
+
+    g_free(cdata->file);
+    g_free(cdata->dir);
+    bdffnt_close_font(cdata->font);
+
+    g_free(fonts);
+}
+
+static void
+fnt_cancel(GtkWidget *w, gpointer data)
+{
+    _bdffnt_callback_data_t *cdata;
+
+    /*
+     * If the load callback stole the data already, this will be NULL.
+     */
+    if ((cdata = g_object_get_data(G_OBJECT(w),
+                                   "bdffnt_callback_data")) == NULL) {
+        /*
+         * Big problem.  Should never happen.
+         */
+        guiutil_error_message(editors[0].shell,
+                              "BIG PROBLEM PASSING OPEN FON/FNT FONT!!!!");
+        return;
+    }
+
+    g_free(cdata->file);
+    g_free(cdata->dir);
+    bdffnt_close_font(cdata->font);
+
+    gtk_widget_hide(fnt_dialog);
+}
+
+static void
+fnt_row_activate(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col,
+                 gpointer data)
+{
+    fnt_load_selected_fonts(GTK_WIDGET(view), data);
+}
+
+static void
+load_windows_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot,
+                  gchar *dir, gchar *file)
+{
+    gint i, nfonts;
+    bdffnt_font_t fnt;
+    bdf_font_t *font;
+    _bdffnt_callback_data_t *cdata;
+    GtkWidget *button, *vbox, *hbox, *swin;
+    GtkListStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *cell_renderer;
+    GtkTreeSelection *sel;
+    GtkTreePath *tpath;
+    GtkTreeIter iter;
+
+    if (bdffnt_open_font(fullpath, &fnt) <= 0) {
+        sprintf(buffer1, "Import Font: Unable to open %s.", file);
+        guiutil_error_message(ed->shell, buffer1);
+        g_free(dir);
+        return;
+    }
+
+    nfonts = bdffnt_font_count(fnt);
+
+    if (nfonts == 1) {
+        guiutil_busy_cursor(ed->shell, TRUE);
+        guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+        if (bdffnt_load_font(fnt, 0, 0, 0, &font) != 0) {
+            sprintf(buffer1, "Import Font: Unable to load %s.", file);
+            guiutil_error_message(ed->shell, buffer1);
+            g_free(dir);
+
+            guiutil_busy_cursor(ed->shell, FALSE);
+            guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+            return;
+        }
+
+        guiutil_busy_cursor(ed->shell, FALSE);
+        guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+        gtk_widget_hide(ed->open_dialog);
+
+        /*
+         * Now set up a file name.
+         */
+        sprintf(buffer1, "%.*s.bdf", (int) (dot - file), file);
+
+        /*
+         * Delete the file and path names so they can be updated.
+         */
+        if (ed->file != 0)
+          g_free(ed->file);
+        if (ed->path != 0)
+          g_free(ed->path);
+
+        ed->file = strdup(buffer1);
+        ed->path = strdup(dir);
+
+        /*
+         * Update the window title.
+         */
+        sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file);
+        gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+        /*
+         * Tell the glyphtest widget to remove references to the current font
+         * if it has any, and redraw.
+         */
+        if (glyphtest != 0)
+          glyphtest_remove_font(GLYPHTEST(glyphtest),
+                                fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+        fontgrid_set_font(FONTGRID(ed->fgrid), font, -1);
+
+        /*
+         * Indicate the font was imported.
+         */
+        ed->imported = TRUE;
+
+        /*
+         * Finally, update the font name field.
+         */
+        gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                           fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+        return;
+    }
+
+    /*
+     * More than one font was found.  Present the dialog to choose the fonts.
+     */
+    if (fnt_dialog == 0) {
+        /*
+         * Create a structure that will hold data needed by a couple callback
+         * routines.
+         */
+        cdata = g_malloc(sizeof(_bdffnt_callback_data_t));
+        cdata->file = strdup(file);
+        cdata->dir = strdup(dir);
+        cdata->dot = cdata->file + (dot - file);
+        cdata->font = fnt;
+
+        fnt_dialog = gtk_dialog_new();
+        gtk_window_set_title(GTK_WINDOW(fnt_dialog), "Windows Font Selection");
+
+        g_object_set_data(G_OBJECT(fnt_dialog), "bdffnt_callback_data",
+                          (gpointer) cdata);
+
+        (void) g_signal_connect(G_OBJECT(fnt_dialog), "delete_event",
+                                G_CALLBACK(fnt_cancel), 0);
+
+        vbox = GTK_DIALOG(fnt_dialog)->vbox;
+        hbox = GTK_DIALOG(fnt_dialog)->action_area;
+
+        swin = gtk_scrolled_window_new(0, 0);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                       GTK_POLICY_AUTOMATIC,
+                                       GTK_POLICY_ALWAYS);
+
+        store = gtk_list_store_new(1, G_TYPE_STRING);
+        fnt_font_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+        g_object_unref(store);
+
+        g_object_set_data(G_OBJECT(fnt_font_list), "bdffnt_callback_data",
+                          (gpointer) cdata);
+
+        gtk_widget_set_size_request(fnt_font_list, -1, 160);
+
+        cell_renderer = gtk_cell_renderer_text_new();
+        column = gtk_tree_view_column_new_with_attributes("Fonts: 0",
+                                                          cell_renderer,
+                                                          "text", 0,
+                                                          NULL);
+
+        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column(GTK_TREE_VIEW(fnt_font_list), column);
+        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(fnt_font_list));
+
+        (void) g_signal_connect(G_OBJECT(sel), "changed",
+                                G_CALLBACK(fnt_check_load_button),
+                                (gpointer) sel);
+
+        gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
+
+        (void) g_signal_connect(G_OBJECT(fnt_font_list), "row_activated",
+                                G_CALLBACK(fnt_row_activate),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_container_add(GTK_CONTAINER(swin), fnt_font_list);
+
+        gtk_box_pack_start(GTK_BOX(vbox), swin, FALSE, FALSE, 0);
+
+        button = gtk_button_new_with_label("Select All");
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(fnt_select_all),
+                                (gpointer) sel);
+
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(fnt_unselect_all),
+                                (gpointer) sel);
+
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(fnt_cancel),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        fnt_load_button = gtk_button_new_from_stock(GTK_STOCK_OK);
+
+        /*
+         * Here we store a bunch of data to the buttons that are necessary to
+         * load FON/FNT fonts in the callback.
+         */
+        g_object_set_data(G_OBJECT(fnt_load_button), "bdffnt_callback_data",
+                          (gpointer) cdata);
+        g_object_set_data(G_OBJECT(button), "bdffnt_callback_data",
+                          (gpointer) cdata);
+
+        (void) g_signal_connect(G_OBJECT(fnt_load_button), "clicked",
+                                G_CALLBACK(fnt_load_selected_fonts),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_container_add(GTK_CONTAINER(hbox), fnt_load_button);
+
+        gtk_widget_show_all(vbox);
+        gtk_widget_show_all(hbox);
+    } else {
+        /*
+         * Fill the CDATA item in with the latest info.
+         */
+        cdata = g_object_get_data(G_OBJECT(fnt_load_button),
+                                  "bdffnt_callback_data");
+        cdata->file = strdup(file);
+        cdata->dir = strdup(dir);
+        cdata->dot = cdata->file + (dot - file);
+        cdata->font = fnt;
+    }
+
+    store =
+        GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(fnt_font_list)));
+    column = gtk_tree_view_get_column(GTK_TREE_VIEW(fnt_font_list), 0);
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(fnt_font_list));
+
+    /*
+     * Set the number of fonts.
+     */
+    sprintf(buffer1, "Fonts: %d", nfonts);
+    gtk_tree_view_column_set_title(column, buffer1);
+
+    /*
+     * Clear the list and add the font names.
+     */
+    gtk_list_store_clear(store);
+    for (i = 0; i < nfonts; i++) {
+        (void) bdffnt_get_facename(fnt, i, 0, (unsigned char *) buffer1);
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter, 0, buffer1, -1);
+    }
+
+    /*
+     * Force the first one to be selected by default.
+     */
+    tpath = gtk_tree_path_new_from_indices(0, -1);
+    gtk_tree_selection_select_path(sel, tpath);
+
+    /*
+     * Show the dialog and wait until the selection is done.
+     */
+    guiutil_show_dialog_centered(fnt_dialog, ed->shell);
+
+    /*
+     * Force the user to interact with this dialog before doing anything else.
+     */
+    gtk_window_set_modal(GTK_WINDOW(fnt_dialog), TRUE);
+}
+
+/**************************************************************************
+ *
+ * OTF section.
+ *
+ **************************************************************************/
+
+#ifdef HAVE_FREETYPE
+
+static void
+choose_otf_encoding(GtkTreeSelection *selection, gpointer data)
+{
+    gint *rows;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *tpath;
+
+    /*
+     * Get the row of the current selection.
+     */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+      return;
+    tpath = gtk_tree_model_get_path(model, &iter);
+    rows = gtk_tree_path_get_indices(tpath);
+    otf_eid_pos = (gint16) rows[0];
+}
+
+static void
+choose_otf_platform(GtkTreeSelection *selection, gpointer data)
+{
+    gchar *name;
+    gint i, ncmaps, sel, *rows;
+    gint16 pid, eid, lasteid;
+    GtkTreeModel *model;
+    GtkListStore *store;
+    GtkTreeIter iter;
+    GtkTreePath *tpath;
+    GtkTreeView *tview;
+
+    /*
+     * Get the row of the current selection.
+     */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+      return;
+    tpath = gtk_tree_model_get_path(model, &iter);
+    rows = gtk_tree_path_get_indices(tpath);
+    otf_pid_pos = (gint16) rows[0];
+
+    /*
+     * Clear the encoding list.
+     */
+    store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(otf_encodings)));
+    gtk_list_store_clear(store);
+
+    /*
+     * Collect the list of encoding IDs and put their names in the encoding
+     * list.
+     */
+    nencodings = 0;
+    ncmaps = face->num_charmaps;
+    for (lasteid = -1, sel = i = 0; i < ncmaps; i++) {
+        pid = face->charmaps[i]->platform_id;
+        eid = face->charmaps[i]->encoding_id;
+        if (pid == platforms[otf_pid_pos] && eid != lasteid) {
+            name = bdfotf_encoding_name(pid, eid);
+            if (strcmp(name, "ISO10646") == 0)
+              sel = nencodings;
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, name, -1);
+            encodings[nencodings++] = eid;
+            lasteid = eid;
+        }
+    }
+
+    /*
+     * Default the selection to the ISO10646 encoding.
+     */
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_encodings));
+    tpath = gtk_tree_path_new_from_indices(sel, -1);
+    gtk_tree_selection_select_path(selection, tpath);
+
+    /*
+     * Make sure the encoding is made visible.
+     */
+    tview = gtk_tree_selection_get_tree_view(selection);
+    gtk_tree_view_scroll_to_cell(tview, tpath, NULL, TRUE, 0.5, 0.5);
+}
+
+static void
+choose_otf(GtkTreeSelection *selection, gpointer data)
+{
+    gchar *name;
+    gint i, ncmaps, sel, row, *rows;
+    gint16 pid, eid, lastpid;
+    GtkTreeModel *model;
+    GtkListStore *store;
+    GtkTreeIter iter;
+    GtkTreePath *tpath;
+    GtkTreeView *tview;
+    GValue val;
+
+    /*
+     * This is called after the list is cleared as well, so return if there is
+     * no selection.
+     */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+      return;
+
+    /*
+     * Get the name of the face currently selected and it's index.  This is
+     * way more complicated than it should be.
+     */
+    (void) memset((char *) &val, 0, sizeof(GValue));
+    tpath = gtk_tree_model_get_path(model, &iter);
+    rows = gtk_tree_path_get_indices(tpath);
+    row = rows[0];
+
+    /*
+     * Clear the platform list before trying to open the new face.
+     */
+    store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(otf_platforms)));
+    gtk_list_store_clear(store);
+
+    if (otf_collection) {
+        if (otf_face_open)
+          FT_Done_Face(face);
+        if (FT_New_Face(library, otf_fullpath, row, &face)) {
+            otf_face_open = FALSE;
+            gtk_tree_selection_get_selected(selection, &model, &iter);
+            gtk_tree_model_get_value(model, &iter, 0, &val);
+            name = (gchar *) g_value_get_string(&val);
+            sprintf(buffer1,
+                    "Import Font: Unable to open OpenType collection %s.",
+                    name);
+            g_value_unset(&val);
+            guiutil_error_message(active_editor->shell, buffer1);
+            return;
+        }
+        otf_face_open = TRUE;
+    }
+
+    /*
+     * Collect the list of platform IDs and put their names in the platform
+     * list.
+     */
+    nplatforms = 0;
+    ncmaps = face->num_charmaps;
+    for (lastpid = -1, sel = i = 0; i < ncmaps; i++) {
+        pid = face->charmaps[i]->platform_id;
+        eid = face->charmaps[i]->encoding_id;
+        if (pid != lastpid) {
+            /*
+             * Add the platform name to the list.  If the name happens to be
+             * Microsoft, select it as the default.
+             */
+            name = bdfotf_platform_name(pid);
+            if (strcmp(name, "Microsoft") == 0)
+              sel = nplatforms;
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, name, -1);
+            platforms[nplatforms++] = pid;
+            lastpid = pid;
+        }
+    }
+
+    /*
+     * Select the default platform, which is hard-coded to be Microsoft at the
+     * moment.
+     */
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_platforms));
+    tpath = gtk_tree_path_new_from_indices(sel, -1);
+    gtk_tree_selection_select_path(selection, tpath);
+
+    /*
+     * Make sure the platform is made visible.
+     */
+    tview = gtk_tree_selection_get_tree_view(selection);
+    gtk_tree_view_scroll_to_cell(tview, tpath, NULL, TRUE, 0.5, 0.5);
+}
+
+static void
+otf_dialog_done(GtkWidget *w, gpointer data)
+{
+    otf_select_done = GPOINTER_TO_INT(data);
+    gtk_widget_hide(otf_dialog);
+}
+
+static void
+otf_reset_metrics(GtkWidget *w, gpointer data)
+{
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_point_size),
+                              (gfloat) options.font_opts.point_size);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_hres),
+                              (gfloat) options.font_opts.resolution_x);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_vres),
+                              (gfloat) options.font_opts.resolution_y);
+}
+
+/*
+ * Synchronize the vertical resolution with the horizontal resolution.
+ */
+static void
+otf_sync_res(GtkWidget *w, GdkEventFocus *ev, gpointer data)
+{
+    gfloat v;
+    GtkSpinButton *b;
+
+    b = GTK_SPIN_BUTTON(data);
+    v = (gfloat) gtk_spin_button_get_value(b);
+
+    if (v != (gfloat) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w)))
+      gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+}
+
+static void
+make_otf_import_dialog(void)
+{
+    GtkWidget *label, *vbox, *hbox, *button, *table, *swin;
+    GtkAdjustment *adj;
+    GtkListStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *cell_renderer;
+    GtkTreeSelection *sel;
+    GList *fchain;
+
+    otf_dialog = gtk_dialog_new();
+    gtk_window_set_title(GTK_WINDOW(otf_dialog), "OpenType Selection");
+
+    (void) g_signal_connect(G_OBJECT(otf_dialog), "delete_event",
+                            G_CALLBACK(gtk_widget_hide), 0);
+
+    vbox = GTK_DIALOG(otf_dialog)->vbox;
+
+    swin = gtk_scrolled_window_new(0, 0);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_ALWAYS);
+
+    store = gtk_list_store_new(1, G_TYPE_STRING);
+    otf_faces = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+    g_object_unref(store);
+
+    cell_renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes ("Faces",
+                                                       cell_renderer,
+                                                       "text", 0,
+                                                       NULL);
+
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+    gtk_tree_view_append_column (GTK_TREE_VIEW(otf_faces), column);
+
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_faces));
+    gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+
+    (void) g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(choose_otf),
+                            NULL);
+
+    /*
+     * Set the size of the list explicitly to make enough space for
+     * approximately five entries.
+     */
+    gtk_widget_set_size_request(otf_faces, -1, 100);
+
+    gtk_container_add(GTK_CONTAINER(swin), otf_faces);
+
+    gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0);
+
+    /*
+     * Create a table to hold the other two lists.
+     */
+    table = gtk_table_new(1, 2, TRUE);
+
+    swin = gtk_scrolled_window_new(0, 0);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_ALWAYS);
+
+    store = gtk_list_store_new(1, G_TYPE_STRING);
+    otf_platforms = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+    g_object_unref(store);
+
+    cell_renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes("Platforms",
+                                                      cell_renderer,
+                                                      "text", 0,
+                                                      NULL);
+
+    gtk_widget_set_size_request(otf_platforms, 200, 70);
+
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(otf_platforms), column);
+
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_platforms));
+    gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+
+    (void) g_signal_connect(G_OBJECT(sel), "changed",
+                            G_CALLBACK(choose_otf_platform), NULL);
+
+    gtk_container_add(GTK_CONTAINER(swin), otf_platforms);
+
+    /*
+     * Attach the platform list to the table.
+     */
+    gtk_table_attach(GTK_TABLE(table), swin, 0, 1, 0, 1,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+    swin = gtk_scrolled_window_new(0, 0);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_ALWAYS);
+
+    store = gtk_list_store_new(1, G_TYPE_STRING);
+    otf_encodings = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+    g_object_unref(store);
+
+    cell_renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes("Encodings",
+                                                      cell_renderer,
+                                                      "text", 0,
+                                                      NULL);
+
+    gtk_widget_set_size_request(otf_encodings, 200, 70);
+
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(otf_encodings), column);
+
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_encodings));
+    gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+
+    (void) g_signal_connect(G_OBJECT(sel), "changed",
+                            G_CALLBACK(choose_otf_encoding), NULL);
+
+    gtk_container_add(GTK_CONTAINER(swin), otf_encodings);
+
+    /*
+     * Attach the encodings list to the table.
+     */
+    gtk_table_attach(GTK_TABLE(table), swin, 1, 2, 0, 1,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
+
+    /*
+     * Make a table that will contain the point size and resolution
+     * spin buttons.
+     */
+    table = gtk_table_new(3, 3, FALSE);
+
+    /*
+     * Make the spin button labels.
+     */
+    label = gtk_label_new("Point Size:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL,
+                     5, 5);
+    label = gtk_label_new("Horizontal Resolution:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL,
+                     5, 5);
+    label = gtk_label_new("Vertical Resolution:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL,
+                     5, 5);
+
+    /*
+     * Make the spin buttons.
+     */
+    adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 4.0, 256.0, 1.0, 2.0, 0.0);
+    otf_point_size = gtk_spin_button_new(adj, 1.0, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(otf_point_size), TRUE);
+    gtk_widget_set_size_request(otf_point_size, 100, -1);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_point_size),
+                              (gfloat) options.font_opts.point_size);
+    gtk_table_attach(GTK_TABLE(table), otf_point_size, 1, 2, 0, 1,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 72.0, 1200.0,
+                                               1.0, 10.0, 0.0);
+    otf_hres = gtk_spin_button_new(adj, 1.0, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(otf_hres), TRUE);
+    gtk_widget_set_size_request(otf_hres, 100, -1);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_hres),
+                              (gfloat) options.font_opts.resolution_x);
+    gtk_table_attach(GTK_TABLE(table), otf_hres, 1, 2, 1, 2,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 72.0, 1200.0,
+                                               1.0, 10.0, 0.0);
+    otf_vres = gtk_spin_button_new(adj, 1.0, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(otf_vres), TRUE);
+    gtk_widget_set_size_request(otf_vres, 100, -1);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_vres),
+                              (gfloat) options.font_opts.resolution_y);
+    (void) g_signal_connect(G_OBJECT(otf_vres), "focus-in-event",
+                            G_CALLBACK(otf_sync_res),
+                            (gpointer) otf_hres);
+    (void) g_signal_connect(G_OBJECT(otf_hres), "focus-in-event",
+                            G_CALLBACK(otf_sync_res),
+                            (gpointer) otf_vres);
+    gtk_table_attach(GTK_TABLE(table), otf_vres, 1, 2, 2, 3,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    /*
+     * Make the reset button.
+     */
+    label = gtk_button_new_with_label("Reset");
+    (void) g_signal_connect(G_OBJECT(label), "clicked",
+                            G_CALLBACK(otf_reset_metrics), 0);
+    gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL,
+                     10, 0);
+
+    /*
+     * Do some fiddling to adjust the focus chain so the Reset button is at
+     * the end instead of in the middle.
+     */
+    fchain = g_list_append(NULL, (gpointer) otf_point_size);
+    fchain = g_list_append(fchain, (gpointer) otf_hres);
+    fchain = g_list_append(fchain, (gpointer) otf_vres);
+    fchain = g_list_append(fchain, (gpointer) label);
+    gtk_container_set_focus_chain(GTK_CONTAINER(table), fchain);
+    g_list_free(fchain);
+
+    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, FALSE, 10);
+
+    /*
+     * Add the buttons at the bottom of the dialog.
+     */
+    hbox = GTK_DIALOG(otf_dialog)->action_area;
+
+    button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    gtk_container_add(GTK_CONTAINER(hbox), button);
+    (void) g_signal_connect(G_OBJECT(button), "clicked",
+                            G_CALLBACK(otf_dialog_done),
+                            GINT_TO_POINTER(-1));
+    button = gtk_button_new_from_stock(GTK_STOCK_OK);
+    gtk_container_add(GTK_CONTAINER(hbox), button);
+    (void) g_signal_connect(G_OBJECT(button), "clicked",
+                            G_CALLBACK(otf_dialog_done),
+                            GINT_TO_POINTER(1));
+
+    gtk_widget_show_all(vbox);
+    gtk_widget_show_all(hbox);
+}
+
+static void
+load_otf_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot,
+              gchar *dir, gchar *file)
+{
+    gint i, res;
+    gint32 psize, hres, vres;
+    gchar *np;
+    bdf_font_t *font;
+    bdf_property_t prop;
+    GtkListStore *store;
+    GtkTreeIter iter;
+
+    active_editor = ed;
+    otf_fullpath = fullpath;
+
+    /*
+     * Determine if this is an OT collection or just a normal font.
+     */
+    np = dot + strlen(dot) - 1;
+    otf_collection = (*np == 'c' || *np == 'C') ? TRUE : FALSE;
+
+    /*
+     * Initialize the FreeType engine once.
+     */
+    if (!ftinit) {
+        if (FT_Init_FreeType(&library) != 0) {
+            strcpy(buffer1,
+                   "Import Font: Unable to initialize the FreeType engine.");
+            guiutil_error_message(ed->shell, buffer1);
+            return;
+        }
+        ftinit = TRUE;
+    }
+
+    /*
+     * Attempt to open the font or collection.
+     */
+    if (FT_New_Face(library, fullpath, 0, &face)) {
+        if (!otf_collection)
+          sprintf(buffer1, "Import Font: Unable to open OpenType font '%s'.",
+                  file);
+        else
+          sprintf(buffer1,
+                  "Import Font: Unable to open OpenType collection '%s'.",
+                  file);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    /*
+     * Construct the dialog that will display various choices that will be
+     * needed when loading the font.
+     */
+    if (otf_dialog == 0)
+      make_otf_import_dialog();
+
+    /*
+     * Clear the lists and reset the values.
+     */
+    store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(otf_faces)));
+    gtk_list_store_clear(store);
+
+    otf_face_open = TRUE;
+    otf_collection = face->num_faces;
+    np = buffer1;
+
+    if (otf_collection == 1) {
+        if (bdfotf_get_english_string(face, BDFOTF_FULLNAME_STRING,
+                                      0, buffer1) == 0)
+          (void) strcpy(buffer1, "Unknown");
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter, 0, buffer1, -1);
+    } else {
+        otf_face_open = FALSE;
+        FT_Done_Face(face);
+        for (i = 0; i < otf_collection; i++) {
+            if (!FT_New_Face(library, fullpath, i, &face)) {
+                if (bdfotf_get_english_string(face, BDFOTF_FULLNAME_STRING,
+                                              0, buffer1) == 0)
+                  sprintf(buffer1, "Unknown%d", i);
+
+                gtk_list_store_append(store, &iter);
+                gtk_list_store_set(store, &iter, 0, buffer1, -1);
+
+                FT_Done_Face(face);
+            }
+        }
+    }
+
+    guiutil_show_dialog_centered(otf_dialog, ed->shell);
+
+    /*
+     * Force the user to interact with this dialog before doing anything else.
+     */
+    gtk_window_set_modal(GTK_WINDOW(otf_dialog), TRUE);
+
+    otf_select_done = 0;
+    while (otf_select_done == 0)
+      gtk_main_iteration();
+
+    /*
+     * Reinitialize various globals when we are done.
+     */
+    active_editor = 0;
+    otf_fullpath = 0;
+
+    if (otf_select_done < 0) {
+        if (otf_face_open)
+          FT_Done_Face(face);
+        otf_face_open = FALSE;
+        return;
+    }
+
+    /*
+     * Get the requested point size and resolutions.
+     */
+    psize = (gint32)
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(otf_point_size));
+    hres = (gint32)
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(otf_hres));
+    vres = (gint32)
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(otf_vres));
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+    /*
+     * Actually store the resolution and point size to the options so they
+     * will be used for other imports.  The setup dialog will unfortunately
+     * assume this are the default values.  May fix later so setup knows the
+     * values changed.
+     */
+    options.font_opts.point_size = psize;
+    options.font_opts.resolution_x = hres;
+    options.font_opts.resolution_y = vres;
+
+    /*
+     * Actually load the font.
+     */
+    res = bdfotf_load_font(face, platforms[otf_pid_pos],
+                           encodings[otf_eid_pos], &options.font_opts,
+                           0, 0, &font);
+
+    guiutil_busy_cursor(ed->shell, FALSE);
+    guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+    FT_Done_Face(face);
+    otf_face_open = FALSE;
+
+    if (!res) {
+        /*
+         * Make an error message.
+         */
+        sprintf(buffer1, "Import Font: Unable to load OpenType font %s.",
+                file);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    /*
+     * Hide the open dialog.
+     */
+    gtk_widget_hide(ed->open_dialog);
+
+    /*
+     * Add the _OTF_FONTFILE property using the original filename.
+     */
+    prop.name = "_OTF_FONTFILE";
+    prop.format = BDF_ATOM;
+    prop.value.atom = file;
+    bdf_add_font_property(font, &prop);
+
+    /*
+     * Now set up a file name.
+     */
+    sprintf(buffer1, "%.*s.bdf", dot - file, file);
+
+    /*
+     * Delete the file and path names so they can be updated.
+     */
+    if (ed->file != 0)
+      g_free(ed->file);
+    if (ed->path != 0)
+      g_free(ed->path);
+
+    ed->file = strdup(buffer1);
+    ed->path = strdup(dir);
+
+    /*
+     * Update the window title.
+     */
+    sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file);
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+    /*
+     * Tell the glyphtest widget to remove references to
+     * the current font if it has any, and redraw.
+     */
+    if (glyphtest != 0)
+      glyphtest_remove_font(GLYPHTEST(glyphtest),
+                            fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+    fontgrid_set_font(FONTGRID(ed->fgrid), font, -1);
+
+    /*
+     * Indicate the font was imported.
+     */
+    ed->imported = TRUE;
+
+    /*
+     * Finally, update the font name field.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                       fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+}
+
+#endif /* HAVE_FREETYPE */
+
+#ifdef HAVE_HBF
+
+/**************************************************************************
+ *
+ * HBF section.
+ *
+ **************************************************************************/
+
+static void
+load_hbf_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot,
+              gchar *dir, gchar *file)
+{
+    bdf_font_t *font;
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    guiutil_busy_cursor(ed->open_dialog, TRUE);
+
+    font = bdf_load_hbf_font(fullpath, &options.font_opts, 0, 0);
+
+    guiutil_busy_cursor(ed->shell, FALSE);
+    guiutil_busy_cursor(ed->open_dialog, FALSE);
+
+    /*
+     * Check to see if the file can be opened.
+     */
+    if (font == 0) {
+        g_free(dir);
+        sprintf(buffer1, "Import Font: Unable to import %s.", file);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    gtk_widget_hide(ed->open_dialog);
+
+    /*
+     * Now set up a file name.
+     */
+    sprintf(buffer1, "%.*s.bdf", (int) (dot - file), file);
+
+    /*
+     * Delete the file and path names so they can be updated.
+     */
+    if (ed->file != 0)
+      g_free(ed->file);
+    if (ed->path != 0)
+      g_free(ed->path);
+
+    ed->file = strdup(buffer1);
+    ed->path = dir;
+
+    /*
+     * Update the window title.
+     */
+    sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file);
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+    /*
+     * Tell the glyphtest widget to remove references to
+     * the current font if it has any, and redraw.
+     */
+    if (glyphtest != 0)
+      glyphtest_remove_font(GLYPHTEST(glyphtest),
+                            fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+    fontgrid_set_font(FONTGRID(ed->fgrid), font, -1);
+
+    /*
+     * Indicate the font was imported.
+     */
+    ed->imported = TRUE;
+
+    /*
+     * Finally, update the font name field.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                       fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+}
+
+#endif
+
+/*
+ * This routine actually does the work of opening the font.
+ */
+static void
+really_open_font(guint ed_id)
+{
+    gbdfed_editor_t *ed = editors + ed_id;
+    gchar *filename, *path, *file, *dot;
+    GtkFileChooser *fs;
+#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10)
+    GtkRecentManager *recent;
+#endif
+
+    fs = GTK_FILE_CHOOSER(ed->open_dialog);
+    filename = gtk_file_chooser_get_filename(fs);
+
+    /*
+     * Split the filename into path and file, locate the extension position in
+     * the file name, and make a version of the name with no '-' characters
+     * which are field separators in XLFD font names.
+     */
+    file = g_path_get_basename(filename);
+    path = g_path_get_dirname(filename);
+    if ((dot = strrchr(file, '.')) == 0)
+      dot = file + strlen(file);
+
+    /*
+     * If the last character of the filename is a slash, no file name was
+     * provided.
+     */
+    if (filename[strlen(filename) - 1] == G_DIR_SEPARATOR) {
+        guiutil_error_message(ed->shell,
+                              "Import Font: No file name provided.");
+        if (path)
+          g_free(path);
+        if (file)
+          g_free(file);
+        g_free(filename);
+        return;
+    }
+
+#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10)
+    recent = gtk_recent_manager_get_default();
+    sprintf(buffer1, "file://%s", filename);
+    if (gtk_recent_manager_has_item(recent,
+                                    (const gchar *) buffer1) == FALSE)
+      gtk_recent_manager_add_item(recent,
+                                  (const gchar *) buffer1);
+#endif
+
+    switch (ed->import_format) {
+      case BDF_FORMAT:
+        load_bdf_font(ed, (const gchar *) filename, (const gchar *) path,
+                      (const gchar *) file);
+        break;
+      case CONSOLE_FORMAT:
+        load_console_font(ed, filename, dot, path, file);
+        break;
+      case PKGF_FORMAT:
+        load_pkgf_font(ed, filename, dot, path, file);
+        break;
+      case FNT_FORMAT:
+        load_windows_font(ed, filename, dot, path, file);
+        break;
+#ifdef HAVE_HBF
+      case HBF_FORMAT:
+        load_hbf_font(ed, filename, dot, path, file);
+        break;
+#endif
+#ifdef HAVE_FREETYPE
+      case OTF_FORMAT:
+        load_otf_font(ed, filename, dot, path, file);
+        break;
+#endif /* HAVE_FREETYPE */
+    }
+
+    if (path)
+      g_free(path);
+    if (file)
+      g_free(file);
+
+    g_free(filename);
+
+    /*
+     * In case the editor list changed, set the pointer to the editor again.
+     */
+    ed = editors + ed_id;
+
+    /*
+     * Force the editor's info to be updated for the new font.  This causes
+     * it to change if it is already visible.
+     */
+    guiedit_update_font_info(ed);
+}
+
+static gchar *
+make_file_dialog_title(guint type, gboolean save)
+{
+    gchar *title = 0;
+
+    switch (type) {
+      case BDF_FORMAT: title = "BDF"; break;
+      case CONSOLE_FORMAT: title = "Console"; break;
+      case PKGF_FORMAT: title = "PK/GF"; break;
+      case FNT_FORMAT: title = "Windows"; break;
+#ifdef HAVE_HBF
+      case HBF_FORMAT: title = "HBF"; break;
+#endif
+      case OTF_FORMAT: title = "TrueType"; break;
+      case PSF_FORMAT: title = "PSF"; break;
+      case HEX_FORMAT: title = "HEX"; break;
+    }
+
+    if (save) {
+        if (type == BDF_FORMAT)
+          sprintf(buffer1, "Save %s Font", title);
+        else
+          sprintf(buffer1, "Export %s Font", title);
+    } else
+      sprintf(buffer1, "Open %s Font", title);
+
+    return buffer1;
+}
+
+static void
+handle_open_response(GtkDialog *d, gint response, gpointer data)
+{
+    switch (response) {
+      case GTK_RESPONSE_ACCEPT:
+        really_open_font(GPOINTER_TO_UINT(data));
+        break;
+      case GTK_RESPONSE_CANCEL:
+        gtk_widget_hide(GTK_WIDGET(d));
+        break;
+    }
+}
+
+static void
+update_open_dialog(gbdfed_editor_t *ed, guint type)
+{
+    GtkFileChooser *fs;
+
+    if (ed->open_dialog == 0) {
+        /*
+         * Create the file chooser filters if they haven't already been
+         * created.
+         */
+        make_file_chooser_filters();
+
+        ed->open_dialog =
+            gtk_file_chooser_dialog_new(make_file_dialog_title(type, FALSE),
+                                        GTK_WINDOW(ed->shell),
+                                        GTK_FILE_CHOOSER_ACTION_OPEN,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                        NULL);
+
+        (void) g_signal_connect(G_OBJECT(ed->open_dialog), "response",
+                                G_CALLBACK(handle_open_response),
+                                GUINT_TO_POINTER(ed->id));
+        (void) g_signal_connect(G_OBJECT(ed->open_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+    } else if (ed->import_format != type)
+      gtk_window_set_title(GTK_WINDOW(ed->open_dialog),
+                           make_file_dialog_title(type, FALSE));
+    fs = GTK_FILE_CHOOSER(ed->open_dialog);
+
+    /*
+     * Set the file filter.
+     */
+    gtk_file_chooser_set_filter(fs, filename_filters[type]);
+
+    ed->import_format = type;
+
+    /*
+     * Set the initial path as a file if it exists.  This is necessary to
+     * force the open to occur in the directory where this font was last
+     * saved, which might be different than the directory that is currently in
+     * the open file selection dialog.
+     */
+    if (ed->path != 0 && ed->path[0] == G_DIR_SEPARATOR)
+      gtk_file_chooser_set_current_folder(fs, ed->path);
+}
+
+static void
+hide_save_dialog(GtkWidget *w, gpointer data)
+{
+    gtk_widget_hide(w);
+    save_dialog_done = TRUE;
+}
+
+static void
+set_psf_option(GtkWidget *w, gpointer data)
+{
+    guint flags = 0;
+    gint dotpos;
+    gchar *fname, *dot, *slash, *suff = 0;
+    GtkFileChooser *fs;
+
+    switch (gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
+      case 0:
+        flags = BDF_PSF_UNIMAP|BDF_PSF_FONT;
+        break;
+      case 1:
+        flags = BDF_PSF_FONT;
+        break;
+      case 2:
+        flags = BDF_PSF_UNIMAP;
+        break;
+    }
+
+    if (flags == BDF_PSF_UNIMAP)
+      /*
+       * Have to change to the .uni suffix.
+       */
+      suff = ".uni";
+    else if (options.font_opts.psf_flags == BDF_PSF_UNIMAP)
+      /*
+       * Have to change back to the .psfu suffix.
+       */
+      suff = ".psfu";
+
+    options.font_opts.psf_flags = flags;
+
+    if (suff) {
+        /*
+         * Change the suffix on the filename in the entry area.
+         */
+        fs = GTK_FILE_CHOOSER(g_object_get_data(G_OBJECT(w),
+                                                  "file-selection-dialog"));
+        fname = gtk_file_chooser_get_filename(fs);
+
+        slash = fname;
+        if ((dot = (gchar *) strrchr(fname, '.')) != 0) {
+            if ((slash = (gchar *) strrchr(fname, G_DIR_SEPARATOR)) == 0)
+              slash = fname;
+            dotpos = (gint) (dot - slash) - 1;
+
+            /*
+             * Copy the new extension in.
+             */
+            (void) strcpy(dot, suff);
+        } else
+          dotpos = -1;
+
+        if (*slash == G_DIR_SEPARATOR)
+          *slash++ = 0;
+
+        gtk_file_chooser_set_current_name(fs, slash);
+        g_free(fname);
+    }
+}
+
+static void
+handle_save_response(GtkDialog *d, gint response, gpointer data)
+{
+    switch (response) {
+      case GTK_RESPONSE_ACCEPT:
+        really_save_font(GPOINTER_TO_UINT(data));
+        break;
+      case GTK_RESPONSE_CANCEL:
+        gtk_widget_hide(GTK_WIDGET(d));
+        break;
+    }
+}
+
+static void
+update_save_dialog(gbdfed_editor_t *ed, guint type)
+{
+    GtkWidget *vbox;
+    gchar *dot, *slash;
+    gint dotpos;
+
+    if (ed->save_dialog == 0) {
+        /*
+         * Create the file chooser filters if they haven't already been
+         * created.
+         */
+        make_file_chooser_filters();
+
+        ed->save_dialog =
+            gtk_file_chooser_dialog_new(make_file_dialog_title(type, TRUE),
+                                        GTK_WINDOW(ed->shell),
+                                        GTK_FILE_CHOOSER_ACTION_SAVE,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                        NULL);
+
+        vbox = GTK_DIALOG(ed->save_dialog)->vbox;
+
+        psf_export_options = gtk_combo_box_new_text();
+        /*
+         * Since the flags have to be set in the save dialog, attach the
+         * save dialog to the object so we can set the bits appropriately.
+         */
+        g_object_set_data(G_OBJECT(psf_export_options),
+                          "file-selection-dialog",
+                          (gpointer) ed->save_dialog);
+        (void) g_signal_connect(G_OBJECT(psf_export_options), "changed",
+                                G_CALLBACK(set_psf_option), 0);
+        gtk_combo_box_append_text(GTK_COMBO_BOX(psf_export_options),
+                                  "Font and Unicode Map");
+        gtk_combo_box_append_text(GTK_COMBO_BOX(psf_export_options),
+                                  "Font Only");
+        gtk_combo_box_append_text(GTK_COMBO_BOX(psf_export_options),
+                                  "Unicode Map Only");
+        gtk_combo_box_set_active(GTK_COMBO_BOX(psf_export_options), 0);
+
+        psf_export_frame = labcon_new_label_defaults("PSF Export Options:",
+                                                     psf_export_options, 0);
+
+        (void) g_signal_connect(G_OBJECT(ed->save_dialog), "delete_event",
+                                G_CALLBACK(hide_save_dialog), 0);
+
+        (void) g_signal_connect(G_OBJECT(ed->save_dialog), "response",
+                                G_CALLBACK(handle_save_response),
+                                GUINT_TO_POINTER(ed->id));
+
+        gtk_widget_show_all(psf_export_frame);
+        gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(ed->save_dialog),
+                                          psf_export_frame);
+    } else if (ed->export_format != type)
+      gtk_window_set_title(GTK_WINDOW(ed->save_dialog),
+                           make_file_dialog_title(type, TRUE));
+
+    /*
+     * Set the file filter.
+     */
+    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(ed->save_dialog),
+                                filename_filters[type]);
+
+
+    ed->export_format = type;
+
+    /*
+     * Show or hide the PSF exporting options.
+     */
+    if (type == PSF_FORMAT)
+      gtk_widget_show(psf_export_frame);
+    else
+      gtk_widget_hide(psf_export_frame);
+
+    /*
+     * Use the current path and filename as the default.  This is done in case
+     * the font was loaded from some directory other than the current default
+     * in the file selection dialog for saving.
+     */
+    if (ed->file != 0)
+      sprintf(buffer1, "%s", ed->file);
+    else
+      sprintf(buffer1, "unnamed%d.bdf", ed->id);
+
+    if ((dot = (gchar *) strrchr(buffer1, '.'))) {
+        if ((slash = (gchar *) strrchr(buffer1, G_DIR_SEPARATOR)) == 0)
+          slash = buffer1;
+        dotpos = (gint) (dot - slash) - 1;
+
+        /*
+         * If a PSF or HEX font is being exported, change the extension
+         * here.
+         */
+        if (type == PSF_FORMAT)
+          (void) strcpy(dot, ".psfu");
+        else if (type == HEX_FORMAT)
+          (void) strcpy(dot, ".hex");
+
+    } else
+      dotpos = -1;
+
+    gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(ed->save_dialog),
+                                      buffer1);
+    if (ed->path != 0 && ed->path[0] == G_DIR_SEPARATOR)
+      gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(ed->save_dialog),
+                                          ed->path);
+#if 0
+    gtk_editable_set_position(GTK_EDITABLE(fs->selection_entry), dotpos);
+    gtk_editable_select_region(GTK_EDITABLE(fs->selection_entry), 0, dotpos);
+#endif
+}
+
+void
+guifile_import_bdf_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    update_open_dialog(ed, BDF_FORMAT);
+
+    guiutil_show_dialog_centered(ed->open_dialog, ed->shell);
+}
+
+void
+guifile_import_console_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    update_open_dialog(ed, CONSOLE_FORMAT);
+
+    guiutil_show_dialog_centered(ed->open_dialog, ed->shell);
+}
+
+void
+guifile_import_pkgf_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    update_open_dialog(ed, PKGF_FORMAT);
+
+    guiutil_show_dialog_centered(ed->open_dialog, ed->shell);
+}
+
+void
+guifile_import_windows_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    update_open_dialog(ed, FNT_FORMAT);
+
+    guiutil_show_dialog_centered(ed->open_dialog, ed->shell);
+}
+
+#ifdef HAVE_HBF
+
+void
+guifile_import_hbf_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    update_open_dialog(ed, HBF_FORMAT);
+
+    guiutil_show_dialog_centered(ed->open_dialog, ed->shell);
+}
+
+#endif
+
+#ifdef HAVE_FREETYPE
+
+void
+guifile_import_otf_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    update_open_dialog(ed, OTF_FORMAT);
+
+    guiutil_show_dialog_centered(ed->open_dialog, ed->shell);
+}
+
+#endif /* HAVE_FREETYPE */
+
+/**************************************************************************
+ *
+ * X server section.
+ *
+ **************************************************************************/
+
+#ifdef HAVE_XLIB
+/*
+ * Only compile this in if it is being built for machine running X.
+ */
+
+static void
+xsrv_filter(GtkWidget *w, gpointer data)
+{
+    gchar *pattern, **fonts;
+    gint i, nfonts;
+    GtkListStore *store;
+    GtkTreeViewColumn *col;
+    GtkTreeIter iter;
+
+    pattern = (gchar *) gtk_entry_get_text(GTK_ENTRY(xsrv_filter_text));
+
+    fonts = XListFonts(GDK_DISPLAY(), pattern, _XSRV_MAX_FONTS, &nfonts);
+
+    store =
+        GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(xsrv_font_list)));
+    col = gtk_tree_view_get_column(GTK_TREE_VIEW(xsrv_font_list), 0);
+
+    /*
+     * Update the label on the font list with the number of fonts.
+     */
+    sprintf(buffer1, "Font List: %d", nfonts);
+    gtk_tree_view_column_set_title(col, buffer1);
+
+    gtk_list_store_clear(store);
+    for (i = 0; i < nfonts; i++) {
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter, 0, fonts[i], -1);
+    }
+
+    XFreeFontNames(fonts);
+}
+
+static void
+xsrv_xlfd_filter(GtkWidget *w, gpointer data)
+{
+    gtk_entry_set_text(GTK_ENTRY(xsrv_filter_text), _XSRV_DEFAULT_FILTER);
+    gtk_widget_activate(xsrv_filter_text);
+}
+
+static void
+xsrv_select_font(GtkTreeSelection *sel, gpointer data)
+{
+    gchar *name;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+
+    if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+        gtk_tree_model_get (model, &iter, 0, &name, -1);
+        gtk_entry_set_text(GTK_ENTRY(xsrv_selection_text), name);
+        g_free(name);
+    }
+}
+
+static void
+xsrv_clear_selection_text(GtkWidget *w, gpointer data)
+{
+    gtk_entry_set_text(GTK_ENTRY(xsrv_selection_text), "");
+}
+
+static void
+xsrv_import_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + xsrv_active_editor;
+    XFontStruct *xfont;
+    bdf_font_t *font;
+    gchar *name;
+
+    name = (gchar *) gtk_entry_get_text(GTK_ENTRY(xsrv_selection_text));
+    if (strcmp(name, "") == 0) {
+        guiutil_error_message(ed->shell,
+                              "Import Font: No font name provided.");
+        return;
+    }
+
+    guiutil_busy_cursor(ed->shell, TRUE);
+    guiutil_busy_cursor(xsrv_dialog, TRUE);
+    if ((xfont = XLoadQueryFont(GDK_DISPLAY(), name)) == 0) {
+        guiutil_busy_cursor(ed->shell, FALSE);
+        guiutil_busy_cursor(xsrv_dialog, FALSE);
+        sprintf(buffer1, "Import Font: Unable to load server font %s.",
+                name);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    font = bdf_load_server_font(GDK_DISPLAY(), xfont, name,
+                                &options.font_opts, 0, 0);
+    guiutil_busy_cursor(ed->shell, FALSE);
+    guiutil_busy_cursor(xsrv_dialog, FALSE);
+    XFreeFont(GDK_DISPLAY(), xfont);
+
+    if (font == 0) {
+        sprintf(buffer1, "Import Font: Unable to import server font %s.",
+                name);
+        guiutil_error_message(ed->shell, buffer1);
+        return;
+    }
+
+    /*
+     * Close the dialog.
+     */
+    gtk_widget_hide(xsrv_dialog);
+
+    if (ed->file != 0)
+      g_free(ed->file);
+    if (ed->path != 0)
+      g_free(ed->path);
+    ed->file = ed->path = 0;
+
+    sprintf(buffer1, "%s - unnamed%d [modified]", g_get_prgname(),
+            ed->id);
+    gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1);
+
+    /*
+     * Tell the glyphtest widget to remove references to
+     * the current font if it has any, and redraw.
+     */
+    if (glyphtest != 0)
+      glyphtest_remove_font(GLYPHTEST(glyphtest),
+                            fontgrid_get_font(FONTGRID(ed->fgrid)));
+
+    fontgrid_set_font(FONTGRID(ed->fgrid), font, -1);
+
+    /*
+     * Indicate the font was imported.
+     */
+    ed->imported = TRUE;
+
+    /*
+     * Finally, update the font name field.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ed->fontname),
+                       fontgrid_get_font_name(FONTGRID(ed->fgrid)));
+}
+
+static void
+xsrv_activate_font(GtkTreeView *view, GtkTreePath *path,
+                   GtkTreeViewColumn *col, gpointer data)
+{
+    xsrv_import_font(GTK_WIDGET(view), data);
+}
+
+void
+guifile_import_xserver_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    GtkWidget *label, *hbox, *vbox, *text, *swin, *button;
+    gchar *name, **fonts;
+    gint i, nfonts;
+    GtkListStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *cell_renderer;
+    GtkTreeSelection *sel;
+    GtkTreeIter iter;
+
+    if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) {
+        if (ed->file == 0)
+          sprintf(buffer1, "Save Font: (unnamed%d) modified.  Save?", ed->id);
+        else
+          sprintf(buffer1, "Save Font: %s modified.  Save?", ed->file);
+        if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) {
+            /*
+             * If the current file was imported, then make sure to use the
+             * Save As... dialog instead of just saving under the name that
+             * was constructed when importing.  The user won't know what the
+             * default BDF file name of imported fonts look like.
+             */
+            if (ed->imported)
+              guifile_save_as_wait(w, data);
+            else
+              guifile_save(w, data);
+            return;
+        }
+    }
+
+    xsrv_active_editor = ed->id;
+
+    if (xsrv_dialog == 0) {
+        xsrv_dialog = gtk_dialog_new();
+        gtk_window_set_title(GTK_WINDOW(xsrv_dialog),
+                             "X Server Font Selection");
+        (void) g_signal_connect(G_OBJECT(xsrv_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        vbox = GTK_DIALOG(xsrv_dialog)->vbox;
+        hbox = GTK_DIALOG(xsrv_dialog)->action_area;
+
+        label = gtk_label_new("Filter");
+        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+        gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+        text = xsrv_filter_text = gtk_entry_new();
+        gtk_entry_set_text(GTK_ENTRY(text), _XSRV_DEFAULT_FILTER);
+        (void) g_signal_connect(G_OBJECT(text), "activate",
+                                G_CALLBACK(xsrv_filter), 0);
+        gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
+
+        swin = gtk_scrolled_window_new(0, 0);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                       GTK_POLICY_AUTOMATIC,
+                                       GTK_POLICY_ALWAYS);
+
+        store = gtk_list_store_new(1, G_TYPE_STRING);
+        xsrv_font_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+        g_object_unref(store);
+
+        (void) g_signal_connect(G_OBJECT(xsrv_font_list), "row_activated",
+                                G_CALLBACK(xsrv_activate_font),
+                                GUINT_TO_POINTER(ed->id));
+
+        cell_renderer = gtk_cell_renderer_text_new();
+        column = gtk_tree_view_column_new_with_attributes ("Fonts Found: 0",
+                                                           cell_renderer,
+                                                           "text", 0,
+                                                           NULL);
+
+        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column (GTK_TREE_VIEW(xsrv_font_list), column);
+
+        /*
+         * Force the list to have a certain width and height.
+         */
+        gtk_widget_set_size_request(xsrv_font_list, 550, 200);
+
+        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(xsrv_font_list));
+        gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+
+        (void) g_signal_connect(G_OBJECT(sel), "changed",
+                                G_CALLBACK(xsrv_select_font), NULL);
+
+        gtk_container_add(GTK_CONTAINER(swin), xsrv_font_list);
+
+        gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0);
+
+        label = gtk_label_new("Selection");
+        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+        gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+        text = xsrv_selection_text = gtk_entry_new();
+        (void) g_signal_connect(G_OBJECT(text), "activate",
+                                G_CALLBACK(xsrv_import_font),
+                                GUINT_TO_POINTER(ed->id));
+        gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
+
+        /*
+         * Now add the buttons.
+         */
+        button = xsrv_import = gtk_button_new_with_label("Import");
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(xsrv_import_font),
+                                GUINT_TO_POINTER(ed->id));
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = xsrv_import = gtk_button_new_with_label("Clear Selection");
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(xsrv_clear_selection_text), 0);
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = gtk_button_new_with_label("Filter");
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(xsrv_filter), 0);
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = gtk_button_new_with_label("XLFD Filter");
+        (void) g_signal_connect(G_OBJECT(button), "clicked",
+                                G_CALLBACK(xsrv_xlfd_filter), 0);
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        button = gtk_button_new_with_label("Cancel");
+        (void) g_signal_connect_object(G_OBJECT(button), "clicked",
+                                       G_CALLBACK(gtk_widget_hide),
+                                       (gpointer) xsrv_dialog,
+                                       G_CONNECT_SWAPPED);
+        gtk_container_add(GTK_CONTAINER(hbox), button);
+
+        gtk_widget_show_all(vbox);
+        gtk_widget_show_all(hbox);
+    }
+
+    store =
+        GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(xsrv_font_list)));
+    column = gtk_tree_view_get_column(GTK_TREE_VIEW(xsrv_font_list), 0);
+
+    /*
+     * Load the list of fonts using the current filter pattern.  This needs to
+     * be done each time in case the list of font paths has changed between
+     * calls.
+     */
+    name = (gchar *) gtk_entry_get_text(GTK_ENTRY(xsrv_filter_text));
+    fonts = XListFonts(GDK_DISPLAY(), name, _XSRV_MAX_FONTS, &nfonts);
+
+    /*
+     * Update the label on the font list with the number of fonts.
+     */
+    sprintf(buffer1, "Fonts Found: %d", nfonts);
+    gtk_tree_view_column_set_title(column, buffer1);
+
+    gtk_list_store_clear(store);
+    for (i = 0; i < nfonts; i++) {
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter, 0, fonts[i], -1);
+    }
+
+    XFreeFontNames(fonts);
+
+    /*
+     * Show the dialog.
+     */
+    guiutil_show_dialog_centered(xsrv_dialog, ed->shell);
+
+}
+
+#endif /* HAVE_XLIB */
+
+void
+guifile_export_psf_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+    bdf_font_t *font;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    /*
+     * Only character cell and mono width fonts can be exported as PSF2.
+     */
+    if (font->spacing == BDF_PROPORTIONAL) {
+        sprintf(buffer2,
+                "Export Font: Font cannot be saved as PSF because %s ",
+                "the font has proportional width.");
+        guiutil_error_message(ed->shell, buffer2);
+        return;
+    }
+
+    update_save_dialog(ed, PSF_FORMAT);
+
+    guiutil_show_dialog_centered(ed->save_dialog, ed->shell);
+}
+
+void
+guifile_export_hex_font(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    /*
+     * No check is done for a "valid" HEX font because it actually pads the
+     * output font into bitmaps of two sizes, the wider size twice as wide
+     * as the narrower size.
+     */
+
+    update_save_dialog(ed, HEX_FORMAT);
+
+    guiutil_show_dialog_centered(ed->save_dialog, ed->shell);
+}
+
+void
+guifile_save_as(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    update_save_dialog(ed, BDF_FORMAT);
+
+    guiutil_show_dialog_centered(ed->save_dialog, ed->shell);
+}
+
+void
+guifile_save_as_wait(GtkWidget *w, gpointer data)
+{
+    save_dialog_done = FALSE;
+
+    guifile_save_as(w, data);
+    while (save_dialog_done == FALSE)
+      gtk_main_iteration();
+}
+
+void
+guifile_save(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    /*
+     * If this is a new font, then we need to show the file selection dialog.
+     * Otherwise, simply write the font out.
+     */
+    if (ed->path == 0 && ed->file == 0) {
+        guifile_save_as(w, data);
+        return;
+    }
+
+    ed->export_format = BDF_FORMAT;
+    sprintf(buffer1, "%s/%s", ed->path, ed->file);
+    export_font(buffer1, ed, FALSE);
+}
+
+void
+guifile_new_editor(GtkWidget *w, gpointer data)
+{
+    guint n;
+
+    n = gbdfed_make_editor(0, FALSE);
+
+    gtk_widget_show_all(editors[n].shell);
+}
+
+/*
+ * A routine to load a BDF font directly.
+ */
+void
+guifile_load_bdf_font(gbdfed_editor_t *ed, const gchar *fullpath)
+{
+    gchar *dir, *file;
+
+    if (fullpath == NULL)
+      return;
+
+    file = g_path_get_basename(fullpath);
+    dir = g_path_get_dirname(fullpath);
+    load_bdf_font(ed, fullpath, dir, file);
+    if (dir != NULL)
+      g_free(dir);
+    if (file != NULL)
+      g_free(file);
+}
diff --git a/guigedit.c b/guigedit.c
new file mode 100644 (file)
index 0000000..235341c
--- /dev/null
@@ -0,0 +1,2267 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+#include "glyphedit.h"
+#include "labcon.h"
+#include "gectrl.h"
+
+#define UPMSG "Glyph Edit: The glyph has been modified.\nDo you want to save?"
+
+static const gchar *lb_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"    32    32        3            1",
+/* colors */
+". c None",
+"# c #000000",
+"r c #ff0000",
+/* pixels */
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"..r....r........................",
+".rr....rr..##...................",
+"rrrrrrrrrr.##...................",
+".rr....rr.......................",
+"..r....r........................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"##..##..##.##.##..##..##..##..##",
+"##..##..##.##.##..##..##..##..##",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##..................."
+};
+
+static const gchar *rb_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"    32    32        3            1",
+/* colors */
+". c None",
+"# c #000000",
+"r c #ff0000",
+/* pixels */
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................r............r..",
+"...........##..rr............rr.",
+"...........##.rrrrrrrrrrrrrrrrrr",
+"...............rr............rr.",
+"................r............r..",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"##..##..##.##.##..##..##..##..##",
+"##..##..##.##.##..##..##..##..##",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##..................."
+};
+
+static const gchar *as_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"    32    32        3            1",
+/* colors */
+". c None",
+"# c #000000",
+"r c #ff0000",
+/* pixels */
+"...........##.........r.........",
+"...........##........rrr........",
+"....................rrrrr.......",
+"......................r.........",
+"...........##.........r.........",
+"...........##.........r.........",
+"......................r.........",
+"......................r.........",
+"...........##.........r.........",
+"...........##.........r.........",
+"......................r.........",
+"......................r.........",
+"...........##.........r.........",
+"...........##.........r.........",
+"......................r.........",
+"....................rrrrr.......",
+"...........##........rrr........",
+"...........##.........r.........",
+"................................",
+"##..##..##.##.##..##..##..##..##",
+"##..##..##.##.##..##..##..##..##",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##..................."
+};
+
+static const char *ds_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"    32    32        3            1",
+/* colors */
+". c None",
+"# c #000000",
+"r c #ff0000",
+/* pixels */
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"................................",
+"...........##...................",
+"...........##...................",
+"................................",
+"##..##..##.##.##..##..##..##..##",
+"##..##..##.##.##..##..##..##..##",
+"................................",
+"...........##.........r.........",
+"...........##........rrr........",
+"....................rrrrr.......",
+"......................r.........",
+"...........##.........r.........",
+"...........##.........r.........",
+"......................r.........",
+"....................rrrrr.......",
+"...........##........rrr........",
+"...........##.........r........."
+};
+
+/*
+ * Global pixbufs.
+ */
+static GdkPixbuf *lb_image = 0;
+static GdkPixbuf *rb_image = 0;
+static GdkPixbuf *as_image = 0;
+static GdkPixbuf *ds_image = 0;
+
+typedef struct {
+    GtkWidget *dialog;
+    GtkWidget *notebook;
+    GtkWidget *apply;
+
+    /*
+     * The rotate/shear tab.
+     */
+    GtkWidget *rotate;
+    GtkWidget *shear;
+    GtkAdjustment *rotate_adj;
+    GtkAdjustment *shear_adj;
+    GtkWidget *degrees;
+    gboolean degrees_modified;
+
+    /*
+     * The bounding box resize tab.
+     */
+    GtkWidget *lbearing;
+    GtkWidget *rbearing;
+    GtkWidget *ascent;
+    GtkWidget *descent;
+    gboolean resize_modified;
+
+    /*
+     * The PSF mappings tab.
+     */
+    GtkWidget *psf_add;
+    GtkWidget *psf_delete;
+    GtkWidget *psf_mappings;
+    GtkWidget *psf_input;
+    gboolean psf_modified;
+} GlypheditNotebookRec;
+
+typedef struct {
+    gulong id;
+    gulong owner;
+    gulong handler;
+
+    GtkAccelGroup *ag;
+
+    GtkWidget *shell;
+    GtkWidget *gedit;
+    GtkWidget *gectrl;
+    GtkWidget *name;
+    GtkWidget *encoding;
+    GtkWidget *dwidth;
+    GtkWidget *metrics;
+    GtkWidget *coords;
+    GtkWidget *gectltips;
+
+    GtkWidget *file_menu;
+    GtkWidget *update;
+    GtkWidget *update_prev;
+    GtkWidget *update_next;
+
+    GtkWidget *button_update;
+    GtkWidget *button_prev;
+    GtkWidget *button_next;
+
+    GtkWidget *edit_menu;
+    GtkWidget *reload;
+    GtkWidget *resize;
+    GtkWidget *paste;
+    GtkWidget *copy;
+    GtkWidget *cut;
+    GtkWidget *select_all;
+    GtkWidget *next;
+    GtkWidget *prev;
+    GtkWidget *unimap;
+    GtkWidget *unimap_page;
+
+    GtkWidget *ops_menu;
+    GlypheditNotebookRec ops;
+} GlypheditRec;
+
+static GlypheditRec *glyph_editors;
+static gulong num_glyph_editors;
+
+static GlypheditRec *
+_guigedit_get_glyph_editor(gulong owner)
+{
+    gulong i;
+    GlypheditRec *ge;
+
+    if (num_glyph_editors == 0) {
+        glyph_editors = ge =
+            (GlypheditRec *) g_malloc0(sizeof(GlypheditRec));
+        ge->id = num_glyph_editors++;
+    } else {
+        for (i = 0; i < num_glyph_editors; i++) {
+            if (glyph_editors[i].owner == ~0) {
+                ge = &glyph_editors[i];
+                ge->owner = owner;
+                return ge;
+            }
+        }
+        glyph_editors = (GlypheditRec *)
+            g_realloc(glyph_editors,
+                      sizeof(GlypheditRec) * (num_glyph_editors + 1));
+
+        ge = glyph_editors + num_glyph_editors;
+        (void) memset((char *) ge, 0, sizeof(GlypheditRec));
+        ge->id = num_glyph_editors++;
+    }
+    ge->owner = owner;
+    return ge;
+}
+
+/**************************************************************************
+ *
+ * Menu construction.
+ *
+ **************************************************************************/
+
+static GtkWidget *
+make_accel_menu_item(GtkWidget *menu, const gchar *text, const gchar *accel,
+                     GtkAccelGroup *ag)
+{
+    GtkWidget *mi;
+    guint key;
+    GdkModifierType mods;
+
+    mi = gtk_menu_item_new_with_mnemonic(text);
+
+    gtk_accelerator_parse(accel, &key, &mods);
+    gtk_widget_add_accelerator(mi, "activate", ag, key, mods,
+                               GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
+
+    return mi;
+}
+
+static void
+update_font(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    gbdfed_editor_t *ed = editors + ge->owner;
+    const gchar *s;
+    gchar *prgname = g_get_prgname();
+    gboolean unencoded;
+    bdf_glyph_t *glyph;
+    GlypheditOperation op;
+
+    if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
+        if (glyphedit_get_selecting(GLYPHEDIT(ge->gedit)) == TRUE) {
+          /*
+           * A selection operation is in progress. Need to switch back to
+           * the Draw operation to finalize the selection and then switch
+           * back.
+           */
+            op = glyphedit_get_operation(GLYPHEDIT(ge->gedit));
+            glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW);
+            glyphedit_change_operation(GLYPHEDIT(ge->gedit), op);
+        }
+
+        glyph = glyphedit_get_glyph(GLYPHEDIT(ge->gedit), &unencoded);
+
+        /*
+         * Set the new name and device width for the glyph. These may not
+         * have actually changed, but this is simplest for the moment.
+         */
+        if (glyph->name != 0)
+          free(glyph->name);
+        glyph->name = (gchar *) gtk_entry_get_text(GTK_ENTRY(ge->name));
+        s = gtk_entry_get_text(GTK_ENTRY(ge->dwidth));
+        glyph->dwidth = (guint16) _bdf_atos((char *) s, 0, 10);
+
+        /*
+         * Now update the font itself.
+         */
+        fontgrid_update_glyph(FONTGRID(ed->fgrid), glyph, unencoded);
+
+        /*
+         * Free the glyph structure. The name has already been deallocated
+         * and replaced with a possibly new name.
+         */
+        if (glyph->bytes > 0)
+          free(glyph->bitmap);
+        free(glyph);
+
+        glyphedit_set_modified(GLYPHEDIT(ge->gedit), FALSE);
+    }
+
+    /*
+     * Just modified the PSF mappings.
+     */
+    fontgrid_update_psf_mappings(FONTGRID(ed->fgrid),
+                                 glyphedit_get_encoding(GLYPHEDIT(ge->gedit)),
+                                 glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit)));
+
+    /*
+     * Unset the modified flag and update the title.
+     */
+    glyphedit_set_modified(GLYPHEDIT(ge->gedit), FALSE);
+    if (ed->file == 0)
+      sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)",
+              prgname, ed->id);
+    else
+      sprintf(buffer1, "%s - Glyph Edit: %s", prgname, ed->file);
+
+    gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
+
+
+    gtk_widget_set_sensitive(ge->update, FALSE);
+    gtk_widget_set_sensitive(ge->update_next, FALSE);
+    gtk_widget_set_sensitive(ge->update_prev, FALSE);
+    gtk_widget_set_sensitive(ge->button_update, FALSE);
+
+    /*
+     * Force the focus to be on the glyph grid
+     */
+    gtk_widget_grab_focus(ge->gedit);
+}
+
+/*
+ * Code common to both next_glyph() and previous_glyph().
+ */
+static void
+update_glyphedit(gbdfed_editor_t *ed, GlypheditRec *ge, bdf_glyph_grid_t *grid)
+{
+    Glyphedit *gw;
+
+    gw = GLYPHEDIT(ge->gedit);
+    gtk_entry_set_text(GTK_ENTRY(ge->name), grid->name);
+
+    if (grid->unencoded)
+      sprintf(buffer1, "-1");
+    else {
+        switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) {
+          case 8: sprintf(buffer1, "%o", grid->encoding); break;
+          case 10: sprintf(buffer1, "%d", grid->encoding); break;
+          case 16: sprintf(buffer1, "%04X", grid->encoding); break;
+        }
+    }
+    gtk_label_set_text(GTK_LABEL(ge->encoding), buffer1);
+
+    sprintf(buffer1, "%hd", grid->dwidth);
+    gtk_widget_set_sensitive(ge->dwidth, TRUE);
+    g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler);
+    gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1);
+    g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler);
+
+    if (grid->spacing != BDF_PROPORTIONAL) {
+        gtk_widget_set_sensitive(ge->dwidth, FALSE);
+        if (ge->unimap_page != 0)
+          gtk_widget_set_sensitive(ge->unimap_page, TRUE);
+    } else if (ge->unimap_page != 0)
+      gtk_widget_set_sensitive(ge->unimap_page, FALSE);
+
+    sprintf(buffer1, "width %hd height %hd\r\nascent %hd descent %hd",
+            grid->glyph_bbx.width, grid->glyph_bbx.height,
+            grid->glyph_bbx.ascent, grid->glyph_bbx.descent);
+    gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1);
+
+    /*
+     * Set the new grid in the glyph editor.
+     */
+    glyphedit_set_grid(gw, grid);
+
+    /*
+     * Set the sensitivity of the update menu items appropriately.
+     */
+    if (grid->modified) {
+        gtk_widget_set_sensitive(ge->update, TRUE);
+        gtk_widget_set_sensitive(ge->update_next, TRUE);
+        gtk_widget_set_sensitive(ge->update_prev, TRUE);
+       gtk_widget_set_sensitive(ge->button_update, TRUE);
+    } else {
+        gtk_widget_set_sensitive(ge->update, FALSE);
+        gtk_widget_set_sensitive(ge->update_next, FALSE);
+        gtk_widget_set_sensitive(ge->update_prev, FALSE);
+       gtk_widget_set_sensitive(ge->button_update, FALSE);
+    }
+
+    if (glyphedit_get_encoding(gw) == 0)
+      gtk_widget_set_sensitive(ge->button_prev, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->button_prev, TRUE);
+
+    if (glyphedit_get_encoding(gw) == 0xffff)
+      gtk_widget_set_sensitive(ge->button_next, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->button_next, TRUE);
+
+    /*
+     * Force the focus to be on the glyph grid.
+     */
+    gtk_widget_grab_focus(ge->gedit);
+}
+
+static void
+next_glyph(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    gbdfed_editor_t *ed = editors + ge->owner;
+    bdf_font_t *font;
+    bdf_glyph_grid_t *grid;
+    bdf_bitmap_t image;
+
+    if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
+        if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE))
+          update_font(w, GUINT_TO_POINTER(ge->id));
+    }
+
+    grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
+
+    if (fontgrid_select_next_glyph(FONTGRID(ed->fgrid), grid->encoding) == FALSE)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    if (grid->unencoded)
+      grid = bdf_make_glyph_grid(font, grid->encoding + 1, 1);
+    else
+      grid = bdf_make_glyph_grid(font, grid->encoding + 1, 0);
+
+    update_glyphedit(ed, ge, grid);
+
+    glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
+    gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
+    if (image.bytes > 0)
+      free(image.bitmap);
+}
+
+static void
+previous_glyph(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    gbdfed_editor_t *ed = editors + ge->owner;
+    bdf_font_t *font;
+    bdf_glyph_grid_t *grid;
+    bdf_bitmap_t image;
+
+    if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
+        if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE))
+          update_font(w, GUINT_TO_POINTER(ge->id));
+    }
+
+    grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
+
+    if (fontgrid_select_previous_glyph(FONTGRID(ed->fgrid), grid->encoding) == FALSE)
+      return;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+
+    if (grid->unencoded)
+      grid = bdf_make_glyph_grid(font, grid->encoding - 1, 1);
+    else
+      grid = bdf_make_glyph_grid(font, grid->encoding - 1, 0);
+
+    update_glyphedit(ed, ge, grid);
+
+    glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
+    gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
+    if (image.bytes > 0)
+      free(image.bitmap);
+}
+
+static void
+update_and_next_glyph(GtkWidget *w, gpointer data)
+{
+    update_font(w, data);
+    next_glyph(w, data);
+}
+
+static void
+update_and_previous_glyph(GtkWidget *w, gpointer data)
+{
+    update_font(w, data);
+    previous_glyph(w, data);
+}
+
+static gboolean
+close_glyph_editor(GtkWidget *w, GdkEvent *ev, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    /*
+     * Glyph editors with no owners are ignored. This lets us call this
+     * routine at application shutdown time to update all modified
+     * glyph editors.
+     */
+    if (ge->owner == ~0)
+      return TRUE;
+
+    /*
+     * We don't check to see if the grid has been modified, because
+     * certain operations cause the modify flag to be set, but they
+     * don't really represent a modification.
+     */
+    if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
+        if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE))
+          update_font(w, GUINT_TO_POINTER(ge->id));
+    }
+
+    /*
+     * Release this editor back into the pool to be reused.
+     */
+    ge->owner = ~0;
+
+    /*
+     * Hide the shell.
+     */
+    gtk_widget_hide(ge->shell);
+
+    return TRUE;
+}
+
+static void
+activate_close_glyph_editor(GtkWidget *w, gpointer data)
+{
+    (void) close_glyph_editor(w, 0, data);
+}
+
+static GtkWidget *
+make_file_menu(GlypheditRec *ge, GtkWidget *menubar)
+{
+    GtkWidget *file, *menu, *mitem, *sep;
+
+    /*
+     * Create the File menu.
+     */
+    file = gtk_menu_item_new_with_mnemonic("_File");
+
+    ge->file_menu = menu = gtk_menu_new();
+
+    ge->update = make_accel_menu_item(menu, "_Update",
+                                      "<Control>S", ge->ag);
+    g_signal_connect(G_OBJECT(ge->update), "activate",
+                     G_CALLBACK(update_font), GUINT_TO_POINTER(ge->id));
+
+    ge->update_next = make_accel_menu_item(menu, "Update and _Next",
+                                           "<Control>U", ge->ag);
+    g_signal_connect(G_OBJECT(ge->update_next), "activate",
+                     G_CALLBACK(update_and_next_glyph),
+                     GUINT_TO_POINTER(ge->id));
+
+    ge->update_prev = make_accel_menu_item(menu, "Update and _Previous",
+                                           "<Control>B", ge->ag);
+    g_signal_connect(G_OBJECT(ge->update_prev), "activate",
+                     G_CALLBACK(update_and_previous_glyph),
+                     GUINT_TO_POINTER(ge->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = make_accel_menu_item(menu, "_Close", "<Control>F4", ge->ag);
+    (void) g_signal_connect(G_OBJECT(mitem), "activate",
+                            G_CALLBACK(activate_close_glyph_editor),
+                            GUINT_TO_POINTER(ge->id));
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), menu);
+
+    return file;
+}
+
+static gboolean
+edit_menu_up(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    Glyphedit *gw;
+
+    gw = GLYPHEDIT(ge->gedit);
+
+    if (glyphedit_clipboard_empty(gw))
+      gtk_widget_set_sensitive(ge->paste, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->paste, TRUE);
+
+    gtk_widget_set_sensitive(ge->copy,
+                             glyphedit_get_selecting(gw));
+    gtk_widget_set_sensitive(ge->cut,
+                             glyphedit_get_selecting(gw));
+
+    if (glyphedit_get_encoding(gw) == 0)
+      gtk_widget_set_sensitive(ge->prev, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->prev, TRUE);
+
+    if (glyphedit_get_encoding(gw) == 0xffff)
+      gtk_widget_set_sensitive(ge->next, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->next, TRUE);
+
+    gtk_widget_set_sensitive(ge->reload, glyphedit_get_modified(gw));
+
+    if (glyphedit_get_spacing(gw) != BDF_PROPORTIONAL)
+      gtk_widget_set_sensitive(ge->unimap, TRUE);
+    else
+      gtk_widget_set_sensitive(ge->unimap, FALSE);
+
+    return FALSE;
+}
+
+static gboolean
+edit_menu_down(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    gtk_widget_set_sensitive(ge->paste, TRUE);
+    gtk_widget_set_sensitive(ge->copy, TRUE);
+    gtk_widget_set_sensitive(ge->cut, TRUE);
+    gtk_widget_set_sensitive(ge->prev, TRUE);
+    gtk_widget_set_sensitive(ge->next, TRUE);
+    gtk_widget_set_sensitive(ge->reload, TRUE);
+    gtk_widget_set_sensitive(ge->unimap, TRUE);
+
+    return FALSE;
+}
+
+static void
+reload_glyph(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    gbdfed_editor_t *ed = editors + ge->owner;
+    bdf_font_t *font;
+    bdf_glyph_grid_t *grid;
+    bdf_bitmap_t image;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+    grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
+
+    grid = bdf_make_glyph_grid(font, grid->encoding, grid->unencoded);
+
+    update_glyphedit(ed, ge, grid);
+
+    glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
+    gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
+    if (image.bytes > 0)
+      free(image.bitmap);
+}
+
+static void
+copy_selection(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    glyphedit_copy_selection(GLYPHEDIT(ge->gedit));
+}
+
+static void
+cut_selection(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    bdf_bitmap_t image;
+
+    glyphedit_cut_selection(GLYPHEDIT(ge->gedit));
+    glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
+    gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
+    if (image.bytes > 0)
+      free(image.bitmap);
+}
+
+static void
+paste_selection(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    bdf_bitmap_t image;
+
+    glyphedit_paste_selection(GLYPHEDIT(ge->gedit));
+    glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
+    gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
+    if (image.bytes > 0)
+      free(image.bitmap);
+}
+
+static void
+select_all(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    glyphedit_select_all(GLYPHEDIT(ge->gedit));
+}
+
+static GtkWidget *
+make_edit_menu(GlypheditRec *ge, GtkWidget *menubar)
+{
+    GtkWidget *edit, *menu, *sep;
+
+    /*
+     * Create the Edit menu.
+     */
+    edit = gtk_menu_item_new_with_mnemonic("_Edit");
+
+    ge->edit_menu = menu = gtk_menu_new();
+    g_signal_connect(G_OBJECT(menu), "map_event", G_CALLBACK(edit_menu_up),
+                     GUINT_TO_POINTER(ge->id));
+    g_signal_connect(G_OBJECT(menu), "unmap_event", G_CALLBACK(edit_menu_down),
+                     GUINT_TO_POINTER(ge->id));
+
+    ge->reload = make_accel_menu_item(menu, "Re_load",
+                                      "<Control>L", ge->ag);
+    g_signal_connect(G_OBJECT(ge->reload), "activate",
+                     G_CALLBACK(reload_glyph), GUINT_TO_POINTER(ge->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    ge->copy = make_accel_menu_item(menu, "_Copy",
+                                    "<Control>C", ge->ag);
+    g_signal_connect(G_OBJECT(ge->copy), "activate",
+                     G_CALLBACK(copy_selection),
+                     GUINT_TO_POINTER(ge->id));
+
+    ge->cut = make_accel_menu_item(menu, "C_ut",
+                                   "<Control>X", ge->ag);
+    g_signal_connect(G_OBJECT(ge->cut), "activate",
+                     G_CALLBACK(cut_selection),
+                     GUINT_TO_POINTER(ge->id));
+
+    ge->paste = make_accel_menu_item(menu, "_Paste",
+                                     "<Control>V", ge->ag);
+    g_signal_connect(G_OBJECT(ge->paste), "activate",
+                     G_CALLBACK(paste_selection),
+                     GUINT_TO_POINTER(ge->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    ge->select_all = make_accel_menu_item(menu, "Select _All",
+                                          "<Control>A", ge->ag);
+    g_signal_connect(G_OBJECT(ge->select_all), "activate",
+                     G_CALLBACK(select_all),
+                     GUINT_TO_POINTER(ge->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    ge->next = make_accel_menu_item(menu, "_Next Glyph",
+                                    "<Control>N", ge->ag);
+    g_signal_connect(G_OBJECT(ge->next), "activate",
+                     G_CALLBACK(next_glyph), GUINT_TO_POINTER(ge->id));
+
+    ge->prev = make_accel_menu_item(menu, "Pre_vious Glyph",
+                                    "<Control>P", ge->ag);
+    g_signal_connect(G_OBJECT(ge->prev), "activate",
+                     G_CALLBACK(previous_glyph), GUINT_TO_POINTER(ge->id));
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit), menu);
+
+    return edit;
+}
+
+static gboolean
+operations_menu_up(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    Glyphedit *gw;
+
+    gw = GLYPHEDIT(ge->gedit);
+
+    if (glyphedit_get_spacing(gw) != BDF_PROPORTIONAL)
+      gtk_widget_set_sensitive(ge->unimap, TRUE);
+    else
+      gtk_widget_set_sensitive(ge->unimap, FALSE);
+
+    return FALSE;
+}
+
+static gboolean
+operations_menu_down(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    Glyphedit *gw;
+
+    gw = GLYPHEDIT(ge->gedit);
+    gtk_widget_set_sensitive(ge->unimap, TRUE);
+
+    return FALSE;
+}
+
+static void
+draw_operation(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW);
+    gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_DRAW);
+}
+
+static void
+move_operation(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_MOVE);
+    gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_MOVE);
+}
+
+static void
+copy_operation(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_COPY);
+    gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_COPY);
+}
+
+static void
+set_rotate_limits(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ge->ops.rotate)))
+      gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(ge->ops.degrees),
+                                     ge->ops.rotate_adj);
+}
+
+static void
+set_shear_limits(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    const gchar *s;
+    gint16 v = -1000;
+
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ge->ops.shear))) {
+        /*
+         * This little tap dance is to avoid a minor, but ugly GUI
+         * situation where the value in the spin button may be obscured
+         * when the adjustment is changed back. The shear value can have
+         * at most 2 digits where the rotate value can have 3. Changing
+         * back to the shear adjustment can cause a resize of the spin
+         * button, sometimes obscuring the value left over from the rotate
+         * adjustment.
+         */
+        s = gtk_entry_get_text(GTK_ENTRY(ge->ops.degrees));
+        v = (gint16) _bdf_atos((char *) s, 0, 10);
+        if (v < -20)
+          v = -20;
+        else if (v > 20)
+          v = 20;
+        if (v != -1000)
+          gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.degrees),
+                                    (gdouble) v);
+        gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(ge->ops.degrees),
+                                       ge->ops.shear_adj);
+    }
+}
+
+/*
+ * Called when the value for rotating or shearing a glyph has changed.
+ */
+static void
+degrees_changed(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    gtk_widget_set_sensitive(ge->ops.apply, TRUE);
+    ge->ops.degrees_modified = TRUE;
+}
+
+/*
+ * Called when any of the fields in the resize tab are changed.
+ */
+static void
+resize_changed(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    gtk_widget_set_sensitive(ge->ops.apply, TRUE);
+    ge->ops.resize_modified = TRUE;
+}
+
+static gboolean
+count_list_items(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *i,
+                 gpointer data)
+{
+    gint *n = (gint *) data;
+    *n = *n + 1;
+
+    return FALSE;
+}
+
+static gboolean
+collect_list_items(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *i,
+                   gpointer data)
+{
+    gchar **mappings = (gchar **) data;
+
+    gtk_tree_model_get(m, i, 0,
+                       &mappings[gtk_tree_path_get_indices(p)[0]], -1);
+    return FALSE;
+}
+
+/*
+ * Called when the Apply button is pressed.
+ */
+static void
+apply_operations(GtkWidget *w, gint response, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    const gchar *s;
+    gint16 lb, rb, as, ds, degrees;
+    gint i, n;
+    GtkTreeModel *model;
+    gchar **mappings;
+    bdf_psf_unimap_t *mp;
+    bdf_metrics_t metrics;
+
+    if (ge->ops.degrees_modified) {
+        /*
+         * The degrees of rotatation or shearing have been modified.
+         */
+        s = gtk_entry_get_text(GTK_ENTRY(ge->ops.degrees));
+        degrees = (gint16) _bdf_atos((char *) s, 0, 10);
+        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ge->ops.rotate)))
+          glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), degrees);
+        else
+          glyphedit_shear_glyph(GLYPHEDIT(ge->gedit), degrees);
+        ge->ops.degrees_modified = FALSE;
+    }
+
+    if (ge->ops.resize_modified) {
+        /*
+         * The bounding box has been modified.
+         */
+        s = gtk_entry_get_text(GTK_ENTRY(ge->ops.lbearing));
+        lb = (gint16) _bdf_atos((char *) s, 0, 10);
+        s = gtk_entry_get_text(GTK_ENTRY(ge->ops.rbearing));
+        rb = (gint16) _bdf_atos((char *) s, 0, 10);
+        s = gtk_entry_get_text(GTK_ENTRY(ge->ops.ascent));
+        as = (gint16) _bdf_atos((char *) s, 0, 10);
+        s = gtk_entry_get_text(GTK_ENTRY(ge->ops.descent));
+        ds = (gint16) _bdf_atos((char *) s, 0, 10);
+
+        metrics.width = rb - lb;
+        metrics.x_offset = lb;
+        metrics.ascent = as;
+        metrics.descent = ds;
+        metrics.height = as + ds;
+        metrics.y_offset = -ds;
+
+        glyphedit_set_metrics(GLYPHEDIT(ge->gedit), &metrics);
+        ge->ops.degrees_modified = FALSE;
+    }
+
+    if (ge->ops.psf_modified) {
+        model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
+        n = 0;
+        gtk_tree_model_foreach(model, count_list_items, (gpointer) &n);
+        mappings = (gchar **) g_malloc(sizeof(gchar *) * n);
+        gtk_tree_model_foreach(model, collect_list_items, (gpointer) mappings);
+        mp = glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit));
+        _bdf_psf_pack_mapping(mappings, n,
+                              glyphedit_get_encoding(GLYPHEDIT(ge->gedit)),
+                              mp);
+        for (i = 0; i < n; i++)
+          g_free(mappings[i]);
+        if (n > 0)
+          g_free(mappings);
+        glyphedit_set_modified(GLYPHEDIT(ge->gedit), TRUE);
+        glyphedit_signal_modified(GLYPHEDIT(ge->gedit));
+        ge->ops.psf_modified = FALSE;
+    }
+
+    /*
+     * Only disable the Apply button if everything has been updated.
+     */
+    if (ge->ops.degrees_modified == FALSE &&
+        ge->ops.resize_modified == FALSE &&
+        ge->ops.psf_modified == FALSE)
+      gtk_widget_set_sensitive(ge->ops.apply, FALSE);
+
+    gtk_widget_hide(ge->ops.dialog);
+}
+
+static void
+change_unimap(GtkTreeModel *m, const gchar *path,
+              const gchar *ntext, gpointer data)
+{
+    gchar *ot;
+    GtkTreePath *p = gtk_tree_path_new_from_string(path);
+    GtkTreeIter iter;
+
+    gtk_tree_model_get_iter(m, &iter, p);
+    gtk_tree_model_get(m, &iter, 0, &ot, -1);
+    g_free(ot);
+
+    gtk_list_store_set(GTK_LIST_STORE(m), &iter, 0, ntext, -1);
+}
+
+static void
+add_mapping(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    const gchar *v;
+    gchar *i;
+    gulong n;
+    GtkTreeModel *model;
+    GtkTreePath *path;
+    GtkTreeIter iter;
+
+    v = gtk_entry_get_text(GTK_ENTRY(ge->ops.psf_input));
+
+    /*
+     * Insure that the value is in the form expected.
+     */
+    n = (gulong) _bdf_atol((char *) v, 0, 16);
+    if (n <= 0xffff)
+      sprintf(buffer1, "U+%04lX", n);
+    else
+      sprintf(buffer1, "U+%06lX", n);
+    v = (const gchar *) buffer1;
+
+    model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
+    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, v, -1);
+
+    i = gtk_tree_model_get_string_from_iter(model, &iter);
+    path = gtk_tree_path_new_from_string(i);
+    g_free(i);
+    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(ge->ops.psf_mappings),
+                                 path, 0, TRUE, 0.5, 0.0);
+    gtk_tree_path_free(path);
+
+    ge->ops.psf_modified = TRUE;
+
+    gtk_entry_set_text(GTK_ENTRY(ge->ops.psf_input), "");
+    gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
+    gtk_widget_set_sensitive(ge->ops.apply, TRUE);
+}
+
+static void
+enable_add(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    const gchar *v = gtk_entry_get_text(GTK_ENTRY(ge->ops.psf_input));
+
+    if (strlen(v) == 0)
+      gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->ops.psf_add, TRUE);
+}
+
+static void
+delete_unimap(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    GtkTreeModel *model;
+    GtkTreeSelection *sel;
+    GtkTreeIter iter;
+
+    model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ge->ops.psf_mappings));
+
+    if (gtk_tree_selection_get_selected(sel, 0, &iter)) {
+        gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+        ge->ops.psf_modified = TRUE;
+        gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
+        gtk_widget_set_sensitive(ge->ops.apply, TRUE);
+    }
+}
+
+static void
+operations_dialog_populate(GlypheditRec *ge)
+{
+    bdf_psf_unimap_t *psf;
+    char **mappings;
+    int i, nmappings;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    bdf_metrics_t metrics;
+
+    /*
+     * Populate the fields of the dialog with initial values.
+     */
+    glyphedit_get_font_metrics(GLYPHEDIT(ge->gedit), &metrics);
+
+    /*
+     * The left bearing cannot be set when the font has character cell
+     * spacing. But make sure it is enabled so the value from the font
+     * can be set.
+     */
+    gtk_widget_set_sensitive(ge->ops.lbearing, TRUE);
+
+    /*
+     * Set the values.
+     */
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.lbearing),
+                              (gdouble) (-metrics.x_offset));
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.rbearing),
+                              (gdouble) (metrics.width + metrics.x_offset));
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.ascent),
+                              (gdouble) metrics.ascent);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.descent),
+                              (gdouble) metrics.descent);
+    if (metrics.font_spacing == BDF_CHARCELL)
+      gtk_widget_set_sensitive(ge->ops.lbearing, FALSE);
+
+    /*
+     * Add the PSF mappings to the list.
+     */
+    if ((psf = glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit)))) {
+        /*
+         * Erase the list store.
+         */
+        model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
+        gtk_list_store_clear(GTK_LIST_STORE(model));
+
+        mappings = _bdf_psf_unpack_mapping(psf, &nmappings);
+
+        /*
+         * Add the mappings to the list.
+         */
+        for (i = 0; i < nmappings; i++) {
+            gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+            gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+                               0, mappings[i], -1);
+        }
+        free((char *) mappings);
+    }
+
+    gtk_entry_set_text(GTK_ENTRY(ge->ops.psf_input), "");
+    gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
+
+    /*
+     * Make the "Apply" button insensitive until the user has modified
+     * something.
+     */
+    gtk_widget_set_sensitive(ge->ops.apply, FALSE);
+
+    ge->ops.degrees_modified = ge->ops.resize_modified =
+        ge->ops.psf_modified = FALSE;
+}
+
+static void
+operations_dialog_setup(GlypheditRec *ge)
+{
+    GtkWidget *nb, *label, *button, *hbox, *vbox, *frame, *swin;
+    GtkListStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *cell_renderer;
+    GtkTreeSelection *sel;
+
+    if (ge->ops.dialog != 0)
+      return;
+
+    /*
+     * Create the pixbufs if necessary.
+     */
+    if (lb_image == 0)
+      lb_image = gdk_pixbuf_new_from_xpm_data(lb_xpm);
+    if (rb_image == 0)
+      rb_image = gdk_pixbuf_new_from_xpm_data(rb_xpm);
+    if (as_image == 0)
+      as_image = gdk_pixbuf_new_from_xpm_data(as_xpm);
+    if (ds_image == 0)
+      ds_image = gdk_pixbuf_new_from_xpm_data(ds_xpm);
+
+    ge->ops.dialog = gtk_dialog_new();
+    g_signal_connect(G_OBJECT(ge->ops.dialog), "response",
+                     G_CALLBACK(apply_operations),
+                     GUINT_TO_POINTER(ge->id));
+    /*
+     * The "delete_event" handling in the dialog doesn't seem to be
+     * working with GTK+ version 2.7.4.
+     */
+    g_signal_connect(G_OBJECT(ge->ops.dialog), "delete_event",
+                     G_CALLBACK(gtk_widget_hide), 0);
+    ge->ops.apply = gtk_dialog_add_button(GTK_DIALOG(ge->ops.dialog),
+                                          GTK_STOCK_APPLY,
+                                          GTK_RESPONSE_APPLY);
+    gtk_widget_set_sensitive(ge->ops.apply, FALSE);
+    button = gtk_dialog_add_button(GTK_DIALOG(ge->ops.dialog),
+                                   GTK_STOCK_CLOSE,
+                                   GTK_RESPONSE_CLOSE);
+
+    nb = ge->ops.notebook = gtk_notebook_new();
+
+    /*
+     * 1. Create the rotate/shear tab.
+     */
+    frame = gtk_frame_new(0);
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    vbox = gtk_vbox_new(FALSE, 10);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    ge->ops.rotate = gtk_radio_button_new_with_label(0, "Rotate");
+    g_signal_connect(G_OBJECT(ge->ops.rotate), "toggled",
+                     G_CALLBACK(set_rotate_limits), GUINT_TO_POINTER(ge->id));
+    ge->ops.shear =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ge->ops.rotate),
+                                        "Shear");
+    g_signal_connect(G_OBJECT(ge->ops.shear), "toggled",
+                     G_CALLBACK(set_shear_limits), GUINT_TO_POINTER(ge->id));
+
+    gtk_box_pack_start(GTK_BOX(hbox), ge->ops.rotate, FALSE, FALSE, 2);
+    gtk_box_pack_start(GTK_BOX(hbox), ge->ops.shear, FALSE, FALSE, 2);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    ge->ops.rotate_adj =
+        (GtkAdjustment *) gtk_adjustment_new(0.0, -359.0, 359.0, 1.0,
+                                             10.0, 0.0);
+    /*
+     * Do this so the adjustment doesn't get unref'ed out of existence
+     * until we explicitly get rid of it later.
+     */
+    g_object_ref(G_OBJECT(ge->ops.rotate_adj));
+    /*gtk_object_sink(GTK_OBJECT(ge->ops.rotate_adj));*/
+
+    ge->ops.shear_adj =
+        (GtkAdjustment *) gtk_adjustment_new(0.0, -20.0, 20.0, 1.0,
+                                             5.0, 0.0);
+    /*
+     * Do this so the adjustment doesn't get unref'ed out of existence
+     * until we explicitly get rid of it later.
+     */
+    g_object_ref(G_OBJECT(ge->ops.shear_adj));
+    /*gtk_object_sink(GTK_OBJECT(ge->ops.shear_adj));*/
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    label = gtk_label_new("Degrees:");
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+    ge->ops.degrees = gtk_widget_new(gtk_spin_button_get_type(),
+                                     "max_length", 6,
+                                     "adjustment", ge->ops.rotate_adj,
+                                     "climb_rate", 1.0,
+                                     "digits", 0,
+                                     "value", 0.0,
+                                     "numeric", TRUE,
+                                     NULL);
+    g_signal_connect(G_OBJECT(ge->ops.degrees), "changed",
+                     G_CALLBACK(degrees_changed), GUINT_TO_POINTER(ge->id));
+
+    gtk_box_pack_start(GTK_BOX(hbox), ge->ops.degrees, FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
+
+    gtk_container_add(GTK_CONTAINER(frame), vbox);
+
+    /*
+     * Add the frame to a notebook page.
+     */
+    gtk_notebook_append_page(GTK_NOTEBOOK(nb), frame,
+                             gtk_label_new("Rotate/Shear"));
+
+    /*
+     * 2. Create the resize font bounding box tab.
+     */
+    vbox = gtk_vbox_new(TRUE, 0);
+
+    frame = gtk_frame_new("Left and Right Bearing");
+
+    hbox = gtk_hbox_new(TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
+
+    ge->ops.lbearing = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
+    g_signal_connect(G_OBJECT(ge->ops.lbearing), "changed",
+                     G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
+    label = labcon_new_pixbuf_defaults(lb_image, ge->ops.lbearing, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+    ge->ops.rbearing = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
+    g_signal_connect(G_OBJECT(ge->ops.rbearing), "changed",
+                     G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
+    label = labcon_new_pixbuf_defaults(rb_image, ge->ops.rbearing, label);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+    frame = gtk_frame_new("Ascent and Descent");
+
+    hbox = gtk_hbox_new(TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
+
+    ge->ops.ascent = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
+    g_signal_connect(G_OBJECT(ge->ops.ascent), "changed",
+                     G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
+    label = labcon_new_pixbuf_defaults(as_image, ge->ops.ascent, label);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+    ge->ops.descent = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
+    g_signal_connect(G_OBJECT(ge->ops.descent), "changed",
+                     G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
+    label = labcon_new_pixbuf_defaults(ds_image, ge->ops.descent, label);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox,
+                             gtk_label_new("Resize BBX"));
+
+    /*
+     * 3. Create the PSF Unicode mapping tab.
+     */
+    vbox = gtk_vbox_new(FALSE, 0);
+
+    swin = gtk_scrolled_window_new(0, 0);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+                                   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+    gtk_container_set_border_width(GTK_CONTAINER(swin), 3);
+
+    store = gtk_list_store_new(1, G_TYPE_STRING);
+    ge->ops.psf_mappings =
+        gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+    g_object_unref(store);
+
+    gtk_widget_set_size_request(ge->ops.psf_mappings, 150, 150);
+    gtk_container_add(GTK_CONTAINER(swin), ge->ops.psf_mappings);
+
+
+    cell_renderer = gtk_cell_renderer_text_new();
+    g_object_set(G_OBJECT(cell_renderer), "editable", TRUE, NULL);
+    g_signal_connect_object(G_OBJECT(cell_renderer), "edited",
+                            G_CALLBACK(change_unimap), (gpointer) store,
+                            G_CONNECT_SWAPPED);
+    column = gtk_tree_view_column_new_with_attributes("Unicode Mappings",
+                                                      cell_renderer,
+                                                      "text", 0,
+                                                      NULL);
+
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(ge->ops.psf_mappings), column);
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ge->ops.psf_mappings));
+
+    gtk_box_pack_start(GTK_BOX(vbox), swin, FALSE, FALSE, 0);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+
+    ge->ops.psf_delete = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+    gtk_box_pack_end(GTK_BOX(hbox), ge->ops.psf_delete, FALSE, FALSE, 5);
+    g_signal_connect(G_OBJECT(ge->ops.psf_delete), "clicked",
+                     G_CALLBACK(delete_unimap), GUINT_TO_POINTER(ge->id));
+
+    ge->ops.psf_add = gtk_button_new_from_stock(GTK_STOCK_ADD);
+    gtk_box_pack_end(GTK_BOX(hbox), ge->ops.psf_add, FALSE, FALSE, 0);
+    g_signal_connect(G_OBJECT(ge->ops.psf_add), "clicked",
+                     G_CALLBACK(add_mapping), GUINT_TO_POINTER(ge->id));
+
+    ge->ops.psf_input = gtk_widget_new(gtk_entry_get_type(),
+                                       "max_length", 8, NULL);
+    g_signal_connect(G_OBJECT(ge->ops.psf_input), "activate",
+                     G_CALLBACK(add_mapping), GUINT_TO_POINTER(ge->id));
+    g_signal_connect(G_OBJECT(ge->ops.psf_input), "changed",
+                     G_CALLBACK(enable_add), GUINT_TO_POINTER(ge->id));
+    gtk_box_pack_end(GTK_BOX(hbox), ge->ops.psf_input, FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox,
+                             gtk_label_new("PSF Unicode Mappings"));
+
+    ge->unimap_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb), 2);
+    if (glyphedit_get_spacing(GLYPHEDIT(ge->gedit)) != BDF_PROPORTIONAL)
+      gtk_widget_set_sensitive(ge->unimap_page, TRUE);
+    else
+      gtk_widget_set_sensitive(ge->unimap_page, FALSE);
+
+    /*
+     * 4. Add the notebook to the dialog.
+     */
+    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(ge->ops.dialog)->vbox), nb);
+
+    gtk_window_set_transient_for(GTK_WINDOW(ge->ops.dialog),
+                                 GTK_WINDOW(ge->shell));
+    gtk_widget_show_all(GTK_DIALOG(ge->ops.dialog)->vbox);
+    gtk_widget_show_all(GTK_DIALOG(ge->ops.dialog)->action_area);
+}
+
+static void
+show_rotate_dialog(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    operations_dialog_setup(ge);
+    operations_dialog_populate(ge);
+
+    /*
+     * Make sure we turn to the first notebook page.
+     */
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 0);
+
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ge->ops.rotate), TRUE);
+
+    /*
+     * Move the focus to the spin box.
+     */
+    gtk_widget_grab_focus(ge->ops.degrees);
+
+    gtk_widget_show(ge->ops.dialog);
+}
+
+static void
+show_shear_dialog(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    operations_dialog_setup(ge);
+    operations_dialog_populate(ge);
+
+    /*
+     * Make sure we turn to the first notebook page.
+     */
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 0);
+
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ge->ops.shear), TRUE);
+
+    /*
+     * Move the focus to the spin box.
+     */
+    gtk_widget_grab_focus(ge->ops.degrees);
+
+    gtk_widget_show(ge->ops.dialog);
+}
+
+static void
+show_resize_dialog(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    operations_dialog_setup(ge);
+    operations_dialog_populate(ge);
+
+    /*
+     * Make sure we turn to the first notebook page.
+     */
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 1);
+
+    /*
+     * Move the focus to the first sensitive spin box.
+     */
+    if (GTK_WIDGET_SENSITIVE(ge->ops.lbearing))
+      gtk_widget_grab_focus(ge->ops.lbearing);
+    else
+      gtk_widget_grab_focus(ge->ops.rbearing);
+
+    gtk_widget_show(ge->ops.dialog);
+}
+
+static void
+embolden_glyph(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    glyphedit_embolden_glyph(GLYPHEDIT(ge->gedit));
+}
+
+static void
+show_unimap_dialog(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+
+    operations_dialog_setup(ge);
+    operations_dialog_populate(ge);
+
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 2);
+
+    /*
+     * Move the focus to the input field.
+     */
+    gtk_widget_grab_focus(ge->ops.psf_input);
+
+    gtk_widget_show(ge->ops.dialog);
+}
+
+static GtkWidget *
+make_ops_menu(GlypheditRec *ge, GtkWidget *menubar)
+{
+    GtkWidget *ops, *menu, *mitem, *sep;
+
+    /*
+     * Create the Operations menu.
+     */
+    ops = gtk_menu_item_new_with_mnemonic("_Operations");
+
+    ge->ops_menu = menu = gtk_menu_new();
+    g_signal_connect(G_OBJECT(menu), "map_event",
+                     G_CALLBACK(operations_menu_up), GUINT_TO_POINTER(ge->id));
+    g_signal_connect(G_OBJECT(menu), "unmap_event",
+                     G_CALLBACK(operations_menu_down),
+                     GUINT_TO_POINTER(ge->id));
+
+    mitem = make_accel_menu_item(menu, "_Draw",
+                                 "<Control>D", ge->ag);
+    g_signal_connect(G_OBJECT(mitem), "activate",
+                     G_CALLBACK(draw_operation),
+                     GUINT_TO_POINTER(ge->id));
+
+    mitem = make_accel_menu_item(menu, "_Move",
+                                 "<Control>M", ge->ag);
+    g_signal_connect(G_OBJECT(mitem), "activate",
+                     G_CALLBACK(move_operation),
+                     GUINT_TO_POINTER(ge->id));
+
+    mitem = make_accel_menu_item(menu, "_Copy",
+                                 "<Control>Y", ge->ag);
+    g_signal_connect(G_OBJECT(mitem), "activate",
+                     G_CALLBACK(copy_operation),
+                     GUINT_TO_POINTER(ge->id));
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    mitem = make_accel_menu_item(menu, "_Rotate",
+                                 "<Control>T", ge->ag);
+    g_signal_connect(G_OBJECT(mitem), "activate",
+                     G_CALLBACK(show_rotate_dialog),
+                     GUINT_TO_POINTER(ge->id));
+
+    mitem = make_accel_menu_item(menu, "_Shear",
+                                 "<Control>E", ge->ag);
+    g_signal_connect(G_OBJECT(mitem), "activate",
+                     G_CALLBACK(show_shear_dialog),
+                     GUINT_TO_POINTER(ge->id));
+
+    mitem = make_accel_menu_item(menu, "_Embolden",
+                                 "<Control>H", ge->ag);
+    g_signal_connect(G_OBJECT(mitem), "activate",
+                     G_CALLBACK(embolden_glyph),
+                     GUINT_TO_POINTER(ge->id));
+
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(ops), menu);
+
+    /*
+     * Separator.
+     */
+    sep = gtk_menu_item_new();
+    gtk_widget_set_sensitive(sep, FALSE);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+    ge->resize = make_accel_menu_item(menu, "_Resize BBX",
+                                           "<Control>R", ge->ag);
+    g_signal_connect(G_OBJECT(ge->resize), "activate",
+                     G_CALLBACK(show_resize_dialog),
+                     GUINT_TO_POINTER(ge->id));
+
+    ge->unimap = make_accel_menu_item(menu, "Edit PSF Unicode _Mappings",
+                                      "<Control>F", ge->ag);
+    g_signal_connect(G_OBJECT(ge->unimap), "activate",
+                     G_CALLBACK(show_unimap_dialog),
+                     GUINT_TO_POINTER(ge->id));
+
+    return ops;
+}
+
+static void
+pointer_moved(GtkWidget *w, gpointer cb, gpointer ged)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
+    GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
+    bdf_glyph_grid_t *g;
+
+    g = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
+    if (g->bpp == 1 || si->color == 0)
+      sprintf(buffer1, "(%d,%d)", si->x, si->y);
+    else {
+        switch (g->bpp) {
+          case 2:
+            sprintf(buffer1, "(%d,%d,%d)", si->x, si->y,
+                    options.colors[si->color-1]);
+            break;
+          case 4:
+            sprintf(buffer1, "(%d,%d,%d)", si->x, si->y,
+                    options.colors[si->color+4-1]);
+            break;
+          case 8:
+            sprintf(buffer1, "(%d,%d,%d)", si->x, si->y, si->color);
+            break;
+        }
+    }
+
+    gtk_label_set_text(GTK_LABEL(ge->coords), buffer1);
+}
+
+/*
+ * Under certain circumstances, the glyphedit widget causes the operation to
+ * change. Basically, when a bitmap is pasted, the widget goes into a MOVE
+ * operation. All the operations are handled here just in case of future
+ * changes.
+ */
+static void
+operation_changed(GtkWidget *w, gpointer cb, gpointer ged)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
+    GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
+
+    if (si->operation == GLYPHEDIT_DRAW)
+      gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_DRAW);
+    else if (si->operation == GLYPHEDIT_MOVE)
+      gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_MOVE);
+    else if (si->operation == GLYPHEDIT_COPY)
+      gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_COPY);
+}
+
+static void
+color_changed(GtkWidget *w, gpointer cb, gpointer ged)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
+    GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
+
+    gecontrol_change_color(GECONTROL(ge->gectrl), si->color);
+}
+
+static void
+glyph_modified(GtkWidget *w, gpointer cb, gpointer ged)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
+    gbdfed_editor_t *ed = editors + ge->owner;
+    GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
+    gchar *prgname = g_get_prgname();
+
+    if (si->metrics == 0)
+      return;
+
+    if (ed->file == 0)
+      sprintf(buffer1, "%s - Glyph Edit: (unnamed%d) [modified]",
+              prgname, ed->id);
+    else
+      sprintf(buffer1, "%s - Glyph Edit: %s [modified]", prgname, ed->file);
+
+    gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
+
+    sprintf(buffer1, "width %hd height %hd\nascent %hd descent %hd",
+            si->metrics->width, si->metrics->height,
+            si->metrics->ascent, si->metrics->descent);
+    gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1);
+
+    if (si->metrics->font_spacing == BDF_PROPORTIONAL) {
+        sprintf(buffer1, "%hd", si->metrics->dwidth);
+        g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler);
+        gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1);
+        g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler);
+    }
+
+    /*
+     * Update the glyph image on the Glyphedit control widget.
+     */
+    gecontrol_update_glyph_image(GECONTROL(ge->gectrl), si->image);
+
+    gtk_widget_set_sensitive(ge->update, TRUE);
+    gtk_widget_set_sensitive(ge->update_next, TRUE);
+    gtk_widget_set_sensitive(ge->update_prev, TRUE);
+    gtk_widget_set_sensitive(ge->button_update, TRUE);
+    gtk_widget_set_sensitive(ge->button_next, TRUE);
+    gtk_widget_set_sensitive(ge->button_prev, TRUE);
+}
+
+/*
+ * This function will be a bit screwy until I can figure out how to make a
+ * signal pass an enum as the first param after the widget.
+ */
+static void
+gectrl_activate(GtkWidget *w, gpointer info, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    GEControlActivateInfo *ai = (GEControlActivateInfo *) info;
+
+    switch (ai->operation) {
+      case GECONTROL_DRAW:
+        glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW);
+        break;
+      case GECONTROL_MOVE:
+        glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_MOVE);
+        break;
+      case GECONTROL_COPY:
+        glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_COPY);
+        break;
+      case GECONTROL_FLIP_HORIZONTAL:
+        glyphedit_flip_glyph(GLYPHEDIT(ge->gedit), GTK_ORIENTATION_HORIZONTAL);
+        break;
+      case GECONTROL_FLIP_VERTICAL:
+        glyphedit_flip_glyph(GLYPHEDIT(ge->gedit), GTK_ORIENTATION_VERTICAL);
+        break;
+      case GECONTROL_SHEAR:
+        show_shear_dialog(w, data);
+        break;
+      case GECONTROL_ROTATE_LEFT_90:
+        glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), -90);
+        break;
+      case GECONTROL_ROTATE_RIGHT_90:
+        glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), 90);
+        break;
+      case GECONTROL_ROTATE:
+        show_rotate_dialog(w, data);
+        break;
+      case GECONTROL_SHIFT_UP_LEFT:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, -1);
+        break;
+      case GECONTROL_SHIFT_UP:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 0, -1);
+        break;
+      case GECONTROL_SHIFT_UP_RIGHT:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, -1);
+        break;
+      case GECONTROL_SHIFT_LEFT:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, 0);
+        break;
+      case GECONTROL_SHIFT_RIGHT:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, 0);
+        break;
+      case GECONTROL_SHIFT_DOWN_LEFT:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, 1);
+        break;
+      case GECONTROL_SHIFT_DOWN:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 0, 1);
+        break;
+      case GECONTROL_SHIFT_DOWN_RIGHT:
+        glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, 1);
+        break;
+      case GECONTROL_COLOR_CHANGE:
+        glyphedit_set_color(GLYPHEDIT(ge->gedit), ai->color);
+        break;
+    }
+}
+
+/*
+ * This is called when the device width field is changed in any way.
+ */
+static void
+enable_update(GtkWidget *w, gpointer data)
+{
+    GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
+    gbdfed_editor_t *ed = editors + ge->owner;
+
+    if (ed->file == 0)
+      sprintf(buffer1, "%s - Glyph Edit: (unnamed%d) [modified]",
+              g_get_prgname(), ed->id);
+    else
+      sprintf(buffer1, "%s - Glyph Edit: %s [modified]",
+              g_get_prgname(), ed->file);
+
+    gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
+    gtk_widget_set_sensitive(ge->update, TRUE);
+    gtk_widget_set_sensitive(ge->update_next, TRUE);
+    gtk_widget_set_sensitive(ge->update_prev, TRUE);
+    gtk_widget_set_sensitive(ge->button_update, TRUE);
+}
+
+static void
+_guigedit_build_editor(GlypheditRec *ge, bdf_glyph_grid_t *grid, guint base,
+                       gbdfed_editor_t *ed)
+{
+    GtkWidget *mb, *mitem, *frame, *vbox, *vbox1, *hbox, *img;
+    bdf_bitmap_t image;
+
+    if (ed->file == 0)
+      sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", g_get_prgname(),
+              ed->id);
+    else
+      sprintf(buffer1, "%s - Glyph Edit: %s", g_get_prgname(), ed->file);
+
+    ge->shell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+    gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
+
+    gtk_window_set_resizable(GTK_WINDOW(ge->shell), TRUE);
+
+    (void) g_signal_connect(G_OBJECT(ge->shell), "destroy_event",
+                            G_CALLBACK(close_glyph_editor),
+                            GUINT_TO_POINTER(ge->id));
+    (void) g_signal_connect(G_OBJECT(ge->shell), "delete_event",
+                            G_CALLBACK(close_glyph_editor),
+                            GUINT_TO_POINTER(ge->id));
+
+    vbox = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(ge->shell), vbox);
+
+    ge->ag = gtk_accel_group_new();
+
+    mb = gtk_menu_bar_new();
+    mitem = make_file_menu(ge, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    mitem = make_edit_menu(ge, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    mitem = make_ops_menu(ge, mb);
+    gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
+
+    gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, TRUE, 0);
+
+    /*
+     * Attach the accelerators to the editor.
+     */
+    gtk_window_add_accel_group(GTK_WINDOW(ge->shell), ge->ag);
+
+    /*
+     * 1. Add the glyph name, next/previous buttons and encoding widgets.
+     */
+    frame = gtk_frame_new(0);
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
+
+    vbox1 = gtk_vbox_new(TRUE, 0);
+    gtk_container_add(GTK_CONTAINER(frame), vbox1);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
+
+    ge->name = gtk_widget_new(gtk_entry_get_type(), "max_length", 128, NULL);
+    mitem = labcon_new_label_defaults("Glyph Name:", ge->name, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), mitem, TRUE, TRUE, 0);
+
+    /* Update button */
+    ge->button_update = gtk_button_new();
+    guiutil_util_set_tooltip(ge->button_update, "Update Font");
+    img = gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_SMALL_TOOLBAR);
+    gtk_button_set_image(GTK_BUTTON(ge->button_update), img);
+    g_signal_connect(G_OBJECT(ge->button_update), "clicked", 
+                    G_CALLBACK(update_font), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), ge->button_update, FALSE, FALSE, 0);
+
+    /* Previous button */
+    ge->button_prev = gtk_button_new();
+    guiutil_util_set_tooltip(ge->button_prev, "Previous Glyph");
+    img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, 
+                                  GTK_ICON_SIZE_SMALL_TOOLBAR);
+    gtk_button_set_image(GTK_BUTTON(ge->button_prev), img);
+    g_signal_connect(G_OBJECT(ge->button_prev), "clicked",
+                    G_CALLBACK(previous_glyph), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), ge->button_prev, FALSE, FALSE, 0);
+
+    /* Next button */
+    ge->button_next = gtk_button_new();
+    guiutil_util_set_tooltip(ge->button_next, "Next Glyph");
+    img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, 
+                                  GTK_ICON_SIZE_SMALL_TOOLBAR);
+    gtk_button_set_image(GTK_BUTTON(ge->button_next), img);
+    g_signal_connect(G_OBJECT(ge->button_next), "clicked", 
+                    G_CALLBACK(next_glyph), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), ge->button_next, FALSE, FALSE, 0);
+
+    /* Encoding */
+    ge->encoding = gtk_label_new("0000");
+    gtk_misc_set_alignment(GTK_MISC(ge->encoding), 0.0, 0.5);
+    mitem = labcon_new_label_defaults("Encoding:", ge->encoding, mitem);
+    gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0);
+
+    /*
+     * 2. Add the device width and metrics widgets.
+     */
+    frame = gtk_frame_new(0);
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
+
+    vbox1 = gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(frame), vbox1);
+
+    ge->dwidth = gtk_widget_new(gtk_entry_get_type(),
+                                "max_length", 6,
+                                NULL);
+    ge->handler = g_signal_connect(G_OBJECT(ge->dwidth), "changed",
+                                   G_CALLBACK(enable_update),
+                                   GUINT_TO_POINTER(ge->id));
+    mitem = labcon_new_label_defaults("Device Width:", ge->dwidth, mitem);
+    gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0);
+
+    ge->metrics = gtk_label_new("width 0 height 0\r\nascent 0 descent 0");
+    gtk_misc_set_alignment(GTK_MISC(ge->metrics), 0.0, 0.5);
+    mitem = labcon_new_label_defaults("Metrics:", ge->metrics, mitem);
+    gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+
+    vbox1 = gtk_vbox_new(FALSE, 0);
+
+    /*
+     * Create the coordinates label.
+     */
+    ge->coords = gtk_label_new("(0,0)");
+    gtk_misc_set_alignment(GTK_MISC(ge->coords), 0.5, 0.5);
+    gtk_box_pack_start(GTK_BOX(vbox1), ge->coords, FALSE, TRUE, 0);
+
+    /*
+     * Create the glyph editor.
+     */
+    ge->gedit = glyphedit_newv(grid, options.pixel_size, options.show_x_height,
+                               options.show_cap_height, options.colors);
+    g_signal_connect(G_OBJECT(ge->gedit), "glyph-modified",
+                     G_CALLBACK(glyph_modified), GUINT_TO_POINTER(ge->id));
+    g_signal_connect(G_OBJECT(ge->gedit), "pointer-moved",
+                     G_CALLBACK(pointer_moved), GUINT_TO_POINTER(ge->id));
+    g_signal_connect(G_OBJECT(ge->gedit), "operation-change",
+                     G_CALLBACK(operation_changed), GUINT_TO_POINTER(ge->id));
+    g_signal_connect(G_OBJECT(ge->gedit), "color-change",
+                     G_CALLBACK(color_changed), GUINT_TO_POINTER(ge->id));
+    gtk_box_pack_start(GTK_BOX(vbox1), ge->gedit, TRUE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0);
+
+    vbox1 = gtk_vbox_new(FALSE, 0);
+
+    ge->gectltips = gtk_label_new("");
+    gtk_misc_set_alignment(GTK_MISC(ge->gectltips), 0.5, 0.5);
+    gtk_box_pack_start(GTK_BOX(vbox1), ge->gectltips, FALSE, TRUE, 0);
+
+    /*
+     * Get the initial glyph image.
+     */
+    glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
+    ge->gectrl = gecontrol_newv(ge->gectltips, &image, options.colors);
+    if (image.bytes > 0)
+      free(image.bitmap);
+    g_signal_connect(G_OBJECT(ge->gectrl), "activate",
+                     G_CALLBACK(gectrl_activate), GUINT_TO_POINTER(ge->id));
+
+    gtk_box_pack_start(GTK_BOX(vbox1), ge->gectrl, TRUE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
+}
+
+void
+guigedit_edit_glyph(gbdfed_editor_t *ed, FontgridSelectionInfo *si)
+{
+    GlypheditRec *ge;
+    bdf_font_t *font;
+    guint base;
+    bdf_glyph_grid_t *grid;
+    bdf_bitmap_t image;
+
+    font = fontgrid_get_font(FONTGRID(ed->fgrid));
+    base = fontgrid_get_code_base(FONTGRID(ed->fgrid));
+
+    if (si->unencoded)
+      grid = bdf_make_glyph_grid(font, si->start, 1);
+    else
+      grid = bdf_make_glyph_grid(font, si->start, 0);
+
+    ge = _guigedit_get_glyph_editor(ed->id);
+
+    if (ge->name == 0) {
+        _guigedit_build_editor(ge, grid, base, ed);
+    } else {
+        if (ed->file == 0)
+          sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", g_get_prgname(),
+                  ed->id);
+        else
+          sprintf(buffer1, "%s - Glyph Edit: %s", g_get_prgname(), ed->file);
+
+        gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
+        glyphedit_set_grid(GLYPHEDIT(ge->gedit), grid);
+
+        /*
+         * Update the image in the glypheditor control panel.
+         */
+        if (grid) {
+            bdf_grid_image(grid, &image);
+            gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
+            if (image.bytes > 0)
+              free(image.bitmap);
+        } else
+          gecontrol_set_glyph_image(GECONTROL(ge->gectrl), 0);
+
+        /*
+         * Make sure the control has the most current list of colors.
+         */
+        gecontrol_set_color_list(GECONTROL(ge->gectrl), options.colors);
+    }
+
+    /*
+     * Update the text fields and labels with the glyph info.
+     */
+    gtk_entry_set_text(GTK_ENTRY(ge->name), grid->name);
+    if (grid->unencoded)
+      sprintf(buffer1, "-1");
+    else {
+        switch (base) {
+          case 8: sprintf(buffer1, "%o", grid->encoding); break;
+          case 10: sprintf(buffer1, "%d", grid->encoding); break;
+          case 16: sprintf(buffer1, "%04X", grid->encoding); break;
+        }
+    }
+    gtk_label_set_text(GTK_LABEL(ge->encoding), buffer1);
+
+    gtk_widget_set_sensitive(ge->dwidth, TRUE);
+    sprintf(buffer1, "%hd", grid->dwidth);
+
+    g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler);
+    gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1);
+    g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler);
+
+    if (grid->spacing != BDF_PROPORTIONAL)
+      gtk_widget_set_sensitive(ge->dwidth, FALSE);
+
+    sprintf(buffer1, "width %hd height %hd\r\nascent %hd descent %hd",
+            grid->glyph_bbx.width, grid->glyph_bbx.height,
+            grid->glyph_bbx.ascent, grid->glyph_bbx.descent);
+    gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1);
+
+    if (grid->modified) {
+        gtk_widget_set_sensitive(ge->update, TRUE);
+        gtk_widget_set_sensitive(ge->button_update, TRUE);
+        gtk_widget_set_sensitive(ge->update_next, TRUE);
+        gtk_widget_set_sensitive(ge->update_prev, TRUE);
+    } else {
+        gtk_widget_set_sensitive(ge->update, FALSE);
+        gtk_widget_set_sensitive(ge->button_update, FALSE);
+        gtk_widget_set_sensitive(ge->update_next, FALSE);
+        gtk_widget_set_sensitive(ge->update_prev, FALSE);
+    }
+
+    /*
+     * Set the sensitivity of the next and previous buttons.
+     */
+    if (glyphedit_get_encoding(GLYPHEDIT(ge->gedit)) == 0)
+      gtk_widget_set_sensitive(ge->button_prev, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->button_prev, TRUE);
+
+    if (glyphedit_get_encoding(GLYPHEDIT(ge->gedit)) == 0xffff)
+      gtk_widget_set_sensitive(ge->button_next, FALSE);
+    else
+      gtk_widget_set_sensitive(ge->button_next, TRUE);
+
+    gtk_widget_show_all(ge->shell);
+
+    /*
+     * Force the focus to be on the glyph grid.
+     */
+    gtk_widget_grab_focus(ge->gedit);
+}
+
+/*
+ * Routine to set the show_cap_height value for all the glyph editors.
+ */
+void
+guigedit_show_cap_height(gboolean show)
+{
+    gulong i;
+
+    for (i = 0; i < num_glyph_editors; i++) {
+        if (glyph_editors[i].owner != ~0)
+          glyphedit_set_show_cap_height(GLYPHEDIT(glyph_editors[i].gedit),
+                                        show);
+    }
+}
+
+/*
+ * Routine to set the show_cap_height value for all the glyph editors.
+ */
+void
+guigedit_show_x_height(gboolean show)
+{
+    gulong i;
+
+    for (i = 0; i < num_glyph_editors; i++) {
+        if (glyph_editors[i].owner != ~0)
+          glyphedit_set_show_x_height(GLYPHEDIT(glyph_editors[i].gedit), show);
+    }
+}
+
+/*
+ * Routine to set the pixel size on all of the visible editors.
+ */
+void
+guigedit_set_pixel_size(guint pixel_size)
+{
+    gulong i;
+
+    for (i = 0; i < num_glyph_editors; i++) {
+        if (glyph_editors[i].owner != ~0)
+          glyphedit_set_pixel_size(GLYPHEDIT(glyph_editors[i].gedit),
+                                   pixel_size);
+    }
+}
+
+/*
+ * Routine to set the spacing and device width on all of the visible editors.
+ */
+void
+guigedit_set_font_spacing(gint spacing, guint16 monowidth)
+{
+    gulong i;
+
+    for (i = 0; i < num_glyph_editors; i++) {
+        if (glyph_editors[i].owner != ~0)
+          glyphedit_set_spacing(GLYPHEDIT(glyph_editors[i].gedit), spacing,
+                                monowidth);
+    }
+}
+
+/*
+ * Routine to set the code base on all the glyph editors that are active.
+ */
+void
+guigedit_set_code_base(gint base)
+{
+    guint i, enc;
+
+    for (i = 0; i < num_glyph_editors; i++) {
+        if (glyph_editors[i].owner != ~0) {
+            enc = (guint) glyphedit_get_encoding(GLYPHEDIT(glyph_editors[i].gedit));
+            switch (base) {
+              case 8:
+                sprintf(buffer1, "%o", enc);
+                break;
+              case 10:
+                sprintf(buffer1, "%d", enc);
+                break;
+              case 16:
+                sprintf(buffer1, "%04X", enc);
+                break;
+            }
+            gtk_label_set_text(GTK_LABEL(glyph_editors[i].encoding), buffer1);
+        }
+    }
+}
+
+/*
+ * Routine to clean up everything that was allocated here.
+ */
+void
+guigedit_cleanup(void)
+{
+    gulong i;
+
+    /*
+     * Cycle through all the glyph editors and check if they have been
+     * modified and if they need to be saved.
+     */
+    for (i = 0; i < num_glyph_editors; i++) {
+        if (glyph_editors[i].id != ~0)
+          close_glyph_editor(0, 0, GUINT_TO_POINTER(i));
+    }
+
+    /*
+     * Unreference all the pixbufs so they go away.
+     */
+    if (lb_image != 0)
+      g_object_unref(G_OBJECT(lb_image));
+    if (rb_image != 0)
+      g_object_unref(G_OBJECT(rb_image));
+    if (as_image != 0)
+      g_object_unref(G_OBJECT(as_image));
+    if (ds_image != 0)
+      g_object_unref(G_OBJECT(ds_image));
+
+    lb_image = rb_image = as_image = ds_image = 0;
+
+    /*
+     * GTK will take care of the widgets in the glyph editors.
+     */
+    if (num_glyph_editors > 0)
+      g_free(glyph_editors);
+}
diff --git a/guihelp.c b/guihelp.c
new file mode 100644 (file)
index 0000000..b20b632
--- /dev/null
+++ b/guihelp.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+#include "htext.h"
+
+static GtkWidget *help_dialog;
+static GtkWidget *help_text;
+static GtkWidget *help_topics;
+
+typedef struct {
+    int mnemonic;
+    gchar *help_topic;
+    gchar *help_text;
+} helpmap_t;
+
+static helpmap_t topics[] = {
+    {'A', "About",               0},
+    {'P', "The Program",         0},
+    {'F', "Font Grid",           0},
+    {'G', "Glyph Editor",        0},
+    {'C', "Configuration File",  0},
+    {'r', "Preferences Dialog",  0},
+    {'W', "Windows Font Notes",  0},
+    {'O', "OpenType Font Notes", 0},
+    {'P', "PSF Font Notes",      0},
+    {'H', "HEX Font Notes",      0},
+    {'o', "Color Notes",         0},
+    {'i', "Tips",                0},
+};
+
+static guint ntopics = sizeof(topics) / sizeof(topics[0]);
+
+/**************************************************************
+ *
+ * Routines for parsing the markup for the help text buffer.
+ *
+ **************************************************************/
+
+#define HTEXT_BULLET 0x0001
+#define HTEXT_UL     0x0002
+
+typedef struct {
+    guint flags;
+    GtkTextBuffer *text;
+    const gchar *tag_name;
+    GtkTextIter iter;
+} htext_parse_t;
+
+static htext_parse_t hp;
+
+static const gchar *bullet = "•";
+
+void
+help_parse_start(GMarkupParseContext *ctx, const gchar *tag,
+                 const gchar **attr_names, const gchar **attr_vals,
+                 gpointer data, GError **err)
+{
+    if (strcmp(tag, "bullet") == 0) {
+        gtk_text_buffer_insert_with_tags_by_name(hp.text, &hp.iter,
+                                                 bullet, 3,
+                                                 "margin",
+                                                 "large_bullet", NULL);
+        hp.flags |= HTEXT_BULLET;
+        hp.tag_name = 0;
+    } else if (strcmp(tag, "help") == 0)
+      hp.tag_name = 0;
+    else
+      hp.tag_name = tag;
+}
+
+void
+help_parse_text(GMarkupParseContext *ctx, const gchar *txt, gsize txtlen,
+                gpointer data, GError **err)
+{
+    if (hp.tag_name != 0)
+      gtk_text_buffer_insert_with_tags_by_name(hp.text, &hp.iter,
+                                               txt, txtlen,
+                                               hp.tag_name, NULL);
+    else {
+        if (hp.flags & HTEXT_BULLET)
+          gtk_text_buffer_insert_with_tags_by_name(hp.text, &hp.iter,
+                                                   txt, txtlen,
+                                                   "tabs", NULL);
+        else
+          /*
+           * Plain text insert.
+           */
+          gtk_text_buffer_insert(hp.text, &hp.iter, txt, txtlen);
+    }
+}
+
+void
+help_parse_end(GMarkupParseContext *ctx, const gchar *tag,
+               gpointer data, GError **err)
+{
+    if (strcmp(tag, "bullet") == 0)
+      hp.flags &= ~HTEXT_BULLET;
+    hp.tag_name = 0;
+}
+
+void
+help_parse_error(GMarkupParseContext *ctx, GError *err, gpointer data)
+{
+}
+
+static GMarkupParser markup_funcs = {
+    help_parse_start,
+    help_parse_end,
+    help_parse_text,
+    0,
+    help_parse_error,
+};
+
+static GMarkupParseContext *markup_context = NULL;
+
+/*
+ * Creates all the markup tags.
+ */
+static void
+help_init_markup(void)
+{
+    PangoTabArray *tabs;
+
+    gtk_text_buffer_create_tag(hp.text, "center",
+                               "justification", GTK_JUSTIFY_CENTER, NULL);
+    gtk_text_buffer_create_tag(hp.text, "margin",
+                               "left_margin", 20, NULL);
+    gtk_text_buffer_create_tag(hp.text, "margin1",
+                               "left_margin", 30, NULL);
+    gtk_text_buffer_create_tag(hp.text, "margin2",
+                               "left_margin", 45, NULL);
+    gtk_text_buffer_create_tag(hp.text, "margin1.5",
+                               "left_margin", 30,
+                               "weight", PANGO_WEIGHT_BOLD,
+                               NULL);
+    gtk_text_buffer_create_tag(hp.text, "margin3",
+                               "left_margin", 60, NULL);
+    gtk_text_buffer_create_tag(hp.text, "large_bullet",
+                               "scale", PANGO_SCALE_X_LARGE, NULL);
+    gtk_text_buffer_create_tag(hp.text, "param",
+                               "weight", PANGO_WEIGHT_BOLD,
+                               "left_margin", 10, NULL);
+    gtk_text_buffer_create_tag(hp.text, "ul",
+                               "underline", PANGO_UNDERLINE_SINGLE, NULL);
+    gtk_text_buffer_create_tag(hp.text, "bul",
+                               "underline", PANGO_UNDERLINE_SINGLE,
+                               "weight", PANGO_WEIGHT_BOLD, NULL);
+
+    tabs = pango_tab_array_new_with_positions(2, TRUE,
+                                              PANGO_TAB_LEFT, 50,
+                                              PANGO_TAB_LEFT, 70);
+    gtk_text_buffer_create_tag(hp.text, "tabs",
+                               "tabs", tabs,
+                               "left_margin", 10,
+                               NULL);
+
+    gtk_text_buffer_create_tag(hp.text, "i",
+                               "style", PANGO_STYLE_ITALIC, NULL);
+    gtk_text_buffer_create_tag(hp.text, "b",
+                               "weight", PANGO_WEIGHT_BOLD, NULL);
+    gtk_text_buffer_create_tag(hp.text, "bi",
+                               "style", PANGO_STYLE_ITALIC,
+                               "weight", PANGO_WEIGHT_BOLD, NULL);
+}
+
+/**************************************************************
+ *
+ * API functions.
+ *
+ **************************************************************/
+
+static void
+help_free_list_item(gpointer data, gpointer user_data)
+{
+    gtk_tree_path_free((GtkTreePath *) data);
+}
+
+static void
+help_on(GtkTreeSelection *sel, gpointer data)
+{
+    GList *rowlist;
+    gint row = 1, *rows;
+    GError *err = 0;
+
+    /*
+     * Because this is called before the help text widget is created,
+     * simply return if the help text widget is NULL.
+     */
+    if (help_text == 0)
+      return;
+
+    /*
+     * Sorta complicated, but can't see any other way around it at the moment
+     * with gtk 2.2.4.
+     */
+    rowlist = gtk_tree_selection_get_selected_rows(sel, NULL);
+    rows =
+        gtk_tree_path_get_indices((GtkTreePath *) g_list_nth_data(rowlist, 0));
+    row = rows[0];
+
+    /*
+     * Clear out the rows that were returned.
+     */
+    g_list_foreach(rowlist, help_free_list_item, 0);
+
+    /*
+     * Set the dialog title.
+     */
+    sprintf(buffer1, "Help: %s", topics[row].help_topic);
+    gtk_window_set_title(GTK_WINDOW(help_dialog), buffer1);
+
+    /*
+     * Clear the text buffer.
+     */
+    gtk_text_buffer_set_text(hp.text, "", 0);
+
+    /*
+     * Get the iterator at the start of the buffer.
+     */
+    gtk_text_buffer_get_start_iter(hp.text, &hp.iter);
+
+    /*
+     * Parse the help text.
+     */
+    if (g_markup_parse_context_parse(markup_context,
+                                     topics[row].help_text, -1, &err) == FALSE)
+      fprintf(stderr, "guihelp.c:%s\n", err->message);
+}
+
+void
+guihelp_show_help(GtkWidget *w, gpointer data)
+{
+    guint i;
+    GtkWidget *hbox, *sw, *button;
+    GtkTextBuffer *text;
+    GtkListStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *cell_renderer;
+    GtkTreeSelection *sel;
+    GtkTreePath *tpath;
+    GtkTreeIter iter;
+
+    if (help_dialog == 0) {
+        /*
+         * Store the help text into the array.
+         */
+        topics[GUIHELP_ABOUT].help_text = about_text;
+        topics[GUIHELP_PROGRAM].help_text = program_text;
+        topics[GUIHELP_FONTGRID].help_text = fgrid_text;
+        topics[GUIHELP_GLYPH_EDITOR].help_text = gedit_text;
+        topics[GUIHELP_CONFIG_FILE].help_text = conf_text;
+        topics[GUIHELP_PREFERENCES].help_text = preferences_text;
+        topics[GUIHELP_FNT].help_text = fnt_text;
+        topics[GUIHELP_OTF].help_text = otf_text;
+        topics[GUIHELP_PSF].help_text = psf_text;
+        topics[GUIHELP_HEX].help_text = hex_text;
+        topics[GUIHELP_COLOR].help_text = color_text;
+        topics[GUIHELP_TIPS].help_text = tips_text;
+
+        help_dialog = gtk_dialog_new();
+
+        /*
+         * Force the help dialog to center over the first editor that
+         * was created.
+         */
+        (void) g_signal_connect(G_OBJECT(help_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), NULL);
+
+        hbox = gtk_hbox_new(FALSE, 5);
+
+        /*
+         * Create the list that will be used for the help topics.
+         */
+        store = gtk_list_store_new(1, G_TYPE_STRING);
+
+        /*
+         * Add the topics to the list.
+         */
+        for (i = 0; i < ntopics; i++) {
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, topics[i].help_topic, -1);
+        }
+        help_topics = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+
+        cell_renderer = gtk_cell_renderer_text_new();
+        column = gtk_tree_view_column_new_with_attributes ("Help Topics",
+                                                           cell_renderer,
+                                                           "text", 0,
+                                                           NULL);
+        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column (GTK_TREE_VIEW(help_topics), column);
+
+        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_topics));
+        gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+
+        (void) g_signal_connect(G_OBJECT(sel), "changed",
+                                G_CALLBACK(help_on), NULL);
+
+        gtk_box_pack_start(GTK_BOX(hbox), help_topics, FALSE, FALSE, 0);
+
+        g_object_unref(store);
+
+        gtk_box_pack_start(GTK_BOX(hbox), gtk_vseparator_new(),
+                           FALSE, FALSE, 0);
+
+        /*
+         * Create the text widget that will display the help text.
+         */
+        sw = gtk_scrolled_window_new(NULL, NULL);
+        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_ALWAYS);
+
+        text = gtk_text_buffer_new(NULL);
+        help_text = gtk_text_view_new_with_buffer(text);
+        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(help_text), GTK_WRAP_WORD);
+        gtk_text_view_set_editable(GTK_TEXT_VIEW(help_text), FALSE);
+        gtk_widget_set_size_request(help_text, 550, 300);
+
+        gtk_container_add(GTK_CONTAINER(sw), help_text);
+
+        gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0);
+
+        /*
+         * Add the table to the dialog widget.
+         */
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(help_dialog)->vbox),
+                          hbox);
+
+        button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+        (void) g_signal_connect_object(G_OBJECT(button), "clicked",
+                                       G_CALLBACK(gtk_widget_hide),
+                                       (gpointer) help_dialog,
+                                       G_CONNECT_SWAPPED);
+
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(help_dialog)->action_area),
+                          button);
+        gtk_widget_show_all(GTK_DIALOG(help_dialog)->vbox);
+        gtk_widget_show_all(GTK_DIALOG(help_dialog)->action_area);
+
+        /*
+         * Create all the markup tags the text buffer will use.
+         */
+        hp.flags = 0;
+        hp.text = text;
+        hp.tag_name = "";
+        help_init_markup();
+
+        /*
+         * Create the context for the parser.
+         */
+        markup_context = g_markup_parse_context_new(&markup_funcs, 0,
+                                                    NULL, NULL);
+    }
+
+    /*
+     * Select the row specified in the callback.
+     */
+    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_topics));
+    tpath = gtk_tree_path_new_from_indices(GPOINTER_TO_INT(data), -1);
+    gtk_tree_selection_select_path(sel, tpath);
+
+    /*
+     * Show the help dialog and force it to the top.
+     */
+    guiutil_show_dialog_centered(help_dialog, editors[0].shell);
+    gdk_window_raise(help_dialog->window);
+}
+
+void
+guihelp_cleanup(void)
+{
+    if (markup_context != NULL)
+      g_markup_parse_context_free(markup_context);
+    markup_context = NULL;
+}
diff --git a/guiops.c b/guiops.c
new file mode 100644 (file)
index 0000000..592170a
--- /dev/null
+++ b/guiops.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+
+static GtkWidget *ops_dialog;
+static GtkWidget *ops_notebook;
+static GtkWidget *ops_dx;
+static GtkWidget *ops_dy;
+static GtkWidget *ops_rotate;
+static GtkWidget *ops_shear;
+static GtkWidget *ops_degrees;
+static GtkWidget *ops_all_glyphs;
+static GtkWidget *ops_selected_glyphs;
+static GtkWidget *ops_apply;
+static gboolean translate_changed;
+static gboolean degrees_changed;
+static gboolean embolden_changed;
+static gint page;
+
+static void
+apply_operation(GtkWidget *w, gint response, gpointer data)
+{
+    gbdfed_editor_t *ed;
+    gint16 dx, dy;
+    gboolean all;
+
+    /*
+     * Turn off the Apply button for this page.
+     */
+    gtk_widget_set_sensitive(ops_apply, FALSE);
+
+    if (response == GTK_RESPONSE_CLOSE) {
+        gtk_widget_hide(ops_dialog);
+        gtk_widget_set_sensitive(ops_apply, FALSE);
+        translate_changed = degrees_changed = embolden_changed = FALSE;
+        return;
+    }
+
+    ed = editors +
+        GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(w), "editor"));
+
+    all = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ops_all_glyphs));
+
+    if (translate_changed) {
+        /*
+         * Do a glyph translate.
+         */
+        dx = _bdf_atos((char *) gtk_entry_get_text(GTK_ENTRY(ops_dx)), 0, 10);
+        dy = _bdf_atos((char *) gtk_entry_get_text(GTK_ENTRY(ops_dy)), 0, 10);
+        fontgrid_translate_glyphs(FONTGRID(ed->fgrid), dx, dy, all);
+        translate_changed = FALSE;
+    }
+
+    if (degrees_changed) {
+        /*
+         * Do either a rotate or a shear.
+         */
+        dx = _bdf_atos((char *) gtk_entry_get_text(GTK_ENTRY(ops_degrees)),
+                       0, 10);
+        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ops_rotate)))
+          fontgrid_rotate_glyphs(FONTGRID(ed->fgrid), dx, all);
+        else
+          fontgrid_shear_glyphs(FONTGRID(ed->fgrid), dx, all);
+        degrees_changed = FALSE;
+    }
+
+    if (embolden_changed) {
+        /*
+         * Embolden some glyphs.
+         */
+        fontgrid_embolden_glyphs(FONTGRID(ed->fgrid), all);
+        embolden_changed = FALSE;
+    }
+}
+
+static void
+notebook_switch_page(GtkNotebook *nb, GtkNotebookPage *nbp, gint pageno,
+                     gpointer data)
+{
+    GtkWidget *which = 0;
+
+    /*
+     * Force the focus on the entry fields when these pages become visible.
+     */
+    switch (pageno) {
+      case 0:
+        if (ops_apply != 0)
+          gtk_widget_set_sensitive(ops_apply, translate_changed);
+        which = ops_dx;
+        break;
+      case 1:
+        if (ops_apply != 0)
+          gtk_widget_set_sensitive(ops_apply, degrees_changed);
+        which = ops_degrees;
+        break;
+      case 2:
+        if (ops_apply != 0)
+          gtk_widget_set_sensitive(ops_apply, embolden_changed);
+        which = 0;
+    }
+    page = pageno;
+
+    if (which)
+      gtk_widget_grab_focus(which);
+}
+
+static void
+enable_apply(GtkWidget *w, gpointer data)
+{
+    gint which = GPOINTER_TO_INT(data);
+    gboolean val = TRUE;
+
+    if (which == 0)
+      translate_changed = TRUE;
+    else if (which == 1)
+      degrees_changed = TRUE;
+    else {
+        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
+          embolden_changed = TRUE;
+        else
+          embolden_changed = FALSE;
+        val = embolden_changed;
+    }
+
+    gtk_widget_set_sensitive(ops_apply, val);
+}
+
+static void
+ops_dialog_setup(gbdfed_editor_t *ed)
+{
+    GtkWidget *vbox, *hbox, *vbox1, *label, *table, *button;
+    GtkRadioButton *rb;
+    GtkAdjustment *adj;
+
+    if (ops_dialog == 0) {
+        translate_changed = degrees_changed = FALSE;
+
+        ops_dialog = gtk_dialog_new();
+        (void) g_signal_connect(G_OBJECT(ops_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        vbox = gtk_vbox_new(FALSE, 0);
+
+        ops_notebook = gtk_notebook_new();
+        (void) g_signal_connect(G_OBJECT(ops_notebook), "switch_page",
+                                G_CALLBACK(notebook_switch_page), 0);
+
+        /*
+         * Create the notebook pages.
+         */
+        table = gtk_table_new(2, 2, FALSE);
+
+        label = gtk_label_new("DX:");
+        gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL,
+                         GTK_FILL, 0, 0);
+
+        label = gtk_label_new("DY:");
+        gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL,
+                         GTK_FILL, 0, 0);
+
+        ops_dx = gtk_widget_new(gtk_entry_get_type(),
+                                "max_length", 6, NULL);
+        (void) g_signal_connect(G_OBJECT(ops_dx), "changed",
+                                G_CALLBACK(enable_apply),
+                                GINT_TO_POINTER(0));
+        gtk_table_attach(GTK_TABLE(table), ops_dx, 1, 2, 0, 1, GTK_FILL,
+                         GTK_FILL, 5, 5);
+        ops_dy = gtk_widget_new(gtk_entry_get_type(),
+                                "max_length", 6, NULL);
+        (void) g_signal_connect(G_OBJECT(ops_dx), "changed",
+                                G_CALLBACK(enable_apply),
+                                GINT_TO_POINTER(0));
+        gtk_table_attach(GTK_TABLE(table), ops_dy, 1, 2, 1, 2, GTK_FILL,
+                         GTK_FILL, 5, 5);
+
+        label = gtk_label_new("Translate Glyphs");
+        gtk_notebook_append_page(GTK_NOTEBOOK(ops_notebook), table, label);
+
+        vbox1 = gtk_vbox_new(FALSE, 0);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+
+        ops_rotate = gtk_radio_button_new_with_label(0, "Rotate");
+        gtk_box_pack_start(GTK_BOX(hbox), ops_rotate, FALSE, FALSE, 2);
+        rb = GTK_RADIO_BUTTON(ops_rotate);
+        ops_shear = gtk_radio_button_new_with_label_from_widget(rb, "Shear");
+        gtk_box_pack_start(GTK_BOX(hbox), ops_shear, FALSE, FALSE, 2);
+
+        gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+        label = gtk_label_new("Degrees:");
+        adj = (GtkAdjustment *) gtk_adjustment_new(0.0, -359.0, 359.0, 1.0,
+                                                   10.0, 0.0);
+        ops_degrees = gtk_widget_new(gtk_spin_button_get_type(),
+                                     "max_length", 6,
+                                     "adjustment", adj,
+                                     "climb_rate", 1.0,
+                                     "digits", 0,
+                                     "value", 0.0,
+                                     "numeric", TRUE,
+                                     NULL);
+        gtk_widget_set_size_request(ops_degrees, 60, -1);
+        (void) g_signal_connect(G_OBJECT(ops_degrees), "changed",
+                                G_CALLBACK(enable_apply),
+                                GINT_TO_POINTER(1));
+        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+        gtk_box_pack_start(GTK_BOX(hbox), ops_degrees, FALSE, FALSE, 0);
+
+        gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 3);
+
+        label = gtk_label_new("Rotate/Shear Glyphs");
+        gtk_notebook_append_page(GTK_NOTEBOOK(ops_notebook), vbox1, label);
+
+        /*
+         * Add the embolden check button.
+         */
+        button = gtk_check_button_new_with_label("Embolden");
+        (void) g_signal_connect(G_OBJECT(button), "toggled",
+                                G_CALLBACK(enable_apply),
+                                GINT_TO_POINTER(2));
+        label = gtk_label_new("Embolden Glyphs");
+        gtk_notebook_append_page(GTK_NOTEBOOK(ops_notebook), button,
+                                 label);
+
+        /*
+         * Add the notebook to the containing vbox.
+         */
+        gtk_box_pack_start(GTK_BOX(vbox), ops_notebook, FALSE, FALSE, 0);
+
+        /*
+         * Add the radio buttons for choosing between all the glyphs or
+         * just the selected glyphs.
+         */
+        hbox = gtk_hbox_new(FALSE, 0);
+        ops_all_glyphs = gtk_radio_button_new_with_label(0, "All Glyphs");
+        rb = GTK_RADIO_BUTTON(ops_all_glyphs);
+        ops_selected_glyphs =
+            gtk_radio_button_new_with_label_from_widget(rb, "Selected Glyphs");
+        /*
+         * Default to the selected glyphs.
+         */
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_selected_glyphs),
+                                     TRUE);
+        gtk_box_pack_start(GTK_BOX(hbox), ops_all_glyphs, FALSE, FALSE, 2);
+        gtk_box_pack_start(GTK_BOX(hbox), ops_selected_glyphs,
+                           FALSE, FALSE, 2);
+
+        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+
+        /*
+         * Add the vbox to the dialog.
+         */
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(ops_dialog)->vbox), vbox);
+
+        ops_apply = gtk_dialog_add_button(GTK_DIALOG(ops_dialog),
+                                          GTK_STOCK_APPLY,
+                                          GTK_RESPONSE_APPLY);
+        gtk_widget_set_sensitive(ops_apply, FALSE);
+
+        button = gtk_dialog_add_button(GTK_DIALOG(ops_dialog),
+                                       GTK_STOCK_CLOSE,
+                                       GTK_RESPONSE_CLOSE);
+
+        g_signal_connect(G_OBJECT(ops_dialog), "response",
+                         G_CALLBACK(apply_operation), 0);
+
+        gtk_widget_show_all(GTK_DIALOG(ops_dialog)->vbox);
+    }
+
+    if (fontgrid_has_selection(FONTGRID(ed->fgrid), 0))
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_selected_glyphs),
+                                   TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_all_glyphs), TRUE);
+
+    /*
+     * Set the title of the dialog.
+     */
+    if (ed->file == 0)
+      sprintf(buffer1, "(unnamed%d): Glyph Operations", ed->id);
+    else
+      sprintf(buffer1, "%s: Glyph Operations", ed->file);
+
+    gtk_window_set_title(GTK_WINDOW(ops_dialog), buffer1);
+}
+
+void
+guiops_show_translate(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    ops_dialog_setup(ed);
+
+    /*
+     * Set the object data to be the editor.
+     */
+    g_object_set_data(G_OBJECT(ops_dialog), "editor", data);
+
+    /*
+     * Show the translate page.
+     */
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 0);
+
+    /*
+     * Show the dialog.
+     */
+    guiutil_show_dialog_centered(ops_dialog, ed->shell);
+
+    gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE);
+}
+
+void
+guiops_show_rotate(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    ops_dialog_setup(ed);
+
+    /*
+     * Set the object data to be the editor.
+     */
+    g_object_set_data(G_OBJECT(ops_dialog), "editor", data);
+
+    /*
+     * Show the rotate page.
+     */
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_rotate), TRUE);
+
+    /*
+     * Show the dialog.
+     */
+    guiutil_show_dialog_centered(ops_dialog, ed->shell);
+
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 1);
+
+    gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE);
+}
+
+void
+guiops_show_shear(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    ops_dialog_setup(ed);
+
+    /*
+     * Set the object data to be the editor.
+     */
+    g_object_set_data(G_OBJECT(ops_dialog), "editor", data);
+
+    /*
+     * Show the shear page.
+     */
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_shear), TRUE);
+
+    /*
+     * Show the dialog.
+     */
+    guiutil_show_dialog_centered(ops_dialog, ed->shell);
+
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 1);
+
+    gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE);
+}
+
+void
+guiops_show_embolden(GtkWidget *w, gpointer data)
+{
+    gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data);
+
+    ops_dialog_setup(ed);
+
+    /*
+     * Set the object data to be the editor.
+     */
+    g_object_set_data(G_OBJECT(ops_dialog), "editor", data);
+
+    /*
+     * Show the dialog.
+     */
+    guiutil_show_dialog_centered(ops_dialog, ed->shell);
+
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 2);
+
+    gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE);
+}
diff --git a/guipref.c b/guipref.c
new file mode 100644 (file)
index 0000000..3f5c768
--- /dev/null
+++ b/guipref.c
@@ -0,0 +1,1161 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+#include "grayswatch.h"
+
+static GtkWidget *pref_dialog;
+static GtkWidget *pref_unicode;
+static GtkWidget *pref_adobe;
+static GtkWidget *pref_cursor_font;
+static GtkWidget *pref_apply;
+
+static GtkWidget *pref_fsel_dialog;
+static gboolean pref_fsel_unicode;
+
+static GtkWidget *pref_color;
+static GtkWidget *pref_color_dialog;
+static GtkWidget *pref_color_win[16];
+
+static gbdfed_options_t tmp_opts;
+
+static void
+pref_toggle(GtkWidget *w, gpointer data)
+{
+    gint which;
+    gboolean val = FALSE;
+
+    which = GPOINTER_TO_INT(data);
+    if (which != 10)
+      val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+
+    switch (which) {
+      case 0: tmp_opts.backups = val; break;
+      case 1: tmp_opts.font_opts.correct_metrics = val; break;
+      case 2: tmp_opts.font_opts.pad_cells = val; break;
+      case 3: tmp_opts.font_opts.keep_unencoded = val; break;
+      case 4: tmp_opts.font_opts.keep_comments = val; break;
+      case 5:
+        if (val == TRUE)
+          tmp_opts.font_opts.otf_flags &= ~FT_LOAD_NO_HINTING;
+        else
+          tmp_opts.font_opts.otf_flags |= FT_LOAD_NO_HINTING;
+        break;
+      case 6: tmp_opts.sbit = val; break;
+      case 7:
+        tmp_opts.show_cap_height = val;
+        break;
+      case 8:
+        tmp_opts.show_x_height = val;
+        break;
+      case 9:
+        /*
+         * Toggle the Really Exit dialog.
+         */
+        tmp_opts.really_exit = val;
+        break;
+      case 10:
+        tmp_opts.pixel_size = (unsigned int)
+            gtk_combo_box_get_active(GTK_COMBO_BOX(w)) + 2;
+        break;
+    }
+
+    /*
+     * Enable the Apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static void
+pref_eol(GtkWidget *w, gpointer data)
+{
+    tmp_opts.font_opts.eol = gtk_combo_box_get_active(GTK_COMBO_BOX(w)) + 1;
+
+    /*
+     * Enable the Apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static GtkWidget *
+pref_make_general_page()
+{
+    GtkWidget *table, *button, *hbox, *tmp, *omenu, *frame, *vbox;
+
+    vbox = gtk_vbox_new(FALSE, 10);
+
+    /*
+     * Create the load/save option selection.
+     */
+    frame = gtk_frame_new("Load/Save");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    table = gtk_table_new(2, 3, FALSE);
+
+    button = gtk_check_button_new_with_label("Make Backups");
+    if (tmp_opts.backups)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(0));
+    gtk_table_attach(GTK_TABLE(table), button, 0, 1, 0, 1,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+    button = gtk_check_button_new_with_label("Correct Metrics");
+    if (tmp_opts.font_opts.correct_metrics)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(1));
+    gtk_table_attach(GTK_TABLE(table), button, 1, 2, 0, 1,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+    button = gtk_check_button_new_with_label("Pad Character Cells");
+    if (tmp_opts.font_opts.pad_cells)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(2));
+    gtk_table_attach(GTK_TABLE(table), button, 2, 3, 0, 1,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+    button = gtk_check_button_new_with_label("Keep Unencoded Glyphs");
+    if (tmp_opts.font_opts.keep_unencoded)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(3));
+    gtk_table_attach(GTK_TABLE(table), button, 0, 1, 1, 2,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+    button = gtk_check_button_new_with_label("Keep Comments");
+    if (tmp_opts.font_opts.keep_comments)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(4));
+    gtk_table_attach(GTK_TABLE(table), button, 1, 2, 1, 2,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    tmp = gtk_label_new("EOL:");
+    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
+
+    omenu = gtk_combo_box_new_text();
+    gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), "Unix [LF]");
+    gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), "WIN/DOS [CRLF]");
+    gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), "MAC [CR]");
+    gtk_combo_box_set_active(GTK_COMBO_BOX(omenu), tmp_opts.font_opts.eol - 1);
+    (void) g_signal_connect(G_OBJECT(omenu), "changed",
+                            G_CALLBACK(pref_eol), 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), omenu, TRUE, TRUE, 0);
+
+    gtk_table_attach(GTK_TABLE(table), hbox, 2, 3, 1, 2,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+    gtk_container_add(GTK_CONTAINER(frame), table);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    frame = gtk_frame_new("OpenType");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    button = gtk_check_button_new_with_label("Hint Glyphs");
+    if (!(tmp_opts.font_opts.otf_flags & FT_LOAD_NO_HINTING))
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+#ifdef HAVE_FREETYPE
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(5));
+#else
+    /*
+     * No Freetype support means no point in being able to toggle this
+     * widget.
+     */
+    gtk_widget_set_sensitive(button, FALSE);
+#endif
+    gtk_container_add(GTK_CONTAINER(frame), button);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    frame = gtk_frame_new("SBIT");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    button = gtk_check_button_new_with_label("Generate SBIT Metrics File");
+    if (tmp_opts.sbit)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(6));
+    gtk_container_add(GTK_CONTAINER(frame), button);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    return vbox;
+}
+
+static void
+pref_change_size(GtkWidget *w, gpointer data)
+{
+    gint v, which = GPOINTER_TO_INT(data);
+
+    v = (gint) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w));
+
+    switch (which) {
+      case 0: tmp_opts.font_opts.point_size = (int) v; break;
+      case 1: tmp_opts.font_opts.resolution_x = (int) v; break;
+      case 2: tmp_opts.font_opts.resolution_y = (int) v; break;
+    }
+
+    /*
+     * Enable the apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+/*
+ * Synchronize the vertical resolution with the horizontal resolution.
+ */
+static void
+pref_sync_res(GtkWidget *w, GdkEventFocus *ev, gpointer data)
+{
+    gfloat v;
+    GtkSpinButton *b;
+
+    b = GTK_SPIN_BUTTON(data);
+    v = (gfloat) gtk_spin_button_get_value(b);
+
+    if (v != (gfloat) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))) {
+        gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v);
+
+        /*
+         * Enable the apply button.
+         */
+        gtk_widget_set_sensitive(pref_apply, TRUE);
+    }
+}
+
+static void
+pref_set_spacing(GtkWidget *w, gpointer data)
+{
+    tmp_opts.font_opts.font_spacing = GPOINTER_TO_INT(data);
+
+    /*
+     * Enable the apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static void
+pref_set_cursor_font(GtkWidget *w, gpointer data)
+{
+    tmp_opts.font_opts.cursor_font =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+
+    /*
+     * Enable the apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static void
+pref_color_response(GtkDialog *d, gint response, gpointer data)
+{
+    gint i;
+
+    if (response == GTK_RESPONSE_REJECT) {
+        /*
+         * Replace the colors with those found in the original
+         * options.
+         */
+        memcpy(&tmp_opts.colors, &options.colors,
+               sizeof(unsigned short) * 20);
+
+        if (tmp_opts.font_opts.bits_per_pixel == 2) {
+            for (i = 0; i < 4; i++)
+              grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]),
+                                  tmp_opts.colors[i]);
+        } else {
+            for (i = 0; i < 16; i++)
+              grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]),
+                                  tmp_opts.colors[i + 4]);
+        }
+    } else if (response == GTK_RESPONSE_CLOSE)
+      gtk_widget_hide(GTK_WIDGET(data));
+}
+
+static void
+pref_color_update_color(GtkWidget *w, gint color, gpointer data)
+{
+    gint which = GPOINTER_TO_INT(data);
+
+    if (tmp_opts.font_opts.bits_per_pixel == 2)
+      tmp_opts.colors[which] = color;
+    else if (tmp_opts.font_opts.bits_per_pixel == 4)
+      tmp_opts.colors[which + 4] = color;
+
+    /*
+     * Make sure the Apply button is enabled.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static void
+pref_select_colors(GtkWidget *w, gpointer data)
+{
+    GtkWidget *hbox;
+    gint i;
+
+    if (pref_color_dialog == 0) {
+
+        pref_color_dialog = gtk_dialog_new();
+        (void) g_signal_connect(G_OBJECT(pref_color_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+        (void) g_signal_connect(G_OBJECT(pref_color_dialog), "response",
+                                G_CALLBACK(pref_color_response),
+                                (gpointer) pref_color_dialog);
+        gtk_window_set_resizable(GTK_WINDOW(pref_color_dialog), TRUE);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+
+        for (i = 0; i < 16; i++) {
+            pref_color_win[i] = grayswatch_new(tmp_opts.colors[i + 4]);
+            g_signal_connect(G_OBJECT(pref_color_win[i]), "value-changed",
+                             G_CALLBACK(pref_color_update_color),
+                             GINT_TO_POINTER(i));
+            gtk_widget_set_size_request(pref_color_win[i], 50, 75);
+            gtk_box_pack_start(GTK_BOX(hbox), pref_color_win[i],
+                               FALSE, FALSE, 0);
+        }
+
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pref_color_dialog)->vbox),
+                          hbox);
+
+        /*
+         * Add the buttons.
+         */
+        gtk_dialog_add_buttons(GTK_DIALOG(pref_color_dialog),
+                               GTK_STOCK_REVERT_TO_SAVED,
+                               GTK_RESPONSE_REJECT,
+                               GTK_STOCK_CLOSE,
+                               GTK_RESPONSE_CLOSE, NULL);
+
+        gtk_dialog_set_default_response(GTK_DIALOG(pref_color_dialog),
+                                        GTK_RESPONSE_CLOSE);
+
+        gtk_widget_show_all(pref_color_dialog);
+    }
+
+    /*
+     * If selecting colors for 2 bits-per-pixel, hide all but the first 4.
+     * Set the first 4 colors depending on the bits-per-pixel value.
+     */
+    for (i = 0; i < 16; i++) {
+        /*
+         * We don't want setting the gray values to trigger the signal.
+         * That causes the Apply button to be made sensitive.
+         */
+        grayswatch_block_signal(GRAYSWATCH(pref_color_win[i]), TRUE);
+        if (tmp_opts.font_opts.bits_per_pixel == 2 && i >= 4)
+          gtk_widget_hide(pref_color_win[i]);
+        else {
+            if (i < 4) {
+                if (tmp_opts.font_opts.bits_per_pixel == 2)
+                  grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]),
+                                      tmp_opts.colors[i]);
+                else
+                  grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]),
+                                      tmp_opts.colors[i + 4]);
+            } else
+              gtk_widget_show(pref_color_win[i]);
+        }
+        grayswatch_block_signal(GRAYSWATCH(pref_color_win[i]), FALSE);
+    }
+
+    /*
+     * Center the dialog and show it.
+     */
+    guiutil_show_dialog_centered(pref_color_dialog, pref_dialog);
+}
+
+static void
+pref_set_bpp(GtkWidget *w, gpointer data)
+{
+    gboolean on;
+
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) == FALSE)
+      return;
+
+    tmp_opts.font_opts.bits_per_pixel = GPOINTER_TO_INT(data);
+    on = (tmp_opts.font_opts.bits_per_pixel == 1 ||
+          tmp_opts.font_opts.bits_per_pixel == 8) ? FALSE : TRUE;
+
+    if (pref_color_dialog != 0 && GTK_WIDGET_VISIBLE(pref_color_dialog)) {
+        if (tmp_opts.font_opts.bits_per_pixel == 1 ||
+            tmp_opts.font_opts.bits_per_pixel == 8)
+          gtk_widget_hide(pref_color_dialog);
+        else
+          pref_select_colors(w, data);
+    }
+
+    gtk_widget_set_sensitive(pref_color, on);
+
+    /*
+     * Enable the apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static GtkWidget *
+pref_make_newfont_page()
+{
+    GtkWidget *label, *table, *button, *hbox, *vbox, *frame, *tmp;
+    GtkAdjustment *adj;
+
+    vbox = gtk_vbox_new(FALSE, 10);
+
+    /*
+     * Create the font size selection.
+     */
+    frame = gtk_frame_new("Font Size");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    table = gtk_table_new(3, 2, FALSE);
+
+    label = gtk_label_new("Point Size:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+                     GTK_FILL, GTK_FILL, 5, 0);
+
+    label = gtk_label_new("Horizontal Resolution:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    label = gtk_label_new("Vertical Resolution:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    /*
+     * Make the spinboxes for the point size and resolutions.
+     */
+    adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 4.0, 256.0, 1.0, 2.0, 0.0);
+    button = gtk_spin_button_new(adj, 1.0, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(button),
+                              (gfloat) tmp_opts.font_opts.point_size);
+    gtk_widget_set_size_request(button, 100, -1);
+    (void) g_signal_connect(G_OBJECT(button), "changed",
+                            G_CALLBACK(pref_change_size),
+                            GINT_TO_POINTER(0));
+    gtk_table_attach(GTK_TABLE(table), button, 1, 2, 0, 1,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0,
+                                               1.0, 10.0, 0.0);
+    tmp = button = gtk_spin_button_new(adj, 1.0, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(button),
+                              (gfloat) tmp_opts.font_opts.resolution_x);
+    gtk_widget_set_size_request(button, 100, -1);
+    (void) g_signal_connect(G_OBJECT(button), "changed",
+                            G_CALLBACK(pref_change_size),
+                            GINT_TO_POINTER(1));
+    gtk_table_attach(GTK_TABLE(table), button, 1, 2, 1, 2,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0,
+                                               1.0, 10.0, 0.0);
+    button = gtk_spin_button_new(adj, 1.0, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(button),
+                              (gfloat) tmp_opts.font_opts.resolution_y);
+    gtk_widget_set_size_request(button, 100, -1);
+    (void) g_signal_connect(G_OBJECT(button), "changed",
+                            G_CALLBACK(pref_change_size),
+                            GINT_TO_POINTER(2));
+    (void) g_signal_connect(G_OBJECT(button), "focus-in-event",
+                            G_CALLBACK(pref_sync_res), (gpointer) tmp);
+    gtk_table_attach(GTK_TABLE(table), button, 1, 2, 2, 3,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    gtk_container_add(GTK_CONTAINER(frame), table);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    /*
+     * Create the spacing selection.
+     */
+    frame = gtk_frame_new("Spacing");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+
+    button = gtk_radio_button_new_with_label(0, "Proportional");
+    if (tmp_opts.font_opts.font_spacing == BDF_PROPORTIONAL)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_spacing),
+                            GINT_TO_POINTER(BDF_PROPORTIONAL));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    button =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
+                                                    "Monowidth");
+    if (tmp_opts.font_opts.font_spacing == BDF_MONOWIDTH)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_spacing),
+                            GINT_TO_POINTER(BDF_MONOWIDTH));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    button =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
+                                                    "Character Cell");
+    if (tmp_opts.font_opts.font_spacing == BDF_CHARCELL)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_spacing),
+                            GINT_TO_POINTER(BDF_CHARCELL));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    /*
+     * Create the bits-per-pixel selection.
+     */
+    frame = gtk_frame_new("Bits Per Pixel");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+
+    button = gtk_radio_button_new_with_label(0, "1 bpp");
+    if (tmp_opts.font_opts.bits_per_pixel == 1)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_bpp),
+                            GINT_TO_POINTER(1));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    button =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
+                                                    "2 bpp");
+    if (tmp_opts.font_opts.bits_per_pixel == 2)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_bpp),
+                            GINT_TO_POINTER(2));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    button =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
+                                                    "4 bpp");
+    if (tmp_opts.font_opts.bits_per_pixel == 4)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_bpp),
+                            GINT_TO_POINTER(4));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    button =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
+                                                    "8 bpp");
+    if (tmp_opts.font_opts.bits_per_pixel == 8)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_set_bpp),
+                            GINT_TO_POINTER(8));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    pref_color = gtk_button_new_with_label("Select Colors");
+    (void) g_signal_connect(G_OBJECT(pref_color), "clicked",
+                            G_CALLBACK(pref_select_colors), 0);
+    if (tmp_opts.font_opts.bits_per_pixel == 1)
+      gtk_widget_set_sensitive(pref_color, FALSE);
+
+    gtk_box_pack_start(GTK_BOX(hbox), pref_color, FALSE, FALSE, 0);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    frame = gtk_frame_new("Cursor Fonts");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    pref_cursor_font = gtk_check_button_new_with_label("Cursor Font");
+    if (tmp_opts.font_opts.cursor_font)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref_cursor_font),
+                                   TRUE);
+    (void) g_signal_connect(G_OBJECT(pref_cursor_font), "toggled",
+                            G_CALLBACK(pref_set_cursor_font), 0);
+    gtk_container_add(GTK_CONTAINER(frame), pref_cursor_font);
+    gtk_widget_set_sensitive(pref_cursor_font, FALSE);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    return vbox;
+}
+
+static void
+pref_fgrid_mode(GtkWidget *w, gpointer data)
+{
+    tmp_opts.overwrite_mode =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+
+    /*
+     * Enable the Apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+}
+
+static void
+pref_set_filename(GtkWidget *w, gpointer data)
+{
+    gchar *fname;
+
+
+    fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(w));
+
+    if (pref_fsel_unicode)
+      gtk_entry_set_text(GTK_ENTRY(pref_unicode), fname);
+    else
+      gtk_entry_set_text(GTK_ENTRY(pref_adobe), fname);
+
+    /*
+     * Enable the Apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, TRUE);
+
+    /*
+     * Hide the dialog.
+     */
+    gtk_widget_hide(w);
+}
+
+static void
+handle_filename_response(GtkDialog *d, gint response, gpointer data)
+{
+    switch (response) {
+      case GTK_RESPONSE_ACCEPT:
+        pref_set_filename(GTK_WIDGET(d), data);
+        break;
+      case GTK_RESPONSE_CANCEL:
+        gtk_widget_hide(GTK_WIDGET(d));
+        break;
+    }
+}
+
+static void
+pref_show_fsel_dialog(GtkWidget *w, gpointer data)
+{
+    pref_fsel_unicode = GPOINTER_TO_INT(data);
+
+    if (pref_fsel_dialog == 0) {
+        pref_fsel_dialog = gtk_file_chooser_dialog_new("Glyph Name",
+                                                       GTK_WINDOW(pref_dialog),
+                                                       GTK_FILE_CHOOSER_ACTION_OPEN,
+                                                       GTK_STOCK_CANCEL,
+                                                       GTK_RESPONSE_CANCEL,
+                                                       GTK_STOCK_APPLY,
+                                                       GTK_RESPONSE_ACCEPT,
+                                                       NULL);
+        (void) g_signal_connect(G_OBJECT(pref_fsel_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        (void) g_signal_connect(G_OBJECT(pref_fsel_dialog), "response",
+                                G_CALLBACK(handle_filename_response),
+                                NULL);
+    }
+
+    /*
+     * Set the title of the dialog.
+     */
+    if (pref_fsel_unicode)
+      strcpy(buffer1, "Unicode Character Database Selection");
+    else
+      strcpy(buffer1, "Adobe Glyph Name File Selection");
+
+    gtk_window_set_title(GTK_WINDOW(pref_fsel_dialog), buffer1);
+
+    guiutil_show_dialog_centered(pref_fsel_dialog, pref_dialog);
+
+    gtk_window_set_modal(GTK_WINDOW(pref_fsel_dialog), TRUE);
+}
+
+static GtkWidget *
+pref_make_edit_page()
+{
+    gint i;
+    GtkWidget *vbox, *hbox, *frame, *button, *label, *omenu;
+    GtkWidget *tmp, *table;
+
+    vbox = gtk_vbox_new(FALSE, 10);
+
+    frame = gtk_frame_new("Font Grid Selection Paste");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    hbox = gtk_hbox_new(FALSE, 5);
+
+    button = gtk_radio_button_new_with_label(0, "Overwrites");
+    if (tmp_opts.overwrite_mode)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_fgrid_mode), 0);
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+    button =
+        gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
+                                                    "Inserts");
+    if (tmp_opts.overwrite_mode)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    else
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    frame = gtk_frame_new("Glyph Editors");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    hbox = gtk_hbox_new(FALSE, 5);
+
+    button = gtk_check_button_new_with_label("Show Cap Height");
+    if (tmp_opts.show_cap_height)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(7));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    button = gtk_check_button_new_with_label("Show X Height");
+    if (tmp_opts.show_cap_height)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GINT_TO_POINTER(8));
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+    label = gtk_label_new("Pixel Size:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+    omenu = gtk_combo_box_new_text();
+    for (i = 2; i < 21; i++) {
+        sprintf(buffer1, "%dx%d", i, i);
+        gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), buffer1);
+    }
+    gtk_combo_box_set_active(GTK_COMBO_BOX(omenu), tmp_opts.pixel_size - 2);
+    g_signal_connect(G_OBJECT(omenu), "changed",
+                     G_CALLBACK(pref_toggle), GINT_TO_POINTER(10));
+
+    gtk_box_pack_start(GTK_BOX(hbox), omenu, TRUE, TRUE, 0);
+
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    frame = gtk_frame_new("Glyph Name Lists");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    table = gtk_table_new(2, 3, FALSE);
+
+    label = gtk_label_new("Unicode Character Database:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    label = gtk_label_new("Adobe Glyph List:");
+    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+                     GTK_FILL, GTK_FILL, 5, 5);
+
+    /*
+     * Add the entries.
+     */
+    tmp = pref_unicode = gtk_entry_new();
+    gtk_widget_set_size_request(tmp, 250, -1);
+    if (tmp_opts.unicode_name_file)
+      gtk_entry_set_text(GTK_ENTRY(pref_unicode), tmp_opts.unicode_name_file);
+    gtk_table_attach(GTK_TABLE(table), tmp, 1, 2, 0, 1,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5);
+
+    tmp = pref_adobe = gtk_entry_new();
+    gtk_widget_set_size_request(tmp, 250, -1);
+    if (tmp_opts.adobe_name_file)
+      gtk_entry_set_text(GTK_ENTRY(pref_adobe), tmp_opts.adobe_name_file);
+    gtk_table_attach(GTK_TABLE(table), tmp, 1, 2, 1, 2,
+                     GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5);
+
+    /*
+     * Add the browse buttons.
+     */
+    button = gtk_button_new_with_label("Browse");
+    (void) g_signal_connect(G_OBJECT(button), "clicked",
+                            G_CALLBACK(pref_show_fsel_dialog),
+                            GINT_TO_POINTER(TRUE));
+    gtk_table_attach(GTK_TABLE(table), button, 2, 3, 0, 1,
+                     GTK_FILL, 0, 5, 0);
+    button = gtk_button_new_with_label("Browse");
+    (void) g_signal_connect(G_OBJECT(button), "clicked",
+                            G_CALLBACK(pref_show_fsel_dialog),
+                            GINT_TO_POINTER(FALSE));
+    gtk_table_attach(GTK_TABLE(table), button, 2, 3, 1, 2,
+                     GTK_FILL, 0, 5, 0);
+
+    gtk_container_add(GTK_CONTAINER(frame), table);
+
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    return vbox;
+}
+
+static GtkWidget *
+pref_make_other_page()
+{
+    GtkWidget *frame, *button, *vbox;
+
+    frame = gtk_frame_new("Dialogs");
+    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+
+    vbox = gtk_vbox_new(FALSE, 0);
+
+    button = gtk_check_button_new_with_label("Show \"Really Exit\" Dialog");
+    if (tmp_opts.really_exit)
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    (void) g_signal_connect(G_OBJECT(button), "toggled",
+                            G_CALLBACK(pref_toggle),
+                            GUINT_TO_POINTER(9));
+    gtk_container_add(GTK_CONTAINER(frame), button);
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    return vbox;
+}
+
+static void
+pref_apply_changes(void)
+{
+    gchar *fname;
+
+    /*
+     * Take care of setting the file names for the glyph name lists.
+     */
+    fname = (gchar *) gtk_entry_get_text(GTK_ENTRY(pref_unicode));
+    if (fname != 0 && fname[0] != 0)
+      tmp_opts.unicode_name_file = g_strdup(fname);
+
+    fname = (gchar *) gtk_entry_get_text(GTK_ENTRY(pref_adobe));
+    if (fname != 0 && fname[0] != 0)
+      tmp_opts.adobe_name_file = g_strdup(fname);
+
+    /*
+     * If the name files are different, delete the old name files first.
+     */
+    if (options.unicode_name_file != 0 &&
+        options.unicode_name_file != tmp_opts.unicode_name_file)
+      g_free(options.unicode_name_file);
+    if (options.adobe_name_file != 0 &&
+        options.adobe_name_file != tmp_opts.adobe_name_file)
+      g_free(options.adobe_name_file);
+
+    /*
+     * Copy the updated options over.
+     */
+    (void) memcpy((char *) &options, (char *) &tmp_opts,
+                  sizeof(gbdfed_options_t));
+
+    /*
+     * Set the glyph edit options.
+     */
+    guigedit_show_cap_height(tmp_opts.show_cap_height);
+    guigedit_show_x_height(tmp_opts.show_x_height);
+    guigedit_set_pixel_size(tmp_opts.pixel_size);
+
+    /*
+     * Disable the apply button.
+     */
+    gtk_widget_set_sensitive(pref_apply, FALSE);
+}
+
+static void
+pref_save(void)
+{
+    FILE *out;
+    gchar *home;
+    gint i;
+
+    /*
+     * If any changes were made, do an update before saving.
+     */
+    if (GTK_WIDGET_SENSITIVE(pref_apply))
+      pref_apply_changes();
+
+    if ((home = getenv("HOME")) == 0) {
+        guiutil_error_message(editors[0].shell,
+                              "Save Preferences: Unable to locate home directory.");
+        return;
+    }
+
+    sprintf(buffer1, "%s/.gbdfedrc", home);
+    if ((out = fopen(buffer1, "w")) == 0) {
+        sprintf(buffer2, "Save Preferences: Unable to write to %s.", buffer1);
+        guiutil_error_message(editors[0].shell, buffer2);
+        return;
+    }
+
+    /*
+     * First, write the gbdfed options.
+     */
+    fprintf(out, "#########################\n");
+    fprintf(out, "#\n# gbdfed options.\n#\n");
+    fprintf(out, "#########################\n\n");
+
+    if (options.no_blanks)
+      fprintf(out, "skip_blank_pages true\n\n");
+    else
+      fprintf(out, "skip_blank_pages false\n\n");
+
+    if (options.really_exit)
+      fprintf(out, "really_exit true\n\n");
+    else
+      fprintf(out, "really_exit false\n\n");
+    if (options.overwrite_mode)
+      fprintf(out, "grid_overwrite_mode true\n\n");
+    else
+      fprintf(out, "grid_overwrite_mode false\n\n");
+
+    if (options.accelerator != 0)
+      fprintf(out, "close_accelerator %s\n\n",
+              options.accelerator);
+    if (options.accelerator_text != 0)
+      fprintf(out, "close_accelerator_text %s\n\n",
+              options.accelerator_text);
+
+    if (options.unicode_name_file != 0)
+      fprintf(out, "name_file %s\n\n", options.unicode_name_file);
+    
+    if (options.adobe_name_file != 0)
+      fprintf(out, "adobe_name_file %s\n\n",
+              options.adobe_name_file);
+
+    fprintf(out, "pixel_size %d\n\n", options.pixel_size);
+
+    if (options.show_cap_height)
+      fprintf(out, "show_cap_height true\n\n");
+    else
+      fprintf(out, "show_cap_height false\n\n");
+
+    if (options.show_x_height)
+      fprintf(out, "show_x_height true\n\n");
+    else
+      fprintf(out, "show_x_height false\n\n");
+
+    if (options.sbit)
+      fprintf(out, "generate_sbit_metrics true\n\n");
+    else
+      fprintf(out, "generate_sbit_metrics false\n\n");
+
+    /*
+     * Save the grayscales.
+     */
+    fprintf(out, "#\n# Grayscale values. Must be between 0 and 255.\n#\n");
+    fprintf(out, "2bpp_grays ");
+    for (i = 0; i < 4; i++) {
+        fprintf(out, "%d", options.colors[i]);
+        if (i + 1 < 4)
+          putc(' ', out);
+    }
+    fprintf(out, "\n4bpp_grays ");
+    for (i = 4; i < 20; i++) {
+        fprintf(out, "%d", options.colors[i]);
+        if (i + 1 < 20)
+          putc(' ', out);
+    }
+    fprintf(out, "\n\n");
+
+#if 0
+    /*
+     * Save the colors.
+     */
+    fprintf(out, "#\n# Color values for 2 bits per pixel.\n#\n");
+    for (i = 0; i < 4; i++) {
+        /*
+         * Do this to avoid writing negative values.
+         */
+        c = options.colors[i];
+        fprintf(out, "color%d %d\n", i, c);
+    }
+
+    fprintf(out, "\n#\n# Color values for 4 bits per pixel.\n#\n");
+    for (i = 4; i < 20; i++) {
+        /*
+         * Do this to avoid writing negative values.
+         */
+        c = options.colors[i];
+        fprintf(out, "color%d %d\n", i, c);
+    }
+    putc('\n', out);
+#endif
+
+    /*
+     * The save the BDF specific options.
+     */
+    fprintf(out, "#########################\n");
+    fprintf(out, "#\n# BDF font options.\n#\n");
+    fprintf(out, "#########################\n\n");
+    bdf_save_options(out, &options.font_opts);
+    fclose(out);
+}
+
+static void
+pref_response(GtkDialog *d, gint response, gpointer data)
+{
+    if (response == GTK_RESPONSE_APPLY)
+      pref_apply_changes();
+    else if (response == GTK_RESPONSE_OK)
+      pref_save();
+    else {
+        /*
+         * Make sure the color chooser dialog is hidden if it
+         * happens to be up.
+         */
+        if (pref_color_dialog != 0 && GTK_WIDGET_VISIBLE(pref_color_dialog))
+          gtk_widget_hide(pref_color_dialog);
+
+        gtk_widget_hide(GTK_WIDGET(d));
+    }
+}
+
+void
+guiedit_show_preferences(GtkWidget *w, gpointer data)
+{
+    GtkWidget *dvbox, *nb, *button, *table, *label;
+
+    if (pref_dialog == 0) {
+        /*
+         * Initialize the temporary options.
+         */
+        (void) memcpy((char *) &tmp_opts, (char *) &options,
+                      sizeof(gbdfed_options_t));
+
+        pref_dialog = gtk_dialog_new();
+        (void) g_signal_connect(G_OBJECT(pref_dialog), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        sprintf(buffer1, "%s Preferences", g_get_prgname());
+        gtk_window_set_title(GTK_WINDOW(pref_dialog), buffer1);
+
+        dvbox = GTK_DIALOG(pref_dialog)->vbox;
+
+        /*
+         * Create the notebook that will contain the Preference tabs.
+         */
+        nb = gtk_notebook_new();
+
+        /*
+         * Create the General Options page.
+         */
+        label = gtk_label_new("General Options");
+        table = pref_make_general_page();
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label);
+
+        /*
+         * Create the New Font Options page.
+         */
+        label = gtk_label_new("New Font Options");
+        table = pref_make_newfont_page();
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label);
+
+        /*
+         * Create the Editing Options page.
+         */
+        label = gtk_label_new("Editing Options");
+        table = pref_make_edit_page();
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label);
+
+        /*
+         * Create the Other Options page.
+         */
+        label = gtk_label_new("Other Options");
+        table = pref_make_other_page();
+        gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label);
+
+        /*
+         * Finally, add the notebook to the dialog's vbox.
+         */
+        gtk_container_add(GTK_CONTAINER(dvbox), nb);
+
+        /*
+         * Add the buttons at the bottom.
+         */
+        pref_apply = gtk_dialog_add_button(GTK_DIALOG(pref_dialog),
+                                           GTK_STOCK_APPLY,
+                                           GTK_RESPONSE_APPLY);
+        gtk_widget_set_sensitive(pref_apply, FALSE);
+
+        button = gtk_dialog_add_button(GTK_DIALOG(pref_dialog),
+                                       GTK_STOCK_SAVE,
+                                       GTK_RESPONSE_OK);
+
+        button = gtk_dialog_add_button(GTK_DIALOG(pref_dialog),
+                                       GTK_STOCK_CLOSE,
+                                       GTK_RESPONSE_CLOSE);
+        gtk_dialog_set_default_response(GTK_DIALOG(pref_dialog),
+                                        GTK_RESPONSE_CLOSE);
+
+        g_signal_connect(G_OBJECT(pref_dialog), "response",
+                         G_CALLBACK(pref_response), 0);
+
+        gtk_widget_show_all(dvbox);
+    }
+
+    guiutil_show_dialog_centered(pref_dialog, editors[0].shell);
+}
+
+void
+guiedit_preference_cleanup()
+{
+    /*
+     * Does nothing at the moment.
+     */
+}
diff --git a/guiutil.c b/guiutil.c
new file mode 100644 (file)
index 0000000..cc7bda4
--- /dev/null
+++ b/guiutil.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "gbdfed.h"
+
+/*
+ * The shared error dialog and the label for the message.
+ */
+static GtkWidget *errd;
+static GtkWidget *errmsg;
+
+/*
+ * The shared question dialog, the label for the question, and the
+ * value representing the answer.
+ */
+static GtkWidget *questd;
+static GtkWidget *question;
+static GtkWidget *yes;
+static GtkWidget *no;
+
+void
+guiutil_show_dialog_centered(GtkWidget *dialog, GtkWidget *parent)
+{
+    if (GTK_WINDOW(parent) != gtk_window_get_transient_for(GTK_WINDOW(dialog)))
+      gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent));
+    gtk_widget_show(dialog);
+}
+
+void
+guiutil_error_message(GtkWidget *parent, gchar *text)
+{
+    GtkWidget *ok, *hbox, *image;
+    GtkStockItem item;
+
+    if (errd == 0) {
+        errd = gtk_dialog_new();
+        (void) g_signal_connect(G_OBJECT(errd), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+
+        /*
+         * Create the error icon.
+         */
+        if (gtk_stock_lookup(GTK_STOCK_DIALOG_ERROR, &item)) {
+            /*
+             * Set the dialog title.
+             */
+            gtk_window_set_title(GTK_WINDOW(errd), item.label);
+            image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR,
+                                             GTK_ICON_SIZE_DIALOG);
+            gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 2);
+        } else {
+            /*
+             * Probably not necessary, but use some default icon here.
+             */
+        }
+
+        errmsg = gtk_label_new(text);
+        gtk_box_pack_start(GTK_BOX(hbox), errmsg, TRUE, TRUE, 2);
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(errd)->vbox), hbox);
+
+        ok = gtk_dialog_add_button(GTK_DIALOG(errd), GTK_STOCK_CLOSE,
+                                   GTK_RESPONSE_CLOSE);
+        (void) g_signal_connect_object(G_OBJECT(ok), "clicked",
+                                       G_CALLBACK(gtk_widget_hide),
+                                       (gpointer) errd,
+                                       G_CONNECT_SWAPPED);
+
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(errd)->action_area), ok);
+
+        gtk_widget_show_all(errd);
+
+        gtk_window_set_modal(GTK_WINDOW(errd), TRUE);
+    } else
+      gtk_label_set_text(GTK_LABEL(errmsg), text);
+
+    /*
+     * Center the dialog and display it.
+     */
+    guiutil_show_dialog_centered(errd, parent);
+
+    /*
+     * Ring the bell.
+     */
+    gdk_beep();
+}
+
+gboolean
+guiutil_yes_or_no(GtkWidget *parent, gchar *text, gboolean default_answer)
+{
+    GtkWidget *hbox, *image;
+    GList *kids;
+    gint ans;
+    GtkStockItem item;
+
+    if (questd == 0) {
+        questd = gtk_dialog_new();
+        gtk_window_set_resizable(GTK_WINDOW(questd), TRUE);
+
+        (void) g_signal_connect(G_OBJECT(questd), "delete_event",
+                                G_CALLBACK(gtk_widget_hide), 0);
+
+        hbox = gtk_hbox_new(FALSE, 0);
+
+        /*
+         * Create the question icon.
+         */
+        if (gtk_stock_lookup(GTK_STOCK_DIALOG_QUESTION, &item)) {
+            /*
+             * Set the dialog title.
+             */
+            gtk_window_set_title(GTK_WINDOW(questd), item.label);
+            image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
+                                             GTK_ICON_SIZE_DIALOG);
+            gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 2);
+        } else {
+            /*
+             * Probably not necessary, but use some default icon here.
+             */
+        }
+
+        question = gtk_label_new(text);
+
+        gtk_box_pack_start(GTK_BOX(hbox), question, TRUE, TRUE, 2);
+        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(questd)->vbox), hbox);
+
+        /*
+         * Make sure the buttons are evenly distributed in the button box.
+         */
+        gtk_button_box_set_layout(GTK_BUTTON_BOX(GTK_DIALOG(questd)->action_area), GTK_BUTTONBOX_SPREAD);
+
+        gtk_dialog_add_buttons(GTK_DIALOG(questd),
+                               GTK_STOCK_YES, GTK_RESPONSE_ACCEPT,
+                               GTK_STOCK_NO, GTK_RESPONSE_CANCEL, NULL);
+
+        /*
+         * Get the two children buttons out now so focus can be set on either
+         * when needed.
+         */
+        kids = gtk_container_get_children(GTK_CONTAINER(GTK_DIALOG(questd)->action_area));
+        no = GTK_WIDGET(g_list_nth_data(kids, 0));
+        yes = GTK_WIDGET(g_list_nth_data(kids, 1));
+        g_list_free(kids);
+
+        gtk_widget_show_all(questd);
+
+        gtk_window_set_modal(GTK_WINDOW(questd), TRUE);
+    } else
+      gtk_label_set_text(GTK_LABEL(question), text);
+
+    /*
+     * Force the dialog to reset to its minimum size.
+     */
+    if (questd->window != NULL)
+      gtk_window_resize(GTK_WINDOW(questd), 1, 1);
+
+    /*
+     * Center the dialog and display it.
+     */
+    guiutil_show_dialog_centered(questd, parent);
+
+    /*
+     * Force the default answer button to have the focus.
+     */
+    if (default_answer)
+      gtk_widget_grab_focus(yes);
+    else
+      gtk_widget_grab_focus(no);
+
+    ans = gtk_dialog_run(GTK_DIALOG(questd));
+
+    gtk_widget_hide(questd);
+
+    return (ans == GTK_RESPONSE_ACCEPT) ? TRUE : FALSE;
+}
+
+void
+guiutil_util_set_tooltip(GtkWidget *w, gchar *text)
+{
+#if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 12
+    gtk_widget_set_tooltip_text(w, text);
+#else
+    GtkTooltips *tt;
+
+    tt = gtk_tooltips_new();
+    gtk_tooltips_set_tip(tt, w, text, 0);
+#endif
+}
+
+static GdkCursor *watch_cursor;
+
+void
+guiutil_busy_cursor(GtkWidget *w, gboolean on)
+{
+    if (watch_cursor == 0)
+      watch_cursor = gdk_cursor_new(GDK_WATCH);
+
+    if (on)
+      gdk_window_set_cursor(w->window, watch_cursor);
+    else
+      gdk_window_set_cursor(w->window, 0);
+}
+
+void
+guiutil_cursor_cleanup()
+{
+    if (watch_cursor != 0)
+      gdk_cursor_unref(watch_cursor);
+    watch_cursor = 0;
+}
diff --git a/hbf.c b/hbf.c
new file mode 100644 (file)
index 0000000..3c4fba9
--- /dev/null
+++ b/hbf.c
@@ -0,0 +1,1590 @@
+/*
+ * Copyright 1993,1994,1995,2005 by Ross Paterson
+ *
+ * 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. The name of the author may not be used to endorse or promote
+ *     products derived from this software without specific prior written
+ *     permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ *
+ * Ross Paterson <ross@soi.city.ac.uk>
+ * 17 October 1995
+ *
+ * The following people have supplied bug fixes:
+ *
+ *   Simon Chow     <khsc@synoptics.com>
+ *   Fung Fung Lee  <lee@simd.stanford.edu>
+ *   Man-Chi Pong   <mcpong@cs.ust.hk>
+ *   Steven Simpson <simpson@math.psu.edu>
+ *   Charles Wang   <charles.wang@infores.com>
+ *   Werner Lemberg <wl@gnu.org>
+ *
+ * Ross no longer maintains this code.  Please send bug reports to
+ * Werner Lemberg <wl@gnu.org>.
+ *
+ */
+
+/*
+ * Two C interfaces to HBF files.
+ *
+ * The multiple interfaces make this code rather messy; I intend
+ * to clean it up as experience is gained on what is really needed.
+ *
+ * There are also two modes of operation:
+ * - the default is to read each bitmap from its file as demanded
+ * - if IN_MEMORY is defined, the whole bitmap file is held in memory.
+ *   In this case, if running under Unix, the bitmap files may be gzipped
+ *   (but the filename used in the HBF file should be the name of the
+ *   file before it was gzipped).
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "hbf.h"
+
+#ifdef __MSDOS__
+#define msdos
+#endif
+
+/*
+ * if the linker complains about an unresolved identifier '_strdup',
+ * uncomment the following definition.
+ */
+/* #define NO_STRDUP */
+
+#ifdef __STDC__
+#      define  _(x)    x
+#else
+#      define  _(x)    ()
+#endif
+
+#define        reg     register
+
+typedef        int     bool;
+#define        TRUE    1
+#define        FALSE   0
+
+#define        Bit(n)  (1<<(7 - (n)))
+
+/*
+ * Messy file system issues
+ */
+
+#ifdef unix
+#define        PATH_DELIMITER ':'
+#define        RelativeFileName(fn)    ((fn)[0] != '/')
+#define        LocalFileName(fn)       (strchr(fn, '/') == NULL)
+#endif /* unix */
+#ifdef msdos
+#define        PATH_DELIMITER ';'
+#define        HasDrive(fn)    (isalpha((fn)[0]) && (fn)[1] == ':')
+#ifdef __EMX__
+#define RelativeFileName(fn)   (! HasDrive(fn) && \
+                               !((fn)[0] == '\\' || (fn)[0] == '/'))
+#define LocalFileName(fn)      (! HasDrive(fn) && \
+                               strchr(fn, '\\') == NULL && \
+                               strchr(fn, '/') == NULL)
+#else
+#define        RelativeFileName(fn)    (! HasDrive(fn) && (fn)[0] != '\\')
+#define        LocalFileName(fn)       (! HasDrive(fn) && strchr(fn, '\\') == NULL)
+#endif /* __EMX__ */
+#define        READ_BINARY     "rb"
+#endif /* msdos */
+#ifdef vms
+#define        PATH_DELIMITER ','
+#define        RelativeFileName(fn)    (strchr(fn, ':') == NULL && ((fn)[0] != '[' || (fn)[1] == '.' || (fn)[1] == '-'))
+#define        LocalFileName(fn)       (strchr(fn, ':') == NULL && strchr(fn, ']') == NULL)
+#endif
+
+#ifndef        RelativeFileName
+#define        RelativeFileName(fn)    FALSE
+#endif
+
+#ifndef        LocalFileName
+#define        LocalFileName(fn)       FALSE
+#endif
+
+#ifndef READ_BINARY
+#define        READ_BINARY     "r"
+#endif
+
+#define        MAX_FILENAME    1024
+
+/*
+ *     Internal structures
+ */
+
+typedef        unsigned char   byte;
+
+#define PROPERTY       struct _PROPERTY
+#define BM_FILE                struct _BM_FILE
+#define B2_RANGE       struct _B2_RANGE
+#define CODE_RANGE     struct _CODE_RANGE
+
+PROPERTY {
+       char            *prop_name;
+       char            *prop_value;
+       PROPERTY        *prop_next;
+};
+
+BM_FILE {
+       char    *bmf_name;
+#ifdef IN_MEMORY
+       byte    *bmf_contents;
+#else
+       FILE    *bmf_file;
+#endif
+       long    bmf_size;
+       BM_FILE *bmf_next;
+};
+
+B2_RANGE {
+       byte            b2r_start;
+       byte            b2r_finish;
+       B2_RANGE        *b2r_next;
+};
+
+typedef        unsigned short  CHAR;
+typedef        unsigned int    CHAR_INDEX;     /* character index in file */
+#define        BAD_CHAR_INDEX  0xffff
+
+CODE_RANGE {
+       CHAR            code_start;
+       CHAR            code_finish;
+       BM_FILE         *code_bm_file;
+       long            code_offset;
+       CHAR_INDEX      code_pos;
+       bool            code_transposed;
+       bool            code_inverted;
+       CODE_RANGE      *code_next;
+};
+
+/*
+ *     Extended internal version of HBF
+ */
+
+typedef struct {
+       /* fields corresponding to the definition */
+       HBF             public;
+       /* plus internal stuff */
+       char            *filename;
+       byte            *bitmap_buffer;
+       unsigned int    b2_size;        /* number of legal byte-2's */
+       PROPERTY        *property;
+       B2_RANGE        *byte_2_range;
+       CODE_RANGE      *code_range;
+       BM_FILE         *bm_file;
+} HBF_STRUCT;
+
+#define        FirstByte(code)         ((code)>>8)
+#define        SecondByte(code)        ((code)&0xff)
+#define        MakeCode(byte1,byte2)   (((byte1)<<8)|(byte2))
+
+/* size of a bitmap in the file (may be affected by transposition) */
+#define        FileBitmapSize(hbfFile,cp) \
+               ((cp)->code_transposed ? \
+                       (hbfBitmapBBox(hbfFile)->hbf_height + 7)/8 * \
+                               hbfBitmapBBox(hbfFile)->hbf_width : \
+                       HBF_BitmapSize(hbfFile))
+
+#define        NEW(type)       ((type *)malloc((unsigned)(sizeof(type))))
+
+#define        QUOTE '"'
+
+#define MAXLINE        1024
+
+#ifdef WIN32
+#define        strdup(x)       _strdup(x)
+#else
+       extern  char    *strdup _((const char *s));
+#endif
+
+static void    add_b2r _((B2_RANGE **last_b2r, int start, int finish));
+static bool    add_code_range _((HBF_STRUCT *hbf, const char *line));
+static void    add_property _((HBF_STRUCT *hbf, const char *lp));
+static CHAR_INDEX      b2_pos _((HBF_STRUCT *hbf, HBF_CHAR code));
+static int     b2_size _((B2_RANGE *b2r));
+static void    clear_bbox _((HBF_BBOX *bbox));
+static void    clear_record _((HBF_STRUCT *hbf));
+static char    *concat _((const char *dir, int dirlen, const char *stem));
+static char    *expand_filename _((const char *name, const char *filename));
+static const   byte *get_bitmap
+               _((HBF_STRUCT *hbf, HBF_CHAR code, byte *buffer));
+static byte    *local_buffer _((HBF_STRUCT *hbf));
+static void    invert _((byte *buffer, unsigned length));
+#ifdef IN_MEMORY
+static bool    read_bitmap_file _((BM_FILE *bmf, FILE *f));
+static bool    copy_transposed
+               _((HBF *hbf, byte *bitmap, const byte *source));
+#else
+static bool    get_transposed _((HBF *hbf, FILE *f, byte *bitmap));
+#endif
+static bool    match _((const char *lp, const char *sp));
+static bool    parse_file _((FILE *f, HBF_STRUCT *hbf));
+static FILE    *path_open
+               _((const char *path, const char *filename, char **fullp));
+static bool    real_open _((const char *filename, HBF_STRUCT *hbf));
+
+/* Error reporting */
+
+int    hbfDebug;       /* set this for error reporting */
+
+#ifdef __STDC__
+#include <stdarg.h>
+
+static void
+eprintf(const char *fmt, ...)
+{
+       if (hbfDebug) {
+               va_list args;
+
+               (void)fprintf(stderr, "HBF: ");
+               va_start(args, fmt);
+               (void)vfprintf(stderr, fmt, args);
+               va_end(args);
+               (void)fprintf(stderr, "\n");
+       }
+}
+#else /* ! __STDC__ */
+/* poor man's variable-length argument list */
+static void
+eprintf(fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9)
+       const   char    *fmt;
+       int     x1, x2, x3, x4, x5, x6, x7, x8, x9;
+{
+       if (hbfDebug) {
+               (void)fprintf(stderr, "HBF: ");
+               (void)fprintf(stderr, fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9);
+               (void)fprintf(stderr, "\n");
+       }
+}
+#endif /* __STDC__ */
+
+static void
+clear_bbox(bbox)
+       HBF_BBOX *bbox;
+{
+       bbox->hbf_width = bbox->hbf_height = 0;
+       bbox->hbf_xDisplacement = bbox->hbf_yDisplacement = 0;
+}
+
+static void
+clear_record(hbf)
+       HBF_STRUCT *hbf;
+{
+       clear_bbox(&(hbf->public.hbf_bitmap_bbox));
+       clear_bbox(&(hbf->public.hbf_font_bbox));
+       hbf->property = NULL;
+       hbf->filename = NULL;
+       hbf->bitmap_buffer = NULL;
+       hbf->byte_2_range = NULL;
+       hbf->code_range = NULL;
+       hbf->bm_file = NULL;
+}
+
+/*
+ *     Byte-2 ranges
+ */
+
+static void
+add_b2r(last_b2r, start, finish)
+reg    B2_RANGE **last_b2r;
+       int     start;
+       int     finish;
+{
+reg    B2_RANGE *b2r;
+
+       b2r = NEW(B2_RANGE);
+       while (*last_b2r != NULL && (*last_b2r)->b2r_start < start)
+               last_b2r = &((*last_b2r)->b2r_next);
+       b2r->b2r_next = *last_b2r;
+       b2r->b2r_start = start;
+       b2r->b2r_finish = finish;
+       *last_b2r = b2r;
+}
+
+static CHAR_INDEX
+b2_pos(hbf, code)
+       HBF_STRUCT      *hbf;
+       HBF_CHAR        code;
+{
+reg    B2_RANGE *b2r;
+reg    unsigned c;
+reg    CHAR_INDEX      pos;
+
+       c = SecondByte(code);
+       pos = 0;
+       for (b2r = hbf->byte_2_range; b2r != NULL; b2r = b2r->b2r_next)
+               if (b2r->b2r_start <= c && c <= b2r->b2r_finish)
+                       return pos + c - b2r->b2r_start;
+               else
+                       pos += b2r->b2r_finish - b2r->b2r_start + 1;
+       return BAD_CHAR_INDEX;
+}
+
+static int
+b2_size(b2r)
+reg    B2_RANGE *b2r;
+{
+reg    int     size;
+
+       size = 0;
+       for ( ; b2r != NULL; b2r = b2r->b2r_next)
+               size += b2r->b2r_finish - b2r->b2r_start + 1;
+       return size;
+}
+
+/* map a position to a character code */
+static long
+code_of(hbf, pos)
+       HBF_STRUCT      *hbf;
+       long            pos;
+{
+       long    code;
+       int     residue;
+reg    B2_RANGE *b2r;
+
+       code = pos / hbf->b2_size * 256;
+       residue = pos % hbf->b2_size;
+       for (b2r = hbf->byte_2_range; b2r != NULL; b2r = b2r->b2r_next)
+               if (b2r->b2r_start + residue <= b2r->b2r_finish)
+                       return code + b2r->b2r_start + residue;
+               else
+                       residue -= b2r->b2r_finish - b2r->b2r_start + 1;
+       /* should never get here */
+       return 0L;
+}
+
+/*
+ *     String stuff
+ */
+
+static bool
+match(lp, sp)
+reg    const   char    *lp;
+reg    const   char    *sp;
+{
+       while (*lp == *sp && *sp != '\0') {
+               lp++;
+               sp++;
+       }
+       return (*lp == '\0' || isspace(*lp)) && *sp == '\0';
+}
+
+#ifdef NO_STRDUP
+char *
+strdup(s)
+       const   char    *s;
+{
+       char    *new_s;
+
+       new_s = malloc((unsigned)strlen(s) + 1);
+       strcpy(new_s, s);
+       return new_s;
+}
+#endif
+
+/*
+ *     Properties
+ */
+
+static void
+add_property(hbf, lp)
+reg    HBF_STRUCT      *hbf;
+reg    const char      *lp;
+{
+reg    PROPERTY        *prop;
+       char    tmp[MAXLINE];
+reg    char    *tp;
+
+       prop = NEW(PROPERTY);
+
+       tp = tmp;
+       while (*lp != '\0' && ! isspace(*lp))
+               *tp++ = *lp++;
+       *tp = '\0';
+       prop->prop_name = strdup(tmp);
+
+       while (*lp != '\0' && isspace(*lp))
+               lp++;
+
+       tp = tmp;
+       if (*lp == QUOTE) {
+               lp++;
+               while (*lp != '\0' && ! (*lp == QUOTE && *++lp != QUOTE))
+                       *tp++ = *lp++;
+       }
+       else
+               for (;;) {
+                       while (*lp != '\0' && ! isspace(*lp))
+                               *tp++ = *lp++;
+                       while (*lp != '\0' && isspace(*lp))
+                               lp++;
+                       if (*lp == '\0')
+                               break;
+                       *tp++ = ' ';
+               }
+       *tp = '\0';
+       prop->prop_value = strdup(tmp);
+
+       prop->prop_next = hbf->property;
+       hbf->property = prop;
+}
+
+const char *
+hbfProperty(hbfFile, propName)
+       HBF             *hbfFile;
+       const   char    *propName;
+{
+reg    HBF_STRUCT      *hbf;
+reg    PROPERTY        *prop;
+
+       hbf = (HBF_STRUCT *)hbfFile;
+       for (prop = hbf->property; prop != NULL; prop = prop->prop_next)
+               if (strcmp(prop->prop_name, propName) == 0)
+                       return prop->prop_value;
+       return NULL;
+}
+
+/*
+ *     Compatability routines
+ */
+
+const char *
+HBF_GetProperty(handle, propertyName)
+       HBF             *handle;
+       const   char    *propertyName;
+{
+       return hbfProperty(handle, propertyName);
+}
+
+int
+HBF_GetFontBoundingBox(handle, width, height, xDisplacement, yDisplacement)
+       HBF_Handle      handle;
+       unsigned int    *width;
+       unsigned int    *height;
+       int             *xDisplacement;
+       int             *yDisplacement;
+{
+       if (width != NULL)
+               *width = hbfFontBBox(handle)->hbf_width;
+       if (height != NULL)
+               *height = hbfFontBBox(handle)->hbf_height;
+       if (xDisplacement != NULL)
+               *xDisplacement = hbfFontBBox(handle)->hbf_xDisplacement;
+       if (yDisplacement != NULL)
+               *yDisplacement = hbfFontBBox(handle)->hbf_yDisplacement;
+       return 0;
+}
+
+int
+HBF_GetBitmapBoundingBox(handle, width, height, xDisplacement, yDisplacement)
+       HBF_Handle      handle;
+       unsigned int    *width;
+       unsigned int    *height;
+       int             *xDisplacement;
+       int             *yDisplacement;
+{
+       if (width != NULL)
+               *width = hbfBitmapBBox(handle)->hbf_width;
+       if (height != NULL)
+               *height = hbfBitmapBBox(handle)->hbf_height;
+       if (xDisplacement != NULL)
+               *xDisplacement = hbfBitmapBBox(handle)->hbf_xDisplacement;
+       if (yDisplacement != NULL)
+               *yDisplacement = hbfBitmapBBox(handle)->hbf_yDisplacement;
+       return 0;
+}
+
+/*
+ * Prepend a directory to a relative filename.
+ */
+static char *
+concat(dir, dirlen, stem)
+       const   char    *dir;   /* not necessarily null-terminated */
+       int     dirlen;         /* number of significant chars in dir */
+       const   char    *stem;  /* relative filename */
+{
+       char    *fullname;
+
+       if (dirlen == 0)        /* null: current directory */
+               return strdup(stem);
+#ifdef unix
+       fullname = malloc(dirlen + strlen(stem) + 2);
+       (void)sprintf(fullname, "%.*s/%s", dirlen, dir, stem);
+#else
+#ifdef msdos
+       fullname = malloc(dirlen + strlen(stem) + 2);
+       (void)sprintf(fullname, "%.*s\\%s", dirlen, dir, stem);
+#else
+#ifdef vms
+       if (dir[dirlen-1] == ']' && stem[0] == '[' && stem[1] == '-') {
+               dirlen--;
+               stem++;
+               fullname = malloc(dirlen + strlen(stem) + 2);
+               (void)sprintf(fullname, "%.*s.%s", dirlen, dir, stem);
+       }
+       else {
+               if (dir[dirlen-1] == ']' && stem[0] == '[' && stem[1] == '.') {
+                       dirlen--;
+                       stem++;
+               }
+               fullname = malloc(dirlen + strlen(stem) + 1);
+               (void)sprintf(fullname, "%.*s%s", dirlen, dir, stem);
+       }
+#else
+       fullname = strdup(stem);
+#endif /* vms */
+#endif /* msdos */
+#endif /* unix */
+       return fullname;
+}
+
+/*
+ *     Bitmap files
+ *
+ *     If the host operating system has a heirarchical file system and
+ *     the bitmap file name is relative, it is relative to the directory
+ *     containing the HBF file.
+ */
+static char *
+expand_filename(name, hbf_name)
+       const   char    *name;
+       const   char    *hbf_name;
+{
+#ifdef unix
+reg    char    *s;
+reg    int     size;
+
+       size = name[0] != '/' && (s = strrchr(hbf_name, '/')) != NULL ?
+               s - hbf_name + 1 : 0;
+       s = malloc((unsigned)size + strlen(name) + 1);
+       (void)sprintf(s, "%.*s%s", size, hbf_name, name);
+       return s;
+#else
+#ifdef msdos
+reg    char    *s;
+reg    int     size;
+
+#ifdef __EMX__
+       s = (unsigned char *)hbf_name + strlen((unsigned char *)hbf_name) - 1;
+       for(;;) {
+               if (*s == '\\' || *s == '/')
+                       break;
+               if (s == hbf_name) {
+                       s = NULL;
+                       break;
+               }
+               s--;
+       }
+       
+       size = HasDrive(name) ? 0 :
+               (name[0] == '\\' || name[0] == '/') ?
+                       (HasDrive(hbf_name) ? 2 : 0) :
+               s != NULL ? s - hbf_name + 1 : 0;
+#else
+       size = HasDrive(name) ? 0 :
+               name[0] == '\\' ? (HasDrive(hbf_name) ? 2 : 0) :
+               (s = strrchr(hbf_name, '\\')) != NULL ?
+                       s - hbf_name + 1 : 0;
+#endif /* __EMX__ */
+       s = malloc((unsigned)size + strlen(name) + 1);
+       (void)sprintf(s, "%.*s%s", size, hbf_name, name);
+       return s;
+#else
+#ifdef vms
+reg    char    *s;
+reg    const   char    *copyto;
+reg    int     size;
+
+       if ((s = strchr(hbf_name, ']')) != NULL && RelativeFileName(name))
+               return concat(hbf_name, (s - hbf_name) + 1, name);
+
+       copyto = hbf_name;
+       if ((s = strstr(copyto, "::")) != NULL && strstr(name, "::") == NULL)
+               copyto = s+2;
+       if ((s = strchr(copyto, ':')) != NULL && strchr(name, ':') == NULL)
+               copyto = s+1;
+       size = copyto - hbf_name;
+       s = malloc((unsigned)size + strlen(name) + 1);
+       (void)sprintf(s, "%.*s%s", size, hbf_name, name);
+       return s;
+#else
+       return strdup(name);
+#endif /* vms */
+#endif /* msdos */
+#endif /* unix */
+}
+
+static BM_FILE *
+find_file(hbf, filename)
+       HBF_STRUCT *hbf;
+       const char *filename;
+{
+       BM_FILE **fp;
+reg    BM_FILE *file;
+       FILE    *f;
+       char    *bmfname;
+#ifdef IN_MEMORY
+#ifdef unix
+       bool    from_pipe;
+#endif
+#endif
+
+       for (fp = &(hbf->bm_file); *fp != NULL; fp = &((*fp)->bmf_next)) {
+               bmfname = strrchr((*fp)->bmf_name, '/');
+               bmfname = (bmfname) ? bmfname + 1 : (*fp)->bmf_name;
+               if (strcmp(bmfname, filename) == 0)
+                       return *fp;
+       }
+
+       file = NEW(BM_FILE);
+       if (file == NULL) {
+               eprintf("out of memory");
+               return NULL;
+       }
+       file->bmf_name = expand_filename(filename, hbf->filename);
+       if (file->bmf_name == NULL) {
+               free((char *)file);
+               return NULL;
+       }
+       f = fopen(file->bmf_name, READ_BINARY);
+#ifdef IN_MEMORY
+#ifdef unix
+       from_pipe = FALSE;
+       if (f == NULL) {
+               char    tmp[400];
+
+               sprintf(tmp, "%s.gz", file->bmf_name);
+               if ((f = fopen(tmp, "r")) != NULL) {
+                       fclose(f);
+                       sprintf(tmp, "gzcat %s.gz", file->bmf_name);
+                       if ((f = popen(tmp, "r")) != NULL)
+                               from_pipe = TRUE;
+               }
+       }
+#endif /* unix */
+#endif /* IN_MEMORY */
+       if (f == NULL) {
+               eprintf("can't open bitmap file '%s'", file->bmf_name);
+               free(file->bmf_name);
+               free((char *)file);
+               return NULL;
+       }
+#ifdef IN_MEMORY
+       if (! read_bitmap_file(file, f)) {
+               free(file->bmf_name);
+               free((char *)file);
+               return NULL;
+       }
+#ifdef unix
+       if (from_pipe)
+               pclose(f);
+       else
+               fclose(f);
+#else /* ! unix */
+       fclose(f);
+#endif /* ! unix */
+#else /* ! IN_MEMORY */
+       file->bmf_file = f;
+       fseek(f, 0L, 2);
+       file->bmf_size = ftell(f);
+#endif /* ! IN_MEMORY */
+       file->bmf_next = NULL;
+       *fp = file;
+       return file;
+}
+
+#ifdef IN_MEMORY
+#define        GRAIN_SIZE      512
+
+static bool
+read_bitmap_file(bmf, f)
+       BM_FILE *bmf;
+       FILE    *f;
+{
+       byte    *contents, *cp;
+       long    size;
+       int     c;
+
+       size = 0;
+       cp = contents = (byte *)malloc((unsigned)GRAIN_SIZE);
+       if (contents == NULL) {
+               eprintf("not enough space for bitmap file");
+               return NULL;
+       }
+       while ((c = getc(f)) != EOF) {
+               if (size%GRAIN_SIZE == 0) {
+                       contents = (byte *)realloc((char *)contents,
+                                       (unsigned)(size + GRAIN_SIZE));
+                       if (contents == NULL) {
+                               eprintf("not enough space for bitmap file");
+                               return NULL;
+                       }
+                       cp = contents + size;
+               }
+               *cp++ = c;
+               size++;
+       }
+       bmf->bmf_size = size;
+       bmf->bmf_contents = (byte *)realloc((char *)contents, (unsigned)size);
+       return TRUE;
+}
+#endif /* IN_MEMORY */
+
+/*
+ *     Code ranges
+ */
+
+/* check that a code range fits within its bitmap file */
+static bool
+too_short(hbf, cp)
+       HBF_STRUCT      *hbf;
+       CODE_RANGE      *cp;
+{
+       int     bm_size;
+       long    offset, end_offset;
+       BM_FILE *bmf;
+       long    start, finish;
+
+       bm_size = FileBitmapSize(&(hbf->public), cp);
+       offset = cp->code_offset;
+       start = cp->code_start;
+       finish = cp->code_finish;
+       end_offset = offset + bm_size *
+                       (hbf->b2_size*(long)FirstByte(finish) +
+                               b2_pos(hbf, finish) - cp->code_pos + 1);
+       bmf = cp->code_bm_file;
+       if (end_offset <= bmf->bmf_size)
+               return FALSE;
+       /* bitmap file is too short: produce a specific error message */
+       if (offset > bmf->bmf_size)
+               eprintf("bitmap file '%s' is shorter than offset 0x%04lx",
+                       bmf->bmf_name, offset);
+       else if (offset + bm_size > bmf->bmf_size)
+               eprintf("bitmap file '%s' too short: no room for any bitmaps at offset 0x%04lx",
+                       bmf->bmf_name, offset);
+       else
+               eprintf("bitmap file '%s' is too short - code range appears to be 0x%04lx-0x%04lx",
+                       bmf->bmf_name,
+                       start,
+                       code_of(hbf, cp->code_pos +
+                                       (bmf->bmf_size - offset)/bm_size) - 1);
+       return TRUE;
+}
+
+static const char *
+skip_word(n, s)
+       int     n;
+       const   char    *s;
+{
+       for ( ; n > 0; n--) {
+               while (*s != '\0' && ! isspace(*s))
+                       s++;
+               while (*s != '\0' && isspace(*s))
+                       s++;
+       }
+       return s;
+}
+
+/* optional keywords at the end of a CODE_RANGE line */
+static void
+parse_keywords(cp, s)
+       CODE_RANGE *cp;
+       const   char    *s;
+{
+       for (s = skip_word(4, s) ; *s != '\0'; s = skip_word(1, s)) {
+               switch (*s) {
+               case 's': case 'S': case 't': case 'T':
+                       /* keyword "sideways" or "transposed" */
+                       cp->code_transposed = TRUE;
+                       break;
+               case 'i': case 'I':
+                       /* keyword "inverted" */
+                       cp->code_inverted = TRUE;
+               }
+       }
+}
+
+static bool
+add_code_range(hbf, line)
+       HBF_STRUCT      *hbf;
+       const char      *line;
+{
+       CODE_RANGE *cp;
+       CODE_RANGE **cpp;
+       long    start, finish;
+       long    offset;
+       char    filename[MAXLINE];
+       BM_FILE *bmf;
+       CHAR_INDEX b2pos;
+
+       if (sscanf(line, "HBF_CODE_RANGE %li-%li %s %li",
+                          &start, &finish, filename, &offset) != 4) {
+               eprintf("syntax error in HBF_CODE_RANGE");
+               return FALSE;
+       }
+       /* code ranges are checked in real_open() */
+       if ((bmf = find_file(hbf, filename)) == NULL)
+               return FALSE;
+       if ((cp = NEW(CODE_RANGE)) == NULL) {
+               eprintf("out of memory");
+               return FALSE;
+       }
+
+       cp->code_start = (CHAR)start;
+       cp->code_finish = (CHAR)finish;
+       cp->code_bm_file = bmf;
+       cp->code_offset = offset;
+       cp->code_transposed = cp->code_inverted = FALSE;
+       parse_keywords(cp, line);
+       /* insert it in order */
+       for (cpp = &hbf->code_range;
+            *cpp != NULL && (*cpp)->code_finish < start;
+            cpp = &((*cpp)->code_next))
+               ;
+       if (*cpp != NULL && (*cpp)->code_start <= finish) {
+               eprintf("code ranges overlap");
+               return FALSE;
+       }
+       cp->code_next = *cpp;
+       *cpp = cp;
+
+       /* set code_pos, and check range */
+       if (start > finish) {
+               eprintf("illegal code range 0x%04lx-0x%04lx", start, finish);
+               return FALSE;
+       }
+       if ((b2pos = b2_pos(hbf, start)) == BAD_CHAR_INDEX) {
+               eprintf("illegal start code 0x%04lx", start);
+               return FALSE;
+       }
+       cp->code_pos = hbf->b2_size*(long)FirstByte(start) + b2pos;
+       if ((b2pos = b2_pos(hbf, finish)) == BAD_CHAR_INDEX) {
+               eprintf("illegal finish code 0x%04lx", finish);
+               return FALSE;
+       }
+       /* check that the bitmap file has enough bitmaps */
+       return ! too_short(hbf, cp);
+}
+
+/*
+ *     Reading and parsing of an HBF file
+ */
+
+/* get line, truncating to len, and trimming trailing spaces */
+static bool
+get_line(buf, len, f)
+       char    *buf;
+       int     len;
+       FILE    *f;
+{
+       int     c;
+       char    *bp;
+
+       bp = buf;
+       for (;;) {
+               if ((c = getc(f)) == EOF) {
+                       eprintf("unexpected end of file");
+                       return FALSE;
+               }
+               if (c == '\n' || c == '\r') {
+                       /* trim trailing space */
+                       while (bp > buf && isspace(*(bp-1)))
+                               bp--;
+                       *bp = '\0';
+                       return TRUE;
+               }
+               if (len > 0) {
+                       *bp++ = c;
+                       len--;
+               }
+       }
+}
+
+/* get next non-COMMENT line */
+static bool
+get_text_line(buf, len, f)
+       char    *buf;
+       int     len;
+       FILE    *f;
+{
+       while (get_line(buf, len, f))
+               if (*buf != '\0' && ! match(buf, "COMMENT"))
+                       return TRUE;
+       return FALSE;
+}
+
+static bool
+get_property(line, keyword, hbf)
+       const   char    *line;
+       const   char    *keyword;
+       HBF_STRUCT      *hbf;
+{
+       if (! match(line, keyword)) {
+               eprintf("%s expected", keyword);
+               return FALSE;
+       }
+       add_property(hbf, line);
+       return TRUE;
+}
+
+static bool
+get_bbox(line, keyword, bbox)
+       const   char    *line;
+       const   char    *keyword;
+       HBF_BBOX        *bbox;
+{
+       int     w, h, xd, yd;
+
+       if (! match(line, keyword) ||
+           sscanf(line + strlen(keyword), "%i %i %i %i",
+                       &w, &h, &xd, &yd) != 4) {
+               eprintf("%s expected", keyword);
+               return FALSE;
+       }
+       if (w <= 0 || h <= 0) {
+               eprintf("illegal %s dimensions %dx%d", keyword, w, h);
+               return FALSE;
+       }
+       bbox->hbf_width = w;
+       bbox->hbf_height = h;
+       bbox->hbf_xDisplacement = xd;
+       bbox->hbf_yDisplacement = yd;
+       return TRUE;
+}
+
+/*
+ *  HBFHeaderFile ::=
+ *     'HBF_START_FONT'                version                 EOLN
+ *     'HBF_CODE_SCHEME'               word ...                EOLN
+ *     'FONT'                          fontName                EOLN
+ *     'SIZE'                          ptsize xres yres        EOLN
+ *     'HBF_BITMAP_BOUNDING_BOX'       w h xd yd               EOLN
+ *     'FONTBOUNDINGBOX'               w h xd yd               EOLN
+ *     X11R5FontPropertySection
+ *     'CHARS'                         n                       EOLN
+ *     HBFByte2RangeSection
+ *     HBFCodeRangeSection
+ *     'HBF_END_FONT'                  EOLN .
+ *
+ * This implementation allows extra lines before HBF_END_FONT.
+ * Anything after HBF_END_FONT is ignored.
+ */
+
+static bool
+parse_file(f, hbf)
+       FILE    *f;
+reg    HBF_STRUCT *hbf;
+{
+       char    line[MAXLINE];
+       int     start, finish;
+
+       if (! get_text_line(line, MAXLINE, f) ||
+           ! get_property(line, "HBF_START_FONT", hbf))
+               return FALSE;
+
+       if (! get_text_line(line, MAXLINE, f) ||
+           ! get_property(line, "HBF_CODE_SCHEME", hbf))
+               return FALSE;
+
+       if (! get_text_line(line, MAXLINE, f) ||
+           ! get_property(line, "FONT", hbf))
+               return FALSE;
+
+       if (! get_text_line(line, MAXLINE, f) ||
+           ! get_property(line, "SIZE", hbf))
+               return FALSE;
+
+       if (! get_text_line(line, MAXLINE, f) ||
+           ! get_bbox(line, "HBF_BITMAP_BOUNDING_BOX",
+                       &(hbf->public.hbf_bitmap_bbox)))
+               return FALSE;
+
+       if (! get_text_line(line, MAXLINE, f) ||
+           ! get_bbox(line, "FONTBOUNDINGBOX", &(hbf->public.hbf_font_bbox)))
+               return FALSE;
+
+       if (! get_text_line(line, MAXLINE, f))
+               return FALSE;
+       if (match(line, "STARTPROPERTIES")) {
+               for (;;) {
+                       if (! get_text_line(line, MAXLINE, f))
+                               return FALSE;
+                       if (match(line, "ENDPROPERTIES"))
+                               break;
+                       add_property(hbf, line);
+               }
+               if (! get_text_line(line, MAXLINE, f))
+                       return FALSE;
+       }
+
+       if (match(line, "CHARS"))
+               if (! get_text_line(line, MAXLINE, f))
+                       return FALSE;
+
+       if (match(line, "HBF_START_BYTE_2_RANGES")) {
+               for (;;) {
+                       if (! get_text_line(line, MAXLINE, f))
+                               return FALSE;
+                       if (match(line, "HBF_END_BYTE_2_RANGES"))
+                               break;
+                       if (sscanf(line, "HBF_BYTE_2_RANGE %i-%i",
+                                       &start, &finish) != 2) {
+                               eprintf("HBF_BYTE_2_RANGE expected");
+                               return FALSE;
+                       }
+                       add_b2r(&(hbf->byte_2_range), start, finish);
+               }
+               if (! get_text_line(line, MAXLINE, f))
+                       return FALSE;
+       }
+       else
+               add_b2r(&(hbf->byte_2_range), 0, 0xff);
+       hbf->b2_size = b2_size(hbf->byte_2_range);
+
+       if (! match(line, "HBF_START_CODE_RANGES")) {
+               eprintf("HBF_START_CODE_RANGES expected");
+               return FALSE;
+       }
+       for (;;) {
+               if (! get_text_line(line, MAXLINE, f))
+                       return FALSE;
+               if (match(line, "HBF_END_CODE_RANGES"))
+                       break;
+               if (! add_code_range(hbf, line))
+                       return FALSE;
+       }
+
+       for (;;) {
+               if (! get_text_line(line, MAXLINE, f))
+                       return FALSE;
+               if (match(line, "HBF_END_FONT"))
+                       break;
+               /* treat extra lines as properties (for private extensions) */
+               add_property(hbf, line);
+       }
+
+       return TRUE;
+}
+
+static FILE *
+path_open(path, filename, fullp)
+       const   char    *path;
+       const   char    *filename;
+       char    **fullp;
+{
+       if (LocalFileName(filename) && path != NULL) {
+#ifdef PATH_DELIMITER
+               int     len;
+               char    *fullname;
+               FILE    *f;
+               const   char    *p_next;
+
+               len = strlen(filename);
+               for (;;) {
+                       p_next = strchr(path, PATH_DELIMITER);
+                       if (p_next == NULL)
+                               p_next = path + strlen(path);
+                       fullname = concat(path, p_next - path, filename);
+                       if ((f = fopen(fullname, "r")) != NULL) {
+                               *fullp = fullname;
+                               return f;
+                       }
+                       free(fullname);
+                       if (*p_next == '\0')
+                               break;
+                       path = p_next + 1;
+               }
+#endif
+               return NULL;
+       }
+       else {
+               *fullp = strdup(filename);
+               return fopen(*fullp, "r");
+       }
+}
+
+static bool
+real_open(filename, hbf)
+       const   char    *filename;
+reg    HBF_STRUCT *hbf;
+{
+       FILE    *f;
+
+       f = path_open(getenv("HBFPATH"), filename, &(hbf->filename));
+       if (f == NULL) {
+               eprintf("can't read file '%s'", filename);
+               return FALSE;
+       }
+       if (! parse_file(f, hbf)) {
+               fclose(f);
+               return FALSE;
+       }
+       fclose(f);
+       return TRUE;
+}
+
+HBF *
+hbfOpen(filename)
+       const   char    *filename;
+{
+reg    HBF_STRUCT *hbf;
+
+       if ((hbf = NEW(HBF_STRUCT)) == NULL) {
+               eprintf("can't allocate HBF structure");
+               return NULL;
+       }
+       clear_record(hbf);
+       if (real_open(filename, hbf))
+               return &(hbf->public);
+       hbfClose(&(hbf->public));
+       return NULL;
+}
+
+int
+HBF_OpenFont(filename, ptrHandleStorage)
+       const   char    *filename;
+       HBF     **ptrHandleStorage;
+{
+       return (*ptrHandleStorage = hbfOpen(filename)) == NULL ? -1 : 0;
+}
+
+/*
+ *     Close files, free everything associated with the HBF.
+ */
+
+int
+HBF_CloseFont(hbfFile)
+       HBF     *hbfFile;
+{
+reg    HBF_STRUCT      *hbf;
+       PROPERTY        *prop_ptr, *prop_next;
+       B2_RANGE        *b2r_ptr, *b2r_next;
+       CODE_RANGE      *code_ptr, *code_next;
+       BM_FILE         *bmf_ptr, *bmf_next;
+       int             status;
+
+       status = 0;
+       hbf = (HBF_STRUCT *)hbfFile;
+
+       if (hbf->filename != NULL)
+               free(hbf->filename);
+       if (hbf->bitmap_buffer != NULL)
+               free(hbf->bitmap_buffer);
+
+       for (prop_ptr = hbf->property;
+            prop_ptr != NULL;
+            prop_ptr = prop_next) {
+               prop_next = prop_ptr->prop_next;
+               free(prop_ptr->prop_name);
+               free(prop_ptr->prop_value);
+               free((char *)prop_ptr);
+       }
+
+       for (b2r_ptr = hbf->byte_2_range;
+            b2r_ptr != NULL;
+            b2r_ptr = b2r_next) {
+               b2r_next = b2r_ptr->b2r_next;
+               free((char *)b2r_ptr);
+       }
+
+       for (code_ptr = hbf->code_range;
+            code_ptr != NULL;
+            code_ptr = code_next) {
+               code_next = code_ptr->code_next;
+               free((char *)code_ptr);
+       }
+
+       for (bmf_ptr = hbf->bm_file;
+            bmf_ptr != NULL;
+            bmf_ptr = bmf_next) {
+               bmf_next = bmf_ptr->bmf_next;
+#ifdef IN_MEMORY
+               free((char *)(bmf_ptr->bmf_contents));
+#else
+               if (bmf_ptr->bmf_file != NULL &&
+                   fclose(bmf_ptr->bmf_file) < 0)
+                       status = -1;
+#endif
+               free(bmf_ptr->bmf_name);
+               free((char *)bmf_ptr);
+       }
+
+       free((char *)hbf);
+
+       return status;
+}
+
+void
+hbfClose(hbfFile)
+       HBF     *hbfFile;
+{
+       (void)HBF_CloseFont(hbfFile);
+}
+
+/*
+ *     Fetch a bitmap
+ */
+
+const byte *
+hbfGetBitmap(hbf, code)
+       HBF             *hbf;
+       HBF_CHAR        code;
+{
+       return get_bitmap((HBF_STRUCT *)hbf, code, (byte *)NULL);
+}
+
+int
+HBF_GetBitmap(hbf, code, buffer)
+       HBF             *hbf;
+       HBF_CHAR        code;
+       byte            *buffer;
+{
+       return get_bitmap((HBF_STRUCT *)hbf, code, buffer) == NULL ? -1 : 0;
+}
+
+/*
+ * Internal function to fetch a bitmap.
+ * If buffer is non-null, it must be used.
+ */
+static const byte *
+get_bitmap(hbf, code, buffer)
+reg    HBF_STRUCT      *hbf;
+       HBF_CHAR        code;
+       byte            *buffer;
+{
+       CHAR_INDEX      pos, b2pos;
+reg    CODE_RANGE      *cp;
+       BM_FILE         *bmf;
+       int             bm_size;
+       long            offset;
+
+       if ((b2pos = b2_pos(hbf, code)) == BAD_CHAR_INDEX)
+               return NULL;
+       pos = hbf->b2_size*FirstByte(code) + b2pos;
+       for (cp = hbf->code_range; cp != NULL; cp = cp->code_next)
+               if (cp->code_start <= code && code <= cp->code_finish) {
+                       bmf = cp->code_bm_file;
+                       bm_size = FileBitmapSize(&(hbf->public), cp);
+                       offset = cp->code_offset +
+                                  (long)(pos - cp->code_pos) * bm_size;
+#ifdef IN_MEMORY
+                       if (buffer == NULL &&
+                           ! cp->code_transposed && ! cp->code_inverted)
+                               return bmf->bmf_contents + offset;
+#endif /* IN_MEMORY */
+                       if (buffer == NULL &&
+                           ((buffer = local_buffer(hbf)) == NULL))
+                               return NULL;
+#ifdef IN_MEMORY
+                       if (cp->code_transposed)
+                               copy_transposed(&(hbf->public),
+                                               buffer,
+                                               bmf->bmf_contents + offset);
+                       else
+                               memcpy((char *)buffer,
+                                      (char *)(bmf->bmf_contents + offset),
+                                      bm_size);
+#else /* ! IN_MEMORY */
+                       if (fseek(bmf->bmf_file, offset, 0) != 0) {
+                               eprintf("seek error on code 0x%04x", code);
+                               return NULL;
+                       }
+                       if (cp->code_transposed ?
+                           ! get_transposed(&(hbf->public), bmf->bmf_file,
+                                               buffer) :
+                           fread((char *)buffer,
+                                       bm_size, 1, bmf->bmf_file) != 1) {
+                               eprintf("read error on code 0x%04x", code);
+                               return NULL;
+                       }
+#endif /* IN_MEMORY */
+                       if (cp->code_inverted)
+                               invert(buffer, HBF_BitmapSize(&(hbf->public)));
+                       return buffer;
+               }
+       eprintf("code 0x%04x out of range", code);
+       return NULL;
+}
+
+static byte *
+local_buffer(hbf)
+       HBF_STRUCT      *hbf;
+{
+       if (hbf->bitmap_buffer == NULL &&
+           (hbf->bitmap_buffer = (byte *)malloc(HBF_BitmapSize(&(hbf->public)))) == NULL) {
+               eprintf("out of memory");
+               return NULL;
+       }
+       return hbf->bitmap_buffer;
+}
+
+static void
+invert(buffer, length)
+       byte    *buffer;
+       unsigned int    length;
+{
+       for ( ; length > 0; length--)
+               *buffer++ ^= 0xff;
+}
+
+#ifdef IN_MEMORY
+static bool
+copy_transposed(hbf, bitmap, source)
+       HBF     *hbf;
+reg    byte    *bitmap;
+reg    const   byte    *source;
+{
+reg    byte    *pos;
+reg    byte    *bm_end;
+       int     x;
+       int     width;
+reg    int     row_size;
+reg    int     c;
+reg    int     imask, omask;
+
+       width = hbfBitmapBBox(hbf)->hbf_width;
+       row_size = HBF_RowSize(hbf);
+       bm_end = bitmap + HBF_BitmapSize(hbf);
+       (void)memset((char *)bitmap, '\0', HBF_BitmapSize(hbf));
+       for (x = 0; x < width; x++) {
+               pos = bitmap + x/8;
+               omask = Bit(x%8);
+               /* y = 0 */
+               for (;;) {
+                       c = *source++;
+                       for (imask = Bit(0); imask != 0; imask >>= 1) {
+                               /*
+                                * At this point,
+                                *
+                                *      imask == Bit(y%8)
+                                *      pos == bitmap + y*row_size + x/8
+                                *
+                                * We examine bit y of row x of the input,
+                                * setting bit x of row y of the output if
+                                * required, by applying omask to *pos.
+                                */
+                               if ((c & imask) != 0)
+                                       *pos |= omask;
+                               /* if (++y > height) goto end_column */
+                               pos += row_size;
+                               if (pos >= bm_end)
+                                       goto end_column;
+                       }
+               }
+end_column:
+               ;
+       }
+       return TRUE;
+}
+#else /* ! IN_MEMORY */
+static bool
+get_transposed(hbf, f, bitmap)
+       HBF     *hbf;
+       FILE    *f;
+reg    byte    *bitmap;
+{
+reg    byte    *pos;
+reg    byte    *bm_end;
+       int     x;
+       int     width;
+reg    int     row_size;
+reg    int     c;
+reg    int     imask, omask;
+
+       width = hbfBitmapBBox(hbf)->hbf_width;
+       row_size = HBF_RowSize(hbf);
+       bm_end = bitmap + HBF_BitmapSize(hbf);
+       (void)memset((char *)bitmap, '\0', HBF_BitmapSize(hbf));
+       for (x = 0; x < width; x++) {
+               pos = bitmap + x/8;
+               omask = Bit(x%8);
+               /* y = 0 */
+               for (;;) {
+                       if ((c = getc(f)) == EOF)
+                               return FALSE;
+                       for (imask = Bit(0); imask != 0; imask >>= 1) {
+                               /*
+                                * At this point,
+                                *
+                                *      imask == Bit(y%8)
+                                *      pos == bitmap + y*row_size + x/8
+                                *
+                                * We examine bit y of row x of the input,
+                                * setting bit x of row y of the output if
+                                * required, by applying omask to *pos.
+                                */
+                               if ((c & imask) != 0)
+                                       *pos |= omask;
+                               /* if (++y > height) goto end_column */
+                               pos += row_size;
+                               if (pos >= bm_end)
+                                       goto end_column;
+                       }
+               }
+end_column:
+               ;
+       }
+       return TRUE;
+}
+#endif /* ! IN_MEMORY */
+
+/*
+ * Call function on each valid code in ascending order.
+ */
+void
+hbfForEach(hbfFile, func, data)
+reg    HBF     *hbfFile;
+reg    void    (*func)_((HBF *sameHbfFile, HBF_CHAR code, void *data));
+reg    void    *data;
+{
+       HBF_STRUCT      *hbf;
+       CODE_RANGE      *cp;
+reg    B2_RANGE        *b2r;
+reg    unsigned        byte1, byte2;
+reg    unsigned        finish;
+
+       hbf = (HBF_STRUCT *)hbfFile;
+       for (cp = hbf->code_range; cp != NULL; cp = cp->code_next) {
+               byte1 = FirstByte(cp->code_start);
+               byte2 = SecondByte(cp->code_start);
+               while (MakeCode(byte1, byte2) <= cp->code_finish) {
+                       for (b2r = hbf->byte_2_range;
+                            b2r != NULL;
+                            b2r = b2r->b2r_next) {
+                               if (byte2 < b2r->b2r_start)
+                                       byte2 = b2r->b2r_start;
+                               finish = b2r->b2r_finish;
+                               if (byte1 == FirstByte(cp->code_finish) &&
+                                   finish > SecondByte(cp->code_finish))
+                                       finish = SecondByte(cp->code_finish);
+                               while (byte2 <= finish) {
+                                       (*func)(hbfFile,
+                                               MakeCode(byte1, byte2), data);
+                                       byte2++;
+                               }
+                       }
+                       byte1++;
+                       byte2 = 0;
+               }
+       }
+}
+
+const char *
+hbfFileName(hbf)
+       HBF     *hbf;
+{
+       return ((HBF_STRUCT *)hbf)->filename;
+}
+
+long
+hbfChars(hbfFile)
+       HBF     *hbfFile;
+{
+       HBF_STRUCT      *hbf;
+       CODE_RANGE      *cp;
+       long            num_chars;
+
+       hbf = (HBF_STRUCT *)hbfFile;
+       num_chars = 0;
+       for (cp = hbf->code_range; cp != NULL; cp = cp->code_next)
+               num_chars +=
+                       hbf->b2_size*FirstByte(cp->code_finish) +
+                       b2_pos(hbf, cp->code_finish) -
+                       (hbf->b2_size*FirstByte(cp->code_start) +
+                       b2_pos(hbf, cp->code_start)) + 1;
+       return num_chars;
+}
+
+/*
+ *     Functions also implemented as macros
+ */
+
+#ifdef hbfBitmapBBox
+#undef hbfBitmapBBox
+#endif
+
+HBF_BBOX *
+hbfBitmapBBox(hbf)
+       HBF     *hbf;
+{
+       return &(hbf->hbf_bitmap_bbox);
+}
+
+#ifdef hbfFontBBox
+#undef hbfFontBBox
+#endif
+
+HBF_BBOX *
+hbfFontBBox(hbf)
+       HBF     *hbf;
+{
+       return &(hbf->hbf_font_bbox);
+}
+
+const void *
+hbfGetByte2Range(hbfFile, b2r_pointer, startp, finishp)
+       HBF             *hbfFile;
+       const void      *b2r_pointer;
+       byte            *startp;
+       byte            *finishp;
+{
+       HBF_STRUCT      *hbf;
+       B2_RANGE        *b2r;
+
+       hbf = (HBF_STRUCT *)hbfFile;
+       if (b2r_pointer == NULL)
+               b2r = hbf->byte_2_range;
+       else
+               b2r = ((B2_RANGE *)b2r_pointer)->b2r_next;
+       if(b2r == NULL)
+               return NULL;
+       *startp = b2r->b2r_start;
+       *finishp = b2r->b2r_finish;
+       return (void *)b2r;
+}
+
+const void *
+hbfGetCodeRange(hbfFile, code_pointer, startp, finishp)
+       HBF             *hbfFile;
+       const void      *code_pointer;
+       HBF_CHAR        *startp;
+       HBF_CHAR        *finishp;
+{
+       HBF_STRUCT      *hbf;
+       CODE_RANGE      *cp;
+
+       hbf = (HBF_STRUCT *)hbfFile;
+       if (code_pointer == NULL)
+               cp = hbf->code_range;
+       else
+               cp = ((CODE_RANGE *)code_pointer)->code_next;
+       if(cp == NULL)
+               return NULL;
+       *startp = cp->code_start;
+       *finishp = cp->code_finish;
+       return (void *)cp;
+}
diff --git a/hbf.h b/hbf.h
new file mode 100644 (file)
index 0000000..22fc4c5
--- /dev/null
+++ b/hbf.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright 1993,1994,1995,2005 by Ross Paterson
+ *
+ * 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. The name of the author may not be used to endorse or promote
+ *     products derived from this software without specific prior written
+ *     permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ *
+ * Two interfaces to HBF files -- take your pick.
+ *
+ * Ross Paterson <ross@soi.city.ac.uk>
+ *
+ * Ross no longer maintains this code.  Please send bug reports to
+ * Werner Lemberg <wl@gnu.org>.
+ *
+ */
+#ifndef _HBF_
+#define _HBF_
+
+#ifndef __STDC__
+#      ifndef const
+#              define const
+#      endif
+#endif
+
+/*
+ *     #1: a lightweight C interface.
+ */
+
+typedef        unsigned int    HBF_CHAR;
+
+typedef struct {
+       unsigned short  hbf_width;
+       unsigned short  hbf_height;
+       short           hbf_xDisplacement;
+       short           hbf_yDisplacement;
+} HBF_BBOX;
+
+typedef struct {
+       /* fields corresponding to the definition */
+       HBF_BBOX        hbf_bitmap_bbox;        /* HBF_BITMAP_BOUNDING_BOX */
+       HBF_BBOX        hbf_font_bbox;          /* FONTBOUNDINGBOX */
+} HBF;
+
+extern HBF *hbfOpen(
+#ifdef __STDC__
+                       const   char    *filename
+#endif
+               );
+
+extern void    hbfClose(
+#ifdef __STDC__
+                       HBF             *hbf
+#endif
+               );
+
+extern const   char    *hbfProperty(
+#ifdef __STDC__
+                       HBF             *hbf,
+                       const   char    *propName
+#endif
+               );
+
+extern const   unsigned char   *hbfGetBitmap(
+#ifdef __STDC__
+                       HBF             *hbf,
+                       HBF_CHAR        code
+#endif
+               );
+
+extern void    hbfForEach(
+#ifdef __STDC__
+                       HBF     *hbf,
+                       void    (*func)(HBF *sameHbf, HBF_CHAR code, void *),
+                       void    *data
+#endif
+               );
+
+extern const   char    *hbfFileName(
+#ifdef __STDC__
+                       HBF     *hbf
+#endif
+               );
+
+extern long    hbfChars(
+#ifdef __STDC__
+                       HBF     *hbf
+#endif
+               );
+
+extern HBF_BBOX *hbfBitmapBBox(
+#ifdef __STDC__
+                       HBF     *hbf
+#endif
+               );
+/* but defined here as a macro */
+#define        hbfBitmapBBox(hbf)      (&((hbf)->hbf_bitmap_bbox))
+
+extern HBF_BBOX *hbfFontBBox(
+#ifdef __STDC__
+                       HBF     *hbf
+#endif
+               );
+/* but defined here as a macro */
+#define        hbfFontBBox(hbf)        (&((hbf)->hbf_font_bbox))
+
+#define        HBF_RowSize(hbf)\
+       ((hbfBitmapBBox(hbf)->hbf_width + 7)/8)
+
+#define        HBF_BitmapSize(hbf)\
+       (HBF_RowSize(hbf) * hbfBitmapBBox(hbf)->hbf_height)
+
+#define        HBF_GetBit(hbf,bitmap,x,y)\
+       (((bitmap)[(y)*HBF_RowSize(hbf) + (x)/8]>>(7 - (x)%8))&01)
+
+extern int     hbfDebug;       /* set non-zero for error reporting */
+
+extern const void *hbfGetCodeRange(
+#ifdef __STDC__
+                       HBF                     *hbfFile,
+                       const void              *code_pointer,
+                       HBF_CHAR                *startp,
+                       HBF_CHAR                *finishp
+#endif
+               );
+
+extern const void *hbfGetByte2Range(
+#ifdef __STDC__
+                       HBF                     *hbfFile,
+                       const void              *b2r_pointer,
+                       unsigned char           *startp,
+                       unsigned char           *finishp
+#endif
+               );
+
+/*
+ *     #2: taken from Appendix 2 of the HBF draft.
+ */
+
+typedef        unsigned int    HBF_HzCode;
+typedef unsigned char  HBF_Byte ;
+typedef HBF_Byte *     HBF_BytePtr ;
+typedef HBF *          HBF_Handle ;
+typedef HBF_Handle *    HBF_HandlePtr ;
+typedef char *         String ;
+
+extern int     HBF_OpenFont(
+#ifdef __STDC__
+               const   char *        filename,
+               HBF_HandlePtr ptrHandleStorage
+#endif
+);
+
+extern int     HBF_CloseFont(
+#ifdef __STDC__
+               HBF_Handle handle
+#endif
+);
+
+extern const char * HBF_GetProperty(
+#ifdef __STDC__
+               HBF_Handle      handle,
+               const   char *  propertyName
+#endif
+);
+
+extern int     HBF_GetFontBoundingBox(
+#ifdef __STDC__
+               HBF_Handle   handle,
+               unsigned int *width,
+               unsigned int *height,
+               int *xDisplacement,
+               int *yDisplacement
+#endif
+);
+
+extern int     HBF_GetBitmapBoundingBox(
+#ifdef __STDC__
+               HBF_Handle   handle,
+               unsigned int *width,
+               unsigned int *height,
+               int *xDisplacement,
+               int *yDisplacement
+#endif
+);
+
+extern int     HBF_GetBitmap(
+#ifdef __STDC__
+               HBF_Handle  handle,
+               HBF_HzCode  hanziCode,
+               HBF_BytePtr ptrBitmapBuffer
+#endif
+);
+
+#endif /* ! _HBF_ */
diff --git a/htext.h b/htext.h
new file mode 100644 (file)
index 0000000..d3339b4
--- /dev/null
+++ b/htext.h
@@ -0,0 +1,1438 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+static gchar *about_text = ""
+"<help>"
+"<center><b>GBDFEditor 1.6</b>\n"
+"mleisher@gmail.com\n"
+"15 April 2010</center>\n"
+"\n"
+"GBDFEditor is a BDF font editor that supports "
+"these main features:\n"
+"\n"
+"<bullet> Multiple fonts can be loaded from the command line.</bullet>\n"
+"<bullet> Multiple fonts can be open at the same time.</bullet>\n"
+"<bullet> Cutting and pasting glyphs between fonts.</bullet>\n"
+"<bullet> Multiple glyph bitmap editors can be open at the same time.</bullet>\n"
+"<bullet> Cutting and pasting between glyph bitmap editors.</bullet>\n"
+"<bullet> Automatic correction of certain metrics when a font is loaded.</bullet>\n"
+"<bullet> Generation of XLFD font names for fonts without XLFD names.</bullet>\n"
+"<bullet> Update an XLFD font name from the font properties.</bullet>\n"
+"<bullet> Update the font properties from an XLFD font name.</bullet>\n"
+"<bullet> Font property editor.</bullet>\n"
+"<bullet> Font comment editor.</bullet>\n"
+"<bullet> Supports unencoded glyphs (ENCODING of -1).</bullet>\n"
+"<bullet> Display of glyph encodings in octal, decimal, or hex.</bullet>\n"
+"<bullet> Builtin on-line help.</bullet>\n"
+"<bullet> Imports PK/GF fonts.</bullet>\n"
+"<bullet> Imports HBF (Han Bitmap Font) fonts.</bullet>\n"
+"<bullet> Imports Linux console fonts (PSF, CP, and FNT).</bullet>\n"
+"<bullet> Imports Sun console fonts (vfont format).</bullet>\n"
+"<bullet> Imports fonts from the X server.</bullet>\n"
+"<bullet> Imports Windows FON/FNT fonts.</bullet>\n"
+"<bullet> Imports OpenType fonts and collections.</bullet>\n"
+"<bullet> Exports Linux console PSF2 fonts.</bullet>\n"
+"<bullet> Exports HEX fonts (http://czyborra.com/unifont).</bullet>\n"
+"<bullet> Edits gray scale fonts with 2, 4 or 8 bits per pixel.</bullet>\n"
+"\n"
+"GBDFEditor was designed to use GTK+ 2.6 or later.\n"
+"</help>";
+
+static gchar *program_text = "<help>"
+"By default, gbdfed automatically collects "
+"comments that are saved with the font, it "
+"preserves the unencoded glyphs, and it will "
+"attempt to make some metrics corrections "
+"automatically. These options can be set on the "
+"command line.\n"
+"\n"
+"More than one font can be specified on the command "
+"line.\n"
+"\n"
+"The command line parameters for gbdfed are:\n"
+"\n"
+"<param>-nc</param>\t\tno comments\n"
+"<param>-nm</param>\t\tno metrics corrections\n"
+"<param>-nu</param>\t\tno unencoded glyphs\n"
+"<param>-np</param>\t\tdo not pad character cell bitmaps\n"
+"<param>-bp</param>\t\tallow blank pages\n"
+"<param>-ed</param>\t\tno <b>Really Exit?</b> dialog\n"
+"<param>-ps</param> <i>n</i>\t\tset point size\n"
+"<param>-hres</param> <i>n</i>\tset horizontal resolution\n"
+"<param>-vres</param> <i>n</i>\tset vertical resolution\n"
+"<param>-res</param> <i>n</i>\tset both resolutions\n"
+"<param>-sp</param> <i>s</i>\t\tset the font spacing (<b>p</b>roportional, <b>m</b>onowidth, <b>c</b>haractercell)\n"
+"<param>-bpp</param> <i>n</i>\tset the font bits per pixel (<b>1</b>, <b>2</b>, <b>4</b>, <b>8</b>)\n"
+"<param>-eol</param> <i>e</i>\tset the default end of line char(s) (<b>u</b>nix, <b>d</b>os, <b>m</b>ac)\n"
+"<param>-g</param> <i>code</i>\tset the initial glyph code to be displayed at startup(can be decimal, hex, or octal)\n"
+"<param>-cb</param> <i>base</i>\tset the code base for glyph codes (<b>oct</b>, <b>dec</b>, <b>hex</b>)\n"
+"</help>";
+
+static gchar *fgrid_text = "<help>"
+"<center><b>Font Grid</b></center>\n"
+"\n"
+"The main window of each font editor is called the "
+"Font Grid. All of the Font Grids have a special "
+"clipboard used for passing glyphs around. This "
+"clipboard is called <b>FONTGRID_CLIPBOARD</b>.\n"
+"\n"
+"At the top of each editor window there are some "
+"fields and buttons. These are:\n"
+"\n"
+"<margin>The <b>Font</b> text field is where the font name "
+"is set so it can be edited.</margin>\n"
+"\n"
+"<margin>The <b>Glyph</b> field is a label that provides "
+"some information about glyph name, encoding, and "
+"metrics when a glyph is selected. When a range "
+"of glyphs are selected, this field displays the "
+"start and end codes of the range.</margin>\n"
+"\n"
+"<margin>The push buttons are used to navigate through the "
+"glyph pages. The <b>Previous Page</b> and <b>Next Page</b> "
+"buttons normally skip glyph pages that are empty, "
+"but that can be changed using the <b>Setup</b> dialog.</margin>\n"
+"\n"
+"<margin>The <b>Page</b> field indicates the current glyph page "
+"and also allows a specific page number to be entered. "
+"Once a page number is entered, pressing the Return "
+"key will cause the Font Grid to shift to that page. "
+"The page number entered is expected to be a decimal "
+"number.</margin>\n"
+"\n"
+"<margin>The <b>Code</b> field is provided for situations where "
+"the page number is not known, but the encoding is "
+"known. The encoding entered in this field must be "
+"in the base (8, 10, or 16) that is currently being "
+"used to display glyph encodings (see the \"View\" "
+"menu below). Once the encoding is entered, pressing "
+"the Return key will cause the Font Grid to shift to "
+"the page containing the encoding.</margin>\n"
+"\n"
+"When a glyph has been modified either by the user or "
+"by automatic metrics corrections when the font is loaded, "
+"the glyph code above the glyph cell will be highlighted.\n"
+"\n"
+"<bul>Font Grid Menus</bul>\n"
+"\n"
+"<b>File</b>\n"
+"<margin1>New &lt;Ctrl+N&gt;</margin1> "
+"<margin2>This creates a new font and asks for the point "
+"size, resolution, and font spacing first.</margin2>\n"
+"\n"
+"<margin1>Open &lt;Ctrl+O&gt;</margin1> "
+"<margin2>This opens a new font in the current Font Grid.</margin2>\n"
+"\n"
+"<margin1>Save &lt;Ctrl+S&gt;</margin1> "
+"<margin2>Save the current font.</margin2>\n"
+"\n"
+"<margin1>Save As &lt;Ctrl+W&gt;</margin1> "
+"<margin2>Save the current font with some other name.</margin2>\n"
+"\n"
+"<margin1.5>Import</margin1.5>\n"
+"\n"
+"<margin2>PK/GF Font &lt;Ctrl+K&gt;</margin2> "
+"<margin3>Import a Metafont PK or GF font.</margin3>\n"
+"\n"
+"<margin2>Console Font &lt;Ctrl+L&gt;</margin2> "
+"<margin3>Import a Linux or Sun console (binary) font. "
+"If the font is a CP (Linux codepage) font, this "
+"will load all three point sizes of the font, "
+"display the 16pt font and create editors for the "
+"14pt and 8pt fonts. If the font is a PSF1 or PSF2 "
+"font, the embedded mapping table is loaded as well.</margin3>\n"
+"\n"
+"<margin2>HBF Font &lt;Ctrl+H&gt;</margin2> "
+"<margin3>Import an HBF font. Only available if "
+"gbdfed was compiled with HBF support.</margin3>\n"
+"\n"
+"<margin2>Windows Font &lt;Ctrl+B&gt;</margin2> "
+"<margin3>Import a Windows FON/FNT font. This will also "
+"import fonts from .EXE and .DLL files.</margin3>\n"
+"\n"
+"<margin2>OpenType Font &lt;Ctrl+Y&gt;</margin2> "
+"<margin3>Import an OpenType (.otf), TrueType font (.ttf) or "
+"TrueType collection (.ttc).</margin3>\n"
+"\n"
+"<margin2>Server Font &lt;Ctrl+G&gt;</margin2> "
+"<margin3>This will import a font from the X server.</margin3>\n"
+"\n"
+"<margin1.5>Export</margin1.5>\n"
+"\n"
+"<margin2>PSF &lt;Ctrl+F&gt;</margin2> "
+"<margin3>This will export the current BDF font or the current selection "
+"of glyphs to a PSF2 font.</margin3>\n"
+"\n"
+"<margin3>During the export, an option menu will let you select whether you "
+"want to:</margin3>\n"
+"\n"
+"<margin3>A. Export the font with its Unicode mappings.</margin3> "
+" "
+"<margin3>B. Export just the glyphs.</margin3>\n"
+"\n"
+"<margin3>C. Export just the Unicode mappings in the simple "
+"ASCII form used by the psfaddtable(1) program.</margin3>\n"
+"\n"
+"<margin3>Only the first 512 glyphs will be exported from "
+"the font.</margin3>\n"
+"\n"
+"<margin2>HEX</margin2> "
+"<margin3>This will export the current BDF font into the "
+"HEX format (See the <b>HEX Font Notes</b> entry).</margin3>\n"
+"\n"
+"<margin1>Exit/Close &lt;Ctrl+F4&gt;</margin1> "
+"<margin2>Exit the program if this is the primary Font "
+"Grid or simply hide the current Font Grid window.</margin2>\n"
+"\n"
+"<margin2>The key binding for this can be changed in the "
+"configuration file. See the <b>Configuration File</b> "
+"help section.</margin2>\n"
+"\n"
+"<b>Edit</b>\n"
+"<margin1>Copy &lt;Ctrl+C&gt;</margin1> "
+"<margin2>This copies the current selection to the Font "
+"Grid clipboard.</margin2>\n"
+"\n"
+"<margin1>Cut &lt;Ctrl+X&gt;</margin1> "
+"<margin2>This copies the current selection to the Font Grid "
+"clipboard and then deletes the selection.</margin2>\n"
+"\n"
+"<margin1>Paste &lt;Ctrl+V&gt;</margin1> "
+"<margin2>This replaces the glyphs starting at the currently "
+"selected position with the Font Grid clipboard.</margin2>\n"
+"\n"
+"<margin1>Overlay &lt;Ctrl+Shift+V&gt;</margin1> "
+"<margin2>This merges the Font Grid clipboard with the glyphs "
+"starting at the currently selected position. "
+"The names of the modified glyphs are not changed.</margin2>\n"
+"\n"
+"<margin1>Insert &lt;Ctrl+Meta+V&gt;</margin1> "
+"<margin2>This inserts the Font Grid cliboard in front of the "
+"currently selected position.</margin2>\n"
+"\n"
+"<margin1>Properties &lt;Ctrl+P&gt;</margin1> "
+"<margin2>This invokes the font property editor.</margin2>\n"
+"\n"
+"<margin1>Comments &lt;Ctrl+M&gt;</margin1> "
+"<margin2>This invokes the font comments editor.</margin2>\n"
+"\n"
+"<margin1>Font Info &lt;Ctrl+I&gt;</margin1> "
+"<margin2>This invokes a dialog that allows changes "
+"to some of the font information so these "
+"values do not have to be changed using the "
+"property editor. These values include the "
+"default character, font device width (for "
+"monowidth and character cell fonts), font "
+"ascent and descent, font vertical and "
+"horizontal resolution, and the font spacing.</margin2>\n"
+"\n"
+"<margin1.5>Font Name</margin1.5>\n"
+"\n"
+"<margin2>Make XLFD Name</margin2> "
+"<margin3>If the font does not have an XLFD name, this "
+"will save the current font name in the "
+"<b>_ORIGINAL_FONT_NAME</b> font property and then "
+"generate an XLFD name for the font.</margin3>\n"
+"\n"
+"<margin2>Update Name From Properties</margin2> "
+"<margin3>This will update the XLFD font name fields from"
+"the font property list.</margin3>\n"
+"\n"
+"<margin2>Update Properties From Name</margin2> "
+"<margin3>This will update the font properties from the "
+"XLFD font name.</margin3>\n"
+"\n"
+"<margin2>Update Average Width</margin2> "
+"<margin3>This will update the average width field of the "
+"XLFD font name and will update the "
+"<b>AVERAGE_WIDTH</b> font property as a side effect.</margin3>\n"
+"\n"
+"<margin1.5>Rename Glyphs</margin1.5>\n"
+"<margin2>Unicode Names</margin2> "
+"<margin3>This option will rename all the glyphs using names "
+"from a Unicode Character Database file set in the "
+"config file or from the <b>Setup-&gt;Other Options</b> "
+"dialog.</margin3>\n"
+"\n"
+"<margin2>Unicode Values</margin2> "
+"<margin3>This option will rename all the glyphs with 16-bit "
+"hexadecimal values prefixed with <b>0x</b>, <b>U+</b>, or <b>\\u</b>.</margin3>\n"
+"\n"
+"<margin1>Test Glyphs &lt;Ctrl+Z&gt;</margin1> "
+"<margin2>This will toggle the glyph test dialog on or off for "
+"the editor. When this is active, clicking on a glyph "
+"in any Font Grid will also add it to the glyph test "
+"dialog. When changes are made to a glyph or the font "
+"bounding box, the glyph test dialog will be updated "
+"accordingly.</margin2>\n"
+"\n"
+"<margin2>The glyph test dialog provides a toggle to turn the "
+"baseline on or off and another toggle to draw from right "
+"to left instead of left to right.</margin2>\n"
+"\n"
+"<margin1>Preferences &lt;Ctrl+T&gt;</margin1> "
+"<margin2>This will invoke the dialog to edit various preferences"
+"used by the editor and when loading/creating fonts.</margin2>\n"
+"\n"
+"<b>View</b>\n"
+"<margin1>Unencoded &lt;Ctrl+E&gt;</margin1> "
+"<margin2>If the font has unencoded glyphs (<b>ENCODING</b> "
+"field is -1), this will toggle between "
+"displaying the unencoded and encoded glyphs.</margin2>\n"
+"\n"
+"<margin1.5>Code Base</margin1.5>\n"
+"<margin2>Octal</margin2> "
+"<margin3>This option will display glyph encodings in "
+"octal (base 8).</margin3>\n"
+"\n"
+"<margin2>Decimal</margin2> "
+"<margin3>This option will display glyph encodings in "
+"decimal (base 10).</margin3>\n"
+"\n"
+"<margin2>Hexadecimal</margin2> "
+"<margin3>This option will display glyph encodings in "
+"hexadecimal (base 16).</margin3>\n"
+"\n"
+"<margin1>Other Page &lt;Ctrl+Shift+S&gt;</margin1> "
+"<margin2>This will toggle between the current glyph page "
+"and the last page that was viewed.</margin2>\n"
+"\n"
+"<margin1>Vertical/Horizontal View &lt;Ctrl+Q&gt;</margin1> "
+"<margin2>This will toggle the FontGrid between showing the "
+"glyphs horizontally (default) or vertically.</margin2>\n"
+"\n"
+"<margin1>Messages &lt;Ctrl+A&gt;</margin1> "
+"<margin2>This will show messages generated when corrections "
+"to the font metrics are done or when errors are "
+"encountered.</margin2>\n"
+"\n"
+"<b>Operations</b>\n"
+"<margin1>Translate &lt;Ctrl+D&gt;</margin1> "
+"<margin2>This will bring up the dialog for entering the X "
+"offset and Y offset used to translate the glyph to "
+"a new location.</margin2>\n"
+"\n"
+"<margin2>The option of translating the selected glyphs or all "
+"of the glyphs is provided.</margin2>\n"
+"\n"
+"<margin1>Rotate &lt;Ctrl+R&gt;</margin1> "
+"<margin2>This will bring up the dialog for entering the "
+"rotation angle. The rotation is limited to between "
+"± 1° and 359°.</margin2>\n"
+"\n"
+"<margin2>The option of rotating the selected glyphs or all "
+"of the glyphs is provided.</margin2>\n"
+"\n"
+"<margin1>Shear &lt;Ctrl+J&gt;</margin1> "
+"<margin2>This will bring up the dialog for entering the "
+"angle of the shear. The shear is limited to between "
+"± 1° and 45°.</margin2>\n"
+"\n"
+"<margin2>The option of rotating the selected glyphs or all "
+"of the glyphs is provided.</margin2>\n"
+"\n"
+"<margin1>Embolden &lt;Ctrl+Shift+B&gt;</margin1> "
+"<margin2>This will bring up the dialog for choosing whether "
+"to embolden the selected glyphs or to embolden all "
+"glyphs.</margin2>\n"
+"\n"
+"<margin2>To <bi>embolden</bi> means to make bold.</margin2> "
+"\n\n"
+"<b>Windows</b> "
+"\n"
+"<margin1>[editor list]</margin1> "
+"<margin2>The remaining menu items are all the Font "
+"Grids that have been created. Choosing one "
+"will force that window to be made visible and "
+"moved to the top.</margin2>\n"
+"\n"
+"<bul>Font Grid Translations</bul>\n"
+"<margin>0..9</margin> "
+"<margin1>Typing digits will accumulate a count which is "
+"applied to movement done with the arrow and page keys.</margin1>\n"
+"\n"
+"<margin>Left</margin> "
+"<margin1>This will move the single cell selection left.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Right</margin> "
+"<margin1>This will move the single cell selection right.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Up</margin> "
+"<margin1>This will move the single cell selection up.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Down</margin> "
+"<margin1>This will move the single cell selection down.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+Left</margin> "
+"<margin1>This will extend the selection to the left.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+Right</margin> "
+"<margin1>This will extend the selection to the right.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+Up</margin> "
+"<margin1>This will extend the selection up a row or column, "
+"depending on the display orientation, horizontal or "
+"vertical.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+Down</margin> "
+"<margin1>This will extend the selection down a row or column, "
+"depending on the display orientation, horizontal or "
+"vertical.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>PageUp</margin> "
+"<margin1>This will switch to the next page of glyphs.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>PageDown</margin> "
+"<margin1>This will switch to the previous page of glyphs.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Home</margin> "
+"<margin1>This will switch to the first page of glyphs.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>End</margin> "
+"<margin1>This will switch to the last page of glyphs.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+PageUp</margin> "
+"<margin1>This will extend the selection to the next page.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+PageDown</margin> "
+"<margin1>This will extend the selection to the previous page.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+Home</margin> "
+"<margin1>This will extend the selection to the first page that "
+"has glyphs.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Shift+End</margin> "
+"<margin1>This will extend the selection to the last page that "
+"has glyphs.</margin1>\n"
+"<margin1>If a decimal number is typed before this, this "
+"operation will be done that number of times.</margin1>\n"
+"\n"
+"<margin>Button1Down</margin> "
+"<margin1>This will start selecting glyphs. If Button1 is "
+"double-clicked, it will edit the current glyph.</margin1>\n"
+"\n"
+"<margin>Button1Motion</margin> "
+"<margin1>This will extend the selected glyphs.</margin1>\n"
+"\n"
+"<margin>Button1Up</margin> "
+"<margin1>This will end glyph selection.</margin1>\n"
+"\n"
+"<margin>Shift+Button1Down</margin> "
+"<margin1>This will adjust the glyphs already selected by "
+"adding or removing glyphs from the selection.</margin1>\n"
+"\n"
+"<margin>Button2Down</margin> "
+"<margin1>This will paste the glyphs on the Font Grid "
+"clipboard at the glyph position under the mouse. "
+"If the paste is done in the unencoded glyph area, "
+"the glyphs will simply be appended to the end. "
+"The unencoded glyph area is simply a container "
+"for unused glyphs.</margin1>\n"
+"\n"
+"<margin>Shift+Button2Down</margin> "
+"<margin1>This will insert the glyphs on the Font Grid "
+"clipboard in front of the glyphs starting at the "
+"position under the mouse. Any glyphs moved past "
+"the 0xffff encoding will be moved to the unencoded "
+"area so they are not lost. This action will always "
+"insert, no matter what mode the font grid is in.</margin1>\n"
+"\n"
+"<margin>Ctrl+Button2Down</margin> "
+"<margin1>This will merge (overlay) the glyphs being pasted with "
+"the glyphs that are in the range of the glyphs being pasted. "
+"If a merge is done in the unencoded glyph area, the glyphs "
+"will simply be appended and not merged (overlayed).</margin1>\n"
+"\n"
+"<margin>Button3Down</margin> "
+"<margin1>This will copy the selected glyphs to the Font "
+"Grid clipboard.</margin1>\n"
+"\n"
+"<margin>Return</margin> "
+"<margin1>This will invoke a Glypheditor for the current glyph.</margin1>\n"
+"\n"
+"<margin>Ctrl+Return</margin> "
+"<margin1>This will cause the end selection callback to be called. "
+"The effect in the gbdfed program is to send the glyph "
+"to the glyph test dialog if it is open.</margin1>\n"
+"\n"
+"<margin>Copy</margin> "
+"<margin1>This will copy the selected glyphs to the Font "
+"Grid clipboard.</margin1>\n"
+"\n"
+"<margin>Cut</margin> "
+"<margin1>This will copy the selected glyphs to the Font "
+"Grid clipboard and then delete the glyphs.</margin1>\n"
+"\n"
+"<margin>Paste</margin> "
+"<margin1>This will paste the glyphs on the Font Grid "
+"clipboard at the currently selected glyph "
+"position.</margin1>\n"
+"\n"
+"<margin>Delete</margin> "
+"<margin1>This will copy the selected glyphs to the Font "
+"Grid clipboard and then delete the glyphs.</margin1>\n"
+"\n"
+"<margin>BackSpace</margin> "
+"<margin1>This will copy the selected glyphs to the Font "
+"Grid clipboard and then delete the glyphs.</margin1>\n"
+"\n"
+"<margin>Double clicking with Button1 will invoke the Glyph "
+"Editor for the current glyph.</margin>\n"
+"\n"
+"<b>Other Font Grid Features</b>\n"
+"<margin>The font name can be edited in the Font Grid and "
+"page switching can be done with the buttons on the "
+"Font Grid.</margin>\n"
+"</help>";
+
+static gchar *gedit_text = "<help>"
+"The Glyph Editor provides a simple bitmap editor "
+"designed to edit glyph bitmaps and other glyph "
+"information. The Glyph Editors all use a special "
+"clipboard used to pass bitmaps between the Glyph "
+"Editors. This clipboard is called "
+"<b>GLYPHEDIT_CLIPBOARD.</b>\n"
+"\n"
+"The only limit on the number of Glyph Editors that "
+"can be open at one time is the amount of memory.\n"
+"\n"
+"<bul>Glyph Editor Menus</bul>\n"
+"\n"
+"<b>File</b>\n"
+"<margin1>Update &lt;Ctrl+S&gt;</margin1> "
+"<margin2>This will update the Font Grid with the "
+"modified glyph.</margin2>\n"
+"<margin1>To the right of the Glyph Name field is a "
+"button that will do this.</margin1>\n"
+"\n"
+"<margin1>Update and Next &lt;Ctrl+U&gt;</margin1> "
+"<margin2>This will update the FontGrid with the "
+"modified glyph and move to the next glyph.</margin2>\n"
+"\n"
+"<margin1>Update and Previous &lt;Ctrl+B&gt;</margin1> "
+"<margin2>This will update the FontGrid with the "
+"modified glyph and move to the previous glyph.</margin2>\n"
+"\n"
+"<margin1>Close &lt;Ctrl+F4&gt;</margin1> "
+"<margin2>This will close the Glyph Editor.</margin2>\n"
+"\n"
+"<b>Edit</b>\n"
+"<margin1>Reload &lt;Ctrl+L&gt;</margin1> "
+"<margin2>This will reload the glyph and discard any "
+"changes in the glyph.</margin2>\n"
+"\n"
+"<margin1>Copy &lt;Ctrl+C&gt;</margin1> "
+"<margin2>This will copy the currently selected portion "
+"of the bitmap to the Glyph Editor clipboard.</margin2>\n"
+"\n"
+"<margin1>Cut &lt;Ctrl+X&gt;</margin1> "
+"<margin2>This will copy the currently selected portion "
+"of the bitmap to the Glyph Editor clipboard "
+"and then delete the selection.</margin2>\n"
+"\n"
+"<margin1>Paste &lt;Ctrl+V&gt;</margin1> "
+"<margin2>This will paste the Glyph Editor clipboard "
+"into the current Glyph Editor with the "
+"top-left coordinate of the bitmap on the "
+"clipboard pasted at the location of the mouse. "
+"If the bitmap is too big to fit if it is "
+"pasted at the mouse location, the bitmap will "
+"be shifted until it fits completely in the "
+"Glyph Editor.</margin2>\n"
+"\n"
+"<margin1>Select All &lt;Ctrl+A&gt;</margin1> "
+"<margin2>This will select the whole glyph bitmap.</margin2>\n"
+"\n"
+"<margin1>Next Glyph &lt;Ctrl+N&gt;</margin1> "
+"<margin2>This will move the Glyph Editor to the next "
+"glyph position in the Font Grid. If the "
+"current glyph has been modified, a save prompt "
+"will appear before moving to the next glyph.</margin2>\n"
+"<margin1>To the right of the Glyph Name field is a "
+"button that will do this.</margin1>\n"
+"\n"
+"<margin1>Previous Glyph &lt;Ctrl+P&gt;</margin1> "
+"<margin2>This will move the Glyph Editor to the previous "
+"glyph position in the Font Grid. If the "
+"current glyph has been modified, a save prompt "
+"will appear before moving to the previous glyph.</margin2>\n"
+"<margin1>To the right of the Glyph Name field is a "
+"button that will do this.</margin1>\n"
+"\n"
+"<margin2>If you do not close this editor, it will be updated "
+"with Unicode mappings if you move to the next or "
+"previous glyph.</margin2>\n"
+"\n"
+"<b>Operation</b>\n"
+"<margin1>Draw &lt;Ctrl+D&gt;</margin1> "
+"<margin2>Change the Glyph Editor into Draw mode.</margin2>\n"
+"\n"
+"<margin1>Move &lt;Ctrl+M&gt;</margin1> "
+"<margin2>Change the Glyph Editor into Move mode. Move "
+"mode allows selecting a portion of the glyph "
+"bitmap and moving it to another location.</margin2>\n"
+"\n"
+"<margin1>Copy &lt;Ctrl+Y&gt;</margin1> "
+"<margin2>Change the Glyph Editor into Copy mode. Copy "
+"mode allows copying a portion of the glyph "
+"bitmap and moving it to another location.</margin2>\n"
+"\n"
+"<margin1>Rotate &lt;Ctrl+T&gt;</margin1> "
+"<margin2>This will invoke the rotation dialog that "
+"allows the degrees of rotation to be specified. "
+"Rotation can be between 1 and 359 degrees.</margin2>\n"
+"\n"
+"<margin1>Shear &lt;Ctrl+E&gt;</margin1> "
+"<margin2>This will invoke the shear dialog that allows "
+"the degrees of horizontal shear to be specified. "
+"Other names for shearing are obliquing or slanting. "
+"Shearing is allowed between 1 and 45 degrees.</margin2>\n"
+"\n"
+"<margin1>Embolden &lt;Ctrl+H&gt;</margin1> "
+"<margin2>This will embolden the current glyph in a simple "
+"way within the width of the glyph.</margin2>\n"
+"\n"
+"<margin1>Resize BBX &lt;Ctrl+R&gt;</margin1> "
+"<margin2>This will allow changing the sizes of the "
+"glyph bounding box including the left/right "
+"bearings and the glyph ascent/descent. If "
+"this change causes the glyph bounding box to "
+"be larger than the font bounding box, the "
+"font bounding box will be resized when the "
+"glyph is saved next.</margin2>\n"
+"\n"
+"<margin1>Edit PSF Unicode Mappings &lt;Ctrl+F&gt;</margin1> "
+"<margin2>This will show a list of Unicode mappings "
+"associated with the glyph. The list can be edited "
+"and once the Apply button has been pressed, the "
+"the changes will be applied to the glyph in the "
+"font proper.</margin2>\n"
+"\n"
+"<bul>Glyph Editor Translations</bul>\n"
+"\n"
+"<margin>ButtonDown</margin> "
+"<margin1>Depending on the operation of the Glyph Editor, this "
+"will start drawing, start selecting for a Move "
+"or start selecting for a Copy. When in Draw "
+"mode, Button1 will set pixels, Button2 will "
+"invert pixels, and Button3 will clear pixels.</margin1>\n"
+"\n"
+"<margin1>When in Move or Copy mode and a selection "
+"exists, pressing Button1 within the selection "
+"will \"grab\" the selection so it can be Moved or "
+"Copied. Pressing Button3 after a selection has "
+"been made will copy the selection to the Glyph Editor "
+"clipboard.</margin1>\n"
+"\n"
+"<margin>Shift+Button2Down</margin> "
+"<margin1>This will paste the contents of the Glyph Editor "
+"clipboard into the Glypheditor at the location "
+"of the mouse.</margin1>\n"
+"\n"
+"<margin>Motion</margin> "
+"<margin1>This will continue the operation started with "
+"<b>ButtonDown</b> as well as report the current mouse "
+"coordinates in Cartesian form relative to the "
+"bounding box for the glyph.</margin1>\n"
+"\n"
+"<margin>ButtonUp</margin> "
+"<margin1>This will end the operation started with "
+"<b>ButtonDown</b>.</margin1>\n"
+"\n"
+"<margin>Copy</margin> "
+"<margin1>This will copy the selected bitmap to the Glyph "
+"Editor clipboard.</margin1>\n"
+"\n"
+"<margin>Cut</margin> "
+"<margin1>This will copy the selected bitmap to the Glyph "
+"Editor clipboard and then delete it.</margin1>\n"
+"\n"
+"<margin>Paste</margin> "
+"<margin1>This will paste the Glyph Editor clipboard at "
+"the mouse position.</margin1>\n"
+"\n"
+"<margin>Right</margin> "
+"<margin1>This will shift the glyph bitmap toward (but not "
+"past) the right edge of the bitmap grid.</margin1>\n"
+"\n"
+"<margin>Left</margin> "
+"<margin1>This will shift the glyph bitmap toward (but not "
+"past) the left edge of the bitmap grid.</margin1>\n"
+"\n"
+"<margin>Up</margin> "
+"<margin1>This will shift the glyph bitmap toward (but not "
+"past) the top edge of the bitmap grid.</margin1>\n"
+"\n"
+"<margin>Down</margin> "
+"<margin1>This will shift the glyph bitmap toward (but not "
+"past) the bottom edge of the bitmap grid.</margin1>\n"
+"\n"
+"<margin>9</margin> "
+"<margin1>This will rotate the glyph bitmap 90° "
+"counter-clockwise.</margin1>\n"
+"\n"
+"<margin>0</margin> "
+"<margin1>This will rotate the glyph bitmap 90° "
+"clockwise.</margin1>\n"
+"\n"
+"<margin>-</margin> "
+"<margin1>This will flip the glyph bitmap around the "
+"vertical axis (horizontal flip).</margin1>\n"
+"\n"
+"<margin>=</margin> "
+"<margin1>This will flip the glyph bitmap around the "
+"horizontal axis (vertical flip).</margin1>\n"
+"\n"
+"<margin>comma, Z, or z</margin> "
+"<margin1>This will select the previous color or "
+"cycle back to the last color.</margin1>\n"
+"\n"
+"<margin>period, X, or x</margin> "
+"<margin1>This will select the next color or cycle "
+"to the first color.</margin1>\n"
+"\n"
+"<bul>Other Metrics Features</bul>\n"
+"\n"
+"If the font defines the X height and the Cap height, "
+"these can be displayed in the Glypheditors by turning "
+"them on or off individually from the "
+"<b>Preferences-&gt;Editing Options</b> tab. The size of the "
+"pixel used in the Glypheditor can also be set here. These "
+"values affect all Glypheditors.\n"
+"\n"
+"<bul>Other Glyph Editor Features</bul>\n"
+"\n"
+"In addition to editing the glyph bitmap, the glyph "
+"editor also allows editing of the glyph name and "
+"setting its device width (BDF <b>DWIDTH</b> field). To "
+"get more aesthetic spacing between glyphs, this "
+"value can be set explicitly. The glyph name "
+"should be a maximum of 14 characters.\n"
+"\n"
+"The Glypheditor also provides a simple toolbox "
+"that has buttons to switch between operations and to "
+"perform various bitmap manipulations.\n"
+"\n"
+"Pressing one of the shift buttons in the toolbox "
+"will repeat the shift operation if the mouse button "
+"is held down longer than 100 milliseconds. This is "
+"not configurable at the moment.\n"
+"\n"
+"If the font uses 2, 4, or 8 bits per pixel, a strip "
+"of colors will be presented down the left side of "
+"the toolbox. These colors can be selected with the "
+"mouse or can be chosen using the keys mentioned "
+"above in the Glypheditor translations. At the moment "
+"the Glypheditor must have the focus for the keys to "
+"work.</help>";
+
+static gchar *conf_text = "<help>"
+"gbdfed can be configured using an external\n"
+"file. This file is always assumed to be in the\n"
+"home directory and is called <b>.gbdfedrc</b>.\n"
+"\n"
+"This file sets default values which can be changed\n"
+"and saved from the editor. The default values\n"
+"apply to either the editor itself or the font\n"
+"management system.\n"
+"\n"
+"For the configuration options, the following types\n"
+"are used:\n"
+"\n"
+"<margin>&lt;boolean&gt;</margin>\n"
+"<margin1>A &lt;boolean&gt; value can be \"0\", \"false\", \"no\", "
+"\"1\", \"true\", or \"yes\". Boolean values are "
+"case insensitive.</margin1>\n"
+"\n"
+"<margin>&lt;labelstring&gt;</margin>\n"
+"<margin1>A &lt;labelstring&gt; value is a string used as a label "
+"for some of the options.</margin1>\n"
+"\n"
+"<margin>&lt;atom&gt;</margin>\n"
+"<margin1>An &lt;atom&gt; is basically a string.</margin1>\n"
+"\n"
+"<margin>&lt;cardinal&gt;</margin>\n"
+"<margin1>A &lt;cardinal&gt; value is an unsigned 32-bit "
+"integer value.</margin1>\n"
+"\n"
+"<margin>&lt;integer&gt;</margin>\n"
+"<margin1>An &lt;integer&gt; is a signed 32-bit integer "
+"value.</margin1>\n"
+"\n"
+"<margin>&lt;property-name&gt;</margin>\n"
+"<margin1>A &lt;property-name&gt; is any name that conforms to "
+"the XLFD definition of a user-defined "
+"property. Basically, the property name must "
+"start with the underscore character (_). "
+"These names are conventionally in upper case "
+"with the underscore character used to provide "
+"\"spaces\" between parts of the name.</margin1>\n"
+"\n"
+"<margin>&lt;property-type&gt;</margin>\n"
+"<margin1>A &lt;property-type&gt; can be one of \"atom\", "
+"\"cardinal\", or \"integer\" (see above).</margin1>\n"
+"\n"
+"<margin>&lt;font-spacing&gt;</margin>\n"
+"<margin1>A &lt;font-spacing&gt; value can be one of "
+"\"proportional\", \"monowidth\", or "
+"\"charactercell\".</margin1>\n"
+"\n"
+"<margin1>If an unknown &lt;font-spacing&gt; value is "
+"encountered, the default value is "
+"\"proportional\".</margin1>\n"
+"\n"
+"<margin>&lt;codebase&gt;</margin>\n"
+"<margin1>A &lt;codebase&gt; value can be one of \"octal\", "
+"\"decimal\", or \"hexadecimal\". It can also "
+"be shortened to just the first letter. Any "
+"unknown &lt;codebase&gt; values are assumed to be "
+"\"hexadecimal\".</margin1>\n"
+"\n"
+"<margin>&lt;translation&gt;</margin>\n"
+"<margin1>A &lt;translation&gt; is a valid GUI toolkit translation "
+"string.</margin1>\n"
+"\n"
+"<margin>&lt;filename&gt;</margin>\n"
+"<margin1>A &lt;filename&gt; is the name of a file including or "
+"excluding a partial or full path to the file.</margin1>\n"
+"\n"
+"<margin>&lt;eolname&gt;</margin>\n"
+"<margin1>An &lt;eolname&gt; value can be one of \"unix\" (^J), "
+"\"dos\" (^M^J), or \"mac\" (^M). This value is "
+"used when saving BDF fonts.</margin1>\n"
+"\n"
+"<bul>gbdfed Configuration File Options</bul>\n"
+"\n"
+"<margin>code_base &lt;codebase&gt; [default: \"hex\"]</margin>\n"
+"\n"
+"<margin1>By default, set the code base used to display the "
+"glyph encodings to base 16, or hex. This option can "
+"be set to \"oct\", \"dec\", or \"hex\".</margin1>\n"
+"\n"
+"<margin>skip_blank_pages &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will skip font pages "
+"that do not have any glyphs when the \"Next "
+"Page\" and \"Previous Page\" buttons are used.</margin1>\n"
+"\n"
+"<margin1>If this option is set to \"false\", the \"Next "
+"Page\" and \"Previous Page\" buttons will simply "
+"move to the next or previous page, even if "
+"they do not have glyphs on them.</margin1>\n"
+"\n"
+"<margin1>This feature is only available in the configuration "
+"file and on the command line.</margin1>\n"
+"\n"
+"<margin>really_exit &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will always present the "
+"\"Really Exit?\" dialog when exiting. If this "
+"option is set to \"false\", then the dialog "
+"not be presented when exiting.</margin1>\n"
+"\n"
+"<margin1>This feature is only available in the configuration "
+"file and on the command line.</margin1>\n"
+"\n"
+"<margin>grid_overwrite_mode &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, pasting glyphs into a Font Grid will "
+"overwrite glyphs that are in the same range as the "
+"glyphs being pasted. If this option is set to "
+"\"false\", pasting glyphs into a Font Grid will "
+"move glyphs to make room for the glyphs being pasted. "
+"Any glyphs moved that have an encoding larger than "
+"65535 will be moved to the unencoded area.</margin1>\n"
+"\n"
+"<margin1>This feature is toggled using the <b>Preferences</b> dialog.</margin1> \n"
+"\n"
+"<margin>close_accelerator_text &lt;labelstring&gt; [default: \"Ctrl+F4\"]</margin>\n"
+"\n"
+"<margin1>The default close accelerator text shown on the "
+"Close/Exit menu options of the FontGrids and "
+"GlyphEditors is \"Ctrl+F4\". This option changes the "
+"label string on those menu options. This option should "
+"be used in conjunction with the next option.</margin1>\n"
+"\n"
+"<margin1>This feature is only available in the configuration "
+"file.</margin1>\n"
+"\n"
+"<margin>close_accelerator &lt;translation&gt; [default: \"&lt;Control&gt;F4\"]</margin>\n"
+"\n"
+"<margin1>The default accelerator for the Close/Exit menu options "
+"in the FontGrids and GlyphEditors can sometimes be "
+"awkward for various reasons. This option allows that "
+"accelerator to be changed. This option should be used "
+"in conjunction with the previous option.</margin1>\n"
+"\n"
+"<margin1>This feature is only available in the configuration "
+"file.</margin1>\n"
+"\n"
+"<margin>unicode_name_file &lt;filename&gt;</margin>\n"
+"\n"
+"<margin1>This specifies a file that contains entries in the UCDB "
+"(Unicode Character Database) format. When glyphs are named "
+"using Unicode names, this file provides the mapping between "
+"the code and the name. This file is assumed to be sorted by "
+"code.</margin1>\n"
+"\n"
+"<margin1>This feature is set using the \"Preferences-&gt;Editing Options\" "
+"tab.</margin1>\n"
+"\n"
+"<margin>adobe_name_file &lt;filename&gt;</margin>\n"
+"\n"
+"<margin1>This specifies a file that contains entries in the Adobe "
+"Glyph List format (see Adobe for details). When glyphs "
+"are named using the Adobe names, this file provides the "
+"mapping between the code and the name. This file is assumed "
+"to be sorted by name and not by code.</margin1>\n"
+"\n"
+"<margin1>This feature is set using the <b>Preferences-&gt;Editing Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>pixel_size &lt;integer&gt; [default: \"10\"]</margin>\n"
+"\n"
+"<margin1>The Glypheditors will use a square of size 10x10 to "
+"represent a pixel in the glyph bitmap. If the glyph "
+"bitmap causes the Glypheditor grid to be larger than "
+"1/2 the display height, then this value will be reduced "
+"until the bitmap fits within 1/2 the display size or "
+"until a pixel size of 2 is reached.</margin1>\n"
+"\n"
+"<margin1>The Glypheditors will always attempt to use this default "
+"value first before reducing the size, if reducing the size "
+"is needed.</margin1>\n"
+"\n"
+"<margin1>This feature is set using the \"Preferences-&gt;Editing Options\" "
+"tab.</margin1>\n"
+"\n"
+"<margin>show_cap_height &lt;boolean&gt; [default: \"false\"]</margin>\n"
+"\n"
+"<margin1>If the font has the <b>CAP_HEIGHT</b> property defined, "
+"this flag will toggle the display of this height "
+"in the Glypheditors.</margin1>\n"
+"\n"
+"<margin1>The <b>CAP_HEIGHT</b> is shown as a solid horizontal line "
+"above the baseline in the same color as the baseline.</margin1>\n"
+"\n"
+"<margin1>This feature is toggled using the <b>Preferences-&gt;Editing Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>show_x_height &lt;boolean&gt; [default: \"false\"]</margin>\n"
+"\n"
+"<margin1>If the font has the <b>X_HEIGHT</b> property defined, "
+"this flag will toggle the display of this height "
+"in the Glypheditors.</margin1>\n"
+"\n"
+"<margin1>The <b>X_HEIGHT</b> is shown as a solid horizontal line "
+"above the baseline in the same color as the baseline.</margin1>\n"
+"\n"
+"<margin1>This feature is toggled using the <b>Setup-&gt;Editing Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>font_grid_horizontal &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>This option determines if the glyphs are displayed "
+"horizontally or vertically. The default is to display "
+"horizontally.</margin1>\n"
+"\n"
+"<margin1>This default orientation option can only be set in "
+"the configuration file at the moment.</margin1>\n"
+"\n"
+"<margin>power2 &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>This option determines whether the font grid always "
+"adjusts the rows and columns to powers of 2. This "
+"option can only be set in the configuration file at "
+"the moment.</margin1>\n"
+"\n"
+"<margin>generate_sbit_metrics &lt;boolean&gt; [default: \"false\"]</margin>\n"
+"\n"
+"<margin1>This option determines whether an SBIT metrics file "
+"will be written after the BDF font has been written. "
+"NOTE: This is for use with the SBIT utility from "
+"Microsoft.</margin1>\n"
+"\n"
+"<margin1>This feature is toggled using the \"Preferences-&gt;General Options\" "
+"tab.</margin1>\n"
+"\n"
+"<bul>General Font Configuration Options</bul>\n"
+"\n"
+"<margin>make_backups &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will make backups when "
+"it saves fonts. The filename will have .bak "
+"on the end. This option will turn this feature "
+"off.</margin1>\n"
+"\n"
+"<margin1>This feature is toggled using the <b>Setup</b> dialog.</margin1>\n"
+"\n"
+"<margin>correct_metrics &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will make certain "
+"corrections to the font metrics when a font "
+"is loaded. If this value is \"false\", then "
+"no metrics corrections will be performed "
+"when a font is loaded.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>keep_unencoded &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will keep any "
+"unencoded glyphs that are found when a font "
+"is loaded. An unencoded glyph will have an "
+"\"ENCODING\" field set to -1. If this option "
+"is set to \"false\", then all unencoded glyphs "
+"will be ignored.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>keep_comments &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will keep all "
+"comments found in the font file. This "
+"allows them to be edited.</margin1>\n"
+"\n"
+"<margin1>If this option is set to \"false\", all "
+"comments except those that appear in the "
+"font properties list will be ignored. The "
+"comments in the font properties list are "
+"kept because they sometimes contain useful "
+"notes about the properties.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>pad_character_cells &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will pad each glyph "
+"bitmap from fonts with \"charactercell\" "
+"spacing. This means that each glyph has "
+"blank bits added around it until it matches "
+"the font bounding box exactly.</margin1>\n"
+"\n"
+"<margin1>This option is \"true\" by default because "
+"that seems to be what most people expect, "
+"based on numerous \"charactercell\" fonts that "
+"were checked.</margin1>\n"
+"\n"
+"<margin1>However, since the BDF format is sometimes "
+"used as a transfer format between programs, "
+"this option can be set to \"false\" to reduce "
+"the size of the BDF font.</margin1>\n"
+"\n"
+"<margin1>In either case, the fonts will display "
+"correctly, and metrics calculations should "
+"not be affected.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>eol &lt;eolname&gt; [default: \"unix\"]</margin>\n"
+"\n"
+"<margin1>By default, BDF fonts will be saved with a Unix "
+"end-of-line character (^J). This option can be "
+"\"unix\", \"dos\", or \"mac\".</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>hint_opentype_glyphs &lt;boolean&gt; [default: \"true\"]</margin>\n"
+"\n"
+"<margin1>By default, importing OpenType fonts will have "
+"the glyphs hinted. If this option is set to "
+"\"false\", the glyphs will not be hinted.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>point_size &lt;cardinal&gt; [default: \"12\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will create new fonts "
+"with point size 12.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>horizontal_resolution &lt;integer&gt; [default: \"display\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will determine the "
+"horizontal resolution based on the X display "
+"being used by the editor. For instance, "
+"this value is often \"90\" for Sun displays.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>vertical_resolution &lt;integer&gt; [default: \"display\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will determine the "
+"vertical resolution based on the X display "
+"being used by the editor. For instance, "
+"this value is often \"90\" for Sun displays.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>font_spacing &lt;font-spacing&gt; [default: \"proportional\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor will create new fonts "
+"with proportional spacing. This option can "
+"be set to \"monowidth\" or \"charactercell\" if "
+"\"proportional\" is not wanted.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>bits_per_pixel &lt;integer&gt; [default: \"1\"]</margin>\n"
+"\n"
+"<margin1>By default, the editor works with fonts that "
+"have one bit per pixel. But it also supports "
+"2, 4, or 8 bits per pixel. This option sets "
+"the default bits per pixel when creating new "
+"fonts.</margin1>\n"
+"\n"
+"<margin1>This feature is available on the <b>Preferences-&gt;New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>2bpp_grays &lt;integer&gt;...</margin>\n"
+"\n"
+"<margin1>This parameter supplies a default set of gray values "
+"between 0 and 255 for 2 bits per pixel fonts. Four values "
+"should be supplied.</margin1>\n"
+"\n"
+"<margin>4bpp_grays &lt;integer&gt;...</margin>\n"
+"\n"
+"<margin1>This parameter supplies a default set of gray values "
+"between 0 and 255 for 2 bits per pixel fonts. Sixteen values "
+"should be supplied.</margin1>\n"
+"\n"
+"<margin>property &lt;property-name&gt; &lt;property-type&gt;</margin>\n"
+"\n"
+"<margin1>To support user-defined properties, the "
+"editor provides the facility to define them "
+"in the configuration file in order to "
+"interpret them correctly (atom, cardinal, or "
+"integer) when editing fonts containing "
+"user-defined properties.</margin1>\n"
+"\n"
+"<margin1>If an unknown user-defined property is "
+"encountered in a font, it always defaults to "
+"a &lt;property-type&gt; of \"atom\".</margin1>\n"
+"\n"
+"<margin1>There is no limit to the number of "
+"\"property\" options set in the configuration "
+"file.</margin1>\n"
+"</help>";
+
+static gchar *otf_text = "<help>"
+"If this program was compiled with the FreeType "
+"library to support importing OpenType fonts "
+"(.otf extension), TrueType fonts (.ttf extension), and "
+"TrueType collections (.ttc extension), "
+"when importing a font or collection, a dialog "
+"will be presented to allow you to choose a single font, "
+"the platform, and encoding. If you are loading a "
+"TrueType collection, there will be more than one font "
+"to choose from.\n"
+"\n"
+"OpenType fonts imported will use the point size and "
+"resolution set in your ~/.gbdfedrc file or the defaults "
+"set at compile time if you do not have a ~/.gbdfedrc.\n"
+"\n"
+"The point size and resolution can also be set before "
+"importing using the <b>Preferences</b> dialog.\n"
+"\n"
+"The renderer used to import OpenType fonts is "
+"available from http://www.freetype.org.\n"
+"</help>";
+
+static gchar *fnt_text = "<help>"
+"When a Windows .FON, .EXE, or .DLL file resource "
+"table holds more than one font, you are presented with "
+"a list of fonts to choose from. You can select as many "
+"of them as you wish or simply import them all using "
+"the <b>Import All</b> button.\n"
+"</help>";
+
+static gchar *psf_text ="<help>"
+"This editor imports both PSF1 and PSF2 Linux "
+"console fonts. It only exports the newer PSF2 "
+"fonts, usually with a \".psfu\" extension.\n"
+"\n"
+"When a PSF1 or PSF2 font is imported, it can have "
+"a Unicode mapping table following the glyphs. "
+"This mapping table can be modified through the "
+"Glypheditor from the Operations menu or by pressing "
+"&lt;Ctrl+F&gt;.\n"
+"\n"
+"When editing the mappings, the codes are expected "
+"to be entered in hex.\n"
+"\n"
+"Unicode mappings are included during cut and paste "
+"operations, allowing them to be transfered to other "
+"fonts or other locations within one font.\n"
+"\n"
+"There is no support currently for attaching an "
+"external mapping table to a font. This can be "
+"done outside this editor using the "
+"\"psfaddtable(1)\" program on Linux.\n"
+"</help>";
+
+static gchar *hex_text = "<help>"
+"The HEX format is described in more detail at: "
+"http://czyborra.com/unifont/.\n"
+"\n"
+"HEX fonts are fonts that have two glyph widths, "
+"with the smaller width being half the size of "
+"the larger width.\n"
+"</help>";
+
+static gchar *preferences_text = "<help>"
+"The Preferences dialog is used to set options for the "
+"Font Grid, Glyph Editors, and the BDF fonts. "
+"The options that can be set are:\n"
+"\n"
+"<margin>Insert Mode or Overwrite Mode</margin>\n"
+"\n"
+"<margin1>This affects the way glyphs are pasted in the "
+"Font Grids.</margin1>\n"
+"\n"
+"<margin1>The default mode, Overwrite, will simply replace "
+"everything in the range of the glyphs being pasted "
+"from the <b>FONTGRID_CLIPBOARD</b>.</margin1>\n"
+"\n"
+"<margin1>If Insert Mode is on, then all glyphs from the "
+"insert point forward are shifted forward to make "
+"room for the glyphs being pasted. Since this shift "
+"changes the encoding of the glyphs moved forward, "
+"it is possible for glyphs to get encodings greater "
+"than the maximum for BDF fonts (65535). If this "
+"happens, then the glyphs that have encodings greater "
+"than 65535 are moved to the unencoded area and are "
+"accessible by switching to the unencoded pages with "
+"\"Ctrl+E\".</margin1>\n"
+"\n"
+"<margin>Correct Metrics, Keep Unencoded, Keep Comments, "
+"Pad Character Cells, and EOL.</margin>\n"
+"\n"
+"<margin1>If <b>Correct Metrics</b> is set, certain metrics will be "
+"adjusted when a BDF font is loaded. If this option "
+"is not set, then the editor will assume the metrics "
+"are correct.</margin1>\n"
+"\n"
+"<margin1>If <b>Keep Unencoded</b> is set, glyphs with an ENCODING "
+"value of -1 will be stored in the unencoded pages "
+"which are accessible by switching using \"Ctrl+E\". "
+"If this is not set, then unencoded glyphs will be "
+"ignored when a BDF font is loaded.</margin1>\n"
+"\n"
+"<margin1>If <b>Keep Comments</b> is set, then comments collected will "
+"be stored with the font and written out before the "
+"PROPERTIES section of the BDF font. Comments can be "
+"edited with the Comment editor invoked with \"Ctrl+M\". "
+"If this is not set, then comments are simply ignored "
+"when the BDF font is loaded.</margin1>\n"
+"\n"
+"<margin1>If <b>Pad Character Cells</b> is set, then character cell "
+"fonts will have all glyphs padded with zero bits "
+"until they fit the font bounding box exactly. "
+"If this is not set, then the bitmaps for each "
+"glyph will be clipped to the minimum rectangle "
+"needed to hold the bitmap.</margin1>\n"
+"\n"
+"<margin1>The EOL menu sets the end-of-line string used "
+"when BDF fonts are saved. The three most common "
+"options are provided: Unix, DOS, and Mac EOLs.</margin1>\n"
+"\n"
+"<margin>Point Size, Horizontal and Vertical Resolution</margin>\n"
+"\n"
+"<margin1>These fields allow these three values to be set "
+"for new fonts created with \"Ctrl+N\" and also are "
+"used to set the desired size and resolution of "
+"OpenType fonts when they are imported.</margin1>\n"
+"\n"
+"<margin>Proportional, Monowidth, and Character Cell</margin>\n"
+"\n"
+"<margin1>These set the font spacing type for new fonts "
+"created using \"Ctrl+N\".</margin1>\n"
+"\n"
+"<margin>Bits Per Pixel</margin>\n"
+"\n"
+"<margin1>This option allows setting the bits per pixel "
+"value for new fonts created using \"Ctrl+N\". "
+"Selecting two or four bits per pixel also "
+"enables the Color button used to edit the colors "
+"used for gray scale fonts.</margin1>\n"
+"\n"
+"Along the bottom are some buttons. These buttons are:\n"
+"\n"
+"<margin>Update</margin>\n"
+"\n"
+"<margin1>When one or more options have changed, this button "
+"becomes active. If it is pressed, it will actually "
+"update the changed values. If it is not pressed and "
+"the dialog is closed, none of the changes made will "
+"take affect.</margin1>\n"
+"\n"
+"<margin>Save Setup</margin>\n"
+"\n"
+"<margin1>This button will become active after the Update "
+"button was pressed to actually change the setup "
+"options. Pressing this button will write all the "
+"setup values to a file in the home directory. This "
+"file is called \".gbdfedrc\".</margin1>\n"
+"\n"
+"<margin>Color</margin>\n"
+"\n"
+"<margin1>If the bits per pixel for new fonts is two or four "
+"this button will invoke the color editor. This editor "
+"allows adjusting what the different colors look like "
+"on the current screen. These colors are really only "
+"useful for testing with the current screen and may "
+"actually look different on other screens.</margin1>\n"
+"\n"
+"<margin1>In the color editor, a button at the bottom toggles "
+"between the colors for two and four bits per pixel.</margin1>\n"
+"\n"
+"<margin>Close</margin>\n"
+"\n"
+"<margin1>This closes the Setup dialog. If any changes were "
+"made and not applied with Update (or saved), then "
+"the changes are discarded.</margin1>\n"
+"\n"
+"<margin>Other Options</margin>\n"
+"\n"
+"<margin1>This button opens another setup dialog to set "
+"more options. The close button at the bottom "
+"simply closes the window. These options are:</margin1>\n"
+"\n"
+"<margin2>Hint OpenType Glyphs</margin2> "
+"\n"
+"<margin3>If this option is set, the OpenType renderer "
+"will use the hints in the font if they exist. "
+"This can sometimes make clearer glyphs at small "
+"point sizes.</margin3>\n"
+"\n"
+"<margin2>Unicode Glyph Name File</margin2> "
+"\n"
+"<margin3>This field is for setting the name of a file "
+"in the Unicode Character Database format. This "
+"file will supply glyph names from the file. "
+"The \"Browse\" button allows a file to be "
+"selected using a FileSelection dialog.</margin3>\n"
+"\n"
+"<margin3>The Unicode Character Database format is basically "
+"a set of semi-colon separated fields on a single line "
+"with the first field being 4 hex digits representing "
+"the encoding of the glyph and the next field being "
+"the name of the glyph. These are the only two "
+"fields used by this editor. The entries in this "
+"file are expected to be sorted in ascending order "
+"by encoding.</margin3>\n"
+"\n"
+"<margin2>Adobe Glyph Name File</margin2> "
+"\n"
+"<margin3>This field is for setting the name of a file "
+"in the Adobe Glyph List format. This file will "
+"supply glyph names from the file.</margin3>\n"
+"\n"
+"<margin3>The Adobe Glyph List format is basically a set of "
+"semi-colon separated fields with the first field "
+"being 4 hex digits representing the encoding of the "
+"glyph and the next field being the Adobe name of the "
+"glyph. The entries are not expected to be in ascending "
+"order by glyph code.</margin3>\n"
+"\n"
+"<margin2>Pixel Size</margin2> "
+"\n"
+"<margin3>This option allows the pixel size in the GlyphEditors "
+"to be set to different sizes. This effectively zooms "
+"the glyph in the editor. If this is changed and the "
+"Update button is pressed, all open GlyphEditors will "
+"be updated with the new value.</margin3>\n"
+"\n"
+"<margin2>Show Cap Height and Show X Height</margin2> "
+"\n"
+"<margin3>These two options can be set to make the cap height "
+"and the x height lines show up in the GlyphEditors. "
+"These will only show up if they are defined in the "
+"font or are set using the property editor. If these "
+"are changed and the Update button is pressed, all "
+"open GlyphEditors will be udated to show the lines.</margin3>\n"
+"\n"
+"<margin2>SBIT Metrics</margin2> "
+"\n"
+"<margin3>This option toggles the generation of an SBIT metrics "
+"file which can be incorporated into a OpenType font "
+"using the SBIT utility provided by Microsoft.</margin3>\n"
+"</help>";
+
+static gchar *color_text = "<help>"
+"The editor supports BDF fonts with 2, 4 and 8 bits per pixel. "
+"This is stored in the BDF file as a number on the end of "
+"the SIZE line.\n"
+"\n"
+"This is a non-standard extension and currently can only be "
+"used to create bitmap fonts (strikes or EBSC entries) that "
+"can be embedded in OpenType fonts.\n"
+"</help>";
+
+static gchar *tips_text = "<help>"
+"Useful Tips:\n"
+"\n"
+"To add space glyphs to proportional fonts, simply set "
+"the Device Width field to the desired width of the blank. "
+"When the font is saved, a bitmap is automatically "
+"generated for it.\n"
+"</help>";
diff --git a/labcon.c b/labcon.c
new file mode 100644 (file)
index 0000000..2306b21
--- /dev/null
+++ b/labcon.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "labcon.h"
+#include <gtk/gtklabel.h>
+#include <gtk/gtkdrawingarea.h>
+
+static GtkContainerClass *parent_class = 0;
+
+static void
+labcon_size_request(GtkWidget *w, GtkRequisition *req)
+{
+    LabCon *l = LABCON(w);
+    guint width = 0;
+    GtkRequisition l_rec, c_rec;
+
+    l_rec.width = c_rec.width = l_rec.height = c_rec.height = 0;
+
+    if (l->label != 0) {
+        if (GTK_WIDGET_VISIBLE(l->label))
+          gtk_widget_size_request(l->label, &l_rec);
+    } else {
+        if (GTK_WIDGET_VISIBLE(l->image))
+          gtk_widget_size_request(l->image, &l_rec);
+    }
+    if (GTK_WIDGET_VISIBLE(l->child))
+      gtk_widget_size_request(l->child, &c_rec);
+
+    if (l->leader)
+      width = LABCON(l->leader)->label_width;
+    width = MAX(width, l_rec.width);
+
+    req->height = MAX(l_rec.height, c_rec.height) +
+        (GTK_CONTAINER(l)->border_width * 2);
+    req->width = width + c_rec.width + l->spacing +
+        (GTK_CONTAINER(l)->border_width * 2);
+}
+
+static void
+labcon_size_allocate(GtkWidget *w, GtkAllocation *all)
+{
+    guint i;
+    GtkWidget *label;
+    LabCon *leader, *l = LABCON(w);
+    GtkRequisition l_rec, c_rec, ll_rec;
+    GtkAllocation w_all;
+
+    w->allocation = *all;
+
+    l_rec.width = c_rec.width = l_rec.height = c_rec.height = 0;
+
+    label = (l->label != 0) ? l->label : l->image;
+
+    if (GTK_WIDGET_VISIBLE(label))
+      gtk_widget_get_child_requisition(label, &l_rec);
+    if (GTK_WIDGET_VISIBLE(l->child))
+      gtk_widget_get_child_requisition(l->child, &c_rec);
+
+    /*
+     * Make sure the height is non-zero and leaves the border.
+     */
+    w_all.x = all->x + GTK_CONTAINER(l)->border_width;
+    w_all.y = all->y + GTK_CONTAINER(l)->border_width;
+    w_all.height = MAX(1, (gint) all->height -
+                       (gint) (GTK_CONTAINER(l)->border_width * 2));
+
+    if (l->leader != 0) {
+        leader = LABCON(l->leader);
+
+        if (leader->label)
+          gtk_widget_get_child_requisition(leader->label, &ll_rec);
+        else
+          gtk_widget_get_child_requisition(leader->image, &ll_rec);
+
+        if (ll_rec.width < l_rec.width) {
+            if (leader->label)
+              gtk_widget_set_size_request(leader->label,
+                                          l_rec.width, ll_rec.height);
+            else
+              gtk_widget_set_size_request(leader->image,
+                                          l_rec.width, ll_rec.height);
+        }
+
+        leader->label_width = MAX(l_rec.width, leader->label_width);
+        l_rec.width = leader->label_width;
+
+        for (i = 0; i < leader->group_used - 1; i++) {
+            if (LABCON(leader->group[i])->label)
+              gtk_widget_get_child_requisition(LABCON(leader->group[i])->label,
+                                               &ll_rec);
+            else
+              gtk_widget_get_child_requisition(LABCON(leader->group[i])->image,
+                                               &ll_rec);
+            if (ll_rec.width < l_rec.width) {
+                if (LABCON(leader->group[i])->label)
+                  gtk_widget_set_size_request(LABCON(leader->group[i])->label,
+                                              l_rec.width, ll_rec.height);
+                else
+                  gtk_widget_set_size_request(LABCON(leader->group[i])->image,
+                                              l_rec.width, ll_rec.height);
+            }
+        }
+    } else
+      l->label_width = l_rec.width;
+
+    if (l->pos == GTK_POS_LEFT) {
+        /*
+         * Position the label on the left of the child.
+         */
+
+        /*
+         * Calculate the allocation for the label widget.
+         */
+        w_all.width = l_rec.width;
+        gtk_widget_size_allocate(label, &w_all);
+
+        /*
+         * Calculate the allocation for the child widget. The child widget
+         * is expanded to fill the remaining space.
+         */
+        w_all.x += w_all.width + l->spacing;
+        w_all.width = all->width - (l_rec.width + l->spacing);
+        gtk_widget_size_allocate(l->child, &w_all);
+    } else {
+        /*
+         * Position the label on the right of the child.
+         */
+
+        /*
+         * Calculate the allocation for the child widget. The child widget
+         * is expanded to fill the remaining space.
+         */
+        w_all.width = all->width - (l_rec.width + l->spacing);
+        gtk_widget_size_allocate(l->child, &w_all);
+
+        /*
+         * Calculate the allocation for the label widget.
+         */
+        w_all.x += w_all.width + l->spacing;
+        w_all.width = l_rec.width;
+        gtk_widget_size_allocate(label, &w_all);
+    }
+}
+
+static void
+labcon_forall(GtkContainer *c, gboolean include_internals,
+              GtkCallback callback, gpointer callback_data)
+{
+    LabCon *l = LABCON(c);
+
+    if (l->label)
+      (*callback)(l->label, callback_data);
+    else
+      (*callback)(l->image, callback_data);
+    (*callback)(l->child, callback_data);
+}
+
+static void
+labcon_remove(GtkContainer *c, GtkWidget *w)
+{
+    guint i;
+    LabCon *l = LABCON(c);
+
+    /*
+     * Make sure that the group list has been deallocated so we don't
+     * leak memory.
+     */
+    if (l->child == w) {
+        if (l->group_size > 0) {
+          for (i = 0; i < l->group_used; i++)
+            LABCON(l->group[i])->leader = 0;
+          l->group_size = l->group_used = 0;
+          g_free(l->group);
+          l->group = 0;
+        }
+    }
+}
+
+static void
+labcon_class_init(gpointer g_class, gpointer class_data)
+{
+    GtkWidgetClass *wc = GTK_WIDGET_CLASS(g_class);
+    LabConClass *lc = LABCON_CLASS(g_class);
+    GtkContainerClass *cc = GTK_CONTAINER_CLASS(g_class);
+
+    wc->size_request = labcon_size_request;
+    wc->size_allocate = labcon_size_allocate;
+
+    cc->remove = labcon_remove;
+    cc->forall = labcon_forall;
+
+    parent_class = g_type_class_peek_parent(lc);
+}
+
+static void
+labcon_init(GTypeInstance *instance, gpointer g_class)
+{
+    LabCon *l = LABCON(instance);
+
+    GTK_WIDGET_SET_FLAGS(l, GTK_NO_WINDOW);
+    gtk_widget_set_redraw_on_allocate(GTK_WIDGET(l), FALSE);
+
+    l->pixbuf = 0;
+    l->image = l->label = l->child = 0;
+    l->spacing = 0;
+    l->align = LABCON_ALIGN_LEFT;
+    l->pos = GTK_POS_LEFT;
+    l->label_width = 0;
+    l->leader = 0;
+    l->group_size = l->group_used = 0;
+}
+
+static gboolean
+draw_pixbuf(GtkWidget *w, GdkEventExpose *event, gpointer data)
+{
+    GdkPixbuf *p = GDK_PIXBUF(data);
+    gint x, y, wd, ht;
+
+    wd = gdk_pixbuf_get_width(p);
+    ht = gdk_pixbuf_get_height(p);
+
+    x = (w->allocation.width >> 1) - (wd >> 1);
+    y = (w->allocation.height >> 1) - (ht >> 1);
+    gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)],
+                    p, 0, 0, x, y, wd, ht, GDK_RGB_DITHER_NONE, 0, 0);
+
+    return FALSE;
+}
+
+/**********************************************************************
+ *
+ * API functions.
+ *
+ **********************************************************************/
+
+GType
+labcon_get_type(void)
+{
+    static GType labcon_type = 0;
+  
+    if (!labcon_type) {
+        static const GTypeInfo labcon_info = {
+            sizeof (LabConClass),              /* class_size           */
+            0,                                 /* base_init            */
+            0,                                 /* base_finalize        */
+            labcon_class_init,                 /* class_init           */
+            0,                                 /* class_finalize       */
+            0,                                 /* class_data           */
+            sizeof(LabCon),                    /* instance_size        */
+            0,                                 /* n_preallocs          */
+            labcon_init,                       /* instance_init        */
+            0,                                 /* value_table          */
+        };
+
+        labcon_type = g_type_register_static(GTK_TYPE_CONTAINER, "LabCon",
+                                             &labcon_info, 0);
+    }
+  
+    return labcon_type;
+}
+
+GtkWidget *
+labcon_new_label(const gchar *label, LabConAlignment align,
+                 GtkPositionType pos, guint spacing,
+                 GtkWidget *child, GtkWidget *group)
+{
+    LabCon *l, *leader = LABCON(group);
+
+    g_return_val_if_fail(GTK_IS_WIDGET(child), NULL);
+    if (group) {
+        g_return_val_if_fail(GTK_IS_WIDGET(group), NULL);
+        g_return_val_if_fail(IS_LABCON(group), NULL);
+    }
+
+    l = g_object_new(labcon_get_type(), NULL);
+    l->pos = pos;
+    l->spacing = spacing;
+    l->child = child;
+
+    l->label = gtk_label_new(label);
+    switch (align) {
+      case LABCON_ALIGN_LEFT:
+        gtk_misc_set_alignment(GTK_MISC(l->label), 0.0, 0.5);
+        break;
+      case LABCON_ALIGN_RIGHT:
+        gtk_misc_set_alignment(GTK_MISC(l->label), 1.0, 0.5);
+        break;
+      case LABCON_ALIGN_CENTER:
+        gtk_misc_set_alignment(GTK_MISC(l->label), 0.5, 0.5);
+        break;
+    }
+
+    /*
+     * Go back until we get the group leader.
+     */
+    if (group && LABCON(group)->leader)
+      l->leader = LABCON(group)->leader;
+    else
+      l->leader = group;
+
+    if (l->leader) {
+        /*
+         * Add this widget to the group which should have all the same
+         * width labels.
+         */
+        leader = LABCON(l->leader);
+        if (leader->group_size == leader->group_used) {
+            if (leader->group_size == 0)
+              leader->group = (GtkWidget **) g_malloc(sizeof(GtkWidget *) * 4);
+            else
+              leader->group = (GtkWidget **)
+                  g_realloc(leader->group,
+                            sizeof(GtkWidget *) * (leader->group_size + 4));
+            leader->group_size += 4;
+        }
+        leader->group[leader->group_used++] = GTK_WIDGET(l);
+    }
+
+    gtk_widget_set_parent(l->label, GTK_WIDGET(l)); 
+    gtk_widget_show(l->label);
+    if (l->child) {
+        gtk_widget_set_parent(l->child, GTK_WIDGET(l)); 
+        gtk_widget_show(l->child);
+    }
+
+    return GTK_WIDGET(l);
+}
+
+GtkWidget *
+labcon_new_label_defaults(const gchar *label, GtkWidget *child,
+                          GtkWidget *group)
+{
+    return labcon_new_label(label, LABCON_ALIGN_RIGHT, GTK_POS_LEFT, 5,
+                            child, group);
+}
+
+GtkWidget *
+labcon_new_pixbuf(const GdkPixbuf *pixbuf, LabConAlignment align,
+                  GtkPositionType pos, guint spacing,
+                  GtkWidget *child, GtkWidget *group)
+{
+    LabCon *l, *leader = LABCON(group);
+
+    g_return_val_if_fail(GTK_IS_WIDGET(child), NULL);
+    if (group) {
+        g_return_val_if_fail(GTK_IS_WIDGET(group), NULL);
+        g_return_val_if_fail(IS_LABCON(group), NULL);
+    }
+
+    l = g_object_new(labcon_get_type(), NULL);
+    l->pixbuf = pixbuf;
+    l->pos = pos;
+    l->spacing = spacing;
+    l->child = child;
+    l->align = align;
+
+    /*
+     * Make the drawing area just big enough to hold the pixbuf at
+     * first.
+     */
+    l->image = gtk_drawing_area_new();
+    gtk_widget_set_size_request(l->image, 
+                                gdk_pixbuf_get_width(l->pixbuf),
+                                gdk_pixbuf_get_height(l->pixbuf));
+    g_signal_connect(G_OBJECT(l->image), "expose_event",
+                     G_CALLBACK(draw_pixbuf), (gpointer) l->pixbuf);
+
+    /*
+     * Go back until we get the group leader.
+     */
+    if (group && LABCON(group)->leader)
+      l->leader = LABCON(group)->leader;
+    else
+      l->leader = group;
+
+    if (l->leader) {
+        /*
+         * Add this widget to the group which should have all the same
+         * width labels or pixbufs.
+         */
+        leader = LABCON(l->leader);
+        if (leader->group_size == leader->group_used) {
+            if (leader->group_size == 0)
+              leader->group = (GtkWidget **) g_malloc(sizeof(GtkWidget *) * 4);
+            else
+              leader->group = (GtkWidget **)
+                  g_realloc(leader->group,
+                            sizeof(GtkWidget *) * (leader->group_size + 4));
+            leader->group_size += 4;
+        }
+        leader->group[leader->group_used++] = GTK_WIDGET(l);
+    }
+
+    gtk_widget_set_parent(l->image, GTK_WIDGET(l)); 
+    gtk_widget_show(l->image);
+    if (l->child) {
+        gtk_widget_set_parent(l->child, GTK_WIDGET(l)); 
+        gtk_widget_show(l->child);
+    }
+
+    return GTK_WIDGET(l);
+}
+
+GtkWidget *
+labcon_new_pixbuf_defaults(const GdkPixbuf *pixbuf, GtkWidget *child,
+                           GtkWidget *group)
+{
+    return labcon_new_pixbuf(pixbuf, LABCON_ALIGN_RIGHT, GTK_POS_LEFT, 5,
+                             child, group);
+}
+
+const GdkPixbuf *
+labcon_get_pixbuf(LabCon *l)
+{
+    g_return_val_if_fail(IS_LABCON(l), 0);
+
+    return l ? l->pixbuf : 0;
+}
+
+GtkWidget *
+labcon_get_image(LabCon *l)
+{
+    g_return_val_if_fail(IS_LABCON(l), 0);
+
+    return l ? l->image : 0;
+}
+
+GtkWidget *
+labcon_get_label(LabCon *l)
+{
+    g_return_val_if_fail(IS_LABCON(l), 0);
+
+    return l ? l->label : 0;
+}
+
+GtkWidget *
+labcon_get_child(LabCon *l)
+{
+    g_return_val_if_fail(IS_LABCON(l), 0);
+
+    return l ? l->child : 0;
+}
diff --git a/labcon.h b/labcon.h
new file mode 100644 (file)
index 0000000..728b83f
--- /dev/null
+++ b/labcon.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _h_labcon
+#define _h_labcon
+
+#include <gtk/gtkcontainer.h>
+#include "gtkcompat.h"
+
+G_BEGIN_DECLS
+
+/*
+ * The LabCon (Labeled Container) widget. Created to provide a container
+ * that contains a label and a single child, and can be part of a group
+ * of other LabCon widgets. All members of the group have the same label
+ * width. The labels will be on the left or the right side of the child
+ * widget and can be aligned to the right, left, or center.
+ */
+
+#define LABCON(obj) \
+      (G_TYPE_CHECK_INSTANCE_CAST(obj, labcon_get_type(), LabCon))
+#define LABCON_CLASS(klass) \
+      (G_TYPE_CHECK_CLASS_CAST(klass, labcon_get_type(), LabConClass))
+
+#define IS_LABCON(obj) \
+      (G_TYPE_CHECK_INSTANCE_TYPE(obj, labcon_get_type()))
+#define IS_LABCON_CLASS(klass) \
+      (G_TYPE_CHECK_CLASS_TYPE(klass, labcon_get_type()))
+
+#define LABCON_GET_CLASS(obj) \
+      (G_TYPE_INSTANCE_GET_CLASS(obj, labcon_get_type(), LabConClass))
+
+typedef struct _LabCon      LabCon;
+typedef struct _LabConClass LabConClass;
+
+typedef enum {
+    LABCON_ALIGN_LEFT,
+    LABCON_ALIGN_RIGHT,
+    LABCON_ALIGN_CENTER
+} LabConAlignment;
+
+struct _LabCon {
+    GtkContainer container;
+
+    GtkWidget *image;
+    GtkWidget *label;
+    GtkWidget *child;
+
+    /*
+     * The pixbuf that will be shown as the image instead of a label.
+     */
+    const GdkPixbuf *pixbuf;
+
+    /*
+     * Spacing between the label and the child widget.
+     */
+    guint spacing;
+
+    /*
+     * Alignment.
+     */
+    LabConAlignment align;
+
+    /*
+     * Label positioning (GTK_POS_LEFT or GTK_POS_RIGHT).
+     */
+    GtkPositionType pos;
+
+    /*
+     * The current width all labels should be.
+     */
+    guint label_width;
+
+    /*
+     * All the widgets that should have the same width labels.
+     */
+    GtkWidget *leader;
+    GtkWidget **group;
+    guint group_size;
+    guint group_used;
+};
+
+struct _LabConClass {
+    GtkContainerClass parent_class;
+};
+
+/**********************************************************************
+ *
+ * API functions.
+ *
+ **********************************************************************/
+
+extern GType labcon_get_type(void);
+
+extern GtkWidget *labcon_new_label(const gchar *label, LabConAlignment align,
+                                   GtkPositionType pos, guint spacing,
+                                   GtkWidget *child, GtkWidget *group);
+
+extern GtkWidget *labcon_new_label_defaults(const gchar *label,
+                                            GtkWidget *child,
+                                            GtkWidget *group);
+
+extern GtkWidget *labcon_new_pixbuf(const GdkPixbuf *pixbuf,
+                                    LabConAlignment align,
+                                    GtkPositionType pos,
+                                    guint spacing,
+                                    GtkWidget *child,
+                                    GtkWidget *group);
+
+extern GtkWidget *labcon_new_pixbuf_defaults(const GdkPixbuf *pixbuf,
+                                             GtkWidget *child,
+                                             GtkWidget *group);
+
+extern const GdkPixbuf *labcon_get_pixbuf(LabCon *l);
+extern GtkWidget *labcon_get_image(LabCon *l);
+extern GtkWidget *labcon_get_label(LabCon *l);
+extern GtkWidget *labcon_get_child(LabCon *l);
+
+G_END_DECLS
+
+#endif /* _h_labcon */
diff --git a/mkinstalldirs b/mkinstalldirs
new file mode 100755 (executable)
index 0000000..f945dbf
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp" 1>&2
+
+        mkdir "$pathcomp" || lasterr=$?
+
+        if test ! -d "$pathcomp"; then
+         errstatus=$lasterr
+        fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here