--- /dev/null
+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
--- /dev/null
+#
+# 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
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+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
+])
+
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+#! /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
+
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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 */
--- /dev/null
+.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>
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 = ℑ
+ 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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.
+ */
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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 <Ctrl+N></margin1> "
+"<margin2>This creates a new font and asks for the point "
+"size, resolution, and font spacing first.</margin2>\n"
+"\n"
+"<margin1>Open <Ctrl+O></margin1> "
+"<margin2>This opens a new font in the current Font Grid.</margin2>\n"
+"\n"
+"<margin1>Save <Ctrl+S></margin1> "
+"<margin2>Save the current font.</margin2>\n"
+"\n"
+"<margin1>Save As <Ctrl+W></margin1> "
+"<margin2>Save the current font with some other name.</margin2>\n"
+"\n"
+"<margin1.5>Import</margin1.5>\n"
+"\n"
+"<margin2>PK/GF Font <Ctrl+K></margin2> "
+"<margin3>Import a Metafont PK or GF font.</margin3>\n"
+"\n"
+"<margin2>Console Font <Ctrl+L></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 <Ctrl+H></margin2> "
+"<margin3>Import an HBF font. Only available if "
+"gbdfed was compiled with HBF support.</margin3>\n"
+"\n"
+"<margin2>Windows Font <Ctrl+B></margin2> "
+"<margin3>Import a Windows FON/FNT font. This will also "
+"import fonts from .EXE and .DLL files.</margin3>\n"
+"\n"
+"<margin2>OpenType Font <Ctrl+Y></margin2> "
+"<margin3>Import an OpenType (.otf), TrueType font (.ttf) or "
+"TrueType collection (.ttc).</margin3>\n"
+"\n"
+"<margin2>Server Font <Ctrl+G></margin2> "
+"<margin3>This will import a font from the X server.</margin3>\n"
+"\n"
+"<margin1.5>Export</margin1.5>\n"
+"\n"
+"<margin2>PSF <Ctrl+F></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 <Ctrl+F4></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 <Ctrl+C></margin1> "
+"<margin2>This copies the current selection to the Font "
+"Grid clipboard.</margin2>\n"
+"\n"
+"<margin1>Cut <Ctrl+X></margin1> "
+"<margin2>This copies the current selection to the Font Grid "
+"clipboard and then deletes the selection.</margin2>\n"
+"\n"
+"<margin1>Paste <Ctrl+V></margin1> "
+"<margin2>This replaces the glyphs starting at the currently "
+"selected position with the Font Grid clipboard.</margin2>\n"
+"\n"
+"<margin1>Overlay <Ctrl+Shift+V></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 <Ctrl+Meta+V></margin1> "
+"<margin2>This inserts the Font Grid cliboard in front of the "
+"currently selected position.</margin2>\n"
+"\n"
+"<margin1>Properties <Ctrl+P></margin1> "
+"<margin2>This invokes the font property editor.</margin2>\n"
+"\n"
+"<margin1>Comments <Ctrl+M></margin1> "
+"<margin2>This invokes the font comments editor.</margin2>\n"
+"\n"
+"<margin1>Font Info <Ctrl+I></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->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 <Ctrl+Z></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 <Ctrl+T></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 <Ctrl+E></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 <Ctrl+Shift+S></margin1> "
+"<margin2>This will toggle between the current glyph page "
+"and the last page that was viewed.</margin2>\n"
+"\n"
+"<margin1>Vertical/Horizontal View <Ctrl+Q></margin1> "
+"<margin2>This will toggle the FontGrid between showing the "
+"glyphs horizontally (default) or vertically.</margin2>\n"
+"\n"
+"<margin1>Messages <Ctrl+A></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 <Ctrl+D></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 <Ctrl+R></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 <Ctrl+J></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 <Ctrl+Shift+B></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 <Ctrl+S></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 <Ctrl+U></margin1> "
+"<margin2>This will update the FontGrid with the "
+"modified glyph and move to the next glyph.</margin2>\n"
+"\n"
+"<margin1>Update and Previous <Ctrl+B></margin1> "
+"<margin2>This will update the FontGrid with the "
+"modified glyph and move to the previous glyph.</margin2>\n"
+"\n"
+"<margin1>Close <Ctrl+F4></margin1> "
+"<margin2>This will close the Glyph Editor.</margin2>\n"
+"\n"
+"<b>Edit</b>\n"
+"<margin1>Reload <Ctrl+L></margin1> "
+"<margin2>This will reload the glyph and discard any "
+"changes in the glyph.</margin2>\n"
+"\n"
+"<margin1>Copy <Ctrl+C></margin1> "
+"<margin2>This will copy the currently selected portion "
+"of the bitmap to the Glyph Editor clipboard.</margin2>\n"
+"\n"
+"<margin1>Cut <Ctrl+X></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 <Ctrl+V></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 <Ctrl+A></margin1> "
+"<margin2>This will select the whole glyph bitmap.</margin2>\n"
+"\n"
+"<margin1>Next Glyph <Ctrl+N></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 <Ctrl+P></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 <Ctrl+D></margin1> "
+"<margin2>Change the Glyph Editor into Draw mode.</margin2>\n"
+"\n"
+"<margin1>Move <Ctrl+M></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 <Ctrl+Y></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 <Ctrl+T></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 <Ctrl+E></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 <Ctrl+H></margin1> "
+"<margin2>This will embolden the current glyph in a simple "
+"way within the width of the glyph.</margin2>\n"
+"\n"
+"<margin1>Resize BBX <Ctrl+R></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 <Ctrl+F></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->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><boolean></margin>\n"
+"<margin1>A <boolean> value can be \"0\", \"false\", \"no\", "
+"\"1\", \"true\", or \"yes\". Boolean values are "
+"case insensitive.</margin1>\n"
+"\n"
+"<margin><labelstring></margin>\n"
+"<margin1>A <labelstring> value is a string used as a label "
+"for some of the options.</margin1>\n"
+"\n"
+"<margin><atom></margin>\n"
+"<margin1>An <atom> is basically a string.</margin1>\n"
+"\n"
+"<margin><cardinal></margin>\n"
+"<margin1>A <cardinal> value is an unsigned 32-bit "
+"integer value.</margin1>\n"
+"\n"
+"<margin><integer></margin>\n"
+"<margin1>An <integer> is a signed 32-bit integer "
+"value.</margin1>\n"
+"\n"
+"<margin><property-name></margin>\n"
+"<margin1>A <property-name> 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><property-type></margin>\n"
+"<margin1>A <property-type> can be one of \"atom\", "
+"\"cardinal\", or \"integer\" (see above).</margin1>\n"
+"\n"
+"<margin><font-spacing></margin>\n"
+"<margin1>A <font-spacing> value can be one of "
+"\"proportional\", \"monowidth\", or "
+"\"charactercell\".</margin1>\n"
+"\n"
+"<margin1>If an unknown <font-spacing> value is "
+"encountered, the default value is "
+"\"proportional\".</margin1>\n"
+"\n"
+"<margin><codebase></margin>\n"
+"<margin1>A <codebase> value can be one of \"octal\", "
+"\"decimal\", or \"hexadecimal\". It can also "
+"be shortened to just the first letter. Any "
+"unknown <codebase> values are assumed to be "
+"\"hexadecimal\".</margin1>\n"
+"\n"
+"<margin><translation></margin>\n"
+"<margin1>A <translation> is a valid GUI toolkit translation "
+"string.</margin1>\n"
+"\n"
+"<margin><filename></margin>\n"
+"<margin1>A <filename> is the name of a file including or "
+"excluding a partial or full path to the file.</margin1>\n"
+"\n"
+"<margin><eolname></margin>\n"
+"<margin1>An <eolname> 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 <codebase> [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 <boolean> [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 <boolean> [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 <boolean> [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 <labelstring> [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 <translation> [default: \"<Control>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 <filename></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->Editing Options\" "
+"tab.</margin1>\n"
+"\n"
+"<margin>adobe_name_file <filename></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->Editing Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>pixel_size <integer> [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->Editing Options\" "
+"tab.</margin1>\n"
+"\n"
+"<margin>show_cap_height <boolean> [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->Editing Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>show_x_height <boolean> [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->Editing Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>font_grid_horizontal <boolean> [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 <boolean> [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 <boolean> [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->General Options\" "
+"tab.</margin1>\n"
+"\n"
+"<bul>General Font Configuration Options</bul>\n"
+"\n"
+"<margin>make_backups <boolean> [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 <boolean> [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->General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>keep_unencoded <boolean> [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->General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>keep_comments <boolean> [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->General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>pad_character_cells <boolean> [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->General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>eol <eolname> [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->General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>hint_opentype_glyphs <boolean> [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->General Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>point_size <cardinal> [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->New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>horizontal_resolution <integer> [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->New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>vertical_resolution <integer> [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->New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>font_spacing <font-spacing> [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->New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>bits_per_pixel <integer> [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->New Font Options</b> "
+"tab.</margin1>\n"
+"\n"
+"<margin>2bpp_grays <integer>...</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 <integer>...</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 <property-name> <property-type></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 <property-type> 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 "
+"<Ctrl+F>.\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>";
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#! /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