From: Nils Faerber Date: Sun, 5 May 2013 01:44:32 +0000 (+0200) Subject: Initial import of upstream V1.6 from X-Git-Url: https://git.karo-electronics.de/?p=gbdfed.git;a=commitdiff_plain;h=12b3170e0a2c3d4a1c5ed89b80b3caea2f6a5f9c Initial import of upstream V1.6 from http://sofia.nmsu.edu/~mleisher/Software/gbdfed/ by Mark Leisher --- 12b3170e0a2c3d4a1c5ed89b80b3caea2f6a5f9c diff --git a/GNUMakefile b/GNUMakefile new file mode 100644 index 0000000..2b668e0 --- /dev/null +++ b/GNUMakefile @@ -0,0 +1,78 @@ +CC = gcc +CFLAGS = -Wall -g +DEFS = -DHAVE_XLIB -DHAVE_HBF -DHAVE_FREETYPE -DG_DISABLE_DEPRECATED \ + -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED \ + -DGTK_DISABLE_DEPRECATED +INCS = -I. `pkg-config gtk+-2.0 --cflags freetype2 --cflags` +LIBS = `pkg-config gtk+-2.0 --libs freetype2 --libs` + +HBF= + +SRCS = bdf.c bdfcons.c bdffnt.c bdfgname.c bdfgrab.c bdfgrid.c bdfotf.c \ + bdfpkgf.c bdfpsf.c $(HBF) \ + labcon.c \ + grayswatch.c \ + glyphedit.c \ + glyphtest.c \ + fontgrid.c \ + gectrl.c \ + gbdfed.c \ + guiedit.c \ + guigedit.c \ + guifile.c \ + guihelp.c \ + guiops.c \ + guipref.c \ + guiutil.c + +OBJS = $(SRCS:%.c=%.o) + +all: gbdfed + +gbdfed: $(OBJS) + $(CC) $(STATIC) $(CFLAGS) $(OBJS) -o gbdfed $(LIBS) + +.c.o: + $(CC) $(CFLAGS) $(DEFS) $(INCS) -c $< -o $@ + +clean: + /bin/rm -f *~ *BAK *CKP *.o + +distclean: clean + /bin/rm -f gbdfed + +realclean: distclean + +deps: + gcc -I. -MM $(SRCS) > deps + +# +# Dependencies. +# +bdf.o: bdf.c bdfP.h bdf.h +bdfcons.o: bdfcons.c bdfP.h bdf.h +bdffnt.o: bdffnt.c bdfP.h bdf.h +bdfgname.o: bdfgname.c bdfP.h bdf.h +bdfgrab.o: bdfgrab.c bdfP.h bdf.h +bdfgrid.o: bdfgrid.c bdfP.h bdf.h +bdfotf.o: bdfotf.c +bdfpkgf.o: bdfpkgf.c bdfP.h bdf.h +bdfpsf.o: bdfpsf.c bdfP.h bdf.h +hbf.o: hbf.c hbf.h +labcon.o: labcon.c labcon.h +grayswatch.o: grayswatch.c grayswatch.h +glyphedit.o: glyphedit.c glyphedit.h bdfP.h bdf.h +glyphtest.o: glyphtest.c glyphtest.h bdfP.h bdf.h +fontgrid.o: fontgrid.c fontgrid.h bdfP.h bdf.h +gectrl.o: gectrl.c gectrl.h bdfP.h bdf.h gectrlbmaps.h +gbdfed.o: gbdfed.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h +guiedit.o: guiedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \ + labcon.h +guigedit.o: guigedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \ + glyphedit.h labcon.h gectrl.h +guifile.o: guifile.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \ + labcon.h +guihelp.o: guihelp.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h htext.h +guiops.o: guiops.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h +guipref.o: guipref.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h +guiutil.o: guiutil.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..b482958 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,152 @@ +# +# Copyright 2008 Department of Mathematical Sciences, New Mexico State University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +RM = @RM@ +CP = @CP@ +MKINSTALLDIRS = ./mkinstalldirs + +CC = @CC@ +CFLAGS = @XX_CFLAGS@ @CFLAGS@ + +DEFINES = @DEFINES@ -DG_DISABLE_DEPRECATED \ + -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED \ + -DGTK_DISABLE_DEPRECATED + +SRCS = bdf.c \ + bdfcons.c \ + bdffnt.c \ + bdfgname.c \ + @BDFGRABSRC@ \ + bdfgrid.c \ + bdfotf.c \ + bdfpkgf.c \ + bdfpsf.c \ + fontgrid.c \ + gbdfed.c \ + gectrl.c \ + glyphedit.c \ + glyphtest.c \ + grayswatch.c \ + guiedit.c \ + guifile.c \ + guigedit.c \ + guihelp.c \ + guiops.c \ + guipref.c \ + guiutil.c \ + @HBFSRC@ \ + labcon.c + +OBJS = bdf.o \ + bdfcons.o \ + bdffnt.o \ + bdfgname.o \ + @BDFGRABOBJ@ \ + bdfgrid.o \ + bdfotf.o \ + bdfpkgf.o \ + bdfpsf.o \ + fontgrid.o \ + gbdfed.o \ + gectrl.o \ + glyphedit.o \ + glyphtest.o \ + grayswatch.o \ + guiedit.o \ + guifile.o \ + guigedit.o \ + guihelp.o \ + guiops.o \ + guipref.o \ + guiutil.o \ + @HBFOBJ@ \ + labcon.o + +# +# Point these at the FreeType source directories. +# +INCS = @CPPFLAGS@ +LIBS = @LIBS@ +LDFLAGS = @LDFLAGS@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +datarootdir = @datarootdir@ +mandir = @mandir@ + +all: gbdfed + +gbdfed: $(OBJS) + $(CC) $(STATIC) $(LDFLAGS) -o gbdfed $(OBJS) $(LIBS) + +clean: + $(RM) -f *.o *BAK *CKP *~ core + +realclean: clean + $(RM) -f gbdfed + +distclean: clean + $(RM) -rf gbdfed config.* Makefile autom4te.cache + +.c.o: + $(CC) $(DEFINES) $(CFLAGS) $(INCS) -c $< -o $@ + +install: gbdfed + $(MKINSTALLDIRS) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1 + $(CP) gbdfed $(DESTDIR)$(bindir)/gbdfed + $(CP) gbdfed.man $(DESTDIR)$(mandir)/man1/gbdfed.1 + +uninstall: + $(RM) -f $(DESTDIR)$(bindir)/gbdfed + $(RM) -f $(DESTDIR)$(mandir)/man1/gbdfed.1 + +# +# Dependencies. +# +bdf.o: bdf.c bdfP.h bdf.h +bdfcons.o: bdfcons.c bdfP.h bdf.h +bdffnt.o: bdffnt.c bdfP.h bdf.h +bdfgname.o: bdfgname.c bdfP.h bdf.h +bdfgrab.o: bdfgrab.c bdfP.h bdf.h +bdfgrid.o: bdfgrid.c bdfP.h bdf.h +bdfotf.o: bdfotf.c +bdfpkgf.o: bdfpkgf.c bdfP.h bdf.h +bdfpsf.o: bdfpsf.c bdfP.h bdf.h +fontgrid.o: fontgrid.c fontgrid.h bdfP.h bdf.h +gbdfed.o: gbdfed.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h +gectrl.o: gectrl.c gectrl.h bdfP.h bdf.h gectrlbmaps.h +glyphedit.o: glyphedit.c glyphedit.h bdfP.h bdf.h +glyphtest.o: glyphtest.c glyphtest.h bdfP.h bdf.h +grayswatch.o: grayswatch.c grayswatch.h +guiedit.o: guiedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h +guifile.o: guifile.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h labcon.h +guigedit.o: guigedit.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h \ +glyphedit.h labcon.h gectrl.h +guihelp.o: guihelp.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h htext.h +guiops.o: guiops.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h +guipref.o: guipref.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h +guiutil.o: guiutil.c gbdfed.h bdf.h fontgrid.h bdfP.h glyphtest.h +hbf.o: hbf.c hbf.h +labcon.o: labcon.c labcon.h + +# end of Makefile diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..89ebedc --- /dev/null +++ b/NEWS @@ -0,0 +1,117 @@ +Changes from 1.5 to 1.6 +----------------------- +1. Fixed a problem with implicit dynamic links which are becoming a no-no. + +2. Fixed a name collision with glib 2.10. + +3. Updated to work with GTK+ 2.20. + +Changes from 1.4 to 1.5 +----------------------- +1. Fixed some documentation and Font Grid selection problems. + +2. Fixed a problem with the device width being unnecessarily adjusted before + editing glyphs in a proportional spacing font. + +3. Fixed a problem of not adding a SPACING property when the font spacing is + changed. This forces the correct spacing even if the font does not have an + XLFD value yet. + +4. Fixed a problem with deletions from the PSF mapping table not being + recorded. + +5. Removed some leftover deprecated sections. + +6. Added glyph navigation buttons to the GlyphEditor. + +Changes from 1.3 to 1.4 +----------------------- +1. Changed last instance of FileSelection to FileChooser. + +2. Updated to work with GTK 2.12 (GtkTooltip). + +3. Fixed a problem when X11 is not available and bdfgrab.c won't compile as a + result. + +4. Updated Makefile.in to work with later versions of autoconf without + complaining. + +5. Improved the on-line documentation. + +6. Remove the _XMBDFED_INFO font property when fonts are loaded or grabbed + from the X server. + +7. Fixed a problem with renaming glyphs using the Adobe Glyph Name List. + +8. Fixed a warning about checking for a point in a NULL region. + +9. Fixed a crash that happened because some file filter objects were being + dereferenced when the import dialog was popped up multiple times. + +10. Added up to 10 recent fonts to the File menu. + +11. Changed the keyboard activate key for Exit from 'e' to 'x' and the + activate key for Export from 'x' to 'p'. + +12. Fixed a problem with fonts grabbed from the X server always being loaded + into the FontGrid that popped up the dialog. + +13. Fixed a couple problems with integer sign promotion affecting import of + OTF fonts. + +Changes from 1.2 to 1.3 +----------------------- +1. Minor make file fix. + +2. Fixes for argument passing on 64-bit architectures. + +3. Changes to reduce the memory footprint on 64-bit architectures. + +Changes from 1.1 to 1.2 +----------------------- +1. Some fixes for 64-bit systems. + +2. Some improvements in the help text. + +3. Added HBF support. + +Changes from 1.0 to 1.1 +----------------------- +1. Fixed a problem opening other editors when multiple fonts are provided + on the command line. + +2. Fixed a memory allocation problem. + +3. Fixed a problem with setting and inverting pixels in the glyph editor. + +4. Added the missing Delete and BackSpace keys for deleting selections. + +5. Big improvements to the on-line help text. More readable now. + +Changes from 1.0 Beta to 1.0 +---------------------------- +1. Fixed a compilation error and a problem displaying the -1 encoding for the + unencoded pages. + +2. Changed several dialogs to make more regular use of the existing GTK dialog + facilities. + +3. Added a default icon list in 48x48, 32x32, and 16x16 order. (gbdfed.c) + +4. Added support for 2, 4 and 8 bits per pixel fonts back in. + +5. Reduced the size of an empty fontgrid. It looked too big. (fontgrid.c) + +6. Removed some unecessary code. (gbdfed.c) + +7. Disabled the cursor font preference until the automask generation code + and hotspot selection is done. + +8. Changed the fontgrid_new() function to accept a vararg list. + +9. Changed the glyphedit_new() function to accept a vararg list. + +10. Fixed a problem with the glyph image not being updated sometimes + (guigedit.c). + +11. Several dialog changes to work more naturally. diff --git a/README b/README new file mode 100644 index 0000000..6f8d0cc --- /dev/null +++ b/README @@ -0,0 +1,262 @@ +gbdfed 1.6 + +INTRO +----- + +gbdfed is a GTK-based BDF font editor with the following features: + + o Multiple fonts can be loaded from the command line. + o Multiple fonts can be open at the same time. + o Cutting and pasting glyphs between fonts. + o Multiple glyph bitmap editors can be open at the same time. + o Cutting and pasting between glyph bitmap editors. + o Export of XBM files from glyph bitmap editors. + o Automatic correction of certain metrics when a font is loaded. + o Generation of XLFD font names for fonts without XLFD names. + o Update an XLFD font name from the font properties. + o Update the font properties from an XLFD font name. + o Font property editor. + o Font comment editor. + o Supports unencoded glyphs (ENCODING of -1). + o Display of glyph encodings in octal, decimal, or hex. + o Builtin on-line help. + o Imports PK/GF fonts. + o Imports HBF (Han Bitmap Font) fonts. + o Imports Linux console fonts (PSF, CP, and FNT). + o Imports Sun console fonts (vfont format). + o Imports fonts from the X server. + o Imports Windows FON/FNT fonts. + o Imports OpenType/TrueType fonts and collections. + o Exports PSF fonts. + o Exports HEX fonts. + +A few things missing from this font editor: + + o No way to create space glyphs in monowidth or character cell fonts. + o No support for scaling fonts. + o Fonts with right-to-left direction (negative widths) not supported. + o No way to edit comments appearing in the properties list. + +Known problems: + + o Selecting the original font editor from the "Windows" menu does not + deiconify, set focus, and place it on top like it should. + o Deleting glyphs from or inserting glyphs into a font grid do not update + the glyph editors if they happen to be editing a deleted glyph or a glyph + that moved due to an insertion. + +COMMAND LINE OPTIONS +-------------------- + gbdfed [options] [font1 font2 ...] + + -nc - do not preserve comments + -nu - do not preserve unencoded glyphs + -nm - do not make metrics corrections + -np - do not pad character-cell bitmaps + -bp - allow blank pages + -ed - do not present the "Really Exit?" dialog + -ps n - set default point size + -hres n - set default horizontal resolution + -vres n - set default vertical resolution + -res n - set both default resolutions + -sp s - set font spacing ("p" for proportional, "m" for monospace, + or "c" for charcell). + -eol e - set the default end-of-line char(s) ("u" for Unix, "d" for DOS, + or "m" for Mac). + -g code - set the initial glyph code (in decimal, hex or octal). + -cb base - set the code base for display of glyph codes, can be "octal," + "decimal," or "hexadecimal." The first letter of these names + work as well. + + By default, gbdfed will set its point size to 12, the horizontal and + vertical resolution to that of the display (e.g. 90x90 dpi for Sun's), and + the font spacing to proportional. Also by default, gbdfed will preserve + comments, preserve unencoded glyphs, and make metrics corrections when + loading fonts. + +COMPILING +--------- + +To build gbdfed, GTK 2.6 or greater is required. It may be that versions as +early as 2.3 will work, but only various versions of 2.7 through 2.20 have been +tested. + +Optional: + + Freetype2 support is optional and can be found at: + + http://www.freetype.org + +Step 1. + + Run the "configure" script to generate the Makefile. By default, the program + installs in /usr/local. You can change that by using the --prefix option of + the "configure" script. + + To disable the File->Import->X Server Font feature, use the --without-x + command line option to the "configure" script. + +Step 2. + + Compile the program + +WARNINGS +-------- + +1. When compiling on HP/UX, the htext.h file has long concatenated strings that + require the -H option (noted by W. Chao). + +2. Compiling with the gcc -pedantic option complains about strings being too + long in the help text file. The help system will be changed in later + versions. + +ACKNOWLEDGEMENTS +---------------- + +Thanks go to the following people: + + Ross Patterson for his HBF code. + + der Mouse for his "getbdf" code. + + K. Carothers and A. Korobka for their "fnt2bdf" code in Wine. + + Mike Stroyan for patches. + + Primoz Peterlin for the man page and + some changes for building on HP/UX. + + Danny Backx for the LessTif Imakefile. + + Donald Page for patches. + + Michal Szymanski for problem reports. + + Werner Lemberg for pointing out a problem + with the HBF code and other problem reports. + + William F. Maton for pointing out a + problem with padding character cell fonts. + + Ivan Nejgebauer for reporting a problem with glyph + names on imported console fonts. + + Solofo 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 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 for providing the Makefile changes + needed to compile on HP/UX and pointing out a problem with the builtin + documentation. + + Andreas Reuter for pointing out a problem + with importing TrueType fonts. + + Leonard Dickens for providing the Makefile + changes needed for IRIX 6.3. + + Markus Kuhn for a handful of good suggestions. + + Jim Knoble for some geometry improvements in some + dialogs. + + Darren Stuart Embry for the donation of + another HP/UX 10.20 X11R6 compilation setup. + + Vladimir Volovich for pointing out something I forgot to + test. + + Ben Fry for IRIX 6.5.2 variables for the Makefile. + + J.H.M. Dassen (Ray) for fixing bugs with the PK/GF + import feature. + + Robert Brady for pointing out a problem with the + length of _XFREE86_GLYPH_RANGES properties and an Exceed font compiler. + + Stefan Monnier for a bug report on highlight thickness + of 0. + + Humphrey Clerx for pointing out some + compilation problem on Digit Unix 4.0 and some compilation problems with + Traditional C compilers. + + Rudolf Cejka for providing patches to fix + problems with grabbing fonts from the X server and alerting me to + uninitialized variable warnings. + + Baruch Even for providing a fix for a bug that should + have shown up long ago dealing with font properties. + + Sergey Vlasov 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 for providing a patch that + fixes a display problem in the Font Grid. + + Pierre HANSER for a patch to fix a + problem loading font names that are not NULL terminated from the end of + FON/FNT files. + + Patrick Hagglund for providing the patches to + use FreeType 2. + + James Cloos for finding problems with gbdfed. + + Ming Hua for locating a problem with editing glyphs. + + Sergio Martins for finding a typo and several + dialog related bugs. + + Viktor Urban for locating a problem when + saving and moving to the next or previous glyph. + + Jiri "BlueBear" Dluhos for producing crash fixes on + 64-bit machines. + + Jan Engelhardt for providing an improvement on the + help text, a missing prototype, and an improvement in Makefile.in. + + Daniel Richard G. for diagnosing problems on 64-bit + architectures. + + Baruch Even for diagnosing problems on 64-bit + architectures. + + Ming Hua for pointing out a warning about testing + a NULL region. + + Ryan Hill 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 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 for discovering that deleting PSF + unicode map entries was being ignored. + + Bertrand Janin for adding glyph navigation buttons + to the GlyphEditors. + + Peter Volkov for fixing a name collision with Glib 2.10. + + Tom "spot" Callaway for the configuration addition + that fixes a problem with implicit dynamic linking that showed up with newer + versions of gcc. + +AUTHOR +------ + Mark Leisher + 15 April 2010 diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..c707181 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,56 @@ +dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +dnl also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN(PKG_CHECK_MODULES, [ + succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + $1_LIBS=`$PKG_CONFIG --libs "$2"` + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi +]) + + diff --git a/bdf.c b/bdf.c new file mode 100644 index 0000000..cf0e9a7 --- /dev/null +++ b/bdf.c @@ -0,0 +1,7049 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "bdfP.h" + +#ifdef HAVE_HBF +#include "hbf.h" +#endif + +#undef MAX +#define MAX(h, i) ((h) > (i) ? (h) : (i)) + +#undef MIN +#define MIN(l, o) ((l) < (o) ? (l) : (o)) + +/************************************************************************** + * + * Masks used for checking different bits per pixel cases. + * + **************************************************************************/ + +unsigned char bdf_onebpp[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +unsigned char bdf_twobpp[] = { 0xc0, 0x30, 0x0c, 0x03 }; +unsigned char bdf_fourbpp[] = { 0xf0, 0x0f }; +unsigned char bdf_eightbpp[] = { 0xff }; + +/************************************************************************** + * + * Default BDF font options. + * + **************************************************************************/ + +static bdf_options_t _bdf_opts = { +#ifdef HAVE_FREETYPE + FT_LOAD_DEFAULT, /* OTF flags - hinting on. */ +#else + 0, /* OTF flags */ +#endif /* HAVE_FREETYPE */ + 1, /* Correct metrics. */ + 1, /* Preserve unencoded glyphs. */ + 1, /* Preserve comments. */ + 1, /* Pad character-cells. */ + BDF_PROPORTIONAL, /* Default spacing. */ + 12, /* Default point size. */ + 0, /* Default horizontal resolution. */ + 0, /* Default vertical resolution. */ + 1, /* Bits per pixel. */ + BDF_UNIX_EOL, /* Line separator. */ + BDF_PSF_ALL, /* PSF font export options. */ + 0, /* An X cursor font. */ +}; + +/************************************************************************** + * + * Builtin BDF font properties. + * + **************************************************************************/ + +/* + * List of most properties that might appear in a font. Doesn't include the + * AXIS_* properties in X11R6 polymorphic fonts. + */ +static bdf_property_t _bdf_properties[] = { + {"ADD_STYLE_NAME", BDF_ATOM, 1}, + {"AVERAGE_WIDTH", BDF_INTEGER, 1}, + {"AVG_CAPITAL_WIDTH", BDF_INTEGER, 1}, + {"AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1}, + {"CAP_HEIGHT", BDF_INTEGER, 1}, + {"CHARSET_COLLECTIONS", BDF_ATOM, 1}, + {"CHARSET_ENCODING", BDF_ATOM, 1}, + {"CHARSET_REGISTRY", BDF_ATOM, 1}, + {"COMMENT", BDF_ATOM, 1}, + {"COPYRIGHT", BDF_ATOM, 1}, + {"DEFAULT_CHAR", BDF_CARDINAL, 1}, + {"DESTINATION", BDF_CARDINAL, 1}, + {"DEVICE_FONT_NAME", BDF_ATOM, 1}, + {"END_SPACE", BDF_INTEGER, 1}, + {"FACE_NAME", BDF_ATOM, 1}, + {"FAMILY_NAME", BDF_ATOM, 1}, + {"FIGURE_WIDTH", BDF_INTEGER, 1}, + {"FONT", BDF_ATOM, 1}, + {"FONTNAME_REGISTRY", BDF_ATOM, 1}, + {"FONT_ASCENT", BDF_INTEGER, 1}, + {"FONT_DESCENT", BDF_INTEGER, 1}, + {"FOUNDRY", BDF_ATOM, 1}, + {"FULL_NAME", BDF_ATOM, 1}, + {"ITALIC_ANGLE", BDF_INTEGER, 1}, + {"MAX_SPACE", BDF_INTEGER, 1}, + {"MIN_SPACE", BDF_INTEGER, 1}, + {"NORM_SPACE", BDF_INTEGER, 1}, + {"NOTICE", BDF_ATOM, 1}, + {"PIXEL_SIZE", BDF_INTEGER, 1}, + {"POINT_SIZE", BDF_INTEGER, 1}, + {"QUAD_WIDTH", BDF_INTEGER, 1}, + {"RAW_ASCENT", BDF_INTEGER, 1}, + {"RAW_AVERAGE_WIDTH", BDF_INTEGER, 1}, + {"RAW_AVG_CAPITAL_WIDTH", BDF_INTEGER, 1}, + {"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1}, + {"RAW_CAP_HEIGHT", BDF_INTEGER, 1}, + {"RAW_DESCENT", BDF_INTEGER, 1}, + {"RAW_END_SPACE", BDF_INTEGER, 1}, + {"RAW_FIGURE_WIDTH", BDF_INTEGER, 1}, + {"RAW_MAX_SPACE", BDF_INTEGER, 1}, + {"RAW_MIN_SPACE", BDF_INTEGER, 1}, + {"RAW_NORM_SPACE", BDF_INTEGER, 1}, + {"RAW_PIXEL_SIZE", BDF_INTEGER, 1}, + {"RAW_POINT_SIZE", BDF_INTEGER, 1}, + {"RAW_PIXELSIZE", BDF_INTEGER, 1}, + {"RAW_POINTSIZE", BDF_INTEGER, 1}, + {"RAW_QUAD_WIDTH", BDF_INTEGER, 1}, + {"RAW_SMALL_CAP_SIZE", BDF_INTEGER, 1}, + {"RAW_STRIKEOUT_ASCENT", BDF_INTEGER, 1}, + {"RAW_STRIKEOUT_DESCENT", BDF_INTEGER, 1}, + {"RAW_SUBSCRIPT_SIZE", BDF_INTEGER, 1}, + {"RAW_SUBSCRIPT_X", BDF_INTEGER, 1}, + {"RAW_SUBSCRIPT_Y", BDF_INTEGER, 1}, + {"RAW_SUPERSCRIPT_SIZE", BDF_INTEGER, 1}, + {"RAW_SUPERSCRIPT_X", BDF_INTEGER, 1}, + {"RAW_SUPERSCRIPT_Y", BDF_INTEGER, 1}, + {"RAW_UNDERLINE_POSITION", BDF_INTEGER, 1}, + {"RAW_UNDERLINE_THICKNESS", BDF_INTEGER, 1}, + {"RAW_X_HEIGHT", BDF_INTEGER, 1}, + {"RELATIVE_SETWIDTH", BDF_CARDINAL, 1}, + {"RELATIVE_WEIGHT", BDF_CARDINAL, 1}, + {"RESOLUTION", BDF_INTEGER, 1}, + {"RESOLUTION_X", BDF_CARDINAL, 1}, + {"RESOLUTION_Y", BDF_CARDINAL, 1}, + {"SETWIDTH_NAME", BDF_ATOM, 1}, + {"SLANT", BDF_ATOM, 1}, + {"SMALL_CAP_SIZE", BDF_INTEGER, 1}, + {"SPACING", BDF_ATOM, 1}, + {"STRIKEOUT_ASCENT", BDF_INTEGER, 1}, + {"STRIKEOUT_DESCENT", BDF_INTEGER, 1}, + {"SUBSCRIPT_SIZE", BDF_INTEGER, 1}, + {"SUBSCRIPT_X", BDF_INTEGER, 1}, + {"SUBSCRIPT_Y", BDF_INTEGER, 1}, + {"SUPERSCRIPT_SIZE", BDF_INTEGER, 1}, + {"SUPERSCRIPT_X", BDF_INTEGER, 1}, + {"SUPERSCRIPT_Y", BDF_INTEGER, 1}, + {"UNDERLINE_POSITION", BDF_INTEGER, 1}, + {"UNDERLINE_THICKNESS", BDF_INTEGER, 1}, + {"WEIGHT", BDF_CARDINAL, 1}, + {"WEIGHT_NAME", BDF_ATOM, 1}, + {"X_HEIGHT", BDF_INTEGER, 1}, + {"_MULE_BASELINE_OFFSET", BDF_INTEGER, 1}, + {"_MULE_RELATIVE_COMPOSE", BDF_INTEGER, 1}, + /* + * Throw this in to make it clear. + */ + {"_XMBDFED_INFO", BDF_ATOM, 1}, +}; + +static unsigned int _num_bdf_properties = +sizeof(_bdf_properties) / sizeof(_bdf_properties[0]); + +/* + * User defined properties. + */ +static bdf_property_t *user_props; +static unsigned int nuser_props = 0; + +/************************************************************************** + * + * Hash table utilities for the properties. + * + **************************************************************************/ + +#define INITIAL_HT_SIZE 241 + +typedef struct { + char *key; + void *data; +} _hashnode, *hashnode; + +typedef struct { + int limit; + int size; + int used; + hashnode *table; +} hashtable; + +typedef void (*hash_free_func)(hashnode node); + +static hashnode * +hash_bucket(char *key, hashtable *ht) +{ + char *kp = key; + unsigned int res = 0; + hashnode *bp = ht->table, *ndp; + + /* + * Mocklisp hash function. + */ + while (*kp) + res = (res << 5) - res + *kp++; + + ndp = bp + (res % ht->size); + while (*ndp) { + kp = (*ndp)->key; + if (kp[0] == key[0] && strcmp(kp, key) == 0) + break; + ndp--; + if (ndp < bp) + ndp = bp + (ht->size - 1); + } + return ndp; +} + +static void +hash_rehash(hashtable *ht) +{ + hashnode *obp = ht->table, *bp, *nbp; + int i, sz = ht->size; + + ht->size <<= 1; + ht->limit = ht->size / 3; + ht->table = (hashnode *) malloc(sizeof(hashnode) * ht->size); + (void) memset((char *) ht->table, 0, sizeof(hashnode) * ht->size); + + for (i = 0, bp = obp; i < sz; i++, bp++) { + if (*bp) { + nbp = hash_bucket((*bp)->key, ht); + *nbp = *bp; + } + } + free((char *) obp); +} + +static void +hash_init(hashtable *ht) +{ + int sz = INITIAL_HT_SIZE; + + ht->size = sz; + ht->limit = sz / 3; + ht->used = 0; + ht->table = (hashnode *) malloc(sizeof(hashnode) * sz); + (void) memset((char *) ht->table, 0, sizeof(hashnode) * sz); +} + +static void +hash_free(hashtable *ht) +{ + int i, sz = ht->size; + hashnode *bp = ht->table; + + for (i = 0; i < sz; i++, bp++) { + if (*bp) + free((char *) *bp); + } + if (sz > 0) + free((char *) ht->table); +} + +static void +hash_insert(char *key, void *data, hashtable *ht) +{ + hashnode nn, *bp = hash_bucket(key, ht); + + nn = *bp; + if (!nn) { + *bp = nn = (hashnode) malloc(sizeof(_hashnode)); + nn->key = key; + nn->data = data; + + if (ht->used >= ht->limit) + hash_rehash(ht); + ht->used++; + } else + nn->data = data; +} + +static hashnode +hash_lookup(char *key, hashtable *ht) +{ + hashnode *np = hash_bucket(key, ht); + return *np; +} + +static void +hash_delete(char *name, hashtable *ht) +{ + hashnode *hp; + + hp = hash_bucket(name, ht); + if (*hp) { + free((char *) *hp); + *hp = 0; + } +} + +/* + * The builtin property table. + */ +static hashtable proptbl; + +/************************************************************************** + * + * Utility types and functions. + * + **************************************************************************/ + +/* + * Function type for parsing lines of a BDF font. + */ +typedef int (*_bdf_line_func_t)( + char *line, + unsigned int linelen, + unsigned int lineno, + void *call_data, + void *client_data +); + +/* + * List structure for splitting lines into fields. + */ +typedef struct { + char **field; + unsigned int size; + unsigned int used; + char *bfield; + unsigned int bsize; + unsigned int bused; +} _bdf_list_t; + +/* + * Structure used while loading BDF fonts. + */ +typedef struct { + unsigned int flags; + unsigned int cnt; + unsigned int row; + unsigned int bpr; + short minlb; + short maxlb; + short maxrb; + short maxas; + short maxds; + short rbearing; + char *glyph_name; + int glyph_enc; + bdf_font_t *font; + bdf_options_t *opts; + void *client_data; + bdf_callback_t callback; + bdf_callback_struct_t cb; + unsigned int have[2048]; + _bdf_list_t list; +} _bdf_parse_t; + +#define setsbit(m, cc) (m[(cc) >> 3] |= (1 << ((cc) & 7))) +#define sbitset(m, cc) (m[(cc) >> 3] & (1 << ((cc) & 7))) + +/* + * An empty string for empty fields. + */ +static char empty[1] = { 0 }; + +/* + * Assume the line is NULL terminated and that the `list' parameter was + * initialized the first time it was used. + */ +static void +_bdf_split(char *separators, char *line, unsigned int linelen, + _bdf_list_t *list) +{ + int mult, final_empty; + char *sp, *ep, *end; + unsigned char seps[32]; + + /* + * Initialize the list. + */ + list->used = list->bused = 0; + + /* + * If the line is empty, then simply return. + */ + if (linelen == 0 || line[0] == 0) + return; + + /* + * If the `separators' parameter is NULL or empty, split the list into + * individual bytes. + */ + if (separators == 0 || *separators == 0) { + if (linelen > list->bsize) { + if (list->bsize) + list->bfield = (char *) malloc(linelen); + else + list->bfield = (char *) realloc(list->bfield, linelen); + list->bsize = linelen; + } + list->bused = linelen; + (void) memcpy(list->bfield, line, linelen); + return; + } + + /* + * Prepare the separator bitmap. + */ + (void) memset((char *) seps, 0, 32); + + /* + * If the very last character of the separator string is a plus, then set + * the `mult' flag to indicate that multiple separators should be + * collapsed into one. + */ + for (mult = 0, sp = separators; sp && *sp; sp++) { + if (*sp == '+' && *(sp + 1) == 0) + mult = 1; + else + setsbit(seps, *sp); + } + + /* + * Break the line up into fields. + */ + for (final_empty = 0, sp = ep = line, end = sp + linelen; + sp < end && *sp;) { + /* + * Collect everything that is not a separator. + */ + for (; *ep && !sbitset(seps, *ep); ep++) ; + + /* + * Resize the list if necessary. + */ + if (list->used == list->size) { + if (list->size == 0) + list->field = (char **) malloc(sizeof(char *) * 5); + else + list->field = (char **) + realloc((char *) list->field, + sizeof(char *) * (list->size + 5)); + + list->size += 5; + } + + /* + * Assign the field appropriately. + */ + list->field[list->used++] = (ep > sp) ? sp : empty; + + sp = ep; + if (mult) { + /* + * If multiple separators should be collapsed, do it now by + * setting all the separator characters to 0. + */ + for (; *ep && sbitset(seps, *ep); ep++) + *ep = 0; + } else if (*ep != 0) + /* + * Don't collapse multiple separators by making them 0, so just + * make the one encountered 0. + */ + *ep++ = 0; + final_empty = (ep > sp && *ep == 0); + sp = ep; + } + + /* + * Finally, NULL terminate the list. + */ + if (list->used + final_empty + 1 >= list->size) { + if (list->used == list->size) { + if (list->size == 0) + list->field = (char **) malloc(sizeof(char *) * 5); + else + list->field = (char **) + realloc((char *) list->field, + sizeof(char *) * (list->size + 5)); + list->size += 5; + } + } + if (final_empty) + list->field[list->used++] = empty; + + if (list->used == list->size) { + if (list->size == 0) + list->field = (char **) malloc(sizeof(char *) * 5); + else + list->field = (char **) + realloc((char *) list->field, + sizeof(char *) * (list->size + 5)); + list->size += 5; + } + list->field[list->used] = 0; +} + +static void +_bdf_shift(unsigned int n, _bdf_list_t *list) +{ + unsigned int i, u; + + if (list == 0 || list->used == 0 || n == 0) + return; + + if (n >= list->used) { + list->used = 0; + return; + } + for (u = n, i = 0; u < list->used; i++, u++) + list->field[i] = list->field[u]; + list->used -= n; +} + +static char * +_bdf_join(int c, unsigned int *len, _bdf_list_t *list) +{ + unsigned int i, j; + char *fp, *dp; + + if (list == 0 || list->used == 0) + return 0; + + *len = 0; + + dp = list->field[0]; + for (i = j = 0; i < list->used; i++) { + fp = list->field[i]; + while (*fp) + dp[j++] = *fp++; + if (i + 1 < list->used) + dp[j++] = c; + } + dp[j] = 0; + + *len = j; + return dp; +} + +/* + * High speed file reader that passes each line to a callback. + */ +static int +_bdf_readlines(int fd, _bdf_line_func_t callback, void *client_data, + unsigned int *lno) +{ + _bdf_line_func_t cb; + unsigned int lineno; + int n, res, done, refill, bytes, hold; + char *ls, *le, *pp, *pe, *hp; + char buf[65536]; + + if (callback == 0) + return -1; + + cb = callback; + lineno = 1; + buf[0] = 0; + res = done = 0; + pp = ls = le = buf; + bytes = 65536; + while (!done && (n = read(fd, pp, bytes)) > 0) { + /* + * Determine the new end of the buffer pages. + */ + pe = pp + n; + + for (refill = 0; done == 0 && refill == 0; ) { + while (le < pe && *le != '\n' && *le != '\r') + le++; + + if (le == pe) { + /* + * Hit the end of the last page in the buffer. Need to find + * out how many pages to shift and how many pages need to be + * read in. Adjust the line start and end pointers down to + * point to the right places in the pages. + */ + pp = buf + (((ls - buf) >> 13) << 13); + n = pp - buf; + ls -= n; + le -= n; + n = pe - pp; + memmove(buf, pp, n); +#if 0 + memcpy(buf, pp, n); +#endif + pp = buf + n; + bytes = 65536 - n; + refill = 1; + } else { + /* + * Temporarily NULL terminate the line. + */ + hp = le; + hold = *le; + *le = 0; + + if (callback && *ls != '#' && *ls != 0x1a && le > ls && + (res = (*cb)(ls, le - ls, lineno, (void *) &cb, + client_data)) != 0) + done = 1; + else { + ls = ++le; + /* + * Handle the case of DOS crlf sequences. + */ + if (le < pe && hold == '\n' && *le =='\r') + ls = ++le; + } + + /* + * Increment the line number. + */ + lineno++; + + /* + * Restore the character at the end of the line. + */ + *hp = hold; + } + } + } + *lno = lineno; + return res; +} + +unsigned char * +_bdf_strdup(unsigned char *s, unsigned int len) +{ + unsigned char *ns; + + if (s == 0 || len == 0) + return 0; + + ns = (unsigned char *) malloc(len); + (void) memcpy((char *) ns, (char *) s, len); + return ns; +} + +void +_bdf_memmove(char *dest, char *src, unsigned int bytes) +{ + int i, j; + + i = (int) bytes; + j = i & 7; + i = (i + 7) >> 3; + + /* + * Do a memmove using Ye Olde Duff's Device for efficiency. + */ + if (src < dest) { + src += bytes; + dest += bytes; + + switch (j) { + case 0: do { + *--dest = *--src; + case 7: *--dest = *--src; + case 6: *--dest = *--src; + case 5: *--dest = *--src; + case 4: *--dest = *--src; + case 3: *--dest = *--src; + case 2: *--dest = *--src; + case 1: *--dest = *--src; + } while (--i > 0); + } + } else if (src > dest) { + switch (j) { + case 0: do { + *dest++ = *src++; + case 7: *dest++ = *src++; + case 6: *dest++ = *src++; + case 5: *dest++ = *src++; + case 4: *dest++ = *src++; + case 3: *dest++ = *src++; + case 2: *dest++ = *src++; + case 1: *dest++ = *src++; + } while (--i > 0); + } + } +} + +static unsigned char a2i[128] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char odigits[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static unsigned char ddigits[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static unsigned char hdigits[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, + 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define isdigok(m, d) (m[(d) >> 3] & (1 << ((d) & 7))) + +/* + * Routine to convert an ASCII string into an unsigned int integer. + */ +unsigned int +_bdf_atoul(char *s, char **end, int base) +{ + unsigned int v; + unsigned char *dmap; + + if (s == 0 || *s == 0) + return 0; + + /* + * Make sure the radix is something recognizable. Default to 10. + */ + switch (base) { + case 8: dmap = odigits; break; + case 16: dmap = hdigits; break; + default: base = 10; dmap = ddigits; break; + } + + /* + * Check for the special hex prefixes of 0[xX] or [Uu][+-]. + */ + if ((*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) || + ((*s == 'U' || *s == 'u') && (*(s + 1) == '+' || *(s + 1) == '-'))) { + base = 16; + dmap = hdigits; + s += 2; + } + + for (v = 0; isdigok(dmap, *s); s++) + v = (v * base) + a2i[(int) *s]; + + if (end != 0) + *end = s; + + return v; +} + +/* + * Routine to convert an ASCII string into an signed int integer. + */ +int +_bdf_atol(char *s, char **end, int base) +{ + int v, neg; + unsigned char *dmap; + + if (s == 0 || *s == 0) + return 0; + + /* + * Make sure the radix is something recognizable. Default to 10. + */ + switch (base) { + case 8: dmap = odigits; break; + case 16: dmap = hdigits; break; + default: base = 10; dmap = ddigits; break; + } + + /* + * Check for a minus sign. + */ + neg = 0; + if (*s == '-') { + s++; + neg = 1; + } + + /* + * Check for the special hex prefix. + */ + if ((*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) || + ((*s == 'U' || *s == 'u') && (*(s + 1) == '+' || *(s + 1) == '-'))) { + base = 16; + dmap = hdigits; + s += 2; + } + + for (v = 0; isdigok(dmap, *s); s++) + v = (v * base) + a2i[(int) *s]; + + if (end != 0) + *end = s; + + return (!neg) ? v : -v; +} + +/* + * Routine to convert an ASCII string into an signed short integer. + */ +short +_bdf_atos(char *s, char **end, int base) +{ + short v, neg; + unsigned char *dmap; + + if (s == 0 || *s == 0) + return 0; + + /* + * Make sure the radix is something recognizable. Default to 10. + */ + switch (base) { + case 8: dmap = odigits; break; + case 16: dmap = hdigits; break; + default: base = 10; dmap = ddigits; break; + } + + /* + * Check for a minus. + */ + neg = 0; + if (*s == '-') { + s++; + neg = 1; + } + + /* + * Check for the special hex prefix. + */ + if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { + base = 16; + dmap = hdigits; + s += 2; + } + + for (v = 0; isdigok(dmap, *s); s++) + v = (v * base) + a2i[(int) *s]; + + if (end != 0) + *end = s; + + return (!neg) ? v : -v; +} + +/* + * Routine to compare two glyphs by encoding so they can be sorted. + */ +static int +by_encoding(const void *a, const void *b) +{ + bdf_glyph_t *c1, *c2; + + c1 = (bdf_glyph_t *) a; + c2 = (bdf_glyph_t *) b; + if (c1->encoding < c2->encoding) + return -1; + else if (c1->encoding > c2->encoding) + return 1; + return 0; +} + +/************************************************************************** + * + * BDF font file parsing flags and functions. + * + **************************************************************************/ + +/* + * Parse flags. + */ +#define _BDF_START 0x0001 +#define _BDF_FONT_NAME 0x0002 +#define _BDF_SIZE 0x0004 +#define _BDF_FONT_BBX 0x0008 +#define _BDF_PROPS 0x0010 +#define _BDF_GLYPHS 0x0020 +#define _BDF_GLYPH 0x0040 +#define _BDF_ENCODING 0x0080 +#define _BDF_SWIDTH 0x0100 +#define _BDF_DWIDTH 0x0200 +#define _BDF_BBX 0x0400 +#define _BDF_BITMAP 0x0800 + +#define _BDF_SWIDTH_ADJ 0x1000 + +#define _BDF_GLYPH_BITS (_BDF_GLYPH|_BDF_ENCODING|_BDF_SWIDTH|\ + _BDF_DWIDTH|_BDF_BBX|_BDF_BITMAP) + +#define _BDF_GLYPH_WIDTH_CHECK 0x40000000 +#define _BDF_GLYPH_HEIGHT_CHECK 0x80000000 + +/* + * Auto correction messages. + */ +#define ACMSG1 "FONT_ASCENT property missing. Added \"FONT_ASCENT %hd\"." +#define ACMSG2 "FONT_DESCENT property missing. Added \"FONT_DESCENT %hd\"." +#define ACMSG3 "Font width != actual width. Old: %hd New: %hd." +#define ACMSG4 "Font left bearing != actual left bearing. Old: %hd New: %hd." +#define ACMSG5 "Font ascent != actual ascent. Old: %hd New: %hd." +#define ACMSG6 "Font descent != actual descent. Old: %hd New: %hd." +#define ACMSG7 "Font height != actual height. Old: %hd New: %hd." +#define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made." +#define ACMSG9 "SWIDTH field missing at line %d. Set automatically." +#define ACMSG10 "DWIDTH field missing at line %d. Set to glyph width." +#define ACMSG11 "SIZE bits per pixel field adjusted to %hd." +#define ACMSG12 "Duplicate encoding %d (%s) changed to unencoded." +#define ACMSG13 "Glyph %d extra rows removed." +#define ACMSG14 "Glyph %d extra columns removed." +#define ACMSG15 "Incorrect glyph count: %d indicated but %d found." + +/* + * Error messages. + */ +#define ERRMSG1 "[line %d] Missing \"%s\" line." +#define ERRMSG2 "[line %d] Font header corrupted or missing fields." +#define ERRMSG3 "[line %d] Font glyphs corrupted or missing fields." + +void +_bdf_add_acmsg(bdf_font_t *font, char *msg, unsigned int len) +{ + char *cp; + + if (font->acmsgs_len == 0) + font->acmsgs = (char *) malloc(len + 2); + else + font->acmsgs = (char *) realloc(font->acmsgs, + font->acmsgs_len + len + 2); + + cp = font->acmsgs + font->acmsgs_len; + (void) memcpy(cp, msg, len); + cp += len; + *cp++ = '\n'; + *cp = 0; + font->acmsgs_len += len + 1; +} + +void +_bdf_add_comment(bdf_font_t *font, char *comment, unsigned int len) +{ + char *cp; + + if (font->comments_len == 0) + font->comments = (char *) malloc(len + 2); + else + font->comments = (char *) realloc(font->comments, + font->comments_len + len + 2); + + cp = font->comments + font->comments_len; + (void) memcpy(cp, comment, len); + cp += len; + *cp++ = '\n'; + *cp = 0; + font->comments_len += len + 1; +} + +/* + * Set the spacing from the font name if it exists, or set it to the default + * specified in the options. + */ +static void +_bdf_set_default_spacing(bdf_font_t *font, bdf_options_t *opts) +{ + unsigned int len; + char name[128]; + _bdf_list_t list; + + if (font == 0 || font->name == 0 || font->name[0] == 0) + return; + + font->spacing = opts->font_spacing; + + len = (unsigned int) (strlen(font->name) + 1); + (void) memcpy(name, font->name, len); + list.size = list.used = 0; + _bdf_split("-", name, len, &list); + if (list.used == 15) { + switch (list.field[11][0]) { + case 'C': case 'c': font->spacing = BDF_CHARCELL; break; + case 'M': case 'm': font->spacing = BDF_MONOWIDTH; break; + case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break; + } + } + if (list.size > 0) + free((char *) list.field); +} + +/* + * Determine if the property is an atom or not. If it is, then clean it up so + * the double quotes are removed if they exist. + */ +static int +_bdf_is_atom(char *line, unsigned int linelen, char **name, char **value) +{ + int hold; + char *sp, *ep; + bdf_property_t *p; + + *name = sp = ep = line; + while (*ep && *ep != ' ' && *ep != '\t') + ep++; + + hold = -1; + if (*ep) { + hold = *ep; + *ep = 0; + } + + p = bdf_get_property(sp); + + /* + * Restore the character that was saved before any return can happen. + */ + if (hold != -1) + *ep = hold; + + /* + * If the propert exists and is not an atom, just return here. + */ + if (p && p->format != BDF_ATOM) + return 0; + + /* + * The property is an atom. Trim all leading and trailing whitespace and + * double quotes for the atom value. + */ + sp = ep; + ep = line + linelen; + + /* + * Trim the leading whitespace if it exists. + */ + *sp++ = 0; + while (*sp && (*sp == ' ' || *sp == '\t')) + sp++; + + /* + * Trim the leading double quote if it exists. + */ + if (*sp == '"') + sp++; + *value = sp; + + /* + * Trim the trailing whitespace if it exists. + */ + while (ep > sp && (*(ep - 1) == ' ' || *(ep - 1) == '\t')) + *--ep = 0; + + /* + * Trim the trailing double quote if it exists. + */ + if (ep > sp && *(ep - 1) == '"') + *--ep = 0; + + return 1; +} + +static void +_bdf_add_property(bdf_font_t *font, char *name, char *value) +{ + unsigned int propid; + hashnode hn; + int len; + bdf_property_t *prop, *fp; + + /* + * First, check to see if the property already exists in the font. + */ + if ((hn = hash_lookup(name, (hashtable *) font->internal)) != 0) { + /* + * The property already exists in the font, so simply replace + * the value of the property with the current value. + */ + fp = font->props + (unsigned int) hn->data; + + switch (fp->format) { + case BDF_ATOM: + /* + * Delete the current atom if it exists. + */ + if (fp->value.atom != 0) + free(fp->value.atom); + + if (value == 0) + len = 1; + else + len = strlen(value) + 1; + if (len > 1) { + fp->value.atom = (char *) malloc(len); + (void) memcpy(fp->value.atom, value, len); + } else + fp->value.atom = 0; + break; + case BDF_INTEGER: + fp->value.int32 = _bdf_atol(value, 0, 10); + break; + case BDF_CARDINAL: + fp->value.card32 = _bdf_atoul(value, 0, 10); + break; + } + return; + } + + /* + * See if this property type exists yet or not. If not, create it. + */ + hn = hash_lookup(name, &proptbl); + if (hn == 0) { + bdf_create_property(name, BDF_ATOM); + hn = hash_lookup(name, &proptbl); + } + + /* + * Allocate another property if this is overflow. + */ + if (font->props_used == font->props_size) { + if (font->props_size == 0) + font->props = (bdf_property_t *) malloc(sizeof(bdf_property_t)); + else + font->props = (bdf_property_t *) + realloc((char *) font->props, sizeof(bdf_property_t) * + (font->props_size + 1)); + fp = font->props + font->props_size; + (void) memset((char *) fp, 0, sizeof(bdf_property_t)); + font->props_size++; + } + + propid = (unsigned int) hn->data; + if (propid >= _num_bdf_properties) + prop = user_props + (propid - _num_bdf_properties); + else + prop = _bdf_properties + propid; + + fp = font->props + font->props_used; + + fp->name = prop->name; + fp->format = prop->format; + fp->builtin = prop->builtin; + + switch (prop->format) { + case BDF_ATOM: + if (value == 0) + len = 1; + else + len = strlen(value) + 1; + if (len > 1) { + fp->value.atom = (char *) malloc(len); + (void) memcpy(fp->value.atom, value, len); + } else + fp->value.atom = 0; + break; + case BDF_INTEGER: + fp->value.int32 = _bdf_atol(value, 0, 10); + break; + case BDF_CARDINAL: + fp->value.card32 = _bdf_atoul(value, 0, 10); + break; + } + + /* + * If the property happens to be a comment, then it doesn't need + * to be added to the internal hash table. + */ + if (memcmp(name, "COMMENT", 7) != 0) + /* + * Add the property to the font property table. + */ + hash_insert(fp->name, (void *) font->props_used, + (hashtable *) font->internal); + + font->props_used++; + + /* + * Some special cases need to be handled here. The DEFAULT_CHAR property + * needs to be located if it exists in the property list, the FONT_ASCENT + * and FONT_DESCENT need to be assigned if they are present, and the + * SPACING property should override the default spacing. + */ + if (memcmp(name, "DEFAULT_CHAR", 12) == 0) + font->default_glyph = fp->value.int32; + else if (memcmp(name, "FONT_ASCENT", 11) == 0) + font->font_ascent = fp->value.int32; + else if (memcmp(name, "FONT_DESCENT", 12) == 0) + font->font_descent = fp->value.int32; + else if (memcmp(name, "SPACING", 7) == 0) { + if (fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P') + font->spacing = BDF_PROPORTIONAL; + else if (fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M') + font->spacing = BDF_MONOWIDTH; + else if (fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C') + font->spacing = BDF_CHARCELL; + } +} + +/* + * Actually parse the glyph info and bitmaps. + */ +static int +_bdf_parse_glyphs(char *line, unsigned int linelen, unsigned int lineno, + void *call_data, void *client_data) +{ + int c; + char *s; + unsigned char *bp; + unsigned int i, slen = 0, nibbles; + double ps, rx, dw, sw; + _bdf_line_func_t *next; + _bdf_parse_t *p; + bdf_glyph_t *glyph; + bdf_font_t *font; + char nbuf[128]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + font = p->font; + + /* + * Check for a comment. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + linelen -= 7; + s = line + 7; + if (*s != 0) { + s++; + linelen--; + } + _bdf_add_comment(p->font, s, linelen); + return 0; + } + + /* + * The very first thing expected is the number of glyphs. + */ + if (!(p->flags & _BDF_GLYPHS)) { + if (memcmp(line, "CHARS", 5) != 0) { + sprintf(nbuf, ERRMSG1, lineno, "CHARS"); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + return BDF_MISSING_CHARS; + } + _bdf_split(" +", line, linelen, &p->list); + p->cnt = font->glyphs_size = _bdf_atoul(p->list.field[1], 0, 10); + + /* + * Make sure the number of glyphs is non-zero. + */ + if (p->cnt == 0) + font->glyphs_size = 64; + + font->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * + font->glyphs_size); + /* + * Make sure the glyph structures are initialized. + */ + (void) memset((char *) font->glyphs, 0, + sizeof(bdf_glyph_t) * font->glyphs_size); + + /* + * Set up the callback to indicate the glyph loading is about to + * begin. + */ + if (p->callback != 0) { + p->cb.reason = BDF_LOAD_START; + p->cb.total = p->cnt; + p->cb.current = 0; + (*p->callback)(&p->cb, p->client_data); + } + p->flags |= _BDF_GLYPHS; + return 0; + } + + /* + * Check for the ENDFONT field. + */ + if (memcmp(line, "ENDFONT", 7) == 0) { + /* + * Sort the glyphs by encoding. + */ + qsort((char *) font->glyphs, font->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); + + p->flags &= ~_BDF_START; + return 0; + } + + /* + * Check for the ENDCHAR field. + */ + if (memcmp(line, "ENDCHAR", 7) == 0) { + /* + * Set up and call the callback if it was passed. + */ + if (p->callback != 0) { + p->cb.reason = BDF_LOADING; + p->cb.total = font->glyphs_size; + p->cb.current = font->glyphs_used; + (*p->callback)(&p->cb, p->client_data); + } + p->glyph_enc = 0; + p->flags &= ~_BDF_GLYPH_BITS; + return 0; + } + + /* + * Check to see if a glyph is being scanned but should be ignored + * because it is an unencoded glyph. + */ + if ((p->flags & _BDF_GLYPH) && + p->glyph_enc == -1 && p->opts->keep_unencoded == 0) + return 0; + + /* + * Check for the STARTCHAR field. + */ + if (memcmp(line, "STARTCHAR", 9) == 0) { + /* + * Set the character name in the parse info first until the + * encoding can be checked for an unencoded character. + */ + if (p->glyph_name != 0) + free(p->glyph_name); + _bdf_split(" +", line, linelen, &p->list); + _bdf_shift(1, &p->list); + s = _bdf_join(' ', &slen, &p->list); + p->glyph_name = (char *) malloc(slen + 1); + (void) memcpy(p->glyph_name, s, slen + 1); + p->flags |= _BDF_GLYPH; + return 0; + } + + /* + * Check for the ENCODING field. + */ + if (memcmp(line, "ENCODING", 8) == 0) { + if (!(p->flags & _BDF_GLYPH)) { + /* + * Missing STARTCHAR field. + */ + sprintf(nbuf, ERRMSG1, lineno, "STARTCHAR"); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + return BDF_MISSING_STARTCHAR; + } + _bdf_split(" +", line, linelen, &p->list); + p->glyph_enc = _bdf_atol(p->list.field[1], 0, 10); + + /* + * Check to see if this encoding has already been encountered. If it + * has then change it to unencoded so it gets added if indicated. + */ + if (p->glyph_enc >= 0) { + if (_bdf_glyph_modified(p->have, p->glyph_enc)) { + /* + * Add a message saying a glyph has been moved to the + * unencoded area. + */ + sprintf(nbuf, ACMSG12, p->glyph_enc, p->glyph_name); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + p->glyph_enc = -1; + font->modified = 1; + } else + _bdf_set_glyph_modified(p->have, p->glyph_enc); + } + + if (p->glyph_enc >= 0) { + /* + * Make sure there are enough glyphs allocated in case the + * number of characters happen to be wrong. + */ + if (font->glyphs_used == font->glyphs_size) { + font->glyphs = (bdf_glyph_t *) + realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * (font->glyphs_size + 64)); + (void) memset((char *) (font->glyphs + font->glyphs_size), + 0, sizeof(bdf_glyph_t) << 6); + font->glyphs_size += 64; + } + + glyph = font->glyphs + font->glyphs_used++; + glyph->name = p->glyph_name; + glyph->encoding = p->glyph_enc; + + /* + * Reset the initial glyph info. + */ + p->glyph_name = 0; + } else { + /* + * Unencoded glyph. Check to see if it should be added or not. + */ + if (p->opts->keep_unencoded != 0) { + /* + * Allocate the next unencoded glyph. + */ + if (font->unencoded_used == font->unencoded_size) { + if (font->unencoded_size == 0) + font->unencoded = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) << 2); + else + font->unencoded = (bdf_glyph_t *) + realloc((char *) font->unencoded, + sizeof(bdf_glyph_t) * + (font->unencoded_size + 4)); + font->unencoded_size += 4; + } + + glyph = font->unencoded + font->unencoded_used; + glyph->name = p->glyph_name; + glyph->encoding = font->unencoded_used++; + } else + /* + * Free up the glyph name if the unencoded shouldn't be + * kept. + */ + free(p->glyph_name); + + p->glyph_name = 0; + } + + /* + * Clear the flags that might be added when width and height are + * checked for consistency. + */ + p->flags &= ~(_BDF_GLYPH_WIDTH_CHECK|_BDF_GLYPH_HEIGHT_CHECK); + + p->flags |= _BDF_ENCODING; + return 0; + } + + /* + * Point at the glyph being constructed. + */ + if (p->glyph_enc == -1) + glyph = font->unencoded + (font->unencoded_used - 1); + else + glyph = font->glyphs + (font->glyphs_used - 1); + + /* + * Check to see if a bitmap is being constructed. + */ + if (p->flags & _BDF_BITMAP) { + /* + * If there are more rows than are specified in the glyph metrics, + * ignore the remaining lines. + */ + if (p->row >= glyph->bbx.height) { + if (!(p->flags & _BDF_GLYPH_HEIGHT_CHECK)) { + sprintf(nbuf, ACMSG13, glyph->encoding); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + p->flags |= _BDF_GLYPH_HEIGHT_CHECK; + font->modified = 1; + } + return 0; + } + + /* + * Only collect the number of nibbles indicated by the glyph metrics. + * If there are more columns, they are simply ignored. + */ + nibbles = p->bpr << 1; + bp = glyph->bitmap + (p->row * p->bpr); + for (i = 0, *bp = 0; i < nibbles; i++) { + c = line[i]; + *bp = (*bp << 4) + a2i[c]; + if (i + 1 < nibbles && (i & 1)) + *++bp = 0; + } + + /* + * If any line has extra columns, indicate they have been removed. + */ + if ((line[nibbles] == '0' || a2i[(int) line[nibbles]] != 0) && + !(p->flags & _BDF_GLYPH_WIDTH_CHECK)) { + sprintf(nbuf, ACMSG14, glyph->encoding); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + p->flags |= _BDF_GLYPH_WIDTH_CHECK; + font->modified = 1; + } + + p->row++; + return 0; + } + + /* + * Expect the SWIDTH (scalable width) field next. + */ + if (memcmp(line, "SWIDTH", 6) == 0) { + if (!(p->flags & _BDF_ENCODING)) { + /* + * Missing ENCODING field. + */ + sprintf(nbuf, ERRMSG1, lineno, "ENCODING"); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + return BDF_MISSING_ENCODING; + } + _bdf_split(" +", line, linelen, &p->list); + glyph->swidth = _bdf_atoul(p->list.field[1], 0, 10); + p->flags |= _BDF_SWIDTH; + return 0; + } + + /* + * Expect the DWIDTH (scalable width) field next. + */ + if (memcmp(line, "DWIDTH", 6) == 0) { + _bdf_split(" +", line, linelen, &p->list); + glyph->dwidth = _bdf_atoul(p->list.field[1], 0, 10); + + if (!(p->flags & _BDF_SWIDTH)) { + /* + * Missing SWIDTH field. Add an auto correction message and set + * the scalable width from the device width. + */ + sprintf(nbuf, ACMSG9, lineno); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) glyph->dwidth; + glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + + p->flags |= _BDF_DWIDTH; + return 0; + } + + /* + * Expect the BBX field next. + */ + if (memcmp(line, "BBX", 3) == 0) { + _bdf_split(" +", line, linelen, &p->list); + glyph->bbx.width = _bdf_atos(p->list.field[1], 0, 10); + glyph->bbx.height = _bdf_atos(p->list.field[2], 0, 10); + glyph->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); + glyph->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); + + /* + * Generate the ascent and descent of the character. + */ + glyph->bbx.ascent = glyph->bbx.height + glyph->bbx.y_offset; + glyph->bbx.descent = -glyph->bbx.y_offset; + + /* + * Determine the overall font bounding box as the characters are + * loaded so corrections can be done later if indicated. + */ + p->maxas = MAX(glyph->bbx.ascent, p->maxas); + p->maxds = MAX(glyph->bbx.descent, p->maxds); + p->rbearing = glyph->bbx.width + glyph->bbx.x_offset; + p->maxrb = MAX(p->rbearing, p->maxrb); + p->minlb = MIN(glyph->bbx.x_offset, p->minlb); + p->maxlb = MAX(glyph->bbx.x_offset, p->maxlb); + + if (!(p->flags & _BDF_DWIDTH)) { + /* + * Missing DWIDTH field. Add an auto correction message and set + * the device width to the glyph width. + */ + sprintf(nbuf, ACMSG10, lineno); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + glyph->dwidth = glyph->bbx.width; + } + + /* + * If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH + * value if necessary. + */ + if (p->opts->correct_metrics != 0) { + /* + * Determine the point size of the glyph. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) glyph->dwidth; + sw = (unsigned short) ((dw * 72000.0) / (ps * rx)); + + if (sw != glyph->swidth) { + glyph->swidth = sw; + if (p->glyph_enc == -1) + _bdf_set_glyph_modified(font->umod, + font->unencoded_used - 1); + else + _bdf_set_glyph_modified(font->nmod, glyph->encoding); + p->flags |= _BDF_SWIDTH_ADJ; + font->modified = 1; + } + } + p->flags |= _BDF_BBX; + return 0; + } + + /* + * And finally, gather up the bitmap. + */ + if (memcmp(line, "BITMAP", 6) == 0) { + if (!(p->flags & _BDF_BBX)) { + /* + * Missing BBX field. + */ + sprintf(nbuf, ERRMSG1, lineno, "BBX"); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + return BDF_MISSING_BBX; + } + /* + * Allocate enough space for the bitmap. + */ + p->bpr = ((glyph->bbx.width * p->font->bpp) + 7) >> 3; + glyph->bytes = p->bpr * glyph->bbx.height; + glyph->bitmap = (unsigned char *) malloc(glyph->bytes); + p->row = 0; + p->flags |= _BDF_BITMAP; + return 0; + } + + return BDF_INVALID_LINE; +} + +/* + * Load the font properties. + */ +static int +_bdf_parse_properties(char *line, unsigned int linelen, unsigned int lineno, + void *call_data, void *client_data) +{ + unsigned int vlen; + _bdf_line_func_t *next; + _bdf_parse_t *p; + char *name, *value, nbuf[128]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + /* + * Check for the end of the properties. + */ + if (memcmp(line, "ENDPROPERTIES", 13) == 0) { + /* + * If the FONT_ASCENT or FONT_DESCENT properties have not been + * encountered yet, then make sure they are added as properties and + * make sure they are set from the font bounding box info. + * + * This is *always* done regardless of the options, because X11 + * requires these two fields to compile fonts. + */ + if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) { + p->font->font_ascent = p->font->bbx.ascent; + sprintf(nbuf, "%hd", p->font->bbx.ascent); + _bdf_add_property(p->font, "FONT_ASCENT", nbuf); + sprintf(nbuf, ACMSG1, p->font->bbx.ascent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) { + p->font->font_descent = p->font->bbx.descent; + sprintf(nbuf, "%hd", p->font->bbx.descent); + _bdf_add_property(p->font, "FONT_DESCENT", nbuf); + sprintf(nbuf, ACMSG2, p->font->bbx.descent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + p->flags &= ~_BDF_PROPS; + *next = _bdf_parse_glyphs; + return 0; + } + + /* + * Ignore the _XFREE86_GLYPH_RANGES and _XMBDFED_INFO properties. + */ + if (memcmp(line, "_XFREE86_GLYPH_RANGES", 21) == 0 || + memcmp(line, "_XMBDFED_INFO", 13) == 0) + return 0; + + /* + * Handle COMMENT fields and properties in a special way to preserve + * the spacing. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + name = value = line; + value += 7; + if (*value) + *value++ = 0; + _bdf_add_property(p->font, name, value); + } else if (_bdf_is_atom(line, linelen, &name, &value)) + _bdf_add_property(p->font, name, value); + else { + _bdf_split(" +", line, linelen, &p->list); + name = p->list.field[0]; + _bdf_shift(1, &p->list); + value = _bdf_join(' ', &vlen, &p->list); + _bdf_add_property(p->font, name, value); + } + + return 0; +} + +/* + * Load the font header. + */ +static int +_bdf_parse_start(char *line, unsigned int linelen, unsigned int lineno, + void *call_data, void *client_data) +{ + unsigned int slen = 0; + _bdf_line_func_t *next; + _bdf_parse_t *p; + bdf_font_t *font; + char *s, nbuf[128]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + /* + * Check for a comment. This is done to handle those fonts that have + * comments before the STARTFONT line for some reason. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + if (p->opts->keep_comments != 0 && p->font != 0) { + linelen -= 7; + s = line + 7; + if (*s != 0) { + s++; + linelen--; + } + _bdf_add_comment(p->font, s, linelen); + } + return 0; + } + + if (!(p->flags & _BDF_START)) { + if (memcmp(line, "STARTFONT", 9) != 0) + /* + * No STARTFONT field is a good indication of a problem. + */ + return BDF_MISSING_START; + p->flags = _BDF_START; + p->font = font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + p->font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) p->font->internal); + p->font->spacing = p->opts->font_spacing; + p->font->default_glyph = -1; + return 0; + } + + /* + * Check for the start of the properties. + */ + if (memcmp(line, "STARTPROPERTIES", 15) == 0) { + _bdf_split(" +", line, linelen, &p->list); + p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->props = (bdf_property_t *) + malloc(sizeof(bdf_property_t) * p->cnt); + p->flags |= _BDF_PROPS; + *next = _bdf_parse_properties; + return 0; + } + + /* + * Check for the FONTBOUNDINGBOX field. + */ + if (memcmp(line, "FONTBOUNDINGBOX", 15) == 0) { + if (!(p->flags & _BDF_SIZE)) { + /* + * Missing the SIZE field. + */ + sprintf(nbuf, ERRMSG1, lineno, "SIZE"); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + return BDF_MISSING_SIZE; + } + _bdf_split(" +", line, linelen, &p->list); + p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10); + p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10); + p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); + p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); + p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset; + p->font->bbx.descent = -p->font->bbx.y_offset; + p->flags |= _BDF_FONT_BBX; + return 0; + } + + /* + * The next thing to check for is the FONT field. + */ + if (memcmp(line, "FONT", 4) == 0) { + _bdf_split(" +", line, linelen, &p->list); + _bdf_shift(1, &p->list); + s = _bdf_join(' ', &slen, &p->list); + p->font->name = (char *) malloc(slen + 1); + (void) memcpy(p->font->name, s, slen + 1); + /* + * If the font name is an XLFD name, set the spacing to the one in the + * font name. If there is no spacing fall back on the default. + */ + _bdf_set_default_spacing(p->font, p->opts); + p->flags |= _BDF_FONT_NAME; + return 0; + } + + /* + * Check for the SIZE field. + */ + if (memcmp(line, "SIZE", 4) == 0) { + if (!(p->flags & _BDF_FONT_NAME)) { + /* + * Missing the FONT field. + */ + sprintf(nbuf, ERRMSG1, lineno, "FONT"); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + return BDF_MISSING_FONTNAME; + } + _bdf_split(" +", line, linelen, &p->list); + p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10); + p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10); + + /* + * Check for the bits per pixel field. + */ + if (p->list.used == 5) { + p->font->bpp = _bdf_atos(p->list.field[4], 0, 10); + if (p->font->bpp > 1 && (p->font->bpp & 1)) { + /* + * Move up to the next bits per pixel value if an odd number + * is encountered. + */ + p->font->bpp++; + if (p->font->bpp <= 4) { + sprintf(nbuf, ACMSG11, p->font->bpp); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + } + } + if (p->font->bpp > 4) { + sprintf(nbuf, ACMSG11, p->font->bpp); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->bpp = 4; + } + } else + p->font->bpp = 1; + + p->flags |= _BDF_SIZE; + return 0; + } + + return BDF_INVALID_LINE; +} + +/************************************************************************** + * + * API. + * + **************************************************************************/ + +void +bdf_setup(void) +{ + unsigned int i; + bdf_property_t *prop; + + hash_init(&proptbl); + for (i = 0, prop = _bdf_properties; i < _num_bdf_properties; i++, prop++) + hash_insert(prop->name, (void *) i, &proptbl); +} + +void +bdf_cleanup(void) +{ + unsigned int i; + bdf_property_t *prop; + + hash_free(&proptbl); + + /* + * Free up the user defined properties. + */ + for (prop = user_props, i = 0; i < nuser_props; i++, prop++) { + free(prop->name); + if (prop->format == BDF_ATOM && prop->value.atom != 0) + free(prop->value.atom); + } + if (nuser_props > 0) + free((char *) user_props); + + _bdf_glyph_name_cleanup(); +} + +bdf_font_t * +bdf_load_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback, + void *data) +{ + int n; + unsigned int lineno; + char msgbuf[128]; + _bdf_parse_t p; + + (void) memset((char *) &p, 0, sizeof(_bdf_parse_t)); + p.opts = (opts != 0) ? opts : &_bdf_opts; + p.minlb = 32767; + p.callback = callback; + p.client_data = data; + n = _bdf_readlines(fileno(in), _bdf_parse_start, (void *) &p, &lineno); + + if (p.font != 0) { + /* + * If the font is not proportional, set the fonts monowidth + * field to the width of the font bounding box. + */ + if (p.font->spacing != BDF_PROPORTIONAL) + p.font->monowidth = p.font->bbx.width; + + /* + * If the number of glyphs loaded is not that of the original count, + * indicate the difference. + */ + if (p.cnt != p.font->glyphs_used + p.font->unencoded_used) { + sprintf(msgbuf, ACMSG15, p.cnt, + p.font->glyphs_used + p.font->unencoded_used); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->modified = 1; + } + + /* + * Once the font has been loaded, adjust the overall font metrics if + * necessary. + */ + if (p.opts->correct_metrics != 0 && + (p.font->glyphs_used > 0 || p.font->unencoded_used > 0)) { + if (p.maxrb - p.minlb != p.font->bbx.width) { + sprintf(msgbuf, ACMSG3, p.font->bbx.width, p.maxrb - p.minlb); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.width = p.maxrb - p.minlb; + p.font->modified = 1; + } + if (p.font->bbx.x_offset != p.minlb) { + sprintf(msgbuf, ACMSG4, p.font->bbx.x_offset, p.minlb); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.x_offset = p.minlb; + p.font->modified = 1; + } + if (p.font->bbx.ascent != p.maxas) { + sprintf(msgbuf, ACMSG5, p.font->bbx.ascent, p.maxas); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.ascent = p.maxas; + p.font->modified = 1; + } + if (p.font->bbx.descent != p.maxds) { + sprintf(msgbuf, ACMSG6, p.font->bbx.descent, p.maxds); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.descent = p.maxds; + p.font->bbx.y_offset = -p.maxds; + p.font->modified = 1; + } + if (p.maxas + p.maxds != p.font->bbx.height) { + sprintf(msgbuf, ACMSG7, p.font->bbx.height, p.maxas + p.maxds); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + } + p.font->bbx.height = p.maxas + p.maxds; + + if (p.flags & _BDF_SWIDTH_ADJ) + _bdf_add_acmsg(p.font, ACMSG8, strlen(ACMSG8)); + } + } + + /* + * Last, if an error happened during loading, handle the messages. + */ + if (n < 0 && callback != 0) { + /* + * An error was returned. Alert the client. + */ + p.cb.reason = BDF_ERROR; + p.cb.errlineno = lineno; + (*callback)(&p.cb, data); + } else if (p.flags & _BDF_START) { + if (p.font != 0) { + /* + * The ENDFONT field was never reached or did not exist. + */ + if (!(p.flags & _BDF_GLYPHS)) + /* + * Error happened while parsing header. + */ + sprintf(msgbuf, ERRMSG2, lineno); + else + /* + * Error happened when parsing glyphs. + */ + sprintf(msgbuf, ERRMSG3, lineno); + + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + } + + if (callback != 0) { + p.cb.reason = BDF_ERROR; + p.cb.errlineno = lineno; + (*callback)(&p.cb, data); + } + } else if (callback != 0) { + /* + * This forces the progress bar to always finish. + */ + p.cb.current = p.cb.total; + (*p.callback)(&p.cb, p.client_data); + } + + /* + * Free up the list used during the parsing. + */ + if (p.list.size > 0) + free((char *) p.list.field); + + if (p.font != 0) { + /* + * Make sure the comments are NULL terminated if they exist. + */ + if (p.font->comments_len > 0) { + p.font->comments = (char *) realloc(p.font->comments, + p.font->comments_len + 1); + p.font->comments[p.font->comments_len] = 0; + } + + /* + * Make sure the auto-correct messages are NULL terminated if they + * exist. + */ + if (p.font->acmsgs_len > 0) { + p.font->acmsgs = (char *) realloc(p.font->acmsgs, + p.font->acmsgs_len + 1); + p.font->acmsgs[p.font->acmsgs_len] = 0; + } + } + + return p.font; +} + +#ifdef HAVE_HBF + +static int +_bdf_parse_hbf_header(char *line, unsigned int linelen, unsigned int lineno, + void *call_data, void *client_data) +{ + unsigned int vlen = 0; + char *name, *value; + _bdf_parse_t *p; + _bdf_line_func_t *next; + char nbuf[24]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + /* + * Check for comments. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + if (p->opts->keep_comments != 0 && p->font != 0) { + name = line; + value = name + 7; + vlen = linelen - 7; + if (*value) { + *value++ = 0; + vlen--; + } + /* + * If the properties are being parsed, add the comment as a + * property. Otherwise, simply add the comment in the normal + * fashion. + */ + if (p->flags & _BDF_PROPS) + _bdf_add_property(p->font, name, value); + else + _bdf_add_comment(p->font, value, vlen); + } + return 0; + } + + if (!(p->flags & _BDF_START)) { + if (memcmp(line, "HBF_START_FONT", 14) != 0) + return -1; + p->flags = _BDF_START; + p->font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + /* + * HBF fonts are always assumed to be 1 bit per pixel. + */ + p->font->bpp = 1; + p->font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) p->font->internal); + p->font->hbf = 1; + p->font->spacing = p->opts->font_spacing; + p->font->default_glyph = -1; + return 0; + } + + /* + * Check for the HBF_END_FONT field. + */ + if (memcmp(line, "HBF_END_FONT", 12) == 0) + /* + * Need to perform some checks here to see whether some fields are + * missing or not. + */ + return 0; + + /* + * Check for HBF keywords which will be added as comments. These should + * never occur in the properties list. Assume they won't. + */ + if (memcmp(line, "HBF_", 4) == 0) { + if (p->opts->keep_comments != 0) + _bdf_add_comment(p->font, line, linelen); + return 0; + } + + if (!(p->flags & _BDF_PROPS)) { + /* + * Check for the start of the properties. + */ + if (memcmp(line, "STARTPROPERTIES", 15) == 0) { + _bdf_split(" +", line, linelen, &p->list); + p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->props = (bdf_property_t *) + malloc(sizeof(bdf_property_t) * p->cnt); + p->flags |= _BDF_PROPS; + return 0; + } + + /* + * Check for the CHARS field. + */ + if (memcmp(line, "CHARS", 5) == 0) { + _bdf_split(" +", line, linelen, &p->list); + p->cnt = p->font->glyphs_size = + _bdf_atoul(p->list.field[1], 0, 10); + p->font->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * p->cnt); + return 0; + } + + /* + * Check for the FONTBOUNDINGBOX field. + */ + if (memcmp(line, "FONTBOUNDINGBOX", 15) == 0) { + if (!(p->flags & (_BDF_START|_BDF_FONT_NAME|_BDF_SIZE))) + return -1; + _bdf_split(" +", line, linelen, &p->list); + p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10); + p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10); + p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); + p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); + p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset; + p->font->bbx.descent = -p->font->bbx.y_offset; + p->flags |= _BDF_FONT_BBX; + return 0; + } + + /* + * The next thing to check for is the FONT field. + */ + if (memcmp(line, "FONT", 4) == 0) { + if (!(p->flags & _BDF_START)) + return -1; + _bdf_split(" +", line, linelen, &p->list); + _bdf_shift(1, &p->list); + value = _bdf_join(' ', &vlen, &p->list); + p->font->name = (char *) malloc(vlen + 1); + (void) memcpy(p->font->name, value, vlen + 1); + /* + * If the font name is an XLFD name, set the spacing to the one in + * the font name. If there is no spacing fall back on the + * default. + */ + _bdf_set_default_spacing(p->font, p->opts); + p->flags |= _BDF_FONT_NAME; + return 0; + } + + /* + * Check for the SIZE field. + */ + if (memcmp(line, "SIZE", 4) == 0) { + if (!(p->flags & (_BDF_START|_BDF_FONT_NAME))) + return -1; + _bdf_split(" +", line, linelen, &p->list); + p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10); + p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10); + p->flags |= _BDF_SIZE; + return 0; + } + } else { + /* + * Check for the end of the properties. + */ + if (memcmp(line, "ENDPROPERTIES", 13) == 0) { + /* + * If the FONT_ASCENT or FONT_DESCENT properties have not been + * encountered yet, then make sure they are added as properties and + * make sure they are set from the font bounding box info. + * + * This is *always* done regardless of the options, because X11 + * requires these two fields to compile fonts. + */ + if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) { + p->font->font_ascent = p->font->bbx.ascent; + sprintf(nbuf, "%hd", p->font->bbx.ascent); + _bdf_add_property(p->font, "FONT_ASCENT", nbuf); + sprintf(nbuf, ACMSG1, p->font->bbx.ascent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) { + p->font->font_descent = p->font->bbx.descent; + sprintf(nbuf, "%hd", p->font->bbx.descent); + _bdf_add_property(p->font, "FONT_DESCENT", nbuf); + sprintf(nbuf, ACMSG2, p->font->bbx.descent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + p->flags &= ~_BDF_PROPS; + return 0; + } + + /* + * Handle the next thing in the usual property fashion. + */ + if (_bdf_is_atom(line, linelen, &name, &value)) + _bdf_add_property(p->font, name, value); + else { + _bdf_split(" +", line, linelen, &p->list); + name = p->list.field[0]; + _bdf_shift(1, &p->list); + value = _bdf_join(' ', &vlen, &p->list); + _bdf_add_property(p->font, name, value); + } + return 0; + } + + /* + * Anything else is an error. + */ + return -1; +} + +#define CONST const + +static void +_bdf_add_hbf_glyph(HBF *hbf, unsigned int code, void *callback_data) +{ + CONST unsigned char *bmap; + unsigned int n; + bdf_glyph_t *gp; + bdf_font_t *font; + _bdf_parse_t *p; + HBF_BBOX *fbbx; + double ps, rx, dw; + char nbuf[24]; + + /* + * Attempt to get the bitmap. + */ + if ((bmap = hbfGetBitmap(hbf, code)) == 0) + /* + * Need some sort of error handling here. + */ + return; + + p = (_bdf_parse_t *) callback_data; + + fbbx = hbfFontBBox(hbf); + + font = p->font; + + /* + * Check to make sure there is enough space to hold this glyph. If not, + * allocate 10 more just in case. + */ + if (font->glyphs_used == font->glyphs_size) { + if (font->glyphs_size == 0) + font->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * 16); + else + font->glyphs = (bdf_glyph_t *) + realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * (font->glyphs_used + 16)); + gp = font->glyphs + font->glyphs_size; + (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) * 16); + font->glyphs_size += 16; + } + + gp = font->glyphs + font->glyphs_used++; + + /* + * Set the glyph name. + */ + sprintf(nbuf, "char%d", code); + n = (unsigned int) strlen(nbuf); + gp->name = (char *) malloc(n + 1); + (void) memcpy(gp->name, nbuf, n + 1); + + /* + * Set encoding. + */ + gp->encoding = (int) code; + + /* + * Set the device width. + */ + gp->dwidth = (unsigned short) fbbx->hbf_width; + + /* + * Set the scalable width. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + + /* + * Set the glyph bounding box. + */ + gp->bbx.width = fbbx->hbf_width; + gp->bbx.height = fbbx->hbf_height; + gp->bbx.x_offset = fbbx->hbf_xDisplacement; + gp->bbx.y_offset = fbbx->hbf_yDisplacement; + gp->bbx.ascent = gp->bbx.height + gp->bbx.y_offset; + gp->bbx.descent = -gp->bbx.y_offset; + + /* + * Add the bitmap by making a copy. Assumes the font bbx is OK for + * determining the number of bytes needed for the glyph bitmap. + */ + gp->bytes = ((gp->bbx.width + 7) >> 3) * gp->bbx.height; + gp->bitmap = (unsigned char *) malloc(gp->bytes); + (void) memcpy((char *) gp->bitmap, (char *) bmap, gp->bytes); + + /* + * Call the callback if it was provided. + */ + if (p->callback != 0) { + p->cb.reason = BDF_LOADING; + p->cb.total = font->glyphs_size; + p->cb.current = font->glyphs_used; + (*p->callback)(&p->cb, p->client_data); + } +} + +bdf_font_t * +bdf_load_hbf_font(char *filename, bdf_options_t *opts, bdf_callback_t callback, + void *data) +{ + int n, diff; + unsigned int lineno; + FILE *in; + HBF *hbf; + bdf_property_t *pp; + char *name; + _bdf_parse_t p; + + if ((hbf = hbfOpen(filename)) == 0) + return 0; + + if ((in = fopen(hbfFileName(hbf), "r")) == 0) { + hbfClose(hbf); + return 0; + } + + /* + * Parse the HBF header for properties and other things. + */ + (void) memset((char *) &p, 0, sizeof(_bdf_parse_t)); + p.opts = (opts != 0) ? opts : &_bdf_opts; + p.minlb = 32767; + p.callback = callback; + p.client_data = data; + + n = _bdf_readlines(fileno(in), _bdf_parse_hbf_header, (void *) &p, + &lineno); + + fclose(in); + + /* + * Determine what spacing the font has so the monowidth field can be set + * if necessary. + */ + if ((pp = bdf_get_font_property(p.font, "SPACING")) != 0) { + switch (pp->value.atom[0]) { + case 'p': case 'P': p.font->spacing = BDF_PROPORTIONAL; break; + case 'm': case 'M': p.font->spacing = BDF_MONOWIDTH; break; + case 'c': case 'C': p.font->spacing = BDF_CHARCELL; break; + } + } + + /* + * Set the monowidth field if necessary. + */ + if (p.font->spacing != BDF_PROPORTIONAL) + p.font->monowidth = p.font->bbx.width; + + /* + * Before loading the glyphs, check to see if any glyph structures have + * been added. If not, check the HBF font for the number of characters. + * Dynamically increasing glyph storage causes memory fragmentation on + * some machines and crashes. This takes care of the cases where the HBF + * file does not provide a "CHARS n" line. + */ + if (p.font->glyphs_size < hbfChars(hbf)) { + if (p.font->glyphs_size == 0) + p.font->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * hbfChars(hbf)); + else + p.font->glyphs = (bdf_glyph_t *) + realloc((char *) p.font->glyphs, + sizeof(bdf_glyph_t) * hbfChars(hbf)); + diff = hbfChars(hbf) - p.font->glyphs_size; + (void) memset((char *) (p.font->glyphs + p.font->glyphs_size), 0, + diff); + p.font->glyphs_size = hbfChars(hbf); + } + + /* + * Call the callback initially to set things up. + */ + if (p.callback != 0) { + p.cb.reason = BDF_LOAD_START; + p.cb.total = p.font->glyphs_size; + p.cb.current = 0; + (*p.callback)(&p.cb, p.client_data); + } + + /* + * Now load the glyphs. + */ + hbfForEach(hbf, _bdf_add_hbf_glyph, (void *) &p); + + /* + * Close the HBF font. + */ + hbfClose(hbf); + + /* + * Sort the glyphs by encoding. + */ + qsort((char *) p.font->glyphs, p.font->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); + + /* + * After loading the HBF header, create an XLFD name. If the XLFD name + * cannot be made then preserve the name found in the HBF file. + */ + if ((name = bdf_make_xlfd_name(p.font, "HBF", "Unknown")) != 0) { + if (p.font->name != 0) + /* + * If a name already exists in the font, free it up. + */ + free(p.font->name); + + /* + * Replace the old name with the XLFD name. + */ + p.font->name = name; + } + + /* + * Mark the font as being modified and generate a message that says + * something about the font being converted from HBF format. + */ + p.font->modified = 1; + _bdf_add_acmsg(p.font, "Font converted from HBF to BDF.", 31); + + return p.font; +} + +#endif /* HAVE_HBF */ + +/* + * Crop the glyph bitmap to the minimum rectangle needed to hold the bits that + * are set. Adjust the metrics based on the provided bounding box. + */ +void +_bdf_crop_glyph(bdf_font_t *font, bdf_glyph_t *glyph) +{ + int byte; + unsigned short x, y, bpr, nbpr, col, colx, si, di; + unsigned short minx, maxx, miny, maxy; + unsigned int bytes; + unsigned char *bmap, *masks; + bdf_bbx_t nbbx; + + if (glyph == 0) + return; + + (void) memcpy((char *) &nbbx, (char *) &glyph->bbx, sizeof(bdf_bbx_t)); + + bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3; + + maxx = maxy = 0; + minx = miny = 32767; + + masks = 0; + switch (font->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + for (y = 0; y < glyph->bbx.height; y++) { + for (col = x = 0; x < glyph->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + if (glyph->bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + minx = MIN(minx, x); + maxx = MAX(maxx, x); + miny = MIN(miny, y); + maxy = MAX(maxy, y); + } + } + } + + /* + * Handle an empty bitmap as a special case. + */ + if (minx == 32767) { + if (glyph->bytes > 0) + free((char *) glyph->bitmap); + glyph->bytes = 0; + (void) memset((char *) &glyph->bbx, 0, sizeof(bdf_bbx_t)); + return; + } + + /* + * Increment the max points so width and height calculations won't go + * wrong. + */ + maxx++; + maxy++; + + if (minx > 0) + nbbx.x_offset += minx; + if (maxx - minx != nbbx.width) + nbbx.width = maxx - minx; + + if (miny > 0) + nbbx.ascent -= miny; + if (maxy - miny != nbbx.height) + nbbx.height = maxy - miny; + nbbx.descent = nbbx.height - nbbx.ascent; + nbbx.y_offset = -nbbx.descent; + + nbpr = ((nbbx.width * font->bpp) + 7) >> 3; + + /* + * If nothing changed, then the glyph is already contained in the + * minimum rectangle. + */ + if (memcmp((char *) &nbbx, (char *) &glyph->bbx, + sizeof(bdf_bbx_t)) == 0 || + (nbpr == bpr && nbbx.height == glyph->bbx.height)) + return; + + /* + * The metrics changed, so a new bitmap is needed. + */ + bytes = nbpr * nbbx.height; + bmap = (unsigned char *) malloc(bytes); + (void) memset((char *) bmap, 0, bytes); + + colx = minx * font->bpp; + for (y = miny; y < maxy; y++) { + for (col = x = minx; x < maxx; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = glyph->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Position the pixel in the byte if necessary. + */ + di = ((col - colx) & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + bmap[((y - miny) * nbpr) + ((col - colx) >> 3)] |= byte; + } + } + } + + if (glyph->bytes > 0) + free((char *) glyph->bitmap); + glyph->bytes = bytes; + glyph->bitmap = bmap; + + (void) memcpy((char *) &glyph->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); +} + +/* + * Pad a character-cell font glyph to match the bounds specified in the + * provided bounding box. + */ +void +_bdf_pad_cell(bdf_font_t *font, bdf_glyph_t *glyph, bdf_glyph_t *cell) +{ + bdf_bbx_t *bbx; + unsigned short si, di, sx, byte; + unsigned short x, y, dx, dy, bx, by, bpr, nbpr; + unsigned char *bmap, *masks; + + masks = 0; + switch (font->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + bbx = &font->bbx; + + if (glyph->bbx.width == bbx->width && glyph->bbx.height == bbx->height) { + /* + * The glyph is already positioned in the cell. Copy the bitmap + * and return. + */ + (void) memcpy((char *) cell->bitmap, (char *) glyph->bitmap, + cell->bytes); + return; + } + + /* + * Determine the X and Y location of the baseline. + */ + bx = MYABS(bbx->x_offset - glyph->bbx.x_offset); + by = (bbx->ascent + bbx->descent) + bbx->y_offset; + + bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3; + nbpr = ((bbx->width * font->bpp) + 7) >> 3; + + /* + * Set various cell values and clear the cell bitmap. + */ + bmap = cell->bitmap; + (void) memset((char *) bmap, 0, cell->bytes); + + for (dy = by - glyph->bbx.ascent, y = 0; y < glyph->bbx.height; + y++, dy++) { + for (dx = bx * font->bpp, sx = x = 0; x < glyph->bbx.width; + x++, dx += font->bpp, sx += font->bpp) { + si = (sx & 7) / font->bpp; + byte = glyph->bitmap[(y * bpr) + (sx >> 3)] & masks[si]; + if (byte) { + di = (dx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + bmap[(dy * nbpr) + (dx >> 3)] |= byte; + } + } + } +} + +static char *unix_eol = "\n"; +static char *dos_eol = "\r\n"; +static char *mac_eol = "\r"; + +void +bdf_save_font(FILE *out, bdf_font_t *font, bdf_options_t *opts, + bdf_callback_t callback, void *data) +{ + int len; + unsigned int i, j, bpr, pcnt; + double dw, ps, rx; + char *sp, *ep, *eol; + bdf_property_t *p; + bdf_glyph_t *c, *cp, cell; + bdf_callback_struct_t cb; + + if (font == 0) + return; + + eol = 0; + switch (opts->eol) { + case BDF_UNIX_EOL: eol = unix_eol; break; + case BDF_DOS_EOL: eol = dos_eol; break; + case BDF_MAC_EOL: eol = mac_eol; break; + } + + /* + * If the font is a character cell font, allocate some space for the + * bitmap. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) { + bpr = ((font->bbx.width * font->bpp) + 7) >> 3; + cell.bytes = bpr * font->bbx.height; + cell.bitmap = (unsigned char *) malloc(cell.bytes); + (void) memcpy((char *) &cell.bbx, (char *) &font->bbx, + sizeof(bdf_bbx_t)); + } + + /* + * Emit the header. + */ + fprintf(out, "STARTFONT 2.1%s", eol); + + /* + * Emit the comments. + */ + if (font->comments_len > 0) { + for (sp = font->comments; *sp; sp++) { + ep = sp; + while (*ep && *ep != '\n') + ep++; + len = (int) (ep - sp); + fprintf(out, "COMMENT %.*s%s", len, sp, eol); + sp = ep; + } + } + + /* + * Emit the font name. + */ + fprintf(out, "FONT %s%s", font->name, eol); + + /* + * Emit the size info. + */ + if (font->bpp == 1) + fprintf(out, "SIZE %d %d %d%s", font->point_size, + font->resolution_x, font->resolution_y, eol); + else + fprintf(out, "SIZE %d %d %d %hd%s", font->point_size, + font->resolution_x, font->resolution_y, font->bpp, eol); + + /* + * Emit the bounding box. + */ + fprintf(out, "FONTBOUNDINGBOX %hd %hd %hd %hd%s", + font->bbx.width, font->bbx.height, font->bbx.x_offset, + font->bbx.y_offset, eol); + + /* + * Emit the properties after counting how many are properties and + * how many are comments. + */ + for (i = pcnt = 0, p = font->props; i < font->props_used; i++, p++) { + if (memcmp(p->name, "COMMENT", 7) != 0) + pcnt++; + } + + fprintf(out, "STARTPROPERTIES %d%s", pcnt, eol); + for (i = 0, p = font->props; i < font->props_used; i++, p++) { + fprintf(out, "%s ", p->name); + if (p->format == BDF_ATOM) { + if (p->value.atom == 0) + fprintf(out, "\"\"%s", eol); + else + fprintf(out, "\"%s\"%s", p->value.atom, eol); + } else + fprintf(out, "%d%s", p->value.int32, eol); + } + + fprintf(out, "ENDPROPERTIES%s", eol); + + /* + * Emit the number of bitmaps in the font. + */ + fprintf(out, "CHARS %d%s", font->unencoded_used + font->glyphs_used, eol); + + /* + * Call the callback if it was passed to start the save. + */ + if (callback != 0) { + cb.reason = BDF_SAVE_START; + cb.total = font->unencoded_used + font->glyphs_used; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Emit the unencoded bitmaps. + */ + for (i = 0, cp = font->unencoded; i < font->unencoded_used; i++, cp++) { + /* + * If the font has character-cell spacing and the option to pad the + * glyphs to the size of the font bbx is set, then pad the glyph. + * Otherwise, crop the glyph to the minimum rectangle needed to hold + * the bitmap. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) { + /* + * Point at the temporary glyph structure and copy the necessary + * glyph info into it. + */ + c = &cell; + c->name = cp->name; + c->encoding = cp->encoding; + c->swidth = cp->swidth; + c->dwidth = cp->dwidth; + _bdf_pad_cell(font, cp, c); + } else { + c = cp; + _bdf_crop_glyph(font, c); + } + + /* + * If the font has monowidth or character-cell spacing, then assign + * the font monowidth field to the device width and recalculate the + * scalable width. + */ + if (font->spacing != BDF_PROPORTIONAL) { + c->dwidth = font->monowidth; + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) c->dwidth; + c->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + if (c->name == 0) + fprintf(out, "STARTCHAR unencoded%d%sENCODING -1%s", i, eol, eol); + else + fprintf(out, "STARTCHAR %s%sENCODING -1%s", c->name, eol, eol); + fprintf(out, "SWIDTH %hd 0%sDWIDTH %hd 0%s", + c->swidth, eol, c->dwidth, eol); + fprintf(out, "BBX %hd %hd %hd %hd%s", c->bbx.width, c->bbx.height, + c->bbx.x_offset, c->bbx.y_offset, eol); + fprintf(out, "BITMAP%s", eol); + bpr = ((c->bbx.width * font->bpp) + 7) >> 3; + for (j = 0; bpr != 0 && j < c->bytes; j++) { + if (j && j % bpr == 0) + fprintf(out, eol); + fprintf(out, "%02X", c->bitmap[j]); + } + /* + * Handle empty bitmaps like this. + */ + if (c->bbx.height > 0) + fprintf(out, eol); + fprintf(out, "ENDCHAR%s", eol); + + /* + * Call the callback if supplied. + */ + if (callback != 0) { + cb.reason = BDF_SAVING; + cb.current++; + (*callback)(&cb, data); + } + } + + /* + * Emit the other bitmaps. + */ + for (i = 0, cp = font->glyphs; i < font->glyphs_used; i++, cp++) { + /* + * If the font has character-cell spacing and the option to pad the + * glyphs to the size of the font bbx is set, then pad the glyph. + * Otherwise, crop the glyph to the minimum rectangle needed to hold + * the bitmap. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) { + /* + * Point at the temporary glyph structure and copy the necessary + * glyph info into it. + */ + c = &cell; + c->name = cp->name; + c->encoding = cp->encoding; + c->swidth = cp->swidth; + c->dwidth = cp->dwidth; + _bdf_pad_cell(font, cp, c); + } else { + c = cp; + _bdf_crop_glyph(font, c); + } + + /* + * If the font has monowidth or character-cell spacing, then assign + * the font monowidth field to the device width and recalculate the + * scalable width. + */ + if (font->spacing != BDF_PROPORTIONAL) { + c->dwidth = font->monowidth; + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) c->dwidth; + c->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + if (c->name == 0) + fprintf(out, "STARTCHAR char%d%sENCODING %d%s", + c->encoding, eol, c->encoding, eol); + else + fprintf(out, "STARTCHAR %s%sENCODING %d%s", + c->name, eol, c->encoding, eol); + fprintf(out, "SWIDTH %hd 0%sDWIDTH %hd 0%s", + c->swidth, eol, c->dwidth, eol); + fprintf(out, "BBX %hd %hd %hd %hd%s", c->bbx.width, c->bbx.height, + c->bbx.x_offset, c->bbx.y_offset, eol); + fprintf(out, "BITMAP%s", eol); + bpr = ((c->bbx.width * font->bpp) + 7) >> 3; + for (j = 0; bpr != 0 && j < c->bytes; j++) { + if (j && j % bpr == 0) + fprintf(out, eol); + fprintf(out, "%02X", c->bitmap[j]); + } + /* + * Handle empty bitmaps like this. + */ + if (c->bbx.height > 0) + fprintf(out, eol); + fprintf(out, "ENDCHAR%s", eol); + + /* + * Call the callback if supplied. + */ + if (callback != 0) { + cb.reason = BDF_SAVING; + cb.current++; + (*callback)(&cb, data); + } + } + + /* + * Emit the trailer. + */ + fprintf(out, "ENDFONT%s", eol); + + /* + * Always force a final call to the callback to make sure things + * get cleaned up. + */ + if (callback != 0) { + cb.reason = BDF_SAVING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + /* + * If the font is a character cell font, clean up the temporary glyph. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) + free((char *) cell.bitmap); +} + +/* + * Routine to write a single set of SBIT metrics. + */ +void +bdf_save_sbit_metrics(FILE *out, bdf_font_t *font, bdf_options_t *opts, + char *appname) +{ + char *eol; + + eol = 0; + switch (opts->eol) { + case BDF_UNIX_EOL: eol = unix_eol; break; + case BDF_DOS_EOL: eol = dos_eol; break; + case BDF_MAC_EOL: eol = mac_eol; break; + } + + /* + * Throw a simple header in. + */ + if (appname) + fprintf(out, ";%s; SBIT metrics file generated by \"%s\".%s;%s%s", eol, + appname, eol, eol, eol); + + /* + * Save PPEM. + */ + fprintf(out, ";%s; Pixels Per Em.%s;%s", eol, eol, eol); + fprintf(out, "PPEM %d%s%s", font->point_size, eol, eol); + + /* + * If the font is character cell or monowidth, set this boolean. + */ + if (font->spacing != BDF_PROPORTIONAL) { + fprintf(out, + ";%s; Font is not proportional, so use mono advance.%s;%s", + eol, eol, eol); + fprintf(out, "FORCECONSTANTMETRICS TRUE%s%s", eol, eol); + } else { + fprintf(out, + ";%s; Font is proportional, so do not use mono advance.%s;%s", + eol, eol, eol); + fprintf(out, "FORCECONSTANTMETRICS FALSE%s%s", eol, eol); + } + + /* + * Do the horizontal line metrics only. + */ + fprintf(out, ";%s; Horizontal line metrics.%s;%s", eol, eol, eol); + + fprintf(out, "H_ASCENDER %d%sH_DESCENDER %d%s", font->font_ascent, eol, + font->font_descent, eol); + fprintf(out, "H_WIDTHMAX %hd%s", font->bbx.width, eol); + fprintf(out, "H_MINORIGINSB %hd%sH_MINADVANCEBL %hd%s", + font->bbx.x_offset, eol, + font->bbx.width + font->bbx.x_offset, eol); + fprintf(out, "H_MAXBEFOREBL %hd%sH_MINAFTERBL %hd%s%s", + font->bbx.ascent, eol, font->bbx.y_offset, eol, eol); + + /* + * Write the default caret info. + */ + fprintf(out, ";%s; Caret slope and offset info.%s;%s", eol, eol, eol); + fprintf(out, "CARETSLOPENUMERATOR 1%sCARETSLOPEDENOMINATOR 0%s", eol, eol); + fprintf(out, "CARETOFFSET 0%s%s", eol, eol); + + /* + * Write the bitmap options. + */ + fprintf(out, ";%s; Bitmap options.%s;%s", eol, eol, eol); + fprintf(out, "DIRECTION H%sSTORAGE FAST%s%s", eol, eol, eol); + + /* + * Scaled bitmaps not implemented yet. + */ + fprintf(out, ";%s; Scaled bitmap info (Not Yet Implemented).%s;%s", + eol, eol, eol); +} + +/* + * Special routine to dump the font in the Roman Czyborra's hex format. It + * only dumps the encoded glyphs and assumes the bitmaps have the correct + * sizes. + */ +void +bdf_export_hex(FILE *out, bdf_font_t *font, bdf_options_t *opts, + bdf_callback_t callback, void *data) +{ + int bpr, fbpr, j, k; + unsigned int i, ng; + bdf_glyph_t *gp, cell; + bdf_callback_struct_t cb; + + if (font == 0 || out == 0) + return; + + if (font->glyphs_used == 0) + return; + + /* + * Call the callback if it was passed to start the export. + */ + if (callback != 0) { + cb.reason = BDF_EXPORT_START; + cb.total = font->glyphs_used; + cb.current = 0; + (*callback)(&cb, data); + } + + fbpr = ((font->bbx.width * font->bpp) + 7) >> 3; + bpr = (((font->bbx.width >> 1) * font->bpp) + 7) >> 3; + cell.bytes = fbpr * font->bbx.height; + cell.bitmap = (unsigned char *) malloc(cell.bytes); + + for (i = 0, ng = font->glyphs_used, gp = font->glyphs; i < ng; i++, gp++) { + _bdf_pad_cell(font, gp, &cell); + fprintf(out, "%04X:", gp->encoding & 0xffff); + if (gp->bbx.width <= (font->bbx.width >> 1)) { + for (j = 0; j < cell.bytes; j += fbpr) { + for (k = 0; k < bpr; k++) + fprintf(out, "%02X", cell.bitmap[j + k]); + } + } else { + for (j = 0; j < cell.bytes; j++) + fprintf(out, "%02X", cell.bitmap[j]); + } + if (cell.bytes > 0) + putc('\n', out); + + /* + * Call the callback if supplied. + */ + if (callback != 0) { + cb.reason = BDF_EXPORTING; + cb.current++; + (*callback)(&cb, data); + } + } + + /* + * Clean up the cell. + */ + free((char *) cell.bitmap); + + /* + * Always call a final callback to make sure the client gets a chance to + * clean things up. + */ + if (callback != 0) { + cb.reason = BDF_EXPORTING; + cb.current = cb.total; + (*callback)(&cb, data); + } +} + +void +bdf_free_font(bdf_font_t *font) +{ + unsigned int i; + bdf_glyph_t *glyphs; + + if (font == 0) + return; + + if (font->name != 0) + free(font->name); + + /* + * Free up the internal hash table of property names. + */ + hash_free((hashtable *) font->internal); + free((char *) font->internal); + + /* + * Free up the comment info. + */ + if (font->comments_len > 0) + free(font->comments); + + /* + * Free up the auto-correction messages. + */ + if (font->acmsgs_len > 0) + free(font->acmsgs); + + /* + * Free up the properties. + */ + for (i = 0; i < font->props_size; i++) { + if (font->props[i].format == BDF_ATOM && font->props[i].value.atom) + free(font->props[i].value.atom); + } + + if (font->props_size > 0 && font->props != 0) + free((char *) font->props); + + /* + * Free up the character info. + */ + for (i = 0, glyphs = font->glyphs; i < font->glyphs_used; i++, glyphs++) { + if (glyphs->name) + free(glyphs->name); + if (glyphs->bytes > 0 && glyphs->bitmap != 0) + free((char *) glyphs->bitmap); + } + + for (i = 0, glyphs = font->unencoded; i < font->unencoded_used; + i++, glyphs++) { + if (glyphs->name) + free(glyphs->name); + if (glyphs->bytes > 0) + free((char *) glyphs->bitmap); + if (glyphs->unicode.map_size > 0) + free((char *) glyphs->unicode.map); + } + + if (font->glyphs_size > 0) + free((char *) font->glyphs); + + if (font->unencoded_size > 0) + free((char *) font->unencoded); + + /* + * Free up the overflow storage if it was used. + */ + for (i = 0, glyphs = font->overflow.glyphs; i < font->overflow.glyphs_used; + i++, glyphs++) { + if (glyphs->name != 0) + free(glyphs->name); + if (glyphs->bytes > 0) + free((char *) glyphs->bitmap);; + if (glyphs->unicode.map_size > 0) + free((char *) glyphs->unicode.map); + } + if (font->overflow.glyphs_size > 0) + free((char *) font->overflow.glyphs); + + free((char *) font); +} + +void +bdf_create_property(char *name, int format) +{ + unsigned int n; + bdf_property_t *p; + + /* + * First check to see if the property has + * already been added or not. If it has, then + * simply ignore it. + */ + + if (hash_lookup(name, &proptbl)) + return; + + if (nuser_props == 0) + user_props = (bdf_property_t *) malloc(sizeof(bdf_property_t)); + else + user_props = (bdf_property_t *) realloc((char *) user_props, + sizeof(bdf_property_t) * + (nuser_props + 1)); + + p = user_props + nuser_props; + (void) memset((char *) p, 0, sizeof(bdf_property_t)); + n = (unsigned int) (strlen(name) + 1); + p->name = (char *) malloc(n); + (void) memcpy(p->name, name, n); + p->format = format; + p->builtin = 0; + + n = _num_bdf_properties + nuser_props; + hash_insert(p->name, (void *) n, &proptbl); + + nuser_props++; +} + +bdf_property_t * +bdf_get_property(char *name) +{ + hashnode hn; + unsigned int propid; + + if (name == 0 || *name == 0) + return 0; + + if ((hn = hash_lookup(name, &proptbl)) == 0) + return 0; + + propid = (unsigned int) hn->data; + if (propid >= _num_bdf_properties) + return user_props + (propid - _num_bdf_properties); + return _bdf_properties + propid; +} + +/* + * Routine to compare two property names. + */ +static int +by_prop_name(const void *a, const void *b) +{ + bdf_property_t *p1, *p2; + + p1 = (bdf_property_t *) a; + p2 = (bdf_property_t *) b; + + return strcmp(p1->name, p2->name); +} + +unsigned int +bdf_property_list(bdf_property_t **props) +{ + unsigned int n; + bdf_property_t *p; + + n = _num_bdf_properties + nuser_props; + if (props != 0 && n != 0) { + p = (bdf_property_t *) malloc(sizeof(bdf_property_t) * n); + (void) memcpy((char *) p, (char *) _bdf_properties, + sizeof(bdf_property_t) * _num_bdf_properties); + (void) memcpy((char *) (p + _num_bdf_properties), (char *) user_props, + sizeof(bdf_property_t) * nuser_props); + qsort((char *) p, n, sizeof(bdf_property_t), by_prop_name); + *props = p; + } + return n; +} + +int +bdf_replace_comments(bdf_font_t *font, char *comments, + unsigned int comments_len) +{ + if (font == 0 || comments_len == 0) + return 0; + + if (font->comments_len > 0) + free(font->comments); + + font->comments = (char *) malloc(comments_len + 1); + (void) memcpy(font->comments, comments, comments_len); + font->comments[comments_len] = 0; + font->comments_len = comments_len; + font->modified = 1; + return 1; +} + +unsigned int +bdf_font_property_list(bdf_font_t *font, bdf_property_t **props) +{ + bdf_property_t *p; + + if (font == 0 || font->props_used == 0) + return 0; + + if (props != 0) { + p = (bdf_property_t *) malloc(sizeof(bdf_property_t) * + font->props_used); + (void) memcpy((char *) p, (char *) font->props, + sizeof(bdf_property_t) * font->props_used); + qsort((char *) p, font->props_used, sizeof(bdf_property_t), + by_prop_name); + *props = p; + } + + return font->props_used; +} + +void +bdf_add_font_property(bdf_font_t *font, bdf_property_t *property) +{ + int len; + unsigned int propid; + hashnode hn; + bdf_property_t *p, *ip; + + if (property == 0 || property->name == 0 || property->name[0] == 0) + return; + + /* + * If the font does not have a property hash table yet, make + * sure it is allocated. + */ + if (font->internal == 0) { + font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) font->internal); + } + + /* + * See if the property is in the general property table yet. + * If it isn't, then add it. + */ + if ((hn = hash_lookup(property->name, &proptbl)) == 0) + bdf_create_property(property->name, property->format); + else { + /* + * If the property exists and is a user defined property, make sure + * its format is updated to match the property being added. + */ + propid = (unsigned int) hn->data; + if (propid >= _num_bdf_properties) { + p = user_props + (propid - _num_bdf_properties); + if (p->format != property->format) + p->format = property->format; + } + } + + /* + * If the font already has this property, then change the existing one. + */ + hn = hash_lookup(property->name, (hashtable *) font->internal); + if (hn != 0) { + /* + * Changing an existing property value. + */ + p = font->props + ((unsigned int) hn->data); + + /* + * If the format changed, then free the atom value if the original + * format was an atom. + */ + if (p->format == BDF_ATOM && property->format != BDF_ATOM && + p->value.atom != 0) + free((char *) p->value.atom); + p->format = property->format; + + switch (p->format) { + case BDF_ATOM: + /* + * If the property value is the same, then just return. + */ + if (property->value.atom == p->value.atom || + (property->value.atom && p->value.atom && + strcmp(property->value.atom, p->value.atom) == 0)) + return; + if (property->value.atom == 0) + len = 1; + else + len = strlen(property->value.atom) + 1; + if (len > 1) { + p->value.atom = (char *) malloc(len); + (void) memcpy(p->value.atom, property->value.atom, len); + } else + p->value.atom = 0; + break; + case BDF_INTEGER: + /* + * If the property value is the same, then just return. + */ + if (p->value.int32 == property->value.int32) + return; + p->value.int32 = property->value.int32; + break; + case BDF_CARDINAL: + /* + * If the property value is the same, then just return. + */ + if (p->value.card32 == property->value.card32) + return; + p->value.card32 = property->value.card32; + break; + } + } else { + /* + * New property being added. + */ + + /* + * Get the internal table entry for a pointer to the + * name of the property. + */ + hn = hash_lookup(property->name, &proptbl); + propid = (unsigned int) hn->data; + if (propid >= _num_bdf_properties) + ip = user_props + (propid - _num_bdf_properties); + else + ip = _bdf_properties + propid; + + /* + * Add it to the property list first. + */ + if (font->props_used == font->props_size) { + if (font->props_size == 0) + font->props = (bdf_property_t *) malloc(sizeof(bdf_property_t)); + else + font->props = (bdf_property_t *) + realloc((char *) font->props, sizeof(bdf_property_t) * + (font->props_size + 1)); + font->props_size++; + } + p = font->props + font->props_used; + + p->name = ip->name; + p->format = ip->format; + p->builtin = ip->builtin; + + switch (p->format) { + case BDF_ATOM: + if (property->value.atom == 0) + len = 1; + else + len = strlen(property->value.atom) + 1; + if (len > 1) { + p->value.atom = (char *) malloc(len); + (void) memcpy(p->value.atom, property->value.atom, len); + } else + p->value.atom = 0; + break; + case BDF_INTEGER: + p->value.int32 = property->value.int32; + break; + case BDF_CARDINAL: + p->value.card32 = property->value.card32; + break; + } + + /* + * Now insert it into the internal hash table. + */ + hash_insert(p->name, (void *) font->props_used, + (hashtable *) font->internal); + font->props_used++; + } + + if (memcmp(property->name, "DEFAULT_CHAR", 12) == 0) + /* + * If the property just added is DEFAULT_CHAR, then make sure the + * default_glyph field is set. + */ + font->default_glyph = p->value.card32; + else if (memcmp(property->name, "FONT_ASCENT", 11) == 0) + /* + * If the property just added is FONT_ASCENT, then adjust the + * font_ascent field. + */ + font->font_ascent = p->value.int32; + else if (memcmp(property->name, "FONT_DESCENT", 12) == 0) + /* + * If the property just added is FONT_DESCENT, then adjust the + * font_descent field. + */ + font->font_descent = p->value.int32; + else if (memcmp(property->name, "RESOLUTION_X", 12) == 0) + /* + * If the property just added is RESOLUTION_X, then adjust the + * resolution_x field. + */ + font->resolution_x = p->value.card32; + else if (memcmp(property->name, "RESOLUTION_Y", 12) == 0) + /* + * If the property just added is RESOLUTION_Y, then adjust the + * resolution_y field. + */ + font->resolution_y = p->value.card32; + else if (memcmp(property->name, "POINT_SIZE", 10) == 0) + /* + * If the property just added is POINT_SIZE, then adjust the + * point_size field. + */ + font->point_size = p->value.int32 / 10; + else if (memcmp(property->name, "SPACING", 7) == 0) { + /* + * Make sure the font spacing is kept in synch if the property + * changes. If the spacing changes from proportional to one + * of the others, force the monowidth to be set. + */ + switch (p->value.atom[0]) { + case 'C': case 'c': + if (font->spacing == BDF_PROPORTIONAL) + font->monowidth = font->bbx.width + font->bbx.x_offset; + font->spacing = BDF_CHARCELL; + break; + case 'M': case 'm': + if (font->spacing == BDF_PROPORTIONAL) + font->monowidth = font->bbx.width + font->bbx.x_offset; + font->spacing = BDF_MONOWIDTH; + break; + case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break; + } + } + + /* + * Make sure the font is marked as modified. + */ + font->modified = 1; +} + +void +bdf_delete_font_property(bdf_font_t *font, char *name) +{ + hashnode hn; + unsigned int off; + bdf_property_t *p; + + if (font == 0 || name == 0 || *name == 0 || font->props_used == 0) + return; + + if ((hn = hash_lookup(name, (hashtable *) font->internal)) == 0) + return; + + off = (unsigned int) hn->data; + p = font->props + off; + + /* + * Delete the ATOM value if appropriate. + */ + if (p->format == BDF_ATOM && p->value.atom != 0) + free(p->value.atom); + + /* + * The property exists. Two things needs to be done: + * 1. Remove the property from the hash table. + * 2. Remove the property from the font's list of properties. + */ + hash_delete(name, (hashtable *) font->internal); + + /* + * Locate its offset in the font property list. + */ + if (off < font->props_used - 1) + /* + * We have to shift the property list down. + */ + _bdf_memmove((char *) p, (char *) (p + 1), + sizeof(bdf_property_t) * ((font->props_used - 1) - off)); + font->props_used--; + + /* + * If the font property happens to be DEFAULT_CHAR, then make sure the + * default_glyph field is reset. + */ + if (strncmp(name, "DEFAULT_CHAR", 12) == 0) + font->default_glyph = -1; + + /* + * Update the hash table with the correct indexes. + */ + for (off = 0, p = font->props; off < font->props_used; off++, p++) + hash_insert(p->name, (void *) off, (hashtable *) font->internal); + + /* + * Mark the font as being modified. + */ + font->modified = 1; +} + +bdf_property_t * +bdf_get_font_property(bdf_font_t *font, char *name) +{ + hashnode hn; + + if (font == 0 || font->props_size == 0 || name == 0 || *name == 0) + return 0; + + hn = hash_lookup(name, (hashtable *) font->internal); + return (hn) ? (font->props + ((unsigned int) hn->data)) : 0; +} + +typedef struct { + bdf_options_t *opts; + bdf_options_callback_t callback; + void *client_data; + _bdf_list_t list; +} _bdf_opts_parse_t; + +static int +_bdf_get_boolean(char *val) +{ + int ok; + + ok = 0; + if (val == 0 || *val == 0) + return ok; + + switch (val[0]) { + case '0': case 'F': case 'f': case 'N': case 'n': ok = 0; break; + case '1': case 'T': case 't': case 'Y': case 'y': ok = 1; break; + } + return ok; +} + +static int +_bdf_parse_options(char *line, unsigned int linelen, unsigned int lineno, + void *call_data, void *client_data) +{ + _bdf_list_t *lp; + _bdf_opts_parse_t *p; + int bpp; + + p = (_bdf_opts_parse_t *) client_data; + lp = &p->list; + + /* + * Split the line into fields. + */ + _bdf_split(" \t+", line, linelen, lp); + + if (lp->field[0][0] == 'b' && + memcmp(lp->field[0], "bits_per_pixel", 14) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %d: incorrect number of fields %d.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %d: bits_per_pixel <1, 2, or 4>.\n", + lineno); + } else { + bpp = _bdf_atol(lp->field[1], 0, 10); + if (!(bpp == 1 || bpp == 2 || bpp == 4)) { + fprintf(stderr, + "bdf: warning: %d: invalid bits per pixel %d.\n", + lineno, bpp); + fprintf(stderr, + "bdf: warning: %d: bits_per_pixel <1, 2, or 4>.\n", + lineno); + } else + p->opts->bits_per_pixel = bpp; + } + return 0; + } + + if (lp->field[0][0] == 'e' && memcmp(lp->field[0], "eol", 3) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %d: incorrect number of fields %d.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %d: eol .\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 .\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 .\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 .\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 .\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 .\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 .\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 .\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 .\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 .\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 .\n", + lineno); + } else { +#ifdef HAVE_FREETYPE + if (_bdf_get_boolean(lp->field[1])) + p->opts->otf_flags &= ~FT_LOAD_NO_HINTING; + else + p->opts->otf_flags |= FT_LOAD_NO_HINTING; +#else + p->opts->otf_flags = 0; +#endif /* HAVE_FREETYPE */ + } + + return 0; + } + + if (lp->field[0][0] == 'g' && + memcmp(lp->field[0], "generate_ranges", 15) == 0) + /* + * Simply ignore the glyph ranges entry in the config file. + */ + return 0; + + /* + * If the callback returns a non-zero value, the caller has handled the + * unknown option found in the file. + */ + if (p->callback != 0 && + (*p->callback)(p->opts, lp->field, lp->used, p->client_data) != 0) + return 0; + + fprintf(stderr, "bdf: warning: %d: unknown configuration option '%s'.\n", + lineno, lp->field[0]); + return 0; +} + +void +bdf_load_options(FILE *in, bdf_options_t *opts, + bdf_options_callback_t callback, void *client_data) +{ + unsigned int lineno; + _bdf_opts_parse_t p; + + /* + * Don't bother loading the options if the file or options structure + * is NULL. + */ + if (in == 0 || opts == 0) + return; + + (void *) memset((char *) &p, 0, sizeof(_bdf_opts_parse_t)); + p.opts = opts; + p.callback = callback; + p.client_data = client_data; + (void) _bdf_readlines(fileno(in), _bdf_parse_options, (void *) &p, + &lineno); + + /* + * Free up the list if there is any space allocated. + */ + if (p.list.size > 0) + free((char *) p.list.field); +} + +void +bdf_save_options(FILE *out, bdf_options_t *opts) +{ + unsigned int i; + + if (out == 0 || opts == 0) + return; + + fprintf(out, "#\n# Metrics corrections.\n#\ncorrect_metrics "); + if (opts->correct_metrics) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Preserve unencoded glyphs.\n#\nkeep_unencoded "); + if (opts->keep_unencoded) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Preserve comments.\n#\nkeep_comments "); + if (opts->keep_comments) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Pad character cells.\n#\npad_character_cells "); + if (opts->pad_cells) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Font spacing.\n#\nfont_spacing "); + switch (opts->font_spacing) { + case BDF_PROPORTIONAL: fprintf(out, "proportional\n\n"); break; + case BDF_MONOWIDTH: fprintf(out, "monowidth\n\n"); break; + case BDF_CHARCELL: fprintf(out, "charactercell\n\n"); break; + } + + fprintf(out, "#\n# Point size.\n#\npoint_size %d\n\n", opts->point_size); + + fprintf(out, + "#\n# Horizontal resolution.\n#\nhorizontal_resolution %d\n\n", + opts->resolution_x); + + fprintf(out, + "#\n# Vertical resolution.\n#\nvertical_resolution %d\n\n", + opts->resolution_x); + + fprintf(out, + "#\n# Bits per pixel.\n#\nbits_per_pixel %d\n\n", + opts->bits_per_pixel); + + fprintf(out, "#\n# Hint OpenType glyphs.\n#\nhint_opentype_glyphs "); +#ifdef HAVE_FREETYPE + if (opts->otf_flags & FT_LOAD_NO_HINTING) + fprintf(out, "false\n\n"); + else + fprintf(out, "true\n\n"); +#else + fprintf(out, "false\n\n"); +#endif /* HAVE_FREETYPE */ + + fprintf(out, "#\n# Set the EOL used when writing BDF fonts.\n#\neol "); + switch (opts->eol) { + case BDF_UNIX_EOL: fprintf(out, "unix\n\n"); break; + case BDF_DOS_EOL: fprintf(out, "dos\n\n"); break; + case BDF_MAC_EOL: fprintf(out, "mac\n\n"); break; + } + + /* + * Write out the user defined properties if they exist. + */ + if (nuser_props == 0) + return; + + fprintf(out, "#\n# User defined properties.\n#\n"); + + for (i = 0; i < nuser_props; i++) { + fprintf(out, "property %s ", user_props[i].name); + switch (user_props[i].format) { + case BDF_ATOM: fprintf(out, "atom\n"); break; + case BDF_CARDINAL: fprintf(out, "cardinal\n"); break; + case BDF_INTEGER: fprintf(out, "integer\n"); break; + } + } +} + +void +bdf_default_options(bdf_options_t *opts) +{ + if (opts == 0) + return; + + (void) memcpy((char *) opts, (char *) &_bdf_opts, sizeof(bdf_options_t)); +} + +bdf_font_t * +bdf_new_font(char *name, int point_size, int resolution_x, int resolution_y, + int spacing, int bpp) +{ + int psize; + char sp[2]; + bdf_font_t *font; + double dp, dr; + bdf_property_t prop; + + font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + if (name != 0 && *name != 0) { + font->name = (char *) malloc(strlen(name) + 1); + (void) strcpy(font->name, name); + } + + font->bpp = bpp; + font->point_size = point_size; + font->resolution_x = resolution_x; + font->resolution_y = resolution_y; + + /* + * Determine the pixel size of the new font based on the + * point size and resolution. + */ + dr = (double) resolution_y; + dp = (double) (point_size * 10); + psize = (int) (((dp * dr) / 722.7) + 0.5); + + /* + * Make the default width about 1.5 smaller than the height. + */ + font->bbx.height = psize; + font->bbx.width = ((double) psize) / 1.5; + + /* + * Now determine the default ascent and descent assuming a + * the descent is about 1/4 the ascent. + */ + font->bbx.descent = psize >> 2; + font->bbx.ascent = psize - font->bbx.descent; + + font->bbx.y_offset = -font->bbx.descent; + + /* + * Allocation the internal hash tables. + */ + font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) font->internal); + + font->default_glyph = -1; + font->spacing = spacing; + + /* + * Add various useful properties. + */ + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->point_size * 10; + bdf_add_font_property(font, &prop); + + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = psize; + bdf_add_font_property(font, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) font->resolution_x; + bdf_add_font_property(font, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) font->resolution_y; + bdf_add_font_property(font, &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) font->bbx.ascent; + bdf_add_font_property(font, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) font->bbx.descent; + bdf_add_font_property(font, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->bbx.width * 10; + bdf_add_font_property(font, &prop); + + sp[0] = 'P'; + sp[1] = 0; + switch (spacing) { + case BDF_PROPORTIONAL: sp[0] = 'P'; break; + case BDF_MONOWIDTH: sp[0] = 'M'; break; + case BDF_CHARCELL: sp[0] = 'C'; break; + } + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = sp; + bdf_add_font_property(font, &prop); + + /* + * Mark the font as unmodified. + */ + font->modified = 0; + + return font; +} + +void +bdf_set_default_metrics(bdf_font_t *font) +{ + int psize; + double dp, dr; + bdf_property_t prop; + + /* + * Determine the pixel size of the new font based on the + * point size and resolution. + */ + dr = (double) font->resolution_y; + dp = (double) (font->point_size * 10); + psize = (int) (((dp * dr) / 722.7) + 0.5); + + /* + * Make the default width about 1.5 smaller than the height. + */ + font->bbx.height = psize; + font->bbx.width = ((double) psize) / 1.5; + + /* + * Now determine the default ascent and descent assuming a + * the descent is about 1/4 the ascent. + */ + font->bbx.descent = psize >> 2; + font->bbx.ascent = psize - font->bbx.descent; + + font->bbx.y_offset = -font->bbx.descent; + + font->default_glyph = -1; + + /* + * Add various useful properties. + */ + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) font->bbx.ascent; + bdf_add_font_property(font, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) font->bbx.descent; + bdf_add_font_property(font, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->bbx.width * 10; + bdf_add_font_property(font, &prop); +} + +int +bdf_glyph_modified(bdf_font_t *font, int which, int unencoded) +{ + if (font == 0 || which < 0) + return 0; + + if (unencoded) + return _bdf_glyph_modified(font->umod, which); + else + return _bdf_glyph_modified(font->nmod, which); +} + +void +bdf_copy_glyphs(bdf_font_t *font, int start, int end, + bdf_glyphlist_t *glyphs, int unencoded) +{ + int tmp, i, nc; + bdf_glyph_t *cp, *dp; + short maxas, maxds, maxrb, minlb, maxlb, rb; + + if (start > end) { + tmp = end; + end = start; + start = tmp; + } + + glyphs->bpp = font->bpp; + glyphs->start = start; + glyphs->end = end; + glyphs->glyphs_used = 0; + + tmp = (end - start) + 1; + if (tmp > glyphs->glyphs_size) { + if (glyphs->glyphs_size == 0) + glyphs->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * tmp); + else + glyphs->glyphs = (bdf_glyph_t *) realloc((char *) glyphs->glyphs, + sizeof(bdf_glyph_t) * tmp); + cp = glyphs->glyphs + glyphs->glyphs_size; + (void) memset((char *) cp, 0, + sizeof(bdf_glyph_t) * (tmp - glyphs->glyphs_size)); + glyphs->glyphs_size = tmp; + } + + /* + * Clear out bitmaps, names and any PSF Unicode mappings in the existing + * entries. + */ + for (cp = glyphs->glyphs, i = 0; i < glyphs->glyphs_size; i++, cp++) { + if (cp->name != 0) + free(cp->name); + if (cp->bytes > 0) + free((char *) cp->bitmap); + if (cp->unicode.map_size > 0) + free((char *) cp->unicode.map); + } + + /* + * Zero out everything. + */ + (void) memset((char *) &glyphs->bbx, 0, sizeof(bdf_bbx_t)); + (void) memset((char *) glyphs->glyphs, 0, + sizeof(bdf_glyph_t) * glyphs->glyphs_size); + + /* + * Initialize the bounds used to generate the overall bounding box for the + * set of glyphs being copied. + */ + minlb = font->bbx.width; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Do the copy. + */ + nc = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + cp = (unencoded == 0) ? font->glyphs : font->unencoded; + dp = glyphs->glyphs; + + for (i = 0; + i < nc && ((unencoded && i <= end) || cp->encoding <= end); + i++, cp++) { + if ((unencoded && i >= start) || cp->encoding >= start) { + (void) memcpy((char *) dp, (char *) cp, sizeof(bdf_glyph_t)); + if (cp->name != 0) { + dp->name = (char *) malloc(strlen(cp->name) + 1); + (void) strcpy(dp->name, cp->name); + } + if (cp->bytes > 0) { + dp->bytes = cp->bytes; + dp->bitmap = (unsigned char *) malloc(cp->bytes); + (void) memcpy((char *) dp->bitmap, (char *) cp->bitmap, + cp->bytes); + } + if (cp->unicode.map_used > 0) { + dp->unicode.map_used = dp->unicode.map_size = + cp->unicode.map_used; + dp->unicode.map = + (unsigned char *) malloc(dp->unicode.map_used); + (void) memcpy((char *) dp->unicode.map, + (char *) cp->unicode.map, + dp->unicode.map_used); + } + + /* + * Determine the overall metrics for the group of characters being + * copied. + */ + maxas = MAX(cp->bbx.ascent, maxas); + maxds = MAX(cp->bbx.descent, maxds); + rb = cp->bbx.width + cp->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(cp->bbx.x_offset, minlb); + maxlb = MAX(cp->bbx.x_offset, maxlb); + + glyphs->glyphs_used++; + dp++; + } + } + + /* + * Set the overall metrics for this set of glyphs. + */ + glyphs->bbx.width = maxrb - minlb; + glyphs->bbx.x_offset = minlb; + + glyphs->bbx.height = maxas + maxds; + glyphs->bbx.ascent = maxas; + glyphs->bbx.descent = maxds; + glyphs->bbx.y_offset = -maxds; +} + +bdf_glyph_t * +_bdf_locate_glyph(bdf_font_t *font, int code, int unencoded) +{ + int l, r, m, nc; + bdf_glyph_t *gl; + + if (code < 0 || font == 0) + return 0; + + if ((unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + if (unencoded) { + gl = font->unencoded; + nc = font->unencoded_used; + } else { + gl = font->glyphs; + nc = font->glyphs_used; + } + for (l = m = 0, r = nc - 1; l < r; ) { + m = (l + r) >> 1; + if (gl[m].encoding < code) + l = m + 1; + else if (gl[m].encoding > code) + r = m - 1; + else + break; + } + + /* + * Go back until we hit the beginning of the glyphs or until + * we find the glyph with a code less than the specified code. + */ + l = m; + while (m > 0 && gl[m].encoding > code) + m--; + + /* + * Look forward if necessary. + */ + m = l; + while (m < nc && gl[m].encoding < code) + m++; + + return (m < nc) ? &gl[m] : &gl[nc - 1]; +} + +int +bdf_delete_glyphs(bdf_font_t *font, int start, int end, int unencoded) +{ + int i, n, nc, cnt, mod; + bdf_glyph_t *cp, *sp, *ep; + + mod = 0; + + if (font == 0) + return mod; + + if (start > end) { + cnt = end; + end = start; + start = cnt; + } + + nc = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + cp = (unencoded == 0) ? font->glyphs : font->unencoded; + sp = ep = 0; + + for (i = 0; i < nc && cp->encoding <= end; i++, cp++) { + if (cp->encoding >= start && sp == 0) + sp = cp; + } + ep = cp; + if (sp == 0) + sp = ep; + + if (ep > sp) { + /* + * There are some glyphs to delete. + * 1. Free the name and bitmap fields of the glyphs being deleted. + * 2. Move the end range down if necessary. + * 3. Clear the glyphs on the end if a move was done. + */ + + /* + * Mark the font as being modified. + */ + mod = font->modified = 1; + + cnt = ep - sp; + + for (cp = sp; cp < ep; cp++) { + /* + * Mark the glyphs being deleted as also being modified so the + * empty cells can be shown correctly by the client programs. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, cp->encoding); + else + _bdf_set_glyph_modified(font->nmod, cp->encoding); + + if (cp->name != 0) + free(cp->name); + if (cp->bytes > 0) + free((char *) cp->bitmap); + } + + cp = (unencoded == 0) ? font->glyphs : font->unencoded; + + /* + * Check to see if there are some glyphs that need to + * be moved down. + */ + if (ep - cp < nc) { + /* + * Shift the glyphs down. + */ + n = nc - (ep - cp); + _bdf_memmove((char *) sp, (char *) ep, sizeof(bdf_glyph_t) * n); + + /* + * Set the starting point for the clear. + */ + ep = sp + n; + } else + /* + * Set the starting point for the clear. + */ + ep = sp; + + /* + * Clear the glyph space just moved. + */ + n = nc - (ep - cp); + (void) memset((char *) ep, 0, sizeof(bdf_glyph_t) * n); + + /* + * Adjust the number of glyphs used. + */ + if (unencoded == 0) + font->glyphs_used -= cnt; + else + font->unencoded_used -= cnt; + + /* + * If unencoded glyphs were deleted, re-encode all + * of them to cause a shift when everything is redrawn. + */ + if (unencoded != 0) { + for (i = 0, cp = font->unencoded; i < font->unencoded_used; + i++, cp++) { + if (_bdf_glyph_modified(font->umod, cp->encoding)) { + _bdf_clear_glyph_modified(font->umod, cp->encoding); + _bdf_set_glyph_modified(font->umod, i); + } + cp->encoding = i; + } + } + } + return mod; +} + +/* + * These values are intended to give pixels mapped from 1bpp to nbpp the + * darkest available index, which is 1. + */ +static unsigned char twobpp_ones[] = {0x40, 0x10, 0x04, 0x01}; +static unsigned char fourbpp_ones[] = {0x10, 0x01}; +static unsigned char eightbpp_ones[] = {0x01}; + +/* + * Routines for quick and dirty dithering. + */ +static void +_bdf_one_to_n(bdf_glyphlist_t *gl, int n) +{ + int i; + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *ones = 0; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + switch (n) { + case 1: ones = bdf_onebpp; break; + case 2: ones = twobpp_ones; break; + case 4: ones = fourbpp_ones; break; + case 8: ones = eightbpp_ones; break; + } + + gl->bpp = n; + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = (gp->bbx.width + 7) >> 3; + bpr = ((gp->bbx.width * n) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += n) { + if (gp->bitmap[(sy * sbpr) + (sx >> 3)] & (0x80 >> (sx & 7))) + nbmap[(sy * bpr) + (col >> 3)] |= ones[(col & 7) / n]; + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } +} + +static void +_bdf_n_to_one(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = 0; + switch (gl->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = ((gp->bbx.width * gl->bpp) + 7) >> 3; + bpr = (gp->bbx.width + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += gl->bpp) { + if (gp->bitmap[(sy * sbpr) + (col >> 3)] & + masks[(col & 7) / gl->bpp]) + nbmap[(sy * bpr) + (sx >> 3)] |= (0x80 >> (sx & 7)); + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 1; +} + +static void +_bdf_two_to_four(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = bdf_twobpp; + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = ((gp->bbx.width << 1) + 7) >> 3; + bpr = ((gp->bbx.width << 2) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) { + si = (col & 7) >> 1; + byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + if (si < 3) + byte >>= (3 - si) << 1; + byte <<= 2; + if ((sx & 1) == 0) + byte <<= 4; + nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 4; +} + +static void +_bdf_four_to_two(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = bdf_fourbpp; + + gl->bpp = 2; + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + sbpr = ((gp->bbx.width << 2) + 7) >> 3; + bpr = ((gp->bbx.width << 1) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 4) { + si = (col & 7) >> 2; + byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si == 0) + byte >>= 4; + + /* + * Scale the index to two bits per pixel and shift it into + * place if necessary. + */ + byte >>= 2; + /* + * Any non-zero byte has to remain non-zero, because index + * zero means no bits set. + */ + if (byte == 0) + byte = 1; + + si = ((sx << 1) & 7) >> 1; + if (si < 3) + byte <<= (3 - si) << 1; + + nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } +} + +static void +_bdf_two_to_eight(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = bdf_twobpp; + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = ((gp->bbx.width << 1) + 7) >> 3; + bpr = gp->bbx.width; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) { + si = (col & 7) >> 1; + byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si < 3) + byte >>= (3 - si) * gl->bpp; + + /* + * Scale the index to four bits per pixel and shift it into + * place before adding it. + */ + byte <<= 6; + nbmap[(sy * bpr) + sx] = byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 8; +} + +static void +_bdf_eight_to_two(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = bdf_fourbpp; + + gl->bpp = 2; + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + sbpr = gp->bbx.width; + bpr = ((gp->bbx.width << 1) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (sx = 0; sx < gp->bbx.width; sx++) { + byte = gp->bitmap[(sy * sbpr) + sx]; + if (byte) { + byte >>= 6; + if (byte == 0) + byte = 1; + + si = ((sx << 1) & 7) >> 1; + if (si < 3) + byte <<= (3 - si) << 1; + + nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } +} + +static void +_bdf_four_to_eight(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = bdf_fourbpp; + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = ((gp->bbx.width << 2) + 7) >> 3; + bpr = gp->bbx.width; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 4) { + si = (col & 7) >> 2; + byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + if (si == 0) + byte >>= 4; + + byte <<= 4; + nbmap[(sy * bpr) + sx] = byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 8; +} + +static void +_bdf_eight_to_four(bdf_glyphlist_t *gl) +{ + int i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = bdf_twobpp; + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = gp->bbx.width; + bpr = ((gp->bbx.width << 2) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) { + byte = gp->bitmap[(sy * sbpr) + sx]; + if (byte) { + byte >>= 4; + if (byte == 0) + byte = 1; + + /* + * Scale the index to four bits per pixel and shift it into + * place before adding it. + */ + si = (col & 7) >> 2; + if (si == 0) + byte <<= 4; + nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 4; +} + +/* + * This only works on glyphs that exist. + */ +int +bdf_replace_mappings(bdf_font_t *font, int encoding, bdf_psf_unimap_t *map, + int unencoded) +{ + bdf_glyph_t *gp; + + if ((gp = _bdf_locate_glyph(font, encoding, unencoded)) == 0 || + gp->encoding != encoding) + return 0; + + if (map->map_size > gp->unicode.map_size) { + if (gp->unicode.map_size == 0) + gp->unicode.map = (unsigned char *) + malloc(sizeof(unsigned char) * map->map_size); + else + gp->unicode.map =(unsigned char *) + realloc((char *) gp->unicode.map, + sizeof(unsigned char) * map->map_size); + gp->unicode.map_size = map->map_size; + } + gp->unicode.map_used = map->map_used; + (void) memcpy((char *) gp->unicode.map, (char *) map->map, + sizeof(unsigned char) * map->map_used); + + /* + * Mark the glyph as modified. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, gp->encoding); + else + _bdf_set_glyph_modified(font->nmod, gp->encoding); + + font->modified = 1; + + return 1; +} + +int +bdf_replace_glyphs(bdf_font_t *font, int start, bdf_glyphlist_t *glyphs, + int unencoded) +{ + int resize, appending; + int i, n, ng, end, del, remaining, off[2]; + bdf_glyph_t *sgp, *gp, *dgp; + short maxas, maxds, maxrb, minlb, maxlb, rb; + double ps, rx, dw; + bdf_bbx_t nbbx; + + resize = 0; + + if (font == 0) + return resize; + + /* + * Dither the incoming bitmaps so they match the same bits per pixel as + * the font. + */ + if (glyphs->bpp != font->bpp) { + if (glyphs->bpp == 1) + _bdf_one_to_n(glyphs, font->bpp); + else if (font->bpp == 1) + _bdf_n_to_one(glyphs); + else if (glyphs->bpp == 2) { + if (font->bpp == 4) + _bdf_two_to_four(glyphs); + else + _bdf_two_to_eight(glyphs); + } else if (glyphs->bpp == 4) { + if (font->bpp == 2) + _bdf_four_to_two(glyphs); + else + _bdf_four_to_eight(glyphs); + } else if (glyphs->bpp == 8) { + if (font->bpp == 2) + _bdf_eight_to_two(glyphs); + else + _bdf_eight_to_four(glyphs); + } + } + + /* + * Set the point size and horizontal resolution so the scalable width can + * be determined. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + + /* + * Determine if a resize is needed. + */ + + /* + * Determine the bounding box for the font without the characters being + * replaced. + */ + minlb = 32767; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Get the font bounds. + */ + maxas = MAX(font->bbx.ascent, maxas); + maxds = MAX(font->bbx.descent, maxds); + rb = font->bbx.width + font->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(font->bbx.x_offset, minlb); + maxlb = MAX(font->bbx.x_offset, maxlb); + + /* + * Get the bounds of the incoming glyphs. + */ + maxas = MAX(glyphs->bbx.ascent, maxas); + maxds = MAX(glyphs->bbx.descent, maxds); + rb = glyphs->bbx.width + glyphs->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(glyphs->bbx.x_offset, minlb); + maxlb = MAX(glyphs->bbx.x_offset, maxlb); + + /* + * Set up the new font bounding box, minus the characters that are being + * removed and with the new characters added. + */ + nbbx.width = maxrb - minlb; + nbbx.x_offset = minlb; + + nbbx.height = maxas + maxds; + nbbx.ascent = maxas; + nbbx.descent = maxds; + nbbx.y_offset = -maxds; + + /* + * Now determine if the combination of the glyphs removed and the new + * glyphs cause the font bounding box to be changed. + */ + resize = (nbbx.width > font->bbx.width || + nbbx.height > font->bbx.height) ? 1 : 0; + + /* + * Set the pointers to the glyphs. + */ + ng = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + sgp = gp = (unencoded == 0) ? font->glyphs : font->unencoded; + + /* + * Locate the closest glyph on or following `start'. + */ + for (i = 0; i < ng && gp->encoding < start; i++, gp++) ; + + appending = (i == ng); + + /* + * Set the starting point for copying the incoming glyphs. + */ + dgp = gp; + + n = glyphs->end - glyphs->start; + end = start + n; + + /* + * Delete all the glyphs between `start' and `end'. + */ + for (del = 0, i = start; i <= end; i++) { + /* + * Mark the character as being modified. + */ + if (ng > 0 && !appending && gp->encoding == i) { + if (unencoded == 0) + _bdf_set_glyph_modified(font->nmod, i); + else + _bdf_set_glyph_modified(font->umod, i); + + if (gp->name != 0) + free(gp->name); + if (gp->bytes > 0) + free((char *) gp->bitmap); + if (gp->unicode.map_size > 0) + free((char *) gp->unicode.map); + del++; + gp++; + } + } + + /* + * Determine how many glyphs remain following the last one deleted. + */ + remaining = ng - (gp - sgp); + + if (glyphs->glyphs_used == 0) { + /* + * If the glyph list is empty, then shift any remaining glyphs down + * to the destination. + */ + _bdf_memmove((char *) dgp, (char *) gp, + sizeof(bdf_glyph_t) * remaining); + if (unencoded == 0) + font->glyphs_used -= del; + else + font->unencoded_used -= del; + } else { + /* + * Insert the glyph list after making sure there is enough space to + * hold them. Also adjust the encoding and scalable width values + * after copying the glyphs. + */ + if (unencoded == 0) { + n = (font->glyphs_used - del) + glyphs->glyphs_used; + if (n > font->glyphs_size) { + off[0] = gp - sgp; + off[1] = dgp - sgp; + if (font->glyphs_size == 0) + font->glyphs = sgp = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * n); + else + font->glyphs = sgp = + (bdf_glyph_t *) realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * n); + gp = sgp + off[0]; + dgp = sgp + off[1]; + font->glyphs_size = n; + } + + /* + * Calculate how many will need to be shifted. + */ + if ((n = glyphs->glyphs_used - del) >= font->glyphs_used) + n = 0; + } else { + n = (font->unencoded_used - del) + glyphs->glyphs_used; + if (n > font->unencoded_size) { + off[0] = gp - sgp; + off[1] = dgp - sgp; + if (font->unencoded_size == 0) + font->unencoded = sgp = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * n); + else + font->unencoded = sgp = + (bdf_glyph_t *) realloc((char *) font->unencoded, + sizeof(bdf_glyph_t) * n); + gp = sgp + off[0]; + dgp = sgp + off[1]; + font->unencoded_size = n; + } + + /* + * Calculate how many will need to be shifted. + */ + if ((n = glyphs->glyphs_used - del) >= font->unencoded_used) + n = 0; + } + + /* + * Shift any following glyphs up or down if needed. + */ + if (n) + _bdf_memmove((char *) (gp + n), (char *) gp, + sizeof(bdf_glyph_t) * remaining); + + /* + * Copy the incoming glyphs, copy their names and bitmaps, + * set their encodings, and set their scalable widths. + */ + (void) memcpy((char *) dgp, (char *) glyphs->glyphs, + sizeof(bdf_glyph_t) * glyphs->glyphs_used); + for (i = 0; i < glyphs->glyphs_used; i++, dgp++) { + if (dgp->name != 0) + dgp->name = (char *) _bdf_strdup((unsigned char *) dgp->name, + strlen(dgp->name) + 1); + + if (dgp->bytes > 0) + dgp->bitmap = _bdf_strdup(dgp->bitmap, dgp->bytes); + + if (dgp->unicode.map_size > 0) + dgp->unicode.map = _bdf_strdup(dgp->unicode.map, + dgp->unicode.map_size); + + dgp->encoding = start + (dgp->encoding - glyphs->start); + + /* + * Mark the glyph as being modified in case it fills a cell that + * was empty before. + */ + _bdf_set_glyph_modified(font->nmod, dgp->encoding); + + dw = (double) dgp->dwidth; + dgp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + + /* + * Adjust the count of glyphs. + */ + ng = (ng - del) + glyphs->glyphs_used; + if (unencoded == 0) + font->glyphs_used = ng; + else + font->unencoded_used = ng; + } + + /* + * Last, if the replacement was done in the unencoded section, + * reencode all the glyphs so they show up properly. + */ + if (unencoded != 0) { + for (i = 0; i < ng; i++, sgp++) { + if (_bdf_glyph_modified(font->umod, sgp->encoding)) { + _bdf_clear_glyph_modified(font->umod, sgp->encoding); + _bdf_set_glyph_modified(font->umod, i); + } + sgp->encoding = i; + } + } + + if (resize) + (void) memcpy((char *) &font->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); + + font->modified = 1; + + return resize; +} + +int +bdf_insert_glyphs(bdf_font_t *font, int start, bdf_glyphlist_t *glyphs) +{ + int resize; + unsigned int i, ng, n, which; + bdf_glyph_t *gp; + + resize = 0; + + if (font == 0) + return resize; + + /* + * Dither the incoming bitmaps so they match the same bits per pixel as + * the font. + */ + if (glyphs->bpp != font->bpp) { + if (glyphs->bpp == 1) + _bdf_one_to_n(glyphs, font->bpp); + else if (font->bpp == 1) + _bdf_n_to_one(glyphs); + else if (glyphs->bpp == 2) + _bdf_two_to_four(glyphs); + else + _bdf_four_to_two(glyphs); + } + + /* + * Locate the starting glyph. + */ + gp = font->glyphs; + ng = font->glyphs_used; + for (i = 0; i < ng && gp->encoding < start; i++, gp++) ; + + /* + * If there are no glyphs at the starting point, then simply do a replace. + */ + if (i == ng) + return bdf_replace_glyphs(font, start, glyphs, 0); + + /* + * Go through the glyphs that would be shifted due to the insertion and + * determine if some of them will overflow the 0xffff boundary. + */ + n = (glyphs->end - glyphs->start) + 1; + for (which = i; i < ng; i++, gp++) { + if (gp->encoding + n > 0xffff) + break; + } + + if (i < ng) { + /* + * Some glyphs have to be moved to the unencoded area because they + * would overflow the 0xffff boundary if they were moved up. + */ + bdf_copy_glyphs(font, gp->encoding, font->glyphs[ng - 1].encoding, + &font->overflow, 0); + bdf_delete_glyphs(font, gp->encoding, font->glyphs[ng - 1].encoding, + 0); + resize += bdf_replace_glyphs(font, font->unencoded_used, + &font->overflow, 1); + } + + /* + * Go back to the insertion point and shift the remaining glyph encodings + * up by `n'. + */ + for (gp = font->glyphs + which; which < font->glyphs_used; which++, gp++) { + /* + * Mark the new glyph locations as being modified. + */ + gp->encoding += n; + _bdf_set_glyph_modified(font->nmod, gp->encoding); + } + + /* + * Finally, mark the font as being modified and insert the new glyphs. + */ + font->modified = 1; + + return resize + bdf_replace_glyphs(font, start, glyphs, 0); +} + +static void +_bdf_combine_glyphs(bdf_font_t *font, bdf_glyph_t *f, bdf_glyph_t *g) +{ + unsigned short x, sx, sy, si, dx, dy, di, byte, dbpr, fbpr, gbpr; + short maxas, maxds, maxrb, minlb, maxlb, rb; + unsigned char *masks; + bdf_bbx_t nbbx; + bdf_glyph_t tmp; + + /* + * Determine the max bounding box for the two glyphs. + */ + minlb = 32767; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Get the font glyph bounds. + */ + maxas = MAX(f->bbx.ascent, maxas); + maxds = MAX(f->bbx.descent, maxds); + rb = f->bbx.width + f->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(f->bbx.x_offset, minlb); + maxlb = MAX(f->bbx.x_offset, maxlb); + + /* + * Get the bounds of the incoming glyph. + */ + maxas = MAX(g->bbx.ascent, maxas); + maxds = MAX(g->bbx.descent, maxds); + rb = g->bbx.width + g->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(g->bbx.x_offset, minlb); + maxlb = MAX(g->bbx.x_offset, maxlb); + + /* + * Set up the new glyph bounding box. + */ + nbbx.width = maxrb - minlb; + nbbx.x_offset = minlb; + nbbx.height = maxas + maxds; + nbbx.ascent = maxas; + nbbx.descent = maxds; + nbbx.y_offset = -maxds; + + masks = 0; + switch (font->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + fbpr = ((f->bbx.width * font->bpp) + 7) >> 3; + gbpr = ((g->bbx.width * font->bpp) + 7) >> 3; + dbpr = ((nbbx.width * font->bpp) + 7) >> 3; + + if (memcmp((char *) &nbbx, (char *) &f->bbx, sizeof(bdf_bbx_t)) == 0) { + /* + * The largest is the first, so merge the second in with it. + */ + dy = f->bbx.ascent - g->bbx.ascent; + for (sy = 0; sy < g->bbx.height; sy++, dy++) { + for (x = sx = 0; x < g->bbx.width; x++, sx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = g->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) + /* + * No shifting of the byte is needed because the x offset + * is the same for both glyphs. + */ + f->bitmap[(dy * fbpr) + (sx >> 3)] |= byte; + } + } + } else if (memcmp((char *) &nbbx, (char *) &g->bbx, + sizeof(bdf_bbx_t)) == 0) { + /* + * The largest is the incoming glyph, so merge into that one and swap + * it with the font glyph. + */ + dy = g->bbx.ascent - f->bbx.ascent; + for (sy = 0; sy < f->bbx.height; sy++, dy++) { + for (x = sx = 0; x < f->bbx.width; x++, sx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = f->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) + /* + * No shifting of the byte is needed because the x offset + * is the same for both glyphs. + */ + g->bitmap[(dy * fbpr) + (sx >> 3)] |= byte; + } + } + + /* + * Now swap the two glyphs while preserving the name and encoding of + * the first glyph. + */ + tmp.swidth = g->swidth; + tmp.dwidth = g->dwidth; + tmp.bytes = g->bytes; + tmp.bitmap = g->bitmap; + (void) memcpy((char *) &tmp.bbx, (char *) &g->bbx, sizeof(bdf_bbx_t)); + + g->swidth = f->swidth; + g->dwidth = f->dwidth; + g->bytes = f->bytes; + g->bitmap = f->bitmap; + (void) memcpy((char *) &g->bbx, (char *) &f->bbx, sizeof(bdf_bbx_t)); + + f->swidth = tmp.swidth; + f->dwidth = tmp.dwidth; + f->bytes = tmp.bytes; + f->bitmap = tmp.bitmap; + (void) memcpy((char *) &f->bbx, (char *) &tmp.bbx, sizeof(bdf_bbx_t)); + } else { + /* + * Need a new bitmap for the combination of the two. + */ + tmp.bytes = nbbx.height * dbpr; + tmp.bitmap = (unsigned char *) malloc(tmp.bytes); + (void) memset((char *) tmp.bitmap, 0, tmp.bytes); + + /* + * Merge the first glyph. + */ + dy = nbbx.ascent - f->bbx.ascent; + for (sy = 0; sy < f->bbx.height; sy++, dy++) { + dx = MYABS(nbbx.x_offset - f->bbx.x_offset) * font->bpp; + for (x = sx = 0; x < f->bbx.width; x++, + sx += font->bpp, dx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = f->bitmap[(sy * fbpr) + (sx >> 3)] & masks[si])) { + di = (dx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + tmp.bitmap[(dy * dbpr) + (dx >> 3)] |= byte; + } + } + } + + /* + * Merge the second glyph. + */ + dy = nbbx.ascent - g->bbx.ascent; + for (sy = 0; sy < g->bbx.height; sy++, dy++) { + dx = MYABS(nbbx.x_offset - g->bbx.x_offset) * font->bpp; + for (x = sx = 0; x < g->bbx.width; x++, + sx += font->bpp, dx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = g->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) { + di = (dx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + tmp.bitmap[(dy * dbpr) + (dx >> 3)] |= byte; + } + } + } + + /* + * Now clear the font glyph and copy the temp glyph to it. + */ + if (f->bytes > 0) + free((char *) f->bitmap); + f->bytes = tmp.bytes; + f->bitmap = tmp.bitmap; + (void) memcpy((char *) &f->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); + + /* + * Set the device width. Pay attention to whether the font is + * monowidth or character cell. + */ + if (font->spacing != BDF_PROPORTIONAL) + f->dwidth = font->monowidth; + else + f->dwidth = MAX(f->dwidth, g->dwidth); + } +} + +int +bdf_merge_glyphs(bdf_font_t *font, int start, bdf_glyphlist_t *glyphs, + int unencoded) +{ + int resize; + int i, n, ng, end, add, enc, off; + bdf_glyph_t *sgp, *gp, *dgp, *base; + short maxas, maxds, maxrb, minlb, maxlb, rb; + double ps, rx, dw; + bdf_bbx_t nbbx; + + resize = 0; + + if (font == 0) + return resize; + + /* + * If the glyphs are being merged in the unencoded area, simply append + * them. The unencoded area is simply storage. + */ + if (unencoded) + return bdf_replace_glyphs(font, font->unencoded_used, glyphs, unencoded); + + /* + * Dither the incoming bitmaps so they match the same bits per pixel as + * the font. + */ + if (glyphs->bpp != font->bpp) { + if (glyphs->bpp == 1) + _bdf_one_to_n(glyphs, font->bpp); + else if (font->bpp == 1) + _bdf_n_to_one(glyphs); + else if (glyphs->bpp == 2) + _bdf_two_to_four(glyphs); + else + _bdf_four_to_two(glyphs); + } + + /* + * Set the point size and horizontal resolution so the scalable width can + * be determined. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + + /* + * Determine if a resize is needed. + */ + + /* + * Determine the bounding box for the font without the characters being + * replaced. + */ + minlb = 32767; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Get the font bounds. + */ + maxas = MAX(font->bbx.ascent, maxas); + maxds = MAX(font->bbx.descent, maxds); + rb = font->bbx.width + font->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(font->bbx.x_offset, minlb); + maxlb = MAX(font->bbx.x_offset, maxlb); + + /* + * Get the bounds of the incoming glyphs. + */ + maxas = MAX(glyphs->bbx.ascent, maxas); + maxds = MAX(glyphs->bbx.descent, maxds); + rb = glyphs->bbx.width + glyphs->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(glyphs->bbx.x_offset, minlb); + maxlb = MAX(glyphs->bbx.x_offset, maxlb); + + /* + * Set up the new font bounding box, minus the characters that are being + * removed and with the new characters added. + */ + nbbx.width = maxrb - minlb; + nbbx.x_offset = minlb; + + nbbx.height = maxas + maxds; + nbbx.ascent = maxas; + nbbx.descent = maxds; + nbbx.y_offset = -maxds; + + /* + * Now determine if the combination of the glyphs removed and the new + * glyphs cause the font bounding box to be changed. + */ + resize = (nbbx.width > font->bbx.width || + nbbx.height > font->bbx.height) ? 1 : 0; + + /* + * Set the pointers to the glyphs. + */ + ng = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + gp = (unencoded == 0) ? font->glyphs : font->unencoded; + + /* + * Locate the closest glyph on or following `start'. + */ + for (i = 0; i < ng && gp->encoding < start; i++, gp++) ; + + if (i == ng) + /* + * If the gylphs are being added off the end of the list, simply insert + * them so any overflows can be handled. + */ + return bdf_insert_glyphs(font, start, glyphs); + + /* + * Set the starting point for copying the incoming glyphs. + */ + dgp = gp; + + n = glyphs->end - glyphs->start; + end = start + n; + + /* + * Count the number of glyphs that will be added and mark all the + * glyphs that will be modified. + */ + for (sgp = glyphs->glyphs, add = 0, i = start; i <= end; i++) { + enc = (sgp->encoding - glyphs->start) + start; + + /* + * Mark the glyph as being modified. + */ + if (unencoded == 0) + _bdf_set_glyph_modified(font->nmod, enc); + else + _bdf_set_glyph_modified(font->umod, enc); + + if (enc == gp->encoding) + sgp++; + else if (enc < gp->encoding) { + add++; + sgp++; + } + + if (gp->encoding == i) + gp++; + } + + if (add > 0) { + ng += add; + + /* + * Need to make room for some glyphs that will be added. + */ + if (unencoded) { + off = dgp - font->unencoded; + if (font->unencoded_used == 0) + font->unencoded = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * ng); + else + font->unencoded = + (bdf_glyph_t *) realloc((char *) font->unencoded, + sizeof(bdf_glyph_t) * ng); + dgp = font->unencoded + off; + font->unencoded_used = ng; + } else { + off = dgp - font->glyphs; + if (font->glyphs_used == 0) + font->glyphs = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * ng); + else + font->glyphs = + (bdf_glyph_t *) realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * ng); + dgp = font->glyphs + off; + font->glyphs_used = ng; + } + } + + /* + * Now go through and do two things: + * 1. Insert new incoming glyphs. + * 2. Combine two glyphs at the same location. + */ + base = (!unencoded) ? font->glyphs : font->unencoded; + for (gp = dgp, sgp = glyphs->glyphs, i = start; i <= end; i++) { + enc = (sgp->encoding - glyphs->start) + start; + if (enc < gp->encoding) { + /* + * Shift the glyphs up by one and add this one. + */ + if (gp - base < ng) + _bdf_memmove((char *) (gp + 1), (char *) gp, + sizeof(bdf_glyph_t) * (ng - (gp - base))); + (void) memcpy((char *) gp, (char *) sgp, sizeof(bdf_glyph_t)); + gp->name = (char *) _bdf_strdup((unsigned char *) gp->name, + strlen(gp->name) + 1); + if (gp->bytes > 0) + gp->bitmap = _bdf_strdup(gp->bitmap, gp->bytes); + if (gp->unicode.map_size > 0) + gp->unicode.map = _bdf_strdup(gp->unicode.map, + gp->unicode.map_size); + gp->encoding = i; + sgp++; + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } else if (enc == gp->encoding) { + _bdf_combine_glyphs(font, gp, sgp); + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + sgp++; + } + if (gp->encoding == i) + gp++; + } + + if (resize) + (void) memcpy((char *) &font->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); + + font->modified = 1; + + return resize; +} + +void +bdf_set_modified(bdf_font_t *font, int modified) +{ + if (font == 0 || font->modified == modified) + return; + + if (modified == 0) { + /* + * Clear out the modified bitmaps. + */ + (void) memset((char *) font->nmod, 0, sizeof(unsigned int) * 2048); + (void) memset((char *) font->umod, 0, sizeof(unsigned int) * 2048); + } + font->modified = modified; +} + +/************************************************************************** + * + * XLFD font name functions. + * + **************************************************************************/ + +static char *xlfdfields[] = { + "FOUNDRY", + "FAMILY_NAME", + "WEIGHT_NAME", + "SLANT", + "SETWIDTH_NAME", + "ADD_STYLE_NAME", + "PIXEL_SIZE", + "POINT_SIZE", + "RESOLUTION_X", + "RESOLUTION_Y", + "SPACING", + "AVERAGE_WIDTH", + "CHARSET_REGISTRY", + "CHARSET_ENCODING", +}; + +int +bdf_is_xlfd_property(char *name) +{ + int i; + + for (i = 0; i < 14; i++) { + if (strcmp(name, xlfdfields[i]) == 0) + return 1; + } + return 0; +} + +int +bdf_has_xlfd_name(bdf_font_t *font) +{ + unsigned int len; + char name[256]; + _bdf_list_t list; + + if (font == 0 || font->name == 0 || font->name[0] == 0) + return 0; + + len = (unsigned int) (strlen(font->name) + 1); + (void) memcpy(name, font->name, len); + list.size = list.used = 0; + _bdf_split("-", name, len, &list); + if (list.size > 0) + free((char *) list.field); + + return (list.used == 15); +} + +char * +bdf_make_xlfd_name(bdf_font_t *font, char *foundry, char *family) +{ + int len; + double dp, dr; + unsigned int i, width, used; + unsigned short awidth, pxsize; + bdf_property_t *pp; + bdf_glyph_t *gp; + char spacing, *name, *val, *np, nbuf[256]; + + if (font == 0 || bdf_has_xlfd_name(font)) + return 0; + + np = nbuf; + + /* + * Add the FOUNDRY field. + */ + if ((pp = bdf_get_font_property(font, "FOUNDRY")) != 0) + foundry = pp->value.atom; + sprintf(np, "-%s", foundry); + np += strlen(np); + + /* + * Add the FAMILY_NAME field. + */ + if ((pp = bdf_get_font_property(font, "FAMILY_NAME")) != 0) + family = pp->value.atom; + sprintf(np, "-%s", family); + np += strlen(np); + + /* + * Add the WEIGHT_NAME field. + */ + val = ((pp = bdf_get_font_property(font, "WEIGHT_NAME")) != 0) ? + pp->value.atom : "Medium"; + if (val == 0) + val = "Medium"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the SLANT field. + */ + val = ((pp = bdf_get_font_property(font, "SLANT")) != 0) ? + pp->value.atom : "R"; + if (val == 0) + val = "R"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the SETWIDTH_NAME field. + */ + val = ((pp = bdf_get_font_property(font, "SETWIDTH_NAME")) != 0) ? + pp->value.atom : "Normal"; + if (val == 0) + val = "Normal"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the ADD_STYLE_NAME field. + */ + val = ((pp = bdf_get_font_property(font, "ADD_STYLE_NAME")) != 0) ? + pp->value.atom : ""; + if (val == 0) + val = ""; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the PIXEL_SIZE field. + */ + if ((pp = bdf_get_font_property(font, "PIXEL_SIZE")) != 0) + sprintf(np, "-%d", pp->value.int32); + else { + /* + * Determine the pixel size. + */ + dp = (double) (font->point_size * 10); + dr = (double) font->resolution_y; + pxsize = (unsigned short) (((dp * dr) / 722.7) + 0.5); + sprintf(np, "-%hd", pxsize); + } + np += strlen(np); + + /* + * Add the POINT_SIZE field. + */ + if ((pp = bdf_get_font_property(font, "POINT_SIZE")) != 0) + sprintf(np, "-%d", pp->value.int32); + else + sprintf(np, "-%d", font->point_size * 10); + np += strlen(np); + + /* + * Add the RESOLUTION_X field. + */ + if ((pp = bdf_get_font_property(font, "RESOLUTION_X")) != 0) + sprintf(np, "-%d", pp->value.card32); + else + sprintf(np, "-%d", font->resolution_x); + np += strlen(np); + + /* + * Add the RESOLUTION_Y field. + */ + if ((pp = bdf_get_font_property(font, "RESOLUTION_Y")) != 0) + sprintf(np, "-%d", pp->value.card32); + else + sprintf(np, "-%d", font->resolution_y); + np += strlen(np); + + /* + * Add the SPACING field. + */ + if ((pp = bdf_get_font_property(font, "SPACING")) != 0) + spacing = pp->value.atom[0]; + else { + spacing = 'P'; + switch (font->spacing) { + case BDF_PROPORTIONAL: spacing = 'P'; break; + case BDF_MONOWIDTH: spacing = 'M'; break; + case BDF_CHARCELL: spacing = 'C'; break; + } + } + sprintf(np, "-%c", spacing); + np += strlen(np); + + /* + * Add the AVERAGE_WIDTH field. + */ + if ((pp = bdf_get_font_property(font, "AVERAGE_WIDTH")) != 0) + sprintf(np, "-%d", pp->value.int32); + else { + /* + * Determine the average width of all the glyphs in the font. + */ + width = 0; + for (i = 0, gp = font->unencoded; i < font->unencoded_used; i++, gp++) + width += gp->dwidth; + for (i = 0, gp = font->glyphs; i < font->glyphs_used; i++, gp++) + width += gp->dwidth; + + used = font->unencoded_used + font->glyphs_used; + if (used == 0) + awidth = font->bbx.width * 10; + else + awidth = (unsigned short) ((((float) width) / + ((float) used)) * 10.0); + sprintf(np, "-%hd", awidth); + } + np += strlen(np); + + /* + * Add the CHARSET_REGISTRY field. + */ + val = ((pp = bdf_get_font_property(font, "CHARSET_REGISTRY")) != 0) ? + pp->value.atom : "FontSpecific"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the CHARSET_ENCODING field. + */ + val = ((pp = bdf_get_font_property(font, "CHARSET_ENCODING")) != 0) ? + pp->value.atom : "0"; + sprintf(np, "-%s", val); + np += strlen(np); + + len = (np - nbuf) + 1; + name = (char *) malloc(len); + (void) memcpy(name, nbuf, len); + return name; +} + +void +bdf_update_name_from_properties(bdf_font_t *font) +{ + unsigned int i; + bdf_property_t *p; + _bdf_list_t list; + char *np, name[128], nname[128]; + + if (font == 0 || bdf_has_xlfd_name(font) == 0) + return; + + (void) memset((char *) &list, 0, sizeof(_bdf_list_t)); + + /* + * Split the name into fields and shift out the first empty field. + * This assumes that the font has a name. + */ + i = (unsigned int) strlen(font->name); + (void) memcpy(name, font->name, i + 1); + _bdf_split("-", name, i, &list); + _bdf_shift(1, &list); + + /* + * Initialize the pointer to the new name and add the '-' prefix. + */ + np = nname; + *np++ = '-'; + *np = 0; + + for (i = 0; i < 14; i++) { + if ((p = bdf_get_font_property(font, xlfdfields[i])) != 0) { + /* + * The property exists, so add it to the new font name. + */ + switch (p->format) { + case BDF_ATOM: + if (p->value.atom != 0) + sprintf(np, "%s", p->value.atom); + break; + case BDF_CARDINAL: + sprintf(np, "%d", p->value.card32); + break; + case BDF_INTEGER: + sprintf(np, "%d", p->value.int32); + break; + } + } else + /* + * The property does not exist, so add the original value to the + * new font name. + */ + sprintf(np, "%s", list.field[i]); + np += strlen(np); + if (i + 1 < 14) { + *np++ = '-'; + *np = 0; + } + } + + /* + * Replace the existing font name with the new one. + */ + free(font->name); + i = (unsigned int) (strlen(nname) + 1); + font->name = (char *) malloc(i); + (void) memcpy(font->name, nname, i); + + /* + * Free up the list. + */ + if (list.size > 0) + free((char *) list.field); + + font->modified = 1; +} + +int +bdf_update_properties_from_name(bdf_font_t *font) +{ + unsigned int i; + bdf_property_t *p, prop; + _bdf_list_t list; + char name[128]; + + if (font == 0 || font->name == 0 || bdf_has_xlfd_name(font) == 0) + return 0; + + (void) memset((char *) &list, 0, sizeof(_bdf_list_t)); + + /* + * Split the name into fields and shift out the first empty field. + */ + i = (unsigned int) strlen(font->name); + (void) memcpy(name, font->name, i + 1); + _bdf_split("-", name, i, &list); + _bdf_shift(1, &list); + + for (i = 0; i < 14; i++) { + p = bdf_get_property(xlfdfields[i]); + prop.name = p->name; + prop.format = p->format; + switch (prop.format) { + case BDF_ATOM: + prop.value.atom = list.field[i]; + break; + case BDF_CARDINAL: + prop.value.card32 = _bdf_atoul(list.field[i], 0, 10); + break; + case BDF_INTEGER: + prop.value.int32 = _bdf_atol(list.field[i], 0, 10); + break; + } + bdf_add_font_property(font, &prop); + } + + /* + * Free up the list. + */ + if (list.size > 0) + free((char *) list.field); + + font->modified = 1; + + return 1; +} + +int +bdf_update_average_width(bdf_font_t *font) +{ + int changed; + unsigned int i; + int oaw, awidth, used; + bdf_glyph_t *gp; + _bdf_list_t list; + bdf_property_t *pp, prop; + char *np, num[16], nbuf[128]; + + changed = 0; + + used = font->unencoded_used + font->glyphs_used; + if (used == 0) + awidth = font->bbx.width * 10; + else { + for (i = 0, awidth = 0, gp = font->unencoded; i < font->unencoded_used; + i++, gp++) + awidth += gp->dwidth; + for (i = 0, gp = font->glyphs; i < font->glyphs_used; i++, gp++) + awidth += gp->dwidth; + awidth = (int) ((((double) awidth) / ((double) used)) * 10.0); + } + + /* + * Check to see if it is different than the average width in the font + * name. + */ + if (bdf_has_xlfd_name(font)) { + (void) memset((char *) &list, 0, sizeof(_bdf_list_t)); + i = (unsigned int) strlen(font->name); + (void) memcpy(nbuf, font->name, i + 1); + _bdf_split("-", nbuf, i, &list); + oaw = _bdf_atol(list.field[12], 0, 10); + if (oaw != awidth) { + /* + * Construct a new font name with the new average width. + */ + changed = 1; + sprintf(num, "%d", awidth); + used = strlen(num) - strlen(list.field[12]); + if (used > 0) { + /* + * Resize the string used for the font name instead of + * creating a new one. + */ + used += i; + font->name = (char *) realloc(font->name, used); + } + + /* + * Copy the elements of the list back into the new font name. + */ + np = font->name; + *np++ = '-'; + for (i = 1; i < list.used; i++) { + if (i == 12) + strcpy(np, num); + else + strcpy(np, list.field[i]); + np += strlen(np); + if (i + 1 < list.used) + *np++ = '-'; + } + } + + /* + * Clear up any space allocated for the list. + */ + if (list.size > 0) + free((char *) list.field); + } + + /* + * Now check for the AVERAGE_WIDTH property. + */ + if ((pp = bdf_get_font_property(font, "AVERAGE_WIDTH")) != 0) { + if (pp->value.int32 != awidth) { + changed = 1; + pp->value.int32 = awidth; + } + } else { + /* + * Property doesn't exist yet, so add it. + */ + changed = 1; + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = awidth; + bdf_add_font_property(font, &prop); + } + + if (changed) + font->modified = 1; + + return changed; +} + +/* + * Change the font bounding box and return a non-zero number if this causes + * the font to get larger or smaller. + */ +int +bdf_set_font_bbx(bdf_font_t *font, bdf_metrics_t *metrics) +{ + int resize; + + resize = 0; + + if (font == 0 || metrics == 0) + return resize; + + resize = (font->bbx.width != metrics->width || + font->bbx.height != metrics->height) ? 1 : 0; + + font->bbx.width = metrics->width; + font->bbx.height = metrics->height; + font->bbx.x_offset = metrics->x_offset; + font->bbx.y_offset = metrics->y_offset; + font->bbx.ascent = metrics->ascent; + font->bbx.descent = metrics->descent; + + /* + * If the font is not proportional, then make sure the monowidth field is + * set to the font bounding box. + */ + if (font->spacing != BDF_PROPORTIONAL) + font->monowidth = font->bbx.width; + + return resize; +} + +int +bdf_translate_glyphs(bdf_font_t *font, short dx, short dy, int start, + int end, bdf_callback_t callback, void *data, + int unencoded) +{ + int resize, diff; + bdf_glyph_t *gp, *sp, *ep; + bdf_callback_struct_t cb; + + if (font == 0 || (dx == 0 && dy == 0)) + return 0; + + if ((unencoded && font->unencoded_used == 0) || font->glyphs_used == 0) + return 0; + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_TRANSLATE_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Locate the first and last glyphs to be shifted. + */ + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (resize = 0, gp = sp; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_TRANSLATING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + /* + * Apply the X translation. + */ + if (dx != 0) { + sp->bbx.x_offset += dx; + diff = sp->bbx.x_offset - font->bbx.x_offset; + if (sp->bbx.x_offset < font->bbx.x_offset) { + font->bbx.x_offset = sp->bbx.x_offset; + font->bbx.width += MYABS(diff); + resize = 1; + } else if (sp->bbx.width + sp->bbx.x_offset > + font->bbx.width + font->bbx.x_offset) { + font->bbx.width += MYABS(diff); + resize = 1; + } + + /* + * Mark the glyph as modified appropriately. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + + /* + * Apply the Y translation. + */ + if (dy != 0) { + sp->bbx.y_offset += dy; + sp->bbx.descent = -sp->bbx.y_offset; + sp->bbx.ascent = sp->bbx.height - sp->bbx.descent; + diff = sp->bbx.y_offset - font->bbx.y_offset; + if (sp->bbx.y_offset < font->bbx.y_offset) { + font->bbx.y_offset = sp->bbx.y_offset; + font->bbx.descent = -font->bbx.y_offset; + font->bbx.height += MYABS(diff); + resize = 1; + } else if (sp->bbx.ascent > font->bbx.ascent) { + font->bbx.ascent += MYABS(diff); + font->bbx.height += MYABS(diff); + resize = 1; + } + + /* + * Mark the glyph as modified appropriately. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_TRANSLATING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + if (resize) + font->modified = 1; + + return resize; +} + +static void +_bdf_resize_rotation(bdf_font_t *font, int mul90, short degrees, + bdf_glyph_t *glyph, bdf_bitmap_t *scratch, + unsigned short *width, unsigned short *height) +{ + unsigned short w, h, wd, ht, bytes; + short cx, cy, x1, y1, x2, y2; + double dx1, dy1, dx2, dy2; + + w = h = 0; + + cx = glyph->bbx.width >> 1; + cy = glyph->bbx.height >> 1; + + /* + * Rotate the lower left and upper right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = glyph->bbx.height; + x2 = glyph->bbx.width; + y2 = 0; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + + w = MAX(wd, w); + h = MAX(ht, h); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + if (ht > font->bbx.height) { + font->bbx.ascent += ht - font->bbx.height; + font->bbx.height += ht - font->bbx.height; + } + + /* + * Rotate the upper left and lower right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = 0; + x2 = glyph->bbx.width; + y2 = glyph->bbx.height; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + + w = MAX(wd, w); + h = MAX(ht, h); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + if (ht > font->bbx.height) { + font->bbx.ascent += ht - font->bbx.height; + font->bbx.height += ht - font->bbx.height; + } + + if (font->bbx.width > scratch->width || + font->bbx.height > scratch->height) { + scratch->width = MAX(font->bbx.width, scratch->width); + scratch->height = MAX(font->bbx.height, scratch->height); + bytes = (((font->bbx.width * font->bpp) + 7) >> 3) * font->bbx.height; + if (scratch->bytes == 0) + scratch->bitmap = (unsigned char *) malloc(bytes); + else + scratch->bitmap = (unsigned char *) + realloc((char *) scratch->bitmap, bytes); + scratch->bytes = bytes; + } + + /* + * Clear the bitmap. + */ + (void) memset((char *) scratch->bitmap, 0, scratch->bytes); + + /* + * Return the new glyph width and height. + */ + *width = w; + *height = h; +} + +int +bdf_rotate_glyphs(bdf_font_t *font, short degrees, int start, + int end, bdf_callback_t callback, void *data, + int unencoded) +{ + int mul90, bpr, sbpr; + unsigned short wd, ht, si, di, byte, col; + short x, y, cx, cy, nx, ny, ox, oy, shiftx, shifty; + bdf_glyph_t *gp, *sp, *ep; + unsigned char *masks; + double dx, dy; + bdf_bitmap_t scratch; + bdf_callback_struct_t cb; + + if (font == 0 || + (unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0 || + degrees == 0) + return 0; + + while (degrees < 0) + degrees += 360; + while (degrees >= 360) + degrees -= 360; + + mul90 = ((degrees % 90) == 0) ? 1 : 0; + + masks = 0; + switch (font->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Initialize the scratch bitmap. + */ + (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t)); + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_ROTATE_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (gp = sp; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_ROTATING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + /* + * Resize the bitmap, adjust the font bounding box, and get the new + * glyph width and height. + */ + _bdf_resize_rotation(font, mul90, degrees, sp, &scratch, &wd, &ht); + + cx = sp->bbx.width >> 1; + cy = sp->bbx.height >> 1; + + shiftx = shifty = 0; + sbpr = ((wd * font->bpp) + 7) >> 3; + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = sp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + dx = (double) (x - cx); + dy = (double) (y - cy); + if (mul90) { + nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } else { + nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += wd; + } else if (nx >= wd) { + ox = (nx - wd) + 1; + shiftx = MAX(shiftx, ox); + nx -= wd; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += ht; + } else if (ny >= ht) { + oy = (ny - ht) + 1; + shifty = MAX(shifty, oy); + ny -= ht; + } + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + scratch.bitmap[(ny * sbpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Resize the glyph bitmap if necessary. + */ + if (wd != sp->bbx.width || ht != sp->bbx.height) { + sp->bbx.width = wd; + sp->bbx.height = ht; + sp->bbx.ascent = ht - sp->bbx.descent; + sp->bytes = (((wd * font->bpp) + 7) >> 3) * ht; + sp->bitmap = (unsigned char *) + realloc((char *) sp->bitmap, sp->bytes); + } + (void) memset((char *) sp->bitmap, 0, sp->bytes); + + /* + * Copy the glyph from the scratch area to the glyph bitmap, + * adjusting for any shift values encountered. + */ + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = scratch.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + if (nx < 0) + nx += sp->bbx.width; + else if (nx >= sp->bbx.width) + nx -= sp->bbx.width; + if (ny < 0) + ny += sp->bbx.height; + else if (ny >= sp->bbx.height) + ny -= sp->bbx.height; + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + sp->bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Mark the glyph as modified. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_TRANSLATING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + if (scratch.bytes > 0) + free((char *) scratch.bitmap); + + /* + * Rotations always change things, so just return a value indicating this. + */ + font->modified = 1; + return 1; +} + +static void +_bdf_resize_shear(bdf_font_t *font, int neg, short degrees, + bdf_glyph_t *glyph, bdf_bitmap_t *scratch, + unsigned short *width, unsigned short *height) +{ + unsigned short wd, w, bytes; + short x1, y1, x2, y2; + + w = 0; + *height = glyph->bbx.height; + + /* + * Shear the lower left and upper right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = glyph->bbx.height; + x2 = glyph->bbx.width; + y2 = 0; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (glyph->bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (glyph->bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + w = MAX(w, wd); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + + /* + * Shear the upper left and lower right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = 0; + x2 = glyph->bbx.width; + y2 = glyph->bbx.height; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (glyph->bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (glyph->bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + w = MAX(w, wd); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + + if (font->bbx.width > scratch->width || + font->bbx.height > scratch->height) { + scratch->width = MAX(font->bbx.width, scratch->width); + scratch->height = MAX(font->bbx.height, scratch->height); + bytes = (((font->bbx.width * font->bpp) + 7) >> 3) * font->bbx.height; + if (scratch->bytes == 0) + scratch->bitmap = (unsigned char *) malloc(bytes); + else + scratch->bitmap = (unsigned char *) + realloc((char *) scratch->bitmap, bytes); + scratch->bytes = bytes; + } + + /* + * Clear the bitmap. + */ + (void) memset((char *) scratch->bitmap, 0, scratch->bytes); + + /* + * Return the new glyph width. + */ + *width = w; +} + +int +bdf_shear_glyphs(bdf_font_t *font, short degrees, int start, + int end, bdf_callback_t callback, void *data, + int unencoded) +{ + int neg, bpr, sbpr; + unsigned short wd, ht, si, di, byte, col; + short x, y, nx, shiftx, ox; + bdf_glyph_t *gp, *sp, *ep; + unsigned char *masks; + bdf_bitmap_t scratch; + bdf_callback_struct_t cb; + + if (font == 0 || (unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + if (degrees == 0 || degrees < -45 || degrees > 45) + return 0; + + if ((neg = (degrees < 0))) + degrees = -degrees; + + masks = 0; + switch (font->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Initialize the scratch bitmap. + */ + (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t)); + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_SHEAR_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (gp = sp; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_SHEARING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + /* + * Resize the bitmap, adjust the font bounding box, and get the new + * glyph width and height. + */ + _bdf_resize_shear(font, neg, degrees, sp, &scratch, &wd, &ht); + + shiftx = 0; + sbpr = ((wd * font->bpp) + 7) >> 3; + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = sp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + if (neg) + nx = x + (short) ((double) y * _bdf_tan_tbl[degrees]); + else + nx = x + (short) ((double) (sp->bbx.height - y) * + _bdf_tan_tbl[degrees]); + + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += wd; + } else if (nx >= wd) { + ox = (nx - wd) + 1; + shiftx = MAX(shiftx, ox); + nx -= wd; + } + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + scratch.bitmap[(y * sbpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Resize the glyph bitmap if necessary. + */ + if (wd != sp->bbx.width || ht != sp->bbx.height) { + sp->bbx.width = wd; + sp->bbx.height = ht; + sp->bbx.ascent = ht - sp->bbx.descent; + sp->bytes = (((wd * font->bpp) + 7) >> 3) * ht; + sp->bitmap = (unsigned char *) + realloc((char *) sp->bitmap, sp->bytes); + } + (void) memset((char *) sp->bitmap, 0, sp->bytes); + + /* + * Copy the glyph from the scratch area to the glyph bitmap, + * adjusting for any shift values encountered. + */ + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = scratch.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + if (nx < 0) + nx += sp->bbx.width; + else if (nx >= sp->bbx.width) + nx -= sp->bbx.width; + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + sp->bitmap[(y * bpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Mark the glyph as modified. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_TRANSLATING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + if (scratch.bytes > 0) + free((char *) scratch.bitmap); + + /* + * Rotations always change things, so just return a value indicating this. + */ + font->modified = 1; + return 1; +} + +static void +_bdf_widen_by(bdf_font_t *f, bdf_glyph_t *g, bdf_bitmap_t *s, int n) +{ + int bytes, sbpr, dbpr, col; + short x, y, si, di; + unsigned char *bmap, *masks; + + masks = 0; + switch (f->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + s->height = g->bbx.height; + s->width = g->bbx.width + n; + + bytes = (((s->width * f->bpp) + 7) >> 3) * s->height; + + if (s->bytes == 0) + s->bitmap = (unsigned char *) malloc(bytes); + else + s->bitmap = (unsigned char *) + realloc((char *) s->bitmap, bytes); + s->bytes = bytes; + + (void) memset((char *) s->bitmap, 0, s->bytes); + + /* + * Copy the glyph bitmap to the scratch area, and then swap the bitmaps. + */ + sbpr = ((g->bbx.width * f->bpp) + 7) >> 3; + dbpr = ((s->width * f->bpp) + 7) >> 3; + for (y = 0; y < g->bbx.height; y++) { + for (col = x = 0; x < g->bbx.width; x++, col += f->bpp) { + si = (col & 7) / f->bpp; + bytes = g->bitmap[(y * sbpr) + (col >> 3)] & masks[si]; + if (bytes) { + di = ((x * f->bpp) & 7) / f->bpp; + if (di < si) + bytes <<= (si - di) * f->bpp; + else if (di > si) + bytes >>= (di - si) * f->bpp; + s->bitmap[(y * dbpr) + (col >> 3)] |= bytes; + } + } + } + g->bbx.width = s->width; + + /* + * Swap the bytes and bitmap fields from the scratch area and the glyph. + */ + bytes = g->bytes; + g->bytes = s->bytes; + s->bytes = bytes; + + bmap = g->bitmap; + g->bitmap = s->bitmap; + s->bitmap = bmap; +} + +int +bdf_embolden_glyphs(bdf_font_t *font, int start, int end, + bdf_callback_t callback, void *data, int unencoded, + int *resize) +{ + int mod, gmod, bpr; + short x, y; + unsigned short si, di, b1, b2, col; + unsigned char *masks; + bdf_glyph_t *gp, *sp, *ep; + bdf_bitmap_t scratch; + bdf_callback_struct_t cb; + + if (font == 0 || (unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + /* + * Initialize the scratch bitmap which may be needed. + */ + (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t)); + + mod = 0; + gp = 0; + + masks = 0; + switch (font->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_EMBOLDEN_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Initialize the resize flag for the caller. + */ + *resize = 0; + + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_EMBOLDENING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + if (font->spacing == BDF_PROPORTIONAL || + (font->spacing == BDF_MONOWIDTH && + sp->bbx.width < font->bbx.width)) { + /* + * Only widen the glyph if it is within reason. + */ + _bdf_widen_by(font, sp, &scratch, 1); + + if (sp->bbx.width > font->bbx.width) { + /* + * Bump the font width up by the difference. + */ + font->bbx.width += sp->bbx.width - font->bbx.width; + *resize = 1; + } + } + + gmod = 0; + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + col = (sp->bbx.width - 1) * font->bpp; + for (x = sp->bbx.width - 1; x > 0; x--, col -= font->bpp) { + si = (col & 7) / font->bpp; + di = ((col - font->bpp) & 7) / font->bpp; + b1 = (x == sp->bbx.width) ? 0 : + sp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + b2 = sp->bitmap[(y * bpr) + ((col - font->bpp) >> 3)] & + masks[di]; + if (!b1 && b2) { + if (di < si) + b2 >>= (si - di) * font->bpp; + else if (di > si) + b2 <<= (di - si) * font->bpp; + sp->bitmap[(y * bpr) + (col >> 3)] |= b2; + gmod = mod = 1; + } + } + } + /* + * Mark the glyph as modified. + */ + if (gmod) { + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_EMBOLDENING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + /* + * Deallocate the scratch bitmap if necessary. + */ + if (scratch.bytes > 0) + free((char *) scratch.bitmap); + + font->modified = mod; + + return mod; +} + +static int _endian = 1; +static char *little_endian = (char *) &_endian; + +int +bdf_little_endian(void) +{ + return *little_endian; +} diff --git a/bdf.h b/bdf.h new file mode 100644 index 0000000..3688df5 --- /dev/null +++ b/bdf.h @@ -0,0 +1,727 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_bdf +#define _h_bdf + +#include +#include +#ifndef __digital__ +#include +#endif +#include + +#ifdef HAVE_XLIB +#include +#include +#include +#endif /* HAVE_XLIB */ + +#ifdef HAVE_FREETYPE +#include +#include FT_FREETYPE_H +#endif /* HAVE_FREETYPE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************** + * + * BDF font options macros and types. + * + **************************************************************************/ + +#define BDF_UNIX_EOL 1 /* Save fonts with Unix LF. */ +#define BDF_DOS_EOL 2 /* Save fonts with DOS CRLF. */ +#define BDF_MAC_EOL 3 /* Save fonts with Mac CR. */ + +#define BDF_CORRECT_METRICS 0x01 /* Correct invalid metrics when loading. */ +#define BDF_KEEP_COMMENTS 0x02 /* Preserve the font comments. */ +#define BDF_KEEP_UNENCODED 0x04 /* Keep the unencoded glyphs. */ +#define BDF_PROPORTIONAL 0x08 /* Font has proportional spacing. */ +#define BDF_MONOWIDTH 0x10 /* Font has mono width. */ +#define BDF_CHARCELL 0x20 /* Font has charcell spacing. */ + +#define BDF_ALL_SPACING (BDF_PROPORTIONAL|BDF_MONOWIDTH|BDF_CHARCELL) + +#define BDF_DEFAULT_LOAD_OPTIONS \ + (BDF_CORRECT_METRICS|BDF_KEEP_COMMENTS|BDF_KEEP_UNENCODED|BDF_PROPORTIONAL) + +typedef struct { + int otf_flags; + int correct_metrics; + int keep_unencoded; + int keep_comments; + int pad_cells; + int font_spacing; + int point_size; + unsigned int resolution_x; + unsigned int resolution_y; + int bits_per_pixel; + int eol; + int psf_flags; + int cursor_font; +} bdf_options_t; + +/* + * Callback function type for unknown configuration options. + */ +typedef int (*bdf_options_callback_t)(bdf_options_t *opts, + char **params, + unsigned int nparams, + void *client_data); + +/************************************************************************** + * + * BDF font property macros and types. + * + **************************************************************************/ + +#define BDF_ATOM 1 +#define BDF_INTEGER 2 +#define BDF_CARDINAL 3 + +/* + * This structure represents a particular property of a font. + * There are a set of defaults and each font has their own. + */ +typedef struct { + char *name; /* Name of the property. */ + int format; /* Format of the property. */ + int builtin; /* A builtin property. */ + union { + char *atom; + int int32; + unsigned int card32; + } value; /* Value of the property. */ +} bdf_property_t; + +/************************************************************************** + * + * SBIT metrics specific structures. + * + **************************************************************************/ + +/* + * Boolean flags for SBIT metrics files. + */ +#define BDF_SBIT_MONO_ADVANCE 0x0001 +#define BDF_SBIT_ADD_EBLC 0x0002 +#define BDF_SBIT_APPLE_COMPAT 0x0004 + +/* + * Direction macros (inclusive, can be combined). + */ +#define BDF_SBIT_HORIZONTAL 0x0008 +#define BDF_SBIT_VERTICAL 0x0010 + +/* + * Bitmap storage options (exclusive, cannot be combined). + */ +#define BDF_SBIT_STORE_SMALL 0x0020 +#define BDF_SBIT_STORE_FAST 0x0040 + +typedef struct { + short cnum; /* Caret slope numerator. */ + short cdenom; /* Caret slope denominator. */ + short coff; /* Caret offset. */ + short sx; /* Scaled version horizontal PPEM size. */ + short sy; /* Scaled version vertical PPEM size (optional).*/ + short flags; /* Booleans and other non-numeric values. */ +} bdf_sbit_t; + +/************************************************************************** + * + * BDF opaque undo information types. + * + **************************************************************************/ + +typedef struct _bdf_undo_t *bdf_undo_t; + +/************************************************************************** + * + * PSF font flags and Unicode mapping tables. Stored internally in UTF-8. + * + **************************************************************************/ + +/* + * Flags used for exporting PSF fonts and their Unicode maps. + */ +#define BDF_PSF_FONT 0x01 +#define BDF_PSF_UNIMAP 0x02 +#define BDF_PSF_ALL (BDF_PSF_FONT|BDF_PSF_UNIMAP) + +typedef struct { + unsigned char *map; + unsigned int map_used; + unsigned int map_size; +} bdf_psf_unimap_t; + +/************************************************************************** + * + * BDF font metric and glyph types. + * + **************************************************************************/ + +/* + * A general bitmap type, mostly used when the glyph bitmap is being edited. + */ +typedef struct { + short x; + short y; + unsigned short width; + unsigned short height; + unsigned short bpp; + unsigned short pad; + unsigned char *bitmap; + unsigned int bytes; +} bdf_bitmap_t; + +typedef struct { + int font_spacing; + unsigned short swidth; + unsigned short dwidth; + unsigned short width; + unsigned short height; + short x_offset; + short y_offset; + short ascent; + short descent; +} bdf_metrics_t; + +typedef struct { + unsigned short width; + unsigned short height; + short x_offset; + short y_offset; + short ascent; + short descent; +} bdf_bbx_t; + +typedef struct { + char *name; /* Glyph name. */ + int encoding; /* Glyph encoding. */ + unsigned short swidth; /* Scalable width. */ + unsigned short dwidth; /* Device width. */ + bdf_bbx_t bbx; /* Glyph bounding box. */ + unsigned char *bitmap; /* Glyph bitmap. */ + unsigned short bytes; /* Number of bytes used for the bitmap. */ + bdf_psf_unimap_t unicode; /* PSF Unicode mappings. */ +} bdf_glyph_t; + +typedef struct { + unsigned short pad; /* Pad to 4-byte boundary. */ + unsigned short bpp; /* Bits per pixel. */ + int start; /* Beginning encoding value of glyphs. */ + int end; /* Ending encoding value of glyphs. */ + bdf_glyph_t *glyphs; /* Glyphs themselves. */ + unsigned int glyphs_size; /* Glyph structures allocated. */ + unsigned int glyphs_used; /* Glyph structures used. */ + bdf_bbx_t bbx; /* Overall bounding box of glyphs. */ +} bdf_glyphlist_t; + +typedef struct { + char *name; /* Name of the font. */ + bdf_bbx_t bbx; /* Font bounding box. */ + + int point_size; /* Point size of the font. */ + unsigned int resolution_x; /* Font horizontal resolution. */ + unsigned int resolution_y; /* Font vertical resolution. */ + + int hbf; /* Font came from an HBF font. */ + + int spacing; /* Font spacing value. */ + + unsigned short monowidth; /* Logical width for monowidth font. */ + + int default_glyph; /* Encoding of the default glyph. */ + + int font_ascent; /* Font ascent. */ + int font_descent; /* Font descent. */ + + int glyphs_size; /* Glyph structures allocated. */ + int glyphs_used; /* Glyph structures used. */ + bdf_glyph_t *glyphs; /* Glyphs themselves. */ + + int unencoded_size; /* Unencoded glyph structures allocated. */ + int unencoded_used; /* Unencoded glyph structures used. */ + bdf_glyph_t *unencoded; /* Unencoded glyphs themselves. */ + + unsigned int props_size; /* Font properties allocated. */ + unsigned int props_used; /* Font properties used. */ + bdf_property_t *props; /* Font properties themselves. */ + + char *comments; /* Font comments. */ + unsigned int comments_len; /* Length of comment string. */ + + char *acmsgs; /* Auto-correction messages. */ + unsigned int acmsgs_len; /* Length of auto-correction messages. */ + + bdf_glyphlist_t overflow; /* Storage used for glyph insertion. */ + + void *internal; /* Internal data for the font. */ + + unsigned int nmod[2048]; /* Bitmap indicating modified glyphs. */ + unsigned int umod[2048]; /* Bitmap indicating modified unencoded. */ + + unsigned short modified; /* Boolean indicating font modified. */ + unsigned short bpp; /* Bits per pixel. */ + + bdf_sbit_t *sbits; /* Associcated SBIT metrics. */ + unsigned int sbits_used; /* Number of SBIT metrics entries. */ + unsigned int sbits_size; /* Amount of entries allocated. */ + + bdf_undo_t *undo_stack; /* Record of undoable operations. */ + unsigned int undo_used; /* Amount of undo stack used. */ + unsigned int undo_size; /* Amount of undo stack allocated. */ + + bdf_psf_unimap_t unicode; /* PSF Unicode table. */ +} bdf_font_t; + +/************************************************************************** + * + * BDF glyph grid structures for editing glyph bitmaps. + * + **************************************************************************/ + +typedef struct { + char *name; + int encoding; /* The glyph encoding. */ + unsigned short unencoded; /* Whether the glyph was unencoded. */ + unsigned short bpp; /* Bits per pixel. */ + int spacing; /* Font spacing. */ + int resolution_x; /* Horizontal resolution. */ + int resolution_y; /* Vertical resolution. */ + unsigned int point_size; /* Font point size. */ + unsigned short swidth; /* Scalable width. */ + unsigned short dwidth; /* Device width. */ + bdf_bbx_t font_bbx; /* Font bounding box. */ + bdf_bbx_t glyph_bbx; /* Glyph bounding box. */ + unsigned char *bitmap; /* The grid bitmap. */ + unsigned short bytes; /* Number of bytes in the grid bitmap. */ + short grid_width; /* Width of the grid. */ + short grid_height; /* Height of the grid. */ + short base_x; /* Baseline X coordinate. */ + short base_y; /* Baseline Y coordinate. */ + short glyph_x; /* Top-left X position of glyph. */ + short glyph_y; /* Top-left Y position of glyph. */ + unsigned short modified; /* Flag indicating if bitmap modified. */ + short cap_height; /* Font CAP_HEIGHT if it exists. */ + short x_height; /* Font X_HEIGHT if it exists. */ + bdf_bitmap_t sel; /* Selected portion of the glyph bitmap.*/ + bdf_psf_unimap_t unicode; /* PSF Unicode mappings for this glyph. */ +} bdf_glyph_grid_t; + +/************************************************************************** + * + * Types for load/save callbacks. + * + **************************************************************************/ + +/* + * Callback reasons. + */ +#define BDF_LOAD_START 1 +#define BDF_LOADING 2 +#define BDF_SAVE_START 3 +#define BDF_SAVING 4 +#define BDF_TRANSLATE_START 5 +#define BDF_TRANSLATING 6 +#define BDF_ROTATE_START 7 +#define BDF_ROTATING 8 +#define BDF_SHEAR_START 9 +#define BDF_SHEARING 10 +#define BDF_GLYPH_NAME_START 11 +#define BDF_GLYPH_NAME 12 +#define BDF_EXPORT_START 13 +#define BDF_EXPORTING 14 +#define BDF_EMBOLDEN_START 15 +#define BDF_EMBOLDENING 16 +#define BDF_WARNING 20 +#define BDF_ERROR 21 + +/* + * Error codes. + */ +#define BDF_OK 0 +#define BDF_MISSING_START -1 +#define BDF_MISSING_FONTNAME -2 +#define BDF_MISSING_SIZE -3 +#define BDF_MISSING_FONTBBX -4 +#define BDF_MISSING_CHARS -5 +#define BDF_MISSING_STARTCHAR -6 +#define BDF_MISSING_ENCODING -7 +#define BDF_MISSING_BBX -8 + +#define BDF_NOT_CONSOLE_FONT -10 +#define BDF_NOT_MF_FONT -11 +#define BDF_NOT_PSF_FONT -12 +#define BDF_PSF_SHORT_TABLE -13 +#define BDF_PSF_LONG_TABLE -14 +#define BDF_PSF_CORRUPT_UTF8 -15 +#define BDF_PSF_BUFFER_OVRFL -16 +#define BDF_PSF_UNSUPPORTED -17 +#define BDF_BAD_RANGE -98 +#define BDF_EMPTY_FONT -99 +#define BDF_INVALID_LINE -100 + +typedef struct { + unsigned int reason; + unsigned int current; + unsigned int total; + unsigned int errlineno; +} bdf_callback_struct_t; + +typedef void (*bdf_callback_t)(bdf_callback_struct_t *call_data, + void *client_data); + +/************************************************************************** + * + * BDF font API. + * + **************************************************************************/ + +/* + * Startup and shutdown functions. + */ +extern void bdf_setup(void); +extern void bdf_cleanup(void); + +/* + * Configuration file loading and saving. + */ +extern void bdf_load_options(FILE *in, bdf_options_t *opts, + bdf_options_callback_t callback, + void *client_data); +extern void bdf_save_options(FILE *out, bdf_options_t *opts); + +/* + * Font options functions. + */ +extern void bdf_default_options(bdf_options_t *opts); + +/* + * Font load, create, save and free functions. + */ +extern bdf_font_t *bdf_new_font(char *name, int point_size, + int resolution_x, int resolution_y, + int spacing, int bpp); +extern bdf_font_t *bdf_load_font(FILE *in, bdf_options_t *opts, + bdf_callback_t callback, void *data); +#ifdef HAVE_HBF +extern bdf_font_t *bdf_load_hbf_font(char *filename, bdf_options_t *opts, + bdf_callback_t callback, void *data); +#endif + +#ifdef HAVE_XLIB +extern bdf_font_t *bdf_load_server_font(Display *d, XFontStruct *f, + char *name, bdf_options_t *opts, + bdf_callback_t callback, + void *data); +#endif /* HAVE_XLIB */ + +extern int bdf_load_console_font(FILE *in, bdf_options_t *opts, + bdf_callback_t callback, void *data, + bdf_font_t *fonts[3], int *nfonts); + +extern int bdf_load_mf_font(FILE *in, bdf_options_t *opts, + bdf_callback_t callback, void *data, + bdf_font_t **font); + +extern void bdf_save_font(FILE *out, bdf_font_t *font, bdf_options_t *opts, + bdf_callback_t callback, void *data); + +extern void bdf_save_sbit_metrics(FILE *out, bdf_font_t *font, + bdf_options_t *opts, char *appname); + +extern void bdf_export_hex(FILE *out, bdf_font_t *font, bdf_options_t *opts, + bdf_callback_t callback, void *data); + +extern int bdf_export_psf(FILE *out, bdf_font_t *font, bdf_options_t *opts, + int start, int end); + +extern void bdf_free_font(bdf_font_t *font); + +#ifdef HAVE_FREETYPE + +/* + * OpenType related macros and functions. + */ + +/* + * ID numbers of the strings that can appear in an OpenType font. + */ +#define BDFOTF_COPYRIGHT_STRING 0 +#define BDFOTF_FAMILY_STRING 1 +#define BDFOTF_SUBFAMILY_STRING 2 +#define BDFOTF_UNIQUEID_STRING 3 +#define BDFOTF_FULLNAME_STRING 4 +#define BDFOTF_VENDOR_STRING 5 +#define BDFOTF_POSTSCRIPT_STRING 6 +#define BDFOTF_TRADEMARK_STRING 7 +#define BDFOTF_FOUNDRY_STRING 8 +#define BDFOTF_DESIGNER_STRING 9 +#define BDFOTF_DESCRIPTION_STRING 10 +#define BDFOTF_VENDORURL_STRING 11 +#define BDFOTF_DESIGNERURL_STRING 12 +#define BDFOTF_LICENSE_STRING 13 +#define BDFOTF_LICENSEURL_STRING 14 +#define BDFOTF_RESERVED_STRING 15 +#define BDFOTF_PREFFAMILY_STRING 16 +#define BDFOTF_PREFSUBFAMILY_STRING 17 +#define BDFOTF_COMPATIBLEMAC_STRING 18 +#define BDFOTF_SAMPLETEXT_STRING 19 +#define BDFOTF_PSCIDFF_STRING 20 + +extern char *bdfotf_platform_name(short pid); +extern char *bdfotf_encoding_name(short pid, short eid); +extern int bdfotf_get_english_string(FT_Face face, int nameID, + int dash_to_space, char *name); + +extern int bdfotf_load_font(FT_Face face, short pid, short eid, + bdf_options_t *opts, bdf_callback_t callback, + void *data, bdf_font_t **font); + +#endif /* HAVE_FREETYPE */ + +/* + * FON/FNT related functions. + */ + +/* + * String ID numbers for FON/FNT fonts. + */ +#define BDFFNT_COPYRIGHT 1 +#define BDFFNT_TYPEFACE 2 + +/* + * Opaque font type. + */ +typedef struct _bdffnt_font_t *bdffnt_font_t; + +extern int bdffnt_open_font(char *path, bdffnt_font_t *font); +extern void bdffnt_close_font(bdffnt_font_t font); +extern int bdffnt_font_count(bdffnt_font_t font); +extern int bdffnt_get_copyright(bdffnt_font_t font, unsigned int fontID, + unsigned char *string); +extern int bdffnt_get_facename(bdffnt_font_t font, unsigned int fontID, + int for_xlfd, unsigned char *string); +extern int bdffnt_char_count(bdffnt_font_t font, unsigned int fontID); +extern int bdffnt_font_pointsize(bdffnt_font_t font, unsigned int fontID); +extern int bdffnt_load_font(bdffnt_font_t font, unsigned int fontID, + bdf_callback_t callback, void *data, + bdf_font_t **out); + +/* + * PSF font section. + * + * In PSF fonts, a Unicode table on the end of the font may map a single + * glyph to several locations. The BDFPSF_SOURCE_GLYPH marks the glyphs that + * are source glyphs and the BDFPSF_PSEUDO_GLYPH flag marks glyphs that are + * clones of a source glyph. + */ +#define BDFPSF_SOURCE_GLYPH 0x0001 +#define BDFPSF_PSEUDO_GLYPH 0x0002 + +extern bdf_font_t *bdf_load_psf(FILE *in, unsigned char *magic, + bdf_options_t *opts, + bdf_callback_t callback, void *data, + int *awidth); + +/* + * Font property functions. + */ +extern void bdf_create_property(char *name, int type); +extern bdf_property_t *bdf_get_property(char *name); +extern unsigned int bdf_property_list(bdf_property_t **props); + +extern void bdf_add_font_property(bdf_font_t *font, bdf_property_t *property); +extern void bdf_delete_font_property(bdf_font_t *font, char *name); +extern bdf_property_t *bdf_get_font_property(bdf_font_t *font, char *name); +extern unsigned int bdf_font_property_list(bdf_font_t *font, + bdf_property_t **props); +extern int bdf_is_xlfd_property(char *name); + +/* + * Font comment functions. + */ +extern int bdf_replace_comments(bdf_font_t *font, char *comments, + unsigned int comments_len); + +/* + * Other miscellaneous functions. + */ +extern void bdf_set_default_metrics(bdf_font_t *font); + +/* + * Font glyph editing functions. + */ +extern int bdf_glyph_modified(bdf_font_t *font, int which, int unencoded); + +extern void bdf_copy_glyphs(bdf_font_t *font, int start, int end, + bdf_glyphlist_t *glyphs, int unencoded); + +extern int bdf_delete_glyphs(bdf_font_t *font, int start, int end, + int unencoded); + +extern int bdf_insert_glyphs(bdf_font_t *font, int start, + bdf_glyphlist_t *glyphs); + +extern int bdf_replace_glyphs(bdf_font_t *font, int start, + bdf_glyphlist_t *glyphs, int unencoded); + +extern int bdf_merge_glyphs(bdf_font_t *font, int start, + bdf_glyphlist_t *glyphs, int unencoded); + +extern int bdf_replace_mappings(bdf_font_t *font, int encoding, + bdf_psf_unimap_t *map, int unencoded); + +/************************************************************************** + * + * Other API functions. + * + **************************************************************************/ + +extern int bdf_set_font_bbx(bdf_font_t *font, bdf_metrics_t *metrics); + +extern void bdf_set_modified(bdf_font_t *font, int modified); + +extern int bdf_has_xlfd_name(bdf_font_t *font); + +extern char *bdf_make_xlfd_name(bdf_font_t *font, char *foundry, + char *family); + +extern void bdf_update_name_from_properties(bdf_font_t *font); + +extern int bdf_update_properties_from_name(bdf_font_t *font); + +extern int bdf_update_average_width(bdf_font_t *font); + +extern int bdf_set_unicode_glyph_names(FILE *in, bdf_font_t *font, + bdf_callback_t callback); + +extern int bdf_set_adobe_glyph_names(FILE *in, bdf_font_t *font, + bdf_callback_t callback); + +extern int bdf_set_glyph_code_names(int prefix, bdf_font_t *font, + bdf_callback_t callback); + +/* + * Routine to add Unicode mappings when editing PSF fonts. + */ +extern int bdf_psf_add_unicode_mapping(bdf_psf_unimap_t *u, + unsigned int *mapping, + unsigned int mapping_cnt); + +/************************************************************************** + * + * Glyph grid API. + * + **************************************************************************/ + +/* + * Glyph grid allocation and deallocation functions. + */ +extern bdf_glyph_grid_t *bdf_make_glyph_grid(bdf_font_t *font, + int code, + int unencoded); +extern void bdf_free_glyph_grid(bdf_glyph_grid_t *grid); + +/* + * Glyph grid information functions. + */ +extern void bdf_grid_image(bdf_glyph_grid_t *grid, bdf_bitmap_t *image); +extern void bdf_grid_origin(bdf_glyph_grid_t *grid, short *x, short *y); +extern bdf_glyph_t *bdf_grid_glyph(bdf_glyph_grid_t *grid); + +/* + * Glyph grid editing functions. + */ +extern int bdf_grid_enlarge(bdf_glyph_grid_t *grid, unsigned short width, + unsigned short height); +extern int bdf_grid_resize(bdf_glyph_grid_t *grid, + bdf_metrics_t *metrics); +extern int bdf_grid_crop(bdf_glyph_grid_t *grid, int grid_modified); + +extern int bdf_grid_set_pixel(bdf_glyph_grid_t *grid, short x, short y, + int val); +extern int bdf_grid_clear_pixel(bdf_glyph_grid_t *grid, short x, short y); +extern int bdf_grid_invert_pixel(bdf_glyph_grid_t *grid, + short x, short y, int val); +extern int bdf_grid_shift(bdf_glyph_grid_t *grid, short xcount, + short ycount); +extern int bdf_grid_flip(bdf_glyph_grid_t *grid, short dir); +extern int bdf_grid_rotate(bdf_glyph_grid_t *grid, short degrees, + int *resize); +extern int bdf_grid_shear(bdf_glyph_grid_t *grid, short degrees, + int *resize); +extern int bdf_grid_embolden(bdf_glyph_grid_t *grid); + +/* + * Glyph grid selection functions. + */ +extern int bdf_has_selection(bdf_glyph_grid_t *grid, short *x, short *y, + short *width, short *height); +extern void bdf_set_selection(bdf_glyph_grid_t *grid, short x, short y, + short width, short height); +extern void bdf_lose_selection(bdf_glyph_grid_t *grid); +extern void bdf_detach_selection(bdf_glyph_grid_t *grid); +extern void bdf_attach_selection(bdf_glyph_grid_t *grid); +extern void bdf_delete_selection(bdf_glyph_grid_t *grid); +extern int bdf_in_selection(bdf_glyph_grid_t *grid, short x, short y, + short *set); +extern void bdf_add_selection(bdf_glyph_grid_t *grid, bdf_bitmap_t *sel); + +/* + * Glyph grid misc functions. + */ +extern int bdf_grid_color_at(bdf_glyph_grid_t *grid, short x, short y); + +/* + * Graphical transformation functions. + */ +extern int bdf_translate_glyphs(bdf_font_t *font, short dx, short dy, + int start, int end, + bdf_callback_t callback, void *data, + int unencoded); + +extern int bdf_rotate_glyphs(bdf_font_t *font, short degrees, + int start, int end, + bdf_callback_t callback, void *data, + int unencoded); + +extern int bdf_shear_glyphs(bdf_font_t *font, short degrees, + int start, int end, + bdf_callback_t callback, void *data, + int unencoded); + +extern int bdf_embolden_glyphs(bdf_font_t *font, int start, int end, + bdf_callback_t callback, void *data, + int unencoded, int *resize); + +extern int bdf_little_endian(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _h_bdf */ diff --git a/bdfP.h b/bdfP.h new file mode 100644 index 0000000..1e9284e --- /dev/null +++ b/bdfP.h @@ -0,0 +1,161 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_bdfP +#define _h_bdfP + +#include "bdf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MYABS +#define MYABS(xx) ((xx) < 0 ? -(xx) : (xx)) +#endif + +/* + * Macros and structures used for undo operations in the font. + */ +#define _UNDO_REPLACE_GLYPHS 1 +#define _UNDO_INSERT_GLYPHS 2 +#define _UNDO_MERGE_GLYPHS 3 + +/* + * This structure is for undo operations of replacing and merging glyphs + * in the font. + */ +typedef struct { + bdf_bbx_t b; + bdf_glyphlist_t g; +} _bdf_undo1_t; + +/* + * This structure is for undo operations of inserting glyphs. + */ +typedef struct { + bdf_bbx_t b; + int start; + int end; +} _bdf_undo2_t; + +/* + * This is the final undo structure used to store undo information with the + * font. + */ +typedef struct { + int type; + union { + _bdf_undo1_t one; + _bdf_undo2_t two; + } field; +} _bdf_undo_t; + +/* + * Tables for rotation and shearing. + */ +extern double _bdf_cos_tbl[]; +extern double _bdf_sin_tbl[]; +extern double _bdf_tan_tbl[]; + +/* + * PSF magic numbers. + */ +extern unsigned char _bdf_psf1magic[]; +extern unsigned char _bdf_psf2magic[]; +extern char _bdf_psfcombined[]; + +/* + * Arrays of masks for test with different bits per pixel. + */ +extern unsigned char bdf_onebpp[]; +extern unsigned char bdf_twobpp[]; +extern unsigned char bdf_fourbpp[]; +extern unsigned char bdf_eightbpp[]; + +/* + * Simple routine for determining the ceiling. + */ +extern short _bdf_ceiling(double v); + +extern unsigned char *_bdf_strdup(unsigned char *s, unsigned int len); +extern void _bdf_memmove(char *dest, char *src, unsigned int bytes); + +extern short _bdf_atos(char *s, char **end, int base); +extern int _bdf_atol(char *s, char **end, int base); +extern unsigned int _bdf_atoul(char *s, char **end, int base); + +/* + * Function to locate the nearest glyph to a specified encoding. + */ +extern bdf_glyph_t *_bdf_locate_glyph(bdf_font_t *font, int encoding, + int unencoded); + +/* + * Macros to test/set the modified status of a glyph. + */ +#define _bdf_glyph_modified(map, e) ((map)[(e) >> 5] & (1 << ((e) & 31))) +#define _bdf_set_glyph_modified(map, e) (map)[(e) >> 5] |= (1 << ((e) & 31)) +#define _bdf_clear_glyph_modified(map, e) (map)[(e) >> 5] &= ~(1 << ((e) & 31)) + +/* + * Function to add a message to the font. + */ +extern void _bdf_add_acmsg(bdf_font_t *font, char *msg, unsigned int len); + +/* + * Function to add a comment to the font. + */ +extern void _bdf_add_comment(bdf_font_t *font, char *comment, + unsigned int len); + +/* + * Function to do glyph name table cleanup when exiting. + */ +extern void _bdf_glyph_name_cleanup(void); + +/* + * Function to pad cells when saving glyphs. + */ +extern void _bdf_pad_cell(bdf_font_t *font, bdf_glyph_t *glyph, + bdf_glyph_t *cell); + +/* + * Function to crop glyphs down to their minimum bitmap. + */ +extern void _bdf_crop_glyph(bdf_font_t *font, bdf_glyph_t *glyph); + +/* + * Routine to generate a string list from the PSF2 Unicode mapping format. + */ +extern char **_bdf_psf_unpack_mapping(bdf_psf_unimap_t *unimap, int *num_seq); + +/* + * Routine to convert a string list of mappings back to PSF2 format. + */ +extern int _bdf_psf_pack_mapping(char **list, int len, int encoding, + bdf_psf_unimap_t *map); + +#ifdef __cplusplus +} +#endif + +#endif /* _h_bdfP */ diff --git a/bdfcons.c b/bdfcons.c new file mode 100644 index 0000000..0e39ee9 --- /dev/null +++ b/bdfcons.c @@ -0,0 +1,594 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "bdfP.h" + +#undef MAX +#undef MIN +#define MAX(h,i) ((h) > (i) ? (h) : (i)) +#define MIN(l,o) ((l) < (o) ? (l) : (o)) + +/* + * Header for Sun VF fonts. + */ +typedef struct { + unsigned short mag; + unsigned short total_bytes; + unsigned short max_width; + unsigned short max_height; + unsigned short pad; +} vfhdr_t; + +/* + * Character metrics data for Sun VF fonts. + */ +typedef struct { + unsigned short offset; + unsigned short bytes; + char ascent; + char descent; + char lbearing; + char rbearing; + unsigned short dwidth; +} vfmetrics_t; + +/************************************************************************** + * + * Support functions. + * + **************************************************************************/ + +static bdf_font_t * +_bdf_load_vfont(FILE *in, vfhdr_t *hdr, bdf_callback_t callback, void *data, + int *awidth) +{ + int first, ismono; + int i, pos; + bdf_font_t *fp; + bdf_glyph_t *gp; + bdf_callback_struct_t cb; + vfmetrics_t met, metrics[256]; + + /* + * Convert the header values to little endian if necessary. + */ + if (bdf_little_endian()) { + hdr->total_bytes = ((hdr->total_bytes & 0xff) << 8) | + ((hdr->total_bytes >> 8) & 0xff); + hdr->max_width = ((hdr->max_width & 0xff) << 8) | + ((hdr->max_width >> 8) & 0xff); + hdr->max_height = ((hdr->max_height & 0xff) << 8) | + ((hdr->max_height >> 8) & 0xff); + } + + /* + * The point size of the font will be the height, the resolution will + * default to 72dpi, and the spacing will default to proportional. + */ + fp = bdf_new_font(0, (int) hdr->max_height, 72, 72, BDF_PROPORTIONAL, 1); + + /* + * Force the bits per pixel to 1. + */ + fp->bpp = 1; + + /* + * Load the glyph metrics and set a marker to the beginning of the glyph + * bitmaps. + */ + fread((char *) metrics, sizeof(vfmetrics_t), 256, in); + pos = ftell(in); + + *awidth = 0; + + /* + * Count the number of glyphs that actually exist and determine the font + * bounding box in the process. + */ + (void) memset((char *) &met, 0, sizeof(vfmetrics_t)); + met.lbearing = 127; + fp->glyphs_size = 0; + for (first = -1, ismono = 1, i = 0; i < 256; i++) { + if (metrics[i].bytes == 0) + continue; + + if (first == -1) + first = i; + + /* + * Start out by assuming the font is monowidth, but if any glyph + * encountered has metrics different than the first glyph defined, + * change that flag. If the font is still flagged as monowidth when + * this loop is done, then change the font to a monowidth font. + */ + if (i != first && ismono && + (metrics[i].ascent != metrics[first].ascent || + metrics[i].descent != metrics[first].descent || + metrics[i].lbearing != metrics[first].lbearing || + metrics[i].rbearing != metrics[first].rbearing)) + ismono = 0; + + /* + * If this is a little endian machine, convert the 16-bit values from + * big endian. + */ + if (bdf_little_endian()) { + metrics[i].offset = ((metrics[i].offset & 0xff) << 8) | + ((metrics[i].offset >> 8) & 0xff); + metrics[i].bytes = ((metrics[i].bytes & 0xff) << 8) | + ((metrics[i].bytes >> 8) & 0xff); + metrics[i].dwidth = ((metrics[i].dwidth & 0xff) << 8) | + ((metrics[i].dwidth >> 8) & 0xff); + } + + /* + * Update the value used for average width calculation. + */ + *awidth = *awidth + (metrics[i].rbearing - metrics[i].lbearing); + + /* + * Increment the count of characters. + */ + fp->glyphs_size++; + + /* + * Determine the font bounding box. + */ + met.ascent = MAX(met.ascent, metrics[i].ascent); + met.descent = MAX(met.descent, metrics[i].descent); + met.lbearing = MIN(met.lbearing, metrics[i].lbearing); + met.rbearing = MAX(met.rbearing, metrics[i].rbearing); + } + + /* + * Adjust the font bounding box accordingly. + */ + fp->bbx.ascent = met.ascent; + fp->bbx.descent = met.descent; + fp->bbx.width = met.rbearing + met.lbearing; + fp->bbx.height = met.ascent + met.descent; + fp->bbx.x_offset = met.lbearing; + fp->bbx.y_offset = -met.descent; + + /* + * If the font is still flagged as a monowidth font, change the font + * spacing. The actual SPACING property will be adjusted once this + * routine returns. + */ + if (ismono) + fp->spacing = BDF_MONOWIDTH; + + /* + * Set up to load the glyphs. + */ + fp->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * fp->glyphs_size); + (void) memset((char *) fp->glyphs, 0, + sizeof(bdf_glyph_t) * fp->glyphs_size); + + /* + * Set the callback up. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = fp->glyphs_size; + (*callback)(&cb, data); + } + + /* + * Get the glyphs. + */ + for (i = 0; i < 256; i++) { + if (metrics[i].bytes == 0) + continue; + + /* + * Put the file pointer back at the beginning of the bitmaps. + */ + fseek(in, pos, 0L); + + gp = fp->glyphs + fp->glyphs_used++; + + gp->encoding = i; + gp->dwidth = metrics[i].dwidth; + gp->swidth = (unsigned short) + (((double) gp->dwidth) * 72000.0) / + ((double) fp->point_size * fp->resolution_x); + + gp->bbx.ascent = metrics[i].ascent; + gp->bbx.descent = metrics[i].descent; + gp->bbx.width = metrics[i].rbearing + metrics[i].lbearing; + gp->bbx.height = metrics[i].ascent + metrics[i].descent; + gp->bbx.x_offset = metrics[i].lbearing; + gp->bbx.y_offset = -metrics[i].descent; + gp->bytes = metrics[i].bytes; + gp->bitmap = (unsigned char *) malloc(gp->bytes); + + fseek(in, (int) metrics[i].offset, 1L); + fread((char *) gp->bitmap, gp->bytes, 1, in); + + /* + * Call the callback if indicated. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = fp->glyphs_size; + cb.current = fp->glyphs_used; + (*callback)(&cb, data); + } + } + + /* + * Add a message indicating the font was converted. + */ + _bdf_add_comment(fp, "Font converted from VF to BDF.", 30); + _bdf_add_acmsg(fp, "Font converted from VF to BDF.", 30); + + /* + * Return the font. + */ + return fp; +} + +/* + * Load a simple binary font. + */ +static bdf_font_t * +_bdf_load_simple(FILE *in, int height, bdf_callback_t callback, void *data, + int type, int *awidth) +{ + int i; + unsigned short dwidth, swidth; + bdf_font_t *fp; + bdf_glyph_t *gp; + bdf_callback_struct_t cb; + + /* + * The point size of the font will be the height, the resolution will + * default to 72dpi, and the spacing will default to character cell. + */ + fp = bdf_new_font(0, (int) height, 72, 72, BDF_CHARCELL, 1); + + /* + * Force the bits per pixel to be one. + */ + fp->bpp = 1; + + /* + * Make sure the width is always set to 8 no matter what. This may + * change in the future, but not anytime soon. + */ + *awidth = fp->bbx.width = 8; + + /* + * Adjust the ascent and descent by hand for the 14pt and 8pt fonts. + */ + if (height != 16) { + fp->bbx.ascent++; + fp->bbx.descent--; + } + + /* + * Default the font ascent and descent to that of the bounding box. + */ + fp->font_ascent = fp->bbx.ascent; + fp->font_descent = fp->bbx.descent; + + /* + * Simple fonts will have at most 256 glyphs. + */ + fp->glyphs_size = 256; + fp->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * fp->glyphs_size); + (void) memset((char *) fp->glyphs, 0, + sizeof(bdf_glyph_t) * fp->glyphs_size); + + /* + * Determine the default scalable and device width for each character. + */ + dwidth = fp->bbx.width; + swidth = (unsigned short) + (((double) dwidth) * 72000.0) / + ((double) fp->point_size * fp->resolution_x); + + /* + * Set up to call the callback. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = fp->glyphs_size; + (*callback)(&cb, data); + } + + /* + * Now load the glyphs, assigning a default encoding. + */ + for (i = 0, gp = fp->glyphs; i < fp->glyphs_size; i++, gp++) { + gp->encoding = i; + gp->dwidth = dwidth; + gp->swidth = swidth; + (void) memcpy((char *) &gp->bbx, (char *) &fp->bbx, sizeof(bdf_bbx_t)); + + gp->bytes = height; + gp->bitmap = (unsigned char *) malloc(height); + fread((char *) gp->bitmap, height, 1, in); + fp->glyphs_used++; + + /* + * Call the callback if indicated. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = fp->glyphs_size; + cb.current = fp->glyphs_used; + (*callback)(&cb, data); + } + } + + /* + * Add a message indicating the font was converted. + */ + if (type == 1) { + _bdf_add_comment(fp, "Font converted from VGA/EGA to BDF.", 35); + _bdf_add_acmsg(fp, "Font converted from VGA/EGA to BDF.", 35); + } else if (type == 2) { + _bdf_add_comment(fp, "Fonts converted from CP to BDF.", 31); + _bdf_add_acmsg(fp, "Fonts converted from CP to BDF.", 31); + } + + /* + * Return the new font. + */ + return fp; +} + +/* + * A structure to pass around in update callbacks. + */ +typedef struct { + unsigned int total; + unsigned int curr; + unsigned int lcurr; + bdf_callback_t cback; + void *data; +} _bdf_update_rec_t; + +/* + * A routine to report the progress of loading a codepage font over all + * three fonts. + */ +static void +_bdf_codepage_progress(bdf_callback_struct_t *cb, void *data) +{ + _bdf_update_rec_t *up; + bdf_callback_struct_t ncb; + + up = (_bdf_update_rec_t *) data; + + if (up->cback == 0) + return; + + if (up->curr != 0 && cb->current == 0) { + up->lcurr = 0; + return; + } + + up->curr += cb->current - up->lcurr; + up->lcurr = cb->current; + + ncb.reason = cb->reason; + ncb.total = up->total; + ncb.current = up->curr; + + if (up->cback != 0) + (*up->cback)(&ncb, up->data); +} + +/* + * Load a codepage font which actually contains three fonts. This makes + * use of the routine that loads the simple fonts. + */ +static int +_bdf_load_codepage(FILE *in, bdf_callback_t callback, void *data, + bdf_font_t *fonts[3], int awidth[3]) +{ + _bdf_update_rec_t up; + + /* + * Initialize an override callback structure. + */ + up.cback = callback; + up.data = data; + up.total = 768; + up.curr = up.lcurr = 0; + + /* + * Load the 16pt font. + */ + if (fseek(in, 40, 0L)) + return BDF_NOT_CONSOLE_FONT; + + fonts[0] = _bdf_load_simple(in, 16, _bdf_codepage_progress, (void *) &up, + 0, &awidth[0]); + + /* + * Load the 14pt font. + */ + if (fseek(in, 4142, 0L)) { + if (fonts[0] != 0) + bdf_free_font(fonts[0]); + fonts[0] = 0; + return BDF_NOT_CONSOLE_FONT; + } + fonts[1] = _bdf_load_simple(in, 14, _bdf_codepage_progress, (void *) &up, + 0, &awidth[1]); + + /* + * Load the 8pt font. + */ + if (fseek(in, 7732, 0L)) { + if (fonts[0] != 0) + bdf_free_font(fonts[0]); + if (fonts[1] != 0) + bdf_free_font(fonts[1]); + fonts[0] = fonts[1] = 0; + return BDF_NOT_CONSOLE_FONT; + } + fonts[2] = _bdf_load_simple(in, 8, _bdf_codepage_progress, (void *) &up, + 2, &awidth[2]); + + /* + * All the fonts loaded OK. + */ + return BDF_OK; +} + +/************************************************************************** + * + * API. + * + **************************************************************************/ + +static unsigned char vfmagic[] = {0x01, 0x1e}; + +int +bdf_load_console_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback, + void *data, bdf_font_t *fonts[3], int *nfonts) +{ + unsigned char hdr[4]; + int res, awidth[3]; + double dp, dr; + bdf_property_t prop; + vfhdr_t vhdr; + struct stat st; + + (void) fstat(fileno(in), &st); + + *nfonts = 1; + awidth[0] = awidth[1] = awidth[2] = 0; + (void) memset((char *) fonts, 0, sizeof(bdf_font_t *) * 3); + + fread((char *) hdr, sizeof(unsigned char), 4, in); + + if (memcmp((char *) hdr, _bdf_psfcombined, 4) == 0) + return BDF_PSF_UNSUPPORTED; + + if (memcmp((char *) hdr, (char *) _bdf_psf1magic, 2) == 0 || + memcmp((char *) hdr, (char *) _bdf_psf2magic, 4) == 0) + /* + * Have a PSF font that may contain a mapping table. + */ + fonts[0] = bdf_load_psf(in, hdr, opts, callback, data, awidth); + else { + /* + * Reset to the beginning of the file. + */ + fseek(in, 0, 0L); + if (memcmp((char *) hdr, (char *) vfmagic, 2) == 0) { + /* + * Have a Sun vfont. Need to reload the header. + */ + (void) fread((char *) &vhdr, sizeof(vfhdr_t), 1, in); + fonts[0] = _bdf_load_vfont(in, &vhdr, callback, data, awidth); + } else if (st.st_size == 9780) { + /* + * Have a CP font with three sizes. Create all three fonts and + * return them. + */ + *nfonts = 3; + if ((res = _bdf_load_codepage(in, callback, data, fonts, awidth))) + return res; + } else { + /* + * Have a plain font with 256 characters. If the file size is not + * evenly divisible by 256, then the file is probably corrupt or + * is not a font. + */ + if (st.st_size & 0xff) + return BDF_NOT_CONSOLE_FONT; + + fonts[0] = _bdf_load_simple(in, st.st_size >> 8, callback, data, + 1, awidth); + } + } + + /* + * Add all the default properties. + */ + for (res = 0; res < *nfonts; res++) { + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = fonts[res]->point_size * 10; + bdf_add_font_property(fonts[res], &prop); + + dr = (double) fonts[res]->resolution_y; + dp = (double) (fonts[res]->point_size * 10); + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) (((dp * dr) / 722.7) + 0.5); + bdf_add_font_property(fonts[res], &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) fonts[res]->resolution_x; + bdf_add_font_property(fonts[res], &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) fonts[res]->resolution_y; + bdf_add_font_property(fonts[res], &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) fonts[res]->bbx.ascent; + bdf_add_font_property(fonts[res], &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) fonts[res]->bbx.descent; + bdf_add_font_property(fonts[res], &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = (awidth[res] / fonts[res]->glyphs_used) * 10; + bdf_add_font_property(fonts[res], &prop); + + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = "P"; + switch (fonts[res]->spacing) { + case BDF_PROPORTIONAL: prop.value.atom = "P"; break; + case BDF_MONOWIDTH: prop.value.atom = "M"; break; + case BDF_CHARCELL: prop.value.atom = "C"; break; + } + bdf_add_font_property(fonts[res], &prop); + } + + return BDF_OK; +} diff --git a/bdffnt.c b/bdffnt.c new file mode 100644 index 0000000..cfcab16 --- /dev/null +++ b/bdffnt.c @@ -0,0 +1,1042 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bdfP.h" + +/************************************************************************** + * + * Executable header and font structures. + * + **************************************************************************/ + +typedef struct { + unsigned short id; + unsigned short count; + unsigned int reshandler; +} res_typeinfo_t; + +typedef struct { + unsigned short offset; + unsigned short length; + unsigned short flags; + unsigned short id; + unsigned short handle; + unsigned short usage; +} res_nameinfo_t; + +typedef struct { + unsigned short e_magic; + unsigned short e_cblp; + unsigned short e_cp; + unsigned short e_crlc; + unsigned short e_cparhdr; + unsigned short e_minalloc; + unsigned short e_maxalloc; + unsigned short e_ss; + unsigned short e_sp; + unsigned short e_csum; + unsigned short e_ip; + unsigned short e_cs; + unsigned short e_lfarlc; + unsigned short e_ovno; + unsigned short e_res[4]; + unsigned short e_oemid; + unsigned short e_oeminfo; + unsigned short e_res2[10]; + unsigned short e_lfanew; +} dos_exe_t; + +typedef struct { + unsigned short ne_magic; + unsigned char linker_version; + unsigned char linker_revision; + unsigned short entry_tab_offset; + unsigned short entry_tab_length; + unsigned int reserved1; + unsigned short format_flags; + unsigned short auto_data_seg; + unsigned short local_heap_length; + unsigned short stack_length; + unsigned short ip; + unsigned short cs; + unsigned short sp; + unsigned short ss; + unsigned short n_segment_tab; + unsigned short n_mod_ref_tab; + unsigned short nrname_tab_length; + unsigned short segment_tab_offset; + unsigned short resource_tab_offset; + unsigned short rname_tab_offset; + unsigned short moduleref_tab_offset; + unsigned short iname_tab_offset; + unsigned int nrname_tab_offset; + unsigned short n_mov_entry_points; + unsigned short align_shift_count; + unsigned short n_resource_seg; + unsigned char operating_system; + unsigned char additional_flags; + unsigned short fastload_offset; + unsigned short fastload_length; + unsigned short reserved2; + unsigned short expect_version; +} win_exe_t; + +typedef struct { + unsigned short dfVersion; + unsigned int dfSize; + unsigned char dfCopyright[60]; + unsigned short dfType; + unsigned short dfPoints; + unsigned short dfVertRes; + unsigned short dfHorizRes; + unsigned short dfAscent; + unsigned short dfInternalLeading; + unsigned short dfExternalLeading; + unsigned char dfItalic; + unsigned char dfUnderline; + unsigned char dfStrikeOut; + unsigned short dfWeight; + unsigned char dfCharSet; + unsigned short dfPixWidth; + unsigned short dfPixHeight; + unsigned char dfPitchAndFamily; + unsigned short dfAvgWidth; + unsigned short dfMaxWidth; + unsigned char dfFirstChar; + unsigned char dfLastChar; + unsigned char dfDefaultChar; + unsigned char dfBreakChar; + unsigned short dfWidthBytes; + unsigned int dfDevice; + unsigned int dfFace; + unsigned int dfBitsPointer; + unsigned int dfBitsOffset; + unsigned char dfReserved; + unsigned int dfFlags; + unsigned short dfAspace; + unsigned short dfBspace; + unsigned short dfCspace; + unsigned short dfColorPointer; + unsigned char dfReserved1[4]; +#if 0 + unsigned int dfColorPointer; + unsigned char dfReserved1[16]; +#endif +} fntinfo_t; + +/* + * A structure used to load the font info data before transfering to the + * real font structure. + */ +typedef struct { + unsigned char dfVersion[2]; + unsigned char dfSize[4]; + unsigned char dfCopyright[60]; + unsigned char dfType[2]; + unsigned char dfPoints[2]; + unsigned char dfVertRes[2]; + unsigned char dfHorizRes[2]; + unsigned char dfAscent[2]; + unsigned char dfInternalLeading[2]; + unsigned char dfExternalLeading[2]; + unsigned char dfItalic[1]; + unsigned char dfUnderline[1]; + unsigned char dfStrikeOut[1]; + unsigned char dfWeight[2]; + unsigned char dfCharSet[1]; + unsigned char dfPixWidth[2]; + unsigned char dfPixHeight[2]; + unsigned char dfPitchAndFamily[1]; + unsigned char dfAvgWidth[2]; + unsigned char dfMaxWidth[2]; + unsigned char dfFirstChar[1]; + unsigned char dfLastChar[1]; + unsigned char dfDefaultChar[1]; + unsigned char dfBreakChar[1]; + unsigned char dfWidthBytes[2]; + unsigned char dfDevice[4]; + unsigned char dfFace[4]; + unsigned char dfBitsPointer[4]; + unsigned char dfBitsOffset[4]; + unsigned char dfReserved[1]; + unsigned char dfFlags[4]; + unsigned char dfAspace[2]; + unsigned char dfBspace[2]; + unsigned char dfCspace[2]; +#if 0 + unsigned char dfColorPointer[4]; + unsigned char dfReserved1[16]; +#endif + unsigned char dfColorPointer[2]; + unsigned char dfReserved1[4]; +} fishadow_t; + +typedef struct { + unsigned int width; + unsigned int offset; +} chrinfo_t; + +/* + * Structure used for opening FON/FNT fonts. Tracks the list of offsets to + * the font or fonts in the file. + */ +typedef struct _bdffnt_font_t { + FILE *in; + unsigned int *fonts; + unsigned int allocated; + unsigned int nfonts; + unsigned int first; + + chrinfo_t *cinfo; + unsigned int cinfo_used; + unsigned int cinfo_size; + + fntinfo_t info; +} _bdffnt_font_t; + +/************************************************************************** + * + * Local macros and variables. + * + **************************************************************************/ + +/* + * Executable signatures. + */ +#define DOS_SIG 0x5a4d +#define WIN_SIG 0x454e + +/* + * Weight values. + */ +#define BDFFNT_WEIGHT_DONTCARE 0 +#define BDFFNT_WEIGHT_THIN 100 +#define BDFFNT_WEIGHT_EXTRALIGHT 200 +#define BDFFNT_WEIGHT_ULTRALIGHT 200 +#define BDFFNT_WEIGHT_LIGHT 300 +#define BDFFNT_WEIGHT_NORMAL 400 +#define BDFFNT_WEIGHT_REGULAR 400 +#define BDFFNT_WEIGHT_MEDIUM 500 +#define BDFFNT_WEIGHT_SEMIBOLD 600 +#define BDFFNT_WEIGHT_DEMIBOLD 600 +#define BDFFNT_WEIGHT_BOLD 700 +#define BDFFNT_WEIGHT_EXTRABOLD 800 +#define BDFFNT_WEIGHT_ULTRABOLD 800 +#define BDFFNT_WEIGHT_HEAVY 900 +#define BDFFNT_WEIGHT_BLACK 900 + +/* + * Local structures to hold header info. + */ +static dos_exe_t dos; +static win_exe_t win; + +/************************************************************************** + * + * Support functions. + * + **************************************************************************/ + +static void +_bdffnt_endian_shorts(unsigned short *sp, unsigned int n) +{ + for (; n > 0; n--, sp++) + *sp = ((*sp >> 8) & 0xff) | + (((*sp & 0xff) << 8) & 0xff00); +} + +static void +_bdffnt_endian_ints(unsigned int *lp, unsigned int n) +{ + for (; n > 0; n--, lp++) + *lp = (((*lp & 0xff) << 24) & 0xff000000) | + (((*lp >> 8) << 16) & 0xff0000) | + (((*lp >> 16) << 8) & 0xff00) | + ((*lp >> 24) & 0xff); +} + +static unsigned short +_bdffnt_get_short(unsigned char *field) +{ + int a = 0, b = 1; + + return (field[a] & 0xff) | ((field[b] & 0xff) << 8); +} + +static unsigned int +_bdffnt_get_int(unsigned char *field) +{ + int a = 0, b = 1, c = 2, d = 3; + + return (field[a] & 0xff) | ((field[b] & 0xff) << 8) | + ((field[c] & 0xff) << 16) | ((field[d] & 0xff) << 24); +} + +/* + * This routine is called when the font header needs some fields adjusted for + * the endianess of the machine. + */ +static void +_bdffnt_transfer_fntinfo(fntinfo_t *fi, fishadow_t *fis) +{ + fi->dfVersion = _bdffnt_get_short(fis->dfVersion); + (void) memcpy(fi->dfCopyright, fis->dfCopyright, 60); + fi->dfSize = _bdffnt_get_int(fis->dfSize); + fi->dfType = _bdffnt_get_short(fis->dfType); + fi->dfPoints = _bdffnt_get_short(fis->dfPoints); + fi->dfVertRes = _bdffnt_get_short(fis->dfVertRes); + fi->dfHorizRes = _bdffnt_get_short(fis->dfHorizRes); + fi->dfAscent = _bdffnt_get_short(fis->dfAscent); + fi->dfInternalLeading = _bdffnt_get_short(fis->dfInternalLeading); + fi->dfExternalLeading = _bdffnt_get_short(fis->dfExternalLeading); + fi->dfItalic = fis->dfItalic[0]; + fi->dfUnderline = fis->dfUnderline[0]; + fi->dfStrikeOut = fis->dfStrikeOut[0]; + fi->dfWeight = _bdffnt_get_short(fis->dfWeight); + fi->dfCharSet = fis->dfCharSet[0]; + fi->dfPixWidth = _bdffnt_get_short(fis->dfPixWidth); + fi->dfPixHeight = _bdffnt_get_short(fis->dfPixHeight); + fi->dfPitchAndFamily = fis->dfPitchAndFamily[0]; + fi->dfAvgWidth = _bdffnt_get_short(fis->dfAvgWidth); + fi->dfMaxWidth = _bdffnt_get_short(fis->dfMaxWidth); + fi->dfFirstChar = fis->dfFirstChar[0]; + fi->dfLastChar = fis->dfLastChar[0]; + fi->dfDefaultChar = fis->dfDefaultChar[0]; + fi->dfBreakChar = fis->dfBreakChar[0]; + fi->dfWidthBytes = _bdffnt_get_short(fis->dfWidthBytes); + fi->dfDevice = _bdffnt_get_int(fis->dfDevice); + fi->dfFace = _bdffnt_get_int(fis->dfFace); + fi->dfBitsPointer = _bdffnt_get_int(fis->dfBitsPointer); + fi->dfBitsOffset = _bdffnt_get_int(fis->dfBitsOffset); + fi->dfReserved = fis->dfReserved[0]; + fi->dfFlags = _bdffnt_get_int(fis->dfFlags); + fi->dfAspace = _bdffnt_get_short(fis->dfAspace); + fi->dfBspace = _bdffnt_get_short(fis->dfBspace); + fi->dfCspace = _bdffnt_get_short(fis->dfCspace); +#if 0 + fi->dfColorPointer = _bdffnt_get_int(fis->dfColorPointer); + (void) memcpy(fi->dfReserved1, fis->dfReserved1, 16); +#endif + fi->dfColorPointer = _bdffnt_get_short(fis->dfColorPointer); + (void) memcpy(fi->dfReserved1, fis->dfReserved1, 4); +} + +static char * +_bdffnt_weight_name(unsigned short weight, int *len) +{ + char *name; + + if (weight == 0) { + name = "Medium"; + *len = 6; + } else if (weight <= BDFFNT_WEIGHT_THIN) { + name = "Thin"; + *len = 4; + } else if (weight <= BDFFNT_WEIGHT_ULTRALIGHT) { + name = "UltraLight"; + *len = 10; + } else if (weight <= BDFFNT_WEIGHT_LIGHT) { + name = "Light"; + *len = 5; + } else if (weight <= BDFFNT_WEIGHT_MEDIUM) { + name = "Medium"; + *len = 6; + } else if (weight <= BDFFNT_WEIGHT_DEMIBOLD) { + name = "DemiBold"; + *len = 8; + } else if (weight <= BDFFNT_WEIGHT_BOLD) { + name = "Bold"; + *len = 4; + } else if (weight <= BDFFNT_WEIGHT_ULTRABOLD) { + name = "UltraBold"; + *len = 9; + } else { + name = "Black"; + *len = 5; + } + return name; +} + +static char * +_bdffnt_cset_name(int cset, int *enc) +{ + *enc = 0; + switch (cset) { + case 0: *enc = 1; return "ISO8859"; + case 1: return "WinDefault"; + case 2: return "Symbol"; + case 128: return "JISX0208.1983"; + case 129: return "MSHangul"; + case 134: return "GB2312.1980"; + case 136: return "Big5"; + case 161: *enc = 1; return "CP1253"; + case 162: *enc = 1; return "CP1254"; + case 177: *enc = 1; return "CP1255"; + case 178: *enc = 1; return "CP1256"; + case 186: *enc = 1; return "CP1257"; + case 204: *enc = 1; return "CP1251"; + case 238: *enc = 1; return "CP1250"; + case 255: return "OEM"; + } + return "Unknown"; +} + +/************************************************************************** + * + * API. + * + **************************************************************************/ + +int +bdffnt_open_font(char *path, bdffnt_font_t *fnt) +{ + unsigned short sshift, version; + int i; + unsigned int off; + FILE *in; + _bdffnt_font_t *f; + res_typeinfo_t rtype; + res_nameinfo_t ninfo; + + if (path == 0 || *path == 0 || fnt == 0) + return 0; + + if ((in = fopen(path, "r")) == 0) + return -1; + + *fnt = 0; + f = (_bdffnt_font_t *) malloc(sizeof(_bdffnt_font_t)); + (void) memset((char *) f, 0, sizeof(_bdffnt_font_t)); + + f->in = in; + + if (fread((char *) &dos, 1, sizeof(dos_exe_t), in) != sizeof(dos_exe_t)) { + fclose(in); + free((char *) f); + return -1; + } + + /* + * Endian everything if on a big-endian machine. + */ + if (!bdf_little_endian()) + _bdffnt_endian_shorts((unsigned short *) &dos, + sizeof(dos_exe_t) / sizeof(unsigned short)); + + /* + * Check for exe signatures. + */ + if (dos.e_magic == DOS_SIG) { + fseek(in, dos.e_lfanew, 0L); + if (fread((char *) &win, 1, sizeof(win_exe_t), in) != + sizeof(win_exe_t)) { + fclose(in); + free((char *) f); + return -1; + } + + /* + * Only endian the fields used. + */ + if (!bdf_little_endian()) { + _bdffnt_endian_shorts(&win.ne_magic, 1); + _bdffnt_endian_shorts(&win.resource_tab_offset, 1); + _bdffnt_endian_shorts(&win.rname_tab_offset, 1); + } + + /* + * This means the file is either NT 32-bit or something else. + */ + if (win.ne_magic != WIN_SIG) { + fclose(in); + free((char *) f); + return -1; + } + + /* + * Seek to the beginning of the resources. + */ + off = dos.e_lfanew + win.resource_tab_offset; + fseek(in, off, 0L); + fread((char *) &sshift, 1, sizeof(unsigned short), in); + if (!bdf_little_endian()) + _bdffnt_endian_shorts(&sshift, 1); + + /* + * Search the resources for all the font resources. + */ + if (fread((char *) &rtype, 1, sizeof(res_typeinfo_t), in) != + sizeof(res_typeinfo_t)) { + fclose(in); + free((char *) f); + return -1; + } + while (rtype.id != 0) { + /* + * Change the endian order of the first two fields if necessary. + */ + if (!bdf_little_endian()) + _bdffnt_endian_shorts((unsigned short *) &rtype, 2); + + if (rtype.id == 0x8008) + break; + + /* + * Seek to the next resource entry and read it. + */ + off = rtype.count * sizeof(res_nameinfo_t); + fseek(in, off, 1L); + + if (fread((char *) &rtype, 1, sizeof(res_typeinfo_t), in) != + sizeof(res_typeinfo_t)) { + fclose(in); + free((char *) f); + return -1; + } + } + if (rtype.id == 0x8008) { + /* + * Found a font resource, cycle through the entries. + */ + for (i = 0; i < rtype.count; i++) { + if (fread((char *) &ninfo, 1, sizeof(res_nameinfo_t), in) != + sizeof(res_nameinfo_t)) { + fclose(in); + if (f->allocated > 0) + free((char *) f->fonts); + free((char *) f); + return -1; + } + + if (!bdf_little_endian()) + _bdffnt_endian_shorts((unsigned short *) &ninfo, + sizeof(res_nameinfo_t) >> 1); + + /* + * Check to make sure that the indicated offset is really a + * valid font. + */ + off = ftell(in); + fseek(in, (ninfo.offset << sshift), 0L); + fread((char *) &version, sizeof(unsigned short), 1, in); + fseek(in, off, 0L); + if (!bdf_little_endian()) + _bdffnt_endian_shorts(&version, 1); + if (version != 0x200 && version != 0x300) + continue; + + if (f->nfonts == 0) + f->first = ninfo.offset << sshift; + else { + if (f->nfonts >= f->allocated) { + if (f->allocated == 0) + f->fonts = (unsigned int *) + malloc(sizeof(unsigned int) << 3); + else + f->fonts = (unsigned int *) + realloc((char *) f->fonts, + sizeof(unsigned int) * + (f->allocated + 8)); + f->allocated += 8; + } + f->fonts[0] = f->first; + f->fonts[f->nfonts] = ninfo.offset << sshift; + } + f->nfonts++; + } + } + } else if (dos.e_magic == 0x200 || dos.e_magic == 0x300) { + /* + * Probably have a .FNT file. + */ + f->first = ftell(in); + f->nfonts = 1; + } else + return -1; + + if (f->nfonts == 0) { + /* + * If no fonts were loaded, free everything up. + */ + free((char *) f); + return -1; + } + *fnt = f; + return 1; +} + +void +bdffnt_close_font(bdffnt_font_t font) +{ + if (font == 0) + return; + + fclose(font->in); + if (font->cinfo_size > 0) + free((char *) font->cinfo); + if (font->allocated > 0) + free((char *) font->fonts); + free((char *) font); +} + +int +bdffnt_font_count(bdffnt_font_t font) +{ + return (font != 0) ? font->nfonts : 0; +} + +int +bdffnt_get_copyright(bdffnt_font_t font, unsigned int fontID, + unsigned char *string) +{ + int off; + unsigned char *sp; + fishadow_t fi; + + off = (font->nfonts == 1) ? font->first : font->fonts[fontID]; + fseek(font->in, off, 0L); + + if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) != + sizeof(fishadow_t)) + return -1; + + for (sp = fi.dfCopyright; (*string = *sp); sp++, string++) ; + return sp - fi.dfCopyright; +} + +int +bdffnt_get_facename(bdffnt_font_t font, unsigned int fontID, int for_xlfd, + unsigned char *string) +{ + int wlen, c; + int off; + unsigned char *sp, *wname; + fishadow_t fi; + + if (font == 0 || fontID >= font->nfonts || string == 0) + return 0; + + off = (font->nfonts == 1) ? font->first : font->fonts[fontID]; + fseek(font->in, off, 0L); + + if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) != + sizeof(fishadow_t)) + return -1; + + _bdffnt_transfer_fntinfo(&font->info, &fi); + + /* + * Seek to the location of the typeface name. + */ + off = off + font->info.dfFace; + fseek(font->in, off, 0L); + + /* + * Copy the typeface name into the parameter. + * + * stops when: - == 0 -> end of string + * - < 0 -> end of file + */ + sp = string; + while ((c = getc(font->in)) > 0) { + *sp = c; + if (for_xlfd && *sp == '-') + *sp = ' '; + sp++; + } + *sp = 0; + + /* + * If the typeface name is not for an XLFD name, then append the style, + * weight and point size so the names will be informative. + */ + if (!for_xlfd) { + *sp++ = ' '; + if (font->info.dfItalic & 1) { + (void) strcpy((char *) sp, "Italic "); + sp += 7; + } + wname = (unsigned char *) _bdffnt_weight_name(font->info.dfWeight, + &wlen); + (void) strcpy((char *) sp, (char *) wname); + sp += wlen; + *sp++ = ' '; + sprintf((char *) sp, "%hdpt", font->info.dfPoints); + sp += strlen((char *) sp); + } + + return sp - string; +} + +int +bdffnt_char_count(bdffnt_font_t font, unsigned int fontID) +{ + int off; + fishadow_t fi; + + if (font == 0 || fontID >= font->nfonts) + return 0; + + off = (font->nfonts == 1) ? font->first : font->fonts[fontID]; + fseek(font->in, off, 0L); + + if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) != + sizeof(fishadow_t)) + return -1; + + _bdffnt_transfer_fntinfo(&font->info, &fi); + + return (font->info.dfLastChar - font->info.dfFirstChar) + 1; +} + +int +bdffnt_font_pointsize(bdffnt_font_t font, unsigned int fontID) +{ + int off; + fishadow_t fi; + + if (font == 0 || fontID >= font->nfonts) + return 0; + + off = (font->nfonts == 1) ? font->first : font->fonts[fontID]; + fseek(font->in, off, 0L); + + if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) != + sizeof(fishadow_t)) + return -1; + + _bdffnt_transfer_fntinfo(&font->info, &fi); + + return font->info.dfPoints; +} + +int +bdffnt_load_font(bdffnt_font_t font, unsigned int fontID, + bdf_callback_t callback, void *data, bdf_font_t **out) +{ + int x, y, i, nchars; + unsigned short tmp, bpr; + int off; + double swscale; + chrinfo_t *cp; + bdf_font_t *f; + bdf_glyph_t *gp; + char name[256]; + bdf_property_t prop; + bdf_callback_struct_t cb; + fishadow_t fi; + + if (font == 0 || fontID >= font->nfonts || out == 0) + return 0; + + off = (font->nfonts == 1) ? font->first : font->fonts[fontID]; + fseek(font->in, off, 0L); + + if (fread((char *) &fi, 1, sizeof(fishadow_t), font->in) != + sizeof(fishadow_t)) + return -1; + + _bdffnt_transfer_fntinfo(&font->info, &fi); + + /* + * This cheap hack needed to get to the character info because the FNT + * docs don't mention that for Win 2.0 fonts, the header was a different + * size. This may be the case for a version 3.* as well, but I have no + * version 3.* fonts to test with. + */ + fseek(font->in, off + 118, 0L); + + /* + * Determine how many character info records there are and make sure + * enough space is allocated in the font structure. + */ + nchars = (font->info.dfLastChar - font->info.dfFirstChar) + 1; + + if (font->cinfo_size < nchars) { + if (font->cinfo_size == 0) + font->cinfo = (chrinfo_t *) malloc(sizeof(chrinfo_t) * nchars); + else + font->cinfo = (chrinfo_t *) realloc((char *) font->cinfo, + sizeof(chrinfo_t) * nchars); + font->cinfo_size = nchars; + } + cp = font->cinfo; + for (i = 0, font->cinfo_used = 0; i < nchars; i++, cp++) { + fread((char *) &tmp, sizeof(unsigned short), 1, font->in); + if (!bdf_little_endian()) + _bdffnt_endian_shorts(&tmp, 1); + cp->width = tmp; + if (font->info.dfVersion == 0x300) { + fread((char *) &cp->offset, sizeof(unsigned int), 1, font->in); + if (!bdf_little_endian()) + _bdffnt_endian_ints(&cp->offset, 1); + } else { + fread((char *) &tmp, sizeof(unsigned short), 1, font->in); + if (!bdf_little_endian()) + _bdffnt_endian_shorts(&tmp, 1); + cp->offset = tmp; + } + } + + /* + * Create the font. + */ + f = (bdf_font_t *) malloc(sizeof(bdf_font_t)); + (void) memset((char *) f, 0, sizeof(bdf_font_t)); + + /* + * Set some defaults. + */ + f->bpp = 1; + f->default_glyph = font->info.dfDefaultChar + font->info.dfFirstChar; + f->spacing = (font->info.dfFlags & 1) ? BDF_CHARCELL : BDF_PROPORTIONAL; + f->glyphs_size = nchars; + f->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * nchars); + (void) memset((char *) f->glyphs, 0, sizeof(bdf_glyph_t) * nchars); + f->point_size = font->info.dfPoints; + f->resolution_x = font->info.dfHorizRes; + f->resolution_y = font->info.dfVertRes; + f->font_ascent = font->info.dfAscent; + f->font_descent = f->font_ascent - font->info.dfPixHeight; + + /* + * Set the font bounding box. + */ + f->bbx.width = font->info.dfMaxWidth; + f->bbx.height = font->info.dfPixHeight; + f->bbx.ascent = font->info.dfAscent; + f->bbx.descent = f->bbx.height - f->bbx.ascent; + f->bbx.y_offset = -f->bbx.descent; + f->bbx.x_offset = 0; + + if (f->spacing == BDF_CHARCELL) + f->monowidth = f->bbx.width; + + /* + * Determine the SWIDTH scale factor. + */ + swscale = ((double) f->resolution_y) * ((double) f->point_size); + + /* + * Call the initial callback if one was provided. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = nchars; + (*callback)(&cb, data); + } + + /* + * Start collecting glyphs. + */ + for (i = 0, cp = font->cinfo, gp = f->glyphs; i < nchars; + i++, cp++, gp++) { + /* + * Set the glyph encoding. + */ + gp->encoding = font->info.dfFirstChar + i; + + /* + * Set the glyph bounding box. + */ + gp->bbx.width = gp->dwidth = cp->width; + gp->bbx.height = font->info.dfPixHeight; + gp->bbx.ascent = font->info.dfAscent; + gp->bbx.descent = gp->bbx.height - gp->bbx.ascent; + gp->bbx.y_offset = -gp->bbx.descent; + gp->bbx.x_offset = 0; + gp->swidth = (unsigned short) + (((double) gp->dwidth) * 72000.0) / swscale; + + /* + * Allocate the glyph bitmap. + */ + bpr = (cp->width + 7) >> 3; + gp->bytes = bpr * font->info.dfPixHeight; + gp->bitmap = (unsigned char *) malloc(gp->bytes); + + /* + * Seek to the bitmap and read the bytes. + */ + fseek(font->in, off + cp->offset, 0L); + if (bpr == 1) + fread((char *) gp->bitmap, gp->bytes, 1, font->in); + else { + /* + * Typical MS wierdness. This awkward section is just to get the + * bytes in the right place. + */ + for (x = 0; x < bpr; x++) { + for (y = 0; y < gp->bbx.height; y++) + gp->bitmap[(y * bpr) + x] = getc(font->in); + } + } + + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.current = i; + cb.total = nchars; + (*callback)(&cb, data); + } + } + + /* + * Call the callback one more time to make sure the client knows the + * load is done. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.current = nchars; + cb.total = nchars; + (*callback)(&cb, data); + } + + /* + * Set the number of glyphs used. + */ + f->glyphs_used = gp - f->glyphs; + + /* + * Add all the properties. + */ + prop.name = "FOUNDRY"; + prop.format = BDF_ATOM; + prop.value.atom = "Windows"; + bdf_add_font_property(f, &prop); + + i = bdffnt_get_facename(font, fontID, 1, (unsigned char *) name); + prop.name = "FAMILY_NAME"; + prop.format = BDF_ATOM; + prop.value.atom = name; + bdf_add_font_property(f, &prop); + + prop.name = "WEIGHT_NAME"; + prop.format = BDF_ATOM; + prop.value.atom = _bdffnt_weight_name(font->info.dfWeight, &i); + bdf_add_font_property(f, &prop); + + prop.name = "SLANT"; + prop.format = BDF_ATOM; + if (font->info.dfItalic & 1) + prop.value.atom = "I"; + else + prop.value.atom = "R"; + bdf_add_font_property(f, &prop); + + prop.name = "SETWIDTH_NAME"; + prop.format = BDF_ATOM; + prop.value.atom = "Normal"; + bdf_add_font_property(f, &prop); + + if (font->info.dfPitchAndFamily & 0xf0) { + prop.name = "ADDSTYLE_NAME"; + prop.format = BDF_ATOM; + switch (font->info.dfPitchAndFamily & 0xf0) { + case 0x20: prop.value.atom = "Swiss"; break; + case 0x30: prop.value.atom = "Modern"; break; + case 0x40: prop.value.atom = "Script"; break; + case 0x50: prop.value.atom = "Decorative"; break; + default: prop.value.atom = 0; + } + if (prop.value.atom != 0) + bdf_add_font_property(f, &prop); + } + + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) + ((((double) (f->point_size * 10) * + (double) f->resolution_y) / 722.7) + 0.5); + bdf_add_font_property(f, &prop); + + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->point_size * 10; + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) f->resolution_x; + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) f->resolution_y; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->font_ascent; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->font_descent; + bdf_add_font_property(f, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->info.dfAvgWidth * 10; + bdf_add_font_property(f, &prop); + + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = "P"; + switch (f->spacing) { + case BDF_PROPORTIONAL: prop.value.atom = "P"; break; + case BDF_MONOWIDTH: prop.value.atom = "M"; break; + case BDF_CHARCELL: prop.value.atom = "C"; break; + } + bdf_add_font_property(f, &prop); + + prop.name = "CHARSET_REGISTRY"; + prop.format = BDF_ATOM; + prop.value.atom = _bdffnt_cset_name(font->info.dfCharSet, &i); + bdf_add_font_property(f, &prop); + + sprintf(name, "%d", i); + prop.name = "CHARSET_ENCODING"; + prop.format = BDF_ATOM; + prop.value.atom = name; + bdf_add_font_property(f, &prop); + + /* + * Generate the XLFD name. + */ + f->name = bdf_make_xlfd_name(f, 0, 0); + + /* + * Add messages indicating the font was converted. + */ + _bdf_add_comment(f, "Font converted from FNT/FON to BDF.", 35); + _bdf_add_acmsg(f, "Font converted from FNT/FON to BDF.", 35); + + /* + * Mark the font as being modified. + */ + f->modified = 1; + + *out = f; + + return 0; +} diff --git a/bdfgname.c b/bdfgname.c new file mode 100644 index 0000000..f038843 --- /dev/null +++ b/bdfgname.c @@ -0,0 +1,357 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bdfP.h" + +typedef struct { + int code; + int start; + int end; + int pad; +} _bdf_adobe_name_t; + +static _bdf_adobe_name_t *adobe_names; +static unsigned int adobe_names_size; +static unsigned int adobe_names_used; + +/* + * Provide a maximum length for glyph names just to make things clearer. + */ +#define MAX_GLYPH_NAME_LEN 127 + +static int +bdf_getline(FILE *in, char *buf, int limit) +{ + int c, i; + + c = EOF; + + for (i = 0; i < limit - 1; i++) { + if ((c = getc(in)) == EOF || (c == '\n' || c == '\r')) + break; + buf[i] = c; + } + buf[i] = 0; + + /* + * Discard the rest of the line which did not fit into the buffer. + */ + while (c != EOF && c != '\n' && c != '\r') + c = getc(in); + + if (c == '\r') { + /* + * Check for a trailing newline. + */ + c = getc(in); + if (c != '\n') + ungetc(c, in); + } + + return i; +} + +static int +_bdf_find_name(int code, char *name, FILE *in) +{ + int c, i, pos; + char *sp, buf[256]; + + while (!feof(in)) { + pos = ftell(in); + (void) bdf_getline(in, buf, 256); + while (!feof(in) && (buf[0] == 0 || buf[0] == '#')) { + buf[0] = 0; + pos = ftell(in); + (void) bdf_getline(in, buf, 256); + } + + if (buf[0] == 0) + return -1; + + c = _bdf_atol(buf, 0, 16); + + if (c > code) { + /* + * Restore the last position read in case the code is not in the + * file and the current code is greater than the expected code. + */ + fseek(in, pos, 0L); + return -1; + } + + if (c == code) { + for (sp = buf; *sp != ';'; sp++) ; + sp++; + for (i = 0; *sp != ';' && i < MAX_GLYPH_NAME_LEN; sp++, i++) + name[i] = *sp; + name[i] = 0; + return i; + } + } + return -1; +} + +static int +by_encoding(const void *a, const void *b) +{ + _bdf_adobe_name_t *c1, *c2; + + c1 = (_bdf_adobe_name_t *) a; + c2 = (_bdf_adobe_name_t *) b; + if (c1->code < c2->code) + return -1; + else if (c1->code > c2->code) + return 1; + return 0; +} + +static void +_bdf_load_adobe_names(FILE *in) +{ + int c, pos; + char *sp, buf[256]; + + /* + * Go back to the beginning of the file to look for the code because the + * codes are not in order in the current Adobe Glyph Name list file. + */ + fseek(in, 0, 0); + + while (!feof(in)) { + pos = ftell(in); + (void) bdf_getline(in, buf, 256); + while (!feof(in) && (buf[0] == 0 || buf[0] == '#')) { + buf[0] = 0; + pos = ftell(in); + (void) bdf_getline(in, buf, 256); + } + + if (adobe_names_used == adobe_names_size) { + if (adobe_names_size == 0) + adobe_names = (_bdf_adobe_name_t *) + malloc(sizeof(_bdf_adobe_name_t) << 9); + else + adobe_names = (_bdf_adobe_name_t *) + realloc((char *) adobe_names, + sizeof(_bdf_adobe_name_t) * + (adobe_names_size + 512)); + (void) memset((char *) (adobe_names + adobe_names_size), 0, + sizeof(_bdf_adobe_name_t) << 9); + adobe_names_size += 512; + } + + adobe_names[adobe_names_used].start = pos; + for (sp = buf; *sp != ';'; sp++) ; + adobe_names[adobe_names_used].end = pos + (sp - buf); + sp++; + + c = _bdf_atol(sp, 0, 16); + + /* + * Ignore the Adobe-specific names in the Private Use Area. + */ + if (c < 0xe000 || c > 0xf8ff) + adobe_names[adobe_names_used++].code = c; + } + + /* + * Sort the results by code. + */ + qsort((char *) adobe_names, adobe_names_used, sizeof(_bdf_adobe_name_t), + by_encoding); +} + +static int +_bdf_find_adobe_name(int code, char *name, FILE *in) +{ + int len; + int l, r, m; + + if (code < 0x20 || (code >= 0x7f && code <= 0x9f) || + code == 0xfffe || code == 0xffff) { + sprintf(name, "char%u", code); + return (int) strlen(name); + } + + if (code >= 0xe000 && code <= 0xf8ff) { + sprintf(name, "uni%04X", code & 0xffff); + return (int) strlen(name); + } + + if (adobe_names_size == 0) + _bdf_load_adobe_names(in); + + l = 0; + r = adobe_names_used - 1; + while (l <= r) { + m = (l + r) >> 1; + if (adobe_names[m].code < code) + l = m + 1; + else if (adobe_names[m].code > code) + r = m - 1; + else { + fseek(in, adobe_names[m].start, 0); + len = adobe_names[m].end - adobe_names[m].start; + if (len > MAX_GLYPH_NAME_LEN) + len = MAX_GLYPH_NAME_LEN; + len = (int) fread(name, sizeof(char), len, in); + name[len] = 0; + return len; + } + } + + sprintf(name, "uni%04X", code & 0xffff); + return (int) strlen(name); +} + +static int +_bdf_set_glyph_names(FILE *in, bdf_font_t *font, bdf_callback_t callback, + int adobe) +{ + int changed; + int i, size, len; + bdf_glyph_t *gp; + bdf_callback_struct_t cb; + char name[MAX_GLYPH_NAME_LEN + 1]; + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME_START; + cb.current = 0; + cb.total = font->glyphs_used; + (*callback)(&cb, 0); + } + for (changed = 0, i = 0, gp = font->glyphs; i < font->glyphs_used; + i++, gp++) { + size = (adobe) ? + _bdf_find_adobe_name(gp->encoding, name, in) : + _bdf_find_name(gp->encoding, name, in); + if (size < 0) + continue; + + len = (gp->name) ? strlen(gp->name) : 0; + if (len == 0) { + gp->name = (char *) _bdf_strdup((unsigned char *) name, size + 1); + changed = 1; + } else if (size != len || strcmp(gp->name, name) != 0) { + /* + * Simply resize existing storage so lots of memory allocations + * are not needed. + */ + if (size > len) + gp->name = (char *) realloc(gp->name, size + 1); + (void) strcpy(gp->name, name); + changed = 1; + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = i; + (*callback)(&cb, 0); + } + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = cb.total; + (*callback)(&cb, 0); + } + + return changed; +} + +int +bdf_set_unicode_glyph_names(FILE *in, bdf_font_t *font, + bdf_callback_t callback) +{ + return _bdf_set_glyph_names(in, font, callback, 0); +} + +int +bdf_set_adobe_glyph_names(FILE *in, bdf_font_t *font, bdf_callback_t callback) +{ + return _bdf_set_glyph_names(in, font, callback, 1); +} + +int +bdf_set_glyph_code_names(int prefix, bdf_font_t *font, bdf_callback_t callback) +{ + int changed; + int i, size, len; + bdf_glyph_t *gp; + bdf_callback_struct_t cb; + char name[128]; + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME_START; + cb.current = 0; + cb.total = font->glyphs_used; + (*callback)(&cb, 0); + } + for (changed = 0, i = 0, gp = font->glyphs; i < font->glyphs_used; + i++, gp++) { + switch (prefix) { + case 'u': sprintf(name, "uni%04X", gp->encoding & 0xffff); break; + case 'x': sprintf(name, "0x%04X", gp->encoding & 0xffff); break; + case '+': sprintf(name, "U+%04X", gp->encoding & 0xffff); break; + case '\\': sprintf(name, "\\u%04X", gp->encoding & 0xffff); break; + } + size = 6; + + len = (gp->name) ? strlen(gp->name) : 0; + if (len == 0) { + gp->name = (char *) _bdf_strdup((unsigned char *) name, size + 1); + changed = 1; + } else if (size != len || strcmp(gp->name, name) != 0) { + /* + * Simply resize existing storage so lots of memory allocations + * are not needed. + */ + if (size > len) + gp->name = (char *) realloc(gp->name, size + 1); + (void) strcpy(gp->name, name); + changed = 1; + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = i; + (*callback)(&cb, 0); + } + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = cb.total; + (*callback)(&cb, 0); + } + + return changed; +} + +void +_bdf_glyph_name_cleanup(void) +{ + if (adobe_names_size > 0) + free((char *) adobe_names); + adobe_names_size = adobe_names_used = 0; +} diff --git a/bdfgrab.c b/bdfgrab.c new file mode 100644 index 0000000..b5548e5 --- /dev/null +++ b/bdfgrab.c @@ -0,0 +1,486 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * This file will only be compiled if the HAVE_XLIB macro is defined. + */ +#ifdef HAVE_XLIB + +/* + * Code to get BDF fonts from the X server. Reimplementation of the famous + * "getbdf" program by the equally famous der Mouse :-) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#include +#endif +#include "bdfP.h" + +/* + * Routine to compare two glyphs by encoding so they can be sorted. + */ +static int +by_encoding(const void *a, const void *b) +{ + bdf_glyph_t *c1, *c2; + + c1 = (bdf_glyph_t *) a; + c2 = (bdf_glyph_t *) b; + if (c1->encoding < c2->encoding) + return -1; + else if (c1->encoding > c2->encoding) + return 1; + return 0; +} + +static void +_bdf_get_glyphs(Display *d, XFontStruct *f, bdf_font_t *font, + bdf_callback_t callback, void *data) +{ + unsigned int off, b1, b2, black, x, y, bpr; + GC cleargc, drawgc; + Pixmap canvas; + XImage *image; + XCharStruct *cp; + bdf_glyph_t *gp; + XChar2b ch; + bdf_callback_struct_t cb; + char name[16]; + XGCValues gcv; + + black = BlackPixel(d, DefaultScreen(d)); + + /* + * Create the Pixmap which will be used to draw the glyphs. + */ + canvas = XCreatePixmap(d, XRootWindow(d, DefaultScreen(d)), + font->bbx.width, font->bbx.height, 1); + + /* + * Create the graphics contexts for drawing. + */ + gcv.function = GXcopy; + gcv.foreground = WhitePixel(d, DefaultScreen(d)); + cleargc = XCreateGC(d, canvas, GCFunction|GCForeground, &gcv); + + gcv.background = gcv.foreground; + gcv.foreground = black; + gcv.font = f->fid; + drawgc = XCreateGC(d, canvas, + GCFunction|GCForeground|GCBackground|GCFont, &gcv); + + /* + * Do it. + */ + for (b1 = f->min_byte1; b1 <= f->max_byte1; b1++) { + + off = (b1 - f->min_byte1) * + (f->max_char_or_byte2 + 1 - f->min_char_or_byte2); + + for (b2 = f->min_char_or_byte2; b2 <= f->max_char_or_byte2; + b2++, off++) { + + /* + * Point at the glyph metrics. + */ + cp = (f->per_char != 0) ? f->per_char + off : &f->min_bounds; + + if (cp->lbearing || cp->rbearing || cp->width || + cp->ascent || cp->descent) { + /* + * Make sure there is enough glyph storage to handle + * the glyphs. + */ + if (font->glyphs_used == font->glyphs_size) { + if (font->glyphs_size == 0) + font->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * 16); + else + font->glyphs = (bdf_glyph_t *) + realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * + (font->glyphs_size + 16)); + font->glyphs_size += 16; + } + + /* + * Point at the next glyph structure. + */ + gp = font->glyphs + font->glyphs_used++; + + /* + * Determine the glyph encoding and set the metrics. + */ + gp->encoding = (b1 << 8) | b2; + gp->dwidth = cp->width; + gp->swidth = (unsigned short) (cp->width * 72000.0 + / (font->point_size * font->resolution_x)); + gp->bbx.width = cp->rbearing - cp->lbearing; + gp->bbx.x_offset = cp->lbearing; + gp->bbx.ascent = cp->ascent; + gp->bbx.descent = cp->descent; + gp->bbx.y_offset = -cp->descent; + gp->bbx.height = cp->ascent + cp->descent; + + /* + * Create a glyph name. + */ + sprintf(name, "char%d", gp->encoding); + gp->name = (char *) malloc(strlen(name) + 1); + (void) strcpy(gp->name, name); + + /* + * Determine the number of bytes that will be needed for this + * glyph. + */ + bpr = (gp->bbx.width + 7) >> 3; + gp->bytes = bpr * gp->bbx.height; + gp->bitmap = (unsigned char *) malloc(gp->bytes); + (void) memset((char *) gp->bitmap, 0, gp->bytes); + + /* + * Clear the PSF Unicode mappings. + */ + gp->unicode.map_size = gp->unicode.map_used = 0; + + /* + * Clear the canvas. + */ + XFillRectangle(d, canvas, cleargc, 0, 0, + font->bbx.width, font->bbx.height); + + /* + * Render the glyph. + */ + ch.byte1 = (b1 == 0) ? (b2 >> 8) : b1; + ch.byte2 = b2; + XDrawString16(d, canvas, drawgc, -cp->lbearing, cp->ascent, + &ch, 1); + image = XGetImage(d, canvas, 0, 0, + font->bbx.width, font->bbx.height, 1L, + XYPixmap); + for (y = 0; y < gp->bbx.height; y++) { + for (x = 0; x < gp->bbx.width; x++) { + if (XGetPixel(image, x, y) == black) + gp->bitmap[(y * bpr) + (x >> 3)] |= + (0x80 >> (x & 7)); + } + } + XDestroyImage(image); + + /* + * Call the update callback if necessary. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = font->glyphs_size; + cb.current = font->glyphs_used; + (*callback)(&cb, data); + } + } + } + } + + /* + * Delete the Pixmap and the GCs. + */ + XFreePixmap(d, canvas); + XFreeGC(d, cleargc); + XFreeGC(d, drawgc); + + /* + * Make sure the glyphs are sorted by encoding. + */ + qsort((char *) font->glyphs, font->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); +} + +static int +error_handler(Display *d, XErrorEvent *event) +{ + + if (event->request_code != X_GetAtomName) + fprintf(stderr, "X Server Error\n"); +#if 0 + XmuPrintDefaultErrorMessage(d, event, stderr); +#endif + return 0; +} + +bdf_font_t * +bdf_load_server_font(Display *d, XFontStruct *f, char *name, + bdf_options_t *opts, bdf_callback_t callback, void *data) +{ + unsigned int i, len, b1, b2; + bdf_font_t *font; + XFontProp *xfp; + XCharStruct *cp; + bdf_property_t *pp, prop; + bdf_callback_struct_t cb; + int (*old_error_handler)(); + + if (f == 0) + return 0; + + font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + + font->bpp = 1; + + /* + * Set up the font bounding box. + */ + font->bbx.width = f->max_bounds.rbearing - f->min_bounds.lbearing; + font->bbx.x_offset = f->min_bounds.lbearing; + font->bbx.height = f->max_bounds.ascent + f->max_bounds.descent; + font->bbx.ascent = f->max_bounds.ascent; + font->bbx.descent = f->max_bounds.descent; + font->bbx.y_offset = -font->bbx.descent; + + /* + * If the font happens to be a character cell or monowidth, make sure that + * value is taken from the max bounds. + */ + font->monowidth = f->max_bounds.width; + + font->default_glyph = (int) f->default_char; + + /* + * Now load the font properties. + */ + old_error_handler = XSetErrorHandler(error_handler); + for (i = 0, xfp = f->properties; i < f->n_properties; i++, xfp++) { + if (xfp->name == XA_FONT) + /* + * Set the font name but don't add it to the list in the font. + */ + font->name = XGetAtomName(d, (Atom) xfp->card32); + else { + /* + * Add the property to the font. + */ + prop.name = XGetAtomName(d, xfp->name); + if (prop.name) { + if ((pp = bdf_get_property(prop.name)) == 0) { + /* + * The property does not exist, so create it with type Atom. + */ + bdf_create_property(prop.name, BDF_ATOM); + pp = bdf_get_property(prop.name); + } + prop.format = pp->format; + switch (prop.format) { + case BDF_ATOM: + prop.value.atom = XGetAtomName(d, (Atom) xfp->card32); + break; + case BDF_CARDINAL: + prop.value.card32 = xfp->card32; + break; + case BDF_INTEGER: + prop.value.int32 = (int) xfp->card32; + break; + } + /* + * Ignore the _XMBDFED_INFO property. + */ + if (strcmp(prop.name, "_XMBDFED_INFO") != 0) + bdf_add_font_property(font, &prop); + } + + /* + * Free up the Atom names returned by X. + */ + XFree(prop.name); + if (prop.format == BDF_ATOM) + XFree(prop.value.atom); + } + } + XSetErrorHandler(old_error_handler); + + /* + * Now go through and initialize the various fields needed for the font. + */ + + /* + * If the font name was not set when the properties were loaded, + * set it to the name that was passed. + */ + if (font->name == 0) { + len = (unsigned int) strlen(name); + font->name = (char *) malloc(len + 1); + (void) memcpy(font->name, name, len + 1); + } + + /* + * If the font default glyph is non-zero, make sure the DEFAULT_CHAR + * property is updated appropriately. Otherwise, make sure the + * DEFAULT_CHAR property is deleted from the font. + */ + if (font->default_glyph > 0) { + prop.name = "DEFAULT_CHAR"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->default_glyph; + bdf_add_font_property(font, &prop); + } else if (bdf_get_font_property(font, "DEFAULT_CHAR") != 0) + bdf_delete_font_property(font, "DEFAULT_CHAR"); + + /* + * Check the point size. + */ + if ((pp = bdf_get_font_property(font, "POINT_SIZE")) != 0) + font->point_size = (pp->value.card32 / 10); + else + font->point_size = 12; + + /* + * Check for the deprecated "RESOLUTION" property first in case it exists + * and "RESOLUTION_X" and "RESOLUTION_Y" do not. + */ + if ((pp = bdf_get_font_property(font, "RESOLUTION")) != 0) + font->resolution_x = font->resolution_y = pp->value.int32; + + if ((pp = bdf_get_font_property(font, "RESOLUTION_X")) != 0) + font->resolution_x = pp->value.int32; + if ((pp = bdf_get_font_property(font, "RESOLUTION_Y")) != 0) + font->resolution_y = pp->value.int32; + + /* + * If the horizontal or vertical resolutions have not been set, then + * define them to be the resolution of the display. + */ + if (font->resolution_x == 0) + font->resolution_x = + (int) (((((double) DisplayWidth(d, DefaultScreen(d))) * 25.4) / + ((double) DisplayWidthMM(d, DefaultScreen(d)))) + 0.5); + if (font->resolution_y == 0) + font->resolution_y = + (int) (((((double) DisplayHeight(d, DefaultScreen(d))) * 25.4) / + ((double) DisplayHeightMM(d, DefaultScreen(d)))) + 0.5); + + /* + * Check the font ascent and descent. + */ + if ((pp = bdf_get_font_property(font, "FONT_ASCENT")) != 0) + font->font_ascent = pp->value.int32; + else { + /* + * Add the FONT_ASCENT property. + */ + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->bbx.ascent; + bdf_add_font_property(font, &prop); + font->font_ascent = font->bbx.ascent; + } + + if ((pp = bdf_get_font_property(font, "FONT_DESCENT")) != 0) + font->font_descent = pp->value.int32; + else { + /* + * Add the FONT_DESCENT property. + */ + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->bbx.descent; + bdf_add_font_property(font, &prop); + font->font_descent = font->bbx.descent; + } + + /* + * Get the font spacing. + */ + font->spacing = BDF_PROPORTIONAL; + if ((pp = bdf_get_font_property(font, "SPACING")) != 0) { + switch (pp->value.atom[0]) { + case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break; + case 'M': case 'm': font->spacing = BDF_MONOWIDTH; break; + case 'C': case 'c': font->spacing = BDF_CHARCELL; break; + } + } + + /* + * Now determine the number of glyphs. + */ + if (f->per_char != 0) { + for (b1 = f->min_byte1; b1 <= f->max_byte1; b1++) { + len = (b1 - f->min_byte1) * + (f->max_char_or_byte2 + 1 - f->min_char_or_byte2); + for (b2 = f->min_char_or_byte2; b2 <= f->max_char_or_byte2; + b2++, len++) { + cp = f->per_char + len; + /* + * If any of the metrics values are non-zero, then count this + * as a glyph. + */ + if (cp->lbearing || cp->rbearing || cp->width || + cp->ascent || cp->descent) + font->glyphs_size++; + } + } + } else + font->glyphs_size = (f->max_byte1 + 1 - f->min_byte1) * + (f->max_char_or_byte2 + 1 - f->min_char_or_byte2); + + /* + * Call the callback if it was provided. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.total = font->glyphs_size; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Allocate enough glyph storage for the specified number of glyphs. + */ + font->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * font->glyphs_size); + + /* + * Actually load the glyphs. + */ + _bdf_get_glyphs(d, f, font, callback, data); + + /* + * Add a message to the font to indicate it was loaded + * from the server. + */ + _bdf_add_comment(font, "Font grabbed from the X server.", 31); + _bdf_add_acmsg(font, "Font grabbed from the X server.", 31); + + /* + * Mark the font as being modified. + */ + font->modified = 1; + + return font; +} + +#endif /* HAVE_XLIB */ diff --git a/bdfgrid.c b/bdfgrid.c new file mode 100644 index 0000000..2362b55 --- /dev/null +++ b/bdfgrid.c @@ -0,0 +1,3402 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bdfP.h" + +#ifndef MYABS +#define MYABS(n) ((n) < 0 ? -(n) : (n)) +#endif + +#undef MAX +#define MAX(h, i) ((h) > (i) ? (h) : (i)) + +#undef MIN +#define MIN(l, o) ((l) < (o) ? (l) : (o)) + +double _bdf_cos_tbl[360] = { + 0.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, + 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, + 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, + 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, + 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, + 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, + 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, + 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, + 0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815, + 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, + 0.500000, 0.484810, 0.469472, 0.453990, 0.438371, 0.422618, + 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, + 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, + 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, + 0.104528, 0.087156, 0.069756, 0.052336, 0.034899, 0.017452, + 0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156, + -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, + -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, + -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731, + -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810, + -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, + -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, + -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354, + -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798636, + -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, + -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, + -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519, + -0.951057, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370, + -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, + -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, + -1.000000, -0.999848, -0.999391, -0.998630, -0.997564, -0.996195, + -0.994522, -0.992546, -0.990268, -0.987688, -0.984808, -0.981627, + -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, + -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, + -0.913545, -0.906308, -0.898794, -0.891007, -0.882948, -0.874620, + -0.866025, -0.857167, -0.848048, -0.838671, -0.829038, -0.819152, + -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710, + -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998, + -0.669131, -0.656059, -0.642788, -0.629320, -0.615661, -0.601815, + -0.587785, -0.573576, -0.559193, -0.544639, -0.529919, -0.515038, + -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618, + -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568, + -0.309017, -0.292372, -0.275637, -0.258819, -0.241922, -0.224951, + -0.207912, -0.190809, -0.173648, -0.156434, -0.139173, -0.121869, + -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452, + -0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, + 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, + 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, + 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, + 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, + 0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, + 0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059, + 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, + 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636, + 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, + 0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308, + 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, + 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, + 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, + 0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848, +}; + +double _bdf_sin_tbl[360] = { + 0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, + 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, + 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, + 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, + 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, + 0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, + 0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059, + 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, + 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636, + 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, + 0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308, + 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, + 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, + 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, + 0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848, + 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, + 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, + 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, + 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, + 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, + 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, + 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, + 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, + 0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815, + 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, + 0.500000, 0.484810, 0.469472, 0.453990, 0.438371, 0.422618, + 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, + 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, + 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, + 0.104528, 0.087156, 0.069756, 0.052336, 0.034899, 0.017452, + 0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156, + -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, + -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, + -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731, + -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810, + -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, + -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, + -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354, + -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798636, + -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, + -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, + -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519, + -0.951057, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370, + -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, + -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, + -1.000000, -0.999848, -0.999391, -0.998630, -0.997564, -0.996195, + -0.994522, -0.992546, -0.990268, -0.987688, -0.984808, -0.981627, + -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, + -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, + -0.913545, -0.906308, -0.898794, -0.891007, -0.882948, -0.874620, + -0.866025, -0.857167, -0.848048, -0.838671, -0.829038, -0.819152, + -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710, + -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998, + -0.669131, -0.656059, -0.642788, -0.629320, -0.615661, -0.601815, + -0.587785, -0.573576, -0.559193, -0.544639, -0.529919, -0.515038, + -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618, + -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568, + -0.309017, -0.292372, -0.275637, -0.258819, -0.241922, -0.224951, + -0.207912, -0.190809, -0.173648, -0.156434, -0.139173, -0.121869, + -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452, +}; + +double _bdf_tan_tbl[90] = { + 0.000000, 0.017455, 0.034921, 0.052408, 0.069927, 0.087489, + 0.105104, 0.122785, 0.140541, 0.158384, 0.176327, 0.194380, + 0.212557, 0.230868, 0.249328, 0.267949, 0.286745, 0.305731, + 0.324920, 0.344328, 0.363970, 0.383864, 0.404026, 0.424475, + 0.445229, 0.466308, 0.487733, 0.509525, 0.531709, 0.554309, + 0.577350, 0.600861, 0.624869, 0.649408, 0.674509, 0.700208, + 0.726543, 0.753554, 0.781286, 0.809784, 0.839100, 0.869287, + 0.900404, 0.932515, 0.965689, 1.000000, 1.035530, 1.072369, + 1.110613, 1.150368, 1.191754, 1.234897, 1.279942, 1.327045, + 1.376382, 1.428148, 1.482561, 1.539865, 1.600335, 1.664279, + 1.732051, 1.804048, 1.880726, 1.962611, 2.050304, 2.144507, + 2.246037, 2.355852, 2.475087, 2.605089, 2.747477, 2.904211, + 3.077684, 3.270853, 3.487414, 3.732051, 4.010781, 4.331476, + 4.704630, 5.144554, 5.671282, 6.313752, 7.115370, 8.144346, + 9.514364, 11.430052, 14.300666, 19.081137, 28.636253, 57.289962, +}; + +/* + * Determine the actual ink bounds. + */ +static int +_bdf_grid_ink_bounds(bdf_glyph_grid_t *grid, short *x, short *y, + short *width, short *height) +{ + short bx, by, bwd, bht, minx, maxx, miny, maxy, dx, dy; + unsigned short bpr, ink, sel, col; + unsigned char *bmap, *masks; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + if (grid->sel.width != 0 && grid->sel.height != 0) { + sel = 1; + bx = by = 0; + bwd = grid->sel.width; + bht = grid->sel.height; + bmap = grid->sel.bitmap; + } else { + sel = 0; + bx = grid->glyph_x; + by = grid->glyph_y; + bwd = grid->glyph_bbx.width; + bht = grid->glyph_bbx.height; + bmap = grid->bitmap; + } + maxx = maxy = 0; + minx = bx + bwd; + miny = by + bht; + + bpr = ((bwd * grid->bpp) + 7) >> 3; + ink = 0; + + bwd += bx; + bht += by; + for (dy = by; dy < bht; dy++) { + for (col = bx * grid->bpp, dx = bx; dx < bwd; dx++, col += grid->bpp) { + if (bmap[(dy * bpr) + (col >> 3)] & masks[(col & 7) / grid->bpp]) { + ink = 1; + minx = MIN(minx, dx); + miny = MIN(miny, dy); + maxx = MAX(maxx, dx); + maxy = MAX(maxy, dy); + } + } + } + + *x = minx + ((sel) ? grid->sel.x : 0); + *y = miny + ((sel) ? grid->sel.y : 0); + if (ink == 0) + *width = *height = 0; + else { + *width = (maxx - minx) + 1; + *height = (maxy - miny) + 1; + } + return ink; +} + +/************************************************************************** + * + * Glyph grid create and destroy functions. + * + **************************************************************************/ + +/* + * Make a glyph grid with the glyph bitmap set in the bitmap. + */ +bdf_glyph_grid_t * +bdf_make_glyph_grid(bdf_font_t *font, int code, int unencoded) +{ + unsigned short si, di, col, colx, byte; + short ht, as, ds, gsize, bpr, x, y, nx, ny; + long l, r, m; + bdf_glyph_grid_t *gr; + bdf_glyph_t *gl, *glp; + bdf_property_t *p; + unsigned char *masks; + char name[24]; + +#if 0 + if (font == 0) + return 0; +#endif + + /* + * Allocate the grid and initialize it. + */ + gr = (bdf_glyph_grid_t *) malloc(sizeof(bdf_glyph_grid_t)); + (void) memset((char *) gr, 0, sizeof(bdf_glyph_grid_t)); + + /* + * Set the encoding and the unencoded flag. + */ + gr->bpp = (font) ? font->bpp : 1; + gr->encoding = code; + gr->unencoded = unencoded; + + /* + * Set the glyph grid spacing. + */ + gr->spacing = (font) ? font->spacing : BDF_CHARCELL; + + /* + * Set the point size and resolutions. + */ + if (font) { + gr->point_size = font->point_size; + gr->resolution_x = font->resolution_x; + gr->resolution_y = font->resolution_y; + } else { + gr->point_size = 12; + gr->resolution_x = gr->resolution_y = 100; + } + + /* + * Set the CAP_HEIGHT and X_HEIGHT if they exist in the font. + */ + if (font) { + if ((p = bdf_get_font_property(font, "CAP_HEIGHT")) != 0) + gr->cap_height = (short) p->value.int32; + if ((p = bdf_get_font_property(font, "X_HEIGHT")) != 0) + gr->x_height = (short) p->value.int32; + } + + masks = 0; + switch (gr->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Copy the font bounding box into the grid. + */ + if (font) + (void) memcpy((char *) &gr->font_bbx, (char *) &font->bbx, + sizeof(bdf_bbx_t)); + else { + gr->font_bbx.height = 17; + gr->font_bbx.width = 8; + gr->font_bbx.descent = 8; + gr->font_bbx.ascent = 9; + gr->font_bbx.y_offset = -8; + } + + + if (font) { + if (unencoded) { + gl = font->unencoded; + r = font->unencoded_used; + } else { + gl = font->glyphs; + r = font->glyphs_used; + } + } else { + gl = 0; + r = 0; + } + + /* + * Locate the specified glyph using a simple binary search. + */ + glp = 0; + if (r > 0) { + for (l = 0; r >= l; ) { + m = (l + r) >> 1; + glp = gl + m; + if (glp->encoding == code) + break; + if (glp->encoding > code) + r = m - 1; + else if (glp->encoding < code) + l = m + 1; + glp = 0; + } + } + + ht = gr->font_bbx.height; + as = gr->font_bbx.ascent; + ds = gr->font_bbx.descent; + + /* + * 1. Determine width and height needed from the largest of the + * width or height. + */ + gr->grid_width = gr->grid_height = + MAX(gr->font_bbx.width, gr->font_bbx.height); + + /* + * 2. Make sure the grid is at least a square of the largest of the width + * or height of the glyph itself to allow room for transformations. + */ + if (glp != 0) { + /* + * Set the glyph name and other metrics. + */ + if (glp->name) { + gr->name = (char *) malloc(strlen(glp->name) + 1); + (void) memcpy(gr->name, glp->name, strlen(glp->name) + 1); + } else { + sprintf(name, "char%d", code); + gr->name = (char *) malloc(strlen(name) + 1); + (void) memcpy(gr->name, name, strlen(name) + 1); + } + gr->dwidth = glp->dwidth; + + /* + * Copy the glyph bounding box into the grid. + */ + (void) memcpy((char *) &gr->glyph_bbx, (char *) &glp->bbx, + sizeof(bdf_bbx_t)); + + if (glp->bbx.height < glp->bbx.ascent + glp->bbx.descent) + gsize = glp->bbx.ascent + glp->bbx.descent; + else + gsize = glp->bbx.height; + + /* + * Figure the maximum of the glyph width and height. + */ + gsize = MAX(gr->glyph_bbx.width, gsize); + + /* + * If either the grid width or grid height is less than the + * grid size just determined, then adjust them to the new grid size. + */ + gr->grid_width = MAX(gr->grid_width, gsize); + gr->grid_height = MAX(gr->grid_height, gsize); + } else { + /* + * The glyph doesn't exist, so make up a name for it. + */ + if (unencoded) + sprintf(name, "unencoded%d", code); + else + sprintf(name, "char%d", code); + gr->name = (char *) malloc(strlen(name) + 1); + (void) memcpy(gr->name, name, strlen(name) + 1); + } + + /* + * If the font has character-cell or mono spacing, make sure the grid + * device width is set to the width stored in the font. + */ + if (gr->spacing != BDF_PROPORTIONAL) + gr->dwidth = (font) ? font->monowidth : 8; + + /* + * Determine the vertical origin based on the font bounding box. + */ + if (ht >= as + ds) + gr->base_y = (((gr->grid_height >> 1) - (ht >> 1)) + ht) - ds; + else + gr->base_y = ((gr->grid_height >> 1) - ((as + ds) >> 1)) + as; + + /* + * The final adjust is to check to see if the glyph positioned relative to + * the baseline would cause the grid to change size. This sometimes + * happens in fonts that have incorrect metrics. + */ + if (gr->base_y + gr->glyph_bbx.descent > gr->grid_height) { + gsize = gr->base_y + gr->glyph_bbx.descent; + gr->grid_width = MAX(gsize, gr->grid_width); + gr->grid_height = MAX(gsize, gr->grid_height); + } + + /* + * Determine the horizontal origin based on the font bounding box and + * centered within the grid. + */ + gr->base_x = (gr->grid_width >> 1) - (gr->font_bbx.width >> 1); + if (gr->font_bbx.x_offset < 0) + gr->base_x += MYABS(gr->font_bbx.x_offset); + + /* + * Allocate double the storage needed for the grid bitmap. The extra + * storage will be used for transformations. + */ + gr->bytes = ((((gr->grid_width * gr->bpp) + 7) >> 3) * + gr->grid_height) << 1; + gr->bitmap = (unsigned char *) malloc(gr->bytes); + (void) memset((char *) gr->bitmap, 0, gr->bytes); + + /* + * Initialize the top-left coordinates of the glyph to the baseline + * coordinates. + */ + gr->glyph_x = gr->base_x; + gr->glyph_y = gr->base_y; + + /* + * If the glyph was not found, simply return the empty grid. + */ + if (glp == 0) + return gr; + + /* + * Determine the top-left coordinates of the glyph with respect to the + * baseline coordinates. + */ + gr->glyph_x = nx = gr->base_x + gr->glyph_bbx.x_offset; + gr->glyph_y = ny = gr->base_y - gr->glyph_bbx.ascent; + + /* + * Now copy the glyph bitmap to the appropriate location in the grid. + */ + bpr = ((gr->glyph_bbx.width * gr->bpp) + 7) >> 3; + gsize = ((gr->grid_width * gr->bpp) + 7) >> 3; + for (y = 0; y < gr->glyph_bbx.height; y++, ny++) { + for (colx = nx * gr->bpp, col = x = 0; x < gr->glyph_bbx.width; + x++, col += gr->bpp, colx += gr->bpp) { + si = (col & 7) / gr->bpp; + byte = glp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / gr->bpp; + if (di < si) + byte <<= (si - di) * gr->bpp; + else if (di > si) + byte >>= (di - si) * gr->bpp; + gr->bitmap[(ny * gsize) + (colx >> 3)] |= byte; + } + } + } + + /* + * Always crop the glyph to the ink bounds before editing. + */ + bdf_grid_crop(gr, 0); + + /* + * Copy any Unicode mappings that might be present for this glyph, even if + * it is a proportional font. It might be changed to a character cell or + * monowidth font later. + */ + gr->unicode.map_size = glp->unicode.map_size; + gr->unicode.map_used = glp->unicode.map_used; + gr->unicode.map = (unsigned char *) + malloc(sizeof(unsigned char) * gr->unicode.map_size); + (void) memcpy((char *) gr->unicode.map, (char *) glp->unicode.map, + sizeof(unsigned char) * gr->unicode.map_used); + + /* + * Return the grid. + */ + return gr; +} + +void +bdf_free_glyph_grid(bdf_glyph_grid_t *grid) +{ + if (grid == 0) + return; + + if (grid->name != 0) + free(grid->name); + if (grid->bytes > 0) + free((char *) grid->bitmap); + if (grid->sel.bytes > 0) + free((char *) grid->sel.bitmap); + if (grid->unicode.map_size > 0) + free((char *) grid->unicode.map); + free((char *) grid); +} + +/************************************************************************** + * + * Glyph grid resize functions. + * + **************************************************************************/ + +/* + * Enlarge the grid without affecting the font or glyph metrics. + */ +int +bdf_grid_enlarge(bdf_glyph_grid_t *grid, unsigned short width, + unsigned short height) +{ + unsigned short si, di, col, colx, byte; + short ht, wd, as, ds, x, y, nx, ny; + unsigned short gwd, ght, bytes, obpr, nbpr, gsize; + unsigned char *bitmap, *masks; + + if (grid == 0 || (width < grid->grid_width && height < grid->grid_height)) + return 0; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + ht = height; + as = grid->font_bbx.ascent; + ds = grid->font_bbx.descent; + + gwd = MAX(width, grid->grid_width); + ght = MAX(height, grid->grid_height); + gsize = MAX(gwd, ght); + + nbpr = ((gsize * grid->bpp) + 7) >> 3; + bytes = (nbpr * ght) << 1; + bitmap = (unsigned char *) malloc(bytes); + (void) memset((char *) bitmap, 0, bytes); + + /* + * Determine the new baseline. + */ + if (ht >= as + ds) + grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds; + else + grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as; + + grid->base_x = (gwd >> 1) - (grid->font_bbx.width >> 1); + if (grid->font_bbx.x_offset < 0) + grid->base_x += MYABS(grid->font_bbx.x_offset); + + nx = grid->base_x + grid->glyph_bbx.x_offset; + ny = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Now copy the bitmap into the new storage base on the new metrics + * values. + */ + obpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + for (y = grid->glyph_y; y < ht; y++, ny++) { + col = grid->glyph_x * grid->bpp; + colx = nx * grid->bpp; + for (x = grid->glyph_x; x < wd; + x++, col += grid->bpp, colx += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + bitmap[(ny * nbpr) + (colx >> 3)] |= byte; + } + } + } + + /* + * Adjust the glyph coordinates. + */ + grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset; + grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Get rid of the old grid bitmap and replace it with the new one. + */ + free((char *) grid->bitmap); + grid->bytes = bytes; + grid->bitmap = bitmap; + + /* + * Update the new grid width and height. + */ + grid->grid_width = grid->grid_height = gsize; + + /* + * Always mark the grid as being modified on a resize. + */ + grid->modified = 1; + + return 1; +} + +/* + * Change the font bounding box values and resize the grid bitmap if + * necessary. + */ +int +bdf_grid_resize(bdf_glyph_grid_t *grid, bdf_metrics_t *metrics) +{ + int changed; + unsigned short si, di, col, colx, byte; + short ht, wd, as, ds, x, y, nx, ny; + unsigned short gwd, ght, bytes, obpr, nbpr, gsize; + unsigned char *bitmap, *masks; + + changed = 0; + + if (grid == 0 || metrics == 0) + return changed; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Create new grid bitmaps in preparation for the various metrics changing. + */ + if (metrics->width > grid->grid_width || + metrics->height > grid->grid_height) { + changed = 1; + + ht = metrics->height; + as = metrics->ascent; + ds = metrics->descent; + + gwd = MAX(metrics->width, grid->grid_width); + ght = MAX(metrics->height, grid->grid_height); + + /* + * Get the larger of the two dimensions. + */ + gsize = MAX(gwd, ght); + + nbpr = ((gsize * grid->bpp) + 7) >> 3; + bytes = (nbpr * gsize) << 1; + bitmap = (unsigned char *) malloc(bytes); + (void) memset((char *) bitmap, 0, bytes); + + /* + * Determine the new baseline. + */ + if (ht >= as + ds) + grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds; + else + grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as; + + grid->base_x = (gwd >> 1) - (metrics->width >> 1); + if (metrics->x_offset < 0) + grid->base_x += MYABS(metrics->x_offset); + + nx = grid->base_x + grid->glyph_bbx.x_offset; + ny = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Now copy the bitmap into the new storage base on the new metrics + * values. + */ + obpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + for (y = grid->glyph_y; y < ht; y++, ny++) { + col = grid->glyph_x * grid->bpp; + colx = nx * grid->bpp; + for (x = grid->glyph_x; x < wd; + x++, col += grid->bpp, colx += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + bitmap[(ny * nbpr) + (colx >> 3)] |= byte; + } + } + } + + /* + * Adjust the glyph coordinates. + */ + grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset; + grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Get rid of the old grid bitmap and replace it with the new one. + */ + free((char *) grid->bitmap); + grid->bytes = bytes; + grid->bitmap = bitmap; + + /* + * Update the new grid width and height. + */ + grid->grid_width = grid->grid_height = gsize; + + /* + * Copy the metrics info into the font bounding box. + */ + grid->font_bbx.width = metrics->width; + grid->font_bbx.x_offset = metrics->x_offset; + grid->font_bbx.height = metrics->height; + grid->font_bbx.ascent = metrics->ascent; + grid->font_bbx.descent = metrics->descent; + grid->font_bbx.y_offset = metrics->y_offset; + } else { + /* + * The grid does not need to resized, but the baseline must + * be recalculated and the bitmap copied again. + */ + bytes = grid->bytes >> 1; + bitmap = grid->bitmap + bytes; + (void) memset((char *) bitmap, 0, bytes); + + ht = metrics->height; + as = metrics->ascent; + ds = metrics->descent; + + gwd = grid->grid_width; + ght = grid->grid_height; + + /* + * Determine the new baseline. + */ + if (ht >= as + ds) + grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds; + else + grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as; + + grid->base_x = (gwd >> 1) - (metrics->width >> 1); + if (metrics->x_offset < 0) + grid->base_x += MYABS(metrics->x_offset); + + nx = grid->base_x + grid->glyph_bbx.x_offset; + ny = grid->base_y - grid->glyph_bbx.ascent; + + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + + obpr = nbpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + for (y = grid->glyph_y; y < ht; y++, ny++) { + col = grid->glyph_x * grid->bpp; + colx = nx * grid->bpp; + for (x = grid->glyph_x; x < wd; + x++, col += grid->bpp, colx += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + bitmap[(ny * nbpr) + (colx >> 3)] |= byte; + } + } + } + + /* + * Copy the adjusted bitmap back into the main area. + */ + (void) memcpy((char *) grid->bitmap, (char *) bitmap, bytes); + + /* + * Adjust the glyph coordinates. + */ + grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset; + grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Copy the metrics info into the font bounding box. + */ + grid->font_bbx.width = metrics->width; + grid->font_bbx.x_offset = metrics->x_offset; + grid->font_bbx.height = metrics->height; + grid->font_bbx.ascent = metrics->ascent; + grid->font_bbx.descent = metrics->descent; + grid->font_bbx.y_offset = metrics->y_offset; + } + + /* + * If the font is not proportional, make sure the device width is adjusted + * to meet the new font bounding box. + */ + if (changed && grid->spacing != BDF_PROPORTIONAL) + grid->dwidth = grid->font_bbx.width; + + /* + * Always mark the grid as being modified on a resize. + */ + grid->modified = 1; + + return changed; +} + +int +bdf_grid_crop(bdf_glyph_grid_t *grid, int grid_modified) +{ + int cropped; + short x, y, delta, maxx, minx, maxy, miny, col; + unsigned short bpr; + unsigned char *masks; + + cropped = 0; + if (grid == 0) + return cropped; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + maxx = maxy = -1; + minx = miny = grid->grid_width; + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) { + if (grid->bitmap[(y * bpr) + (col >> 3)] & + masks[(col & 7) / grid->bpp]) { + minx = MIN(minx, x); + maxx = MAX(maxx, x); + miny = MIN(miny, y); + maxy = MAX(maxy, y); + } + } + } + + /* + * Handle an empty bitmap as a special case. + */ + if (maxx == -1) { + /* + * If the glyph bounding box indicated something was there originally, + * then indicate that it was cropped. + */ + if (grid->glyph_bbx.width != 0 || grid->glyph_bbx.height != 0) + cropped = 1; + (void) memset((char *) &grid->glyph_bbx, 0, sizeof(bdf_bbx_t)); + grid->glyph_x = grid->base_x; + grid->glyph_y = grid->base_y; + if (cropped && grid_modified) + grid->modified = 1; + return cropped; + } + + /* + * Increment the max points so width and height calculations won't go + * wrong. + */ + maxx++; + maxy++; + + if (minx != grid->glyph_x) { + cropped = 1; + delta = minx - grid->glyph_x; + grid->glyph_x += delta; + grid->glyph_bbx.x_offset += delta; + } + if (maxx - minx != grid->glyph_bbx.width) { + cropped = 1; + delta = (maxx - minx) - grid->glyph_bbx.width; + grid->glyph_bbx.width += delta; + } + + if (miny != grid->glyph_y) { + cropped = 1; + delta = miny - grid->glyph_y; + grid->glyph_y += delta; + grid->glyph_bbx.y_offset = + grid->base_y - (grid->glyph_y + (maxy - miny)); + } + if (maxy - miny != grid->glyph_bbx.height) { + cropped = 1; + delta = (maxy - miny) - grid->glyph_bbx.height; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset = + grid->base_y - (grid->glyph_y + (maxy - miny)); + grid->glyph_bbx.ascent = + grid->glyph_bbx.height + grid->glyph_bbx.y_offset; + grid->glyph_bbx.descent = -grid->glyph_bbx.y_offset; + } + + /* + * Indicate that the grid was modified if the glyph had to be cropped. + */ + if (cropped && grid_modified) + grid->modified = 1; + + return cropped; +} + +/************************************************************************** + * + * Glyph grid pixel functions. + * + **************************************************************************/ + +int +bdf_grid_set_pixel(bdf_glyph_grid_t *grid, short x, short y, int val) +{ + unsigned short si, di, dx; + int set, bpr, delta; + unsigned char *masks; + + set = 0; + + if (grid == 0 || x < 0 || x >= grid->grid_width || + y < 0 || y >= grid->grid_height) + return set; + + si = 0; + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; si = 7; break; + case 2: masks = bdf_twobpp; si = 3; break; + case 4: masks = bdf_fourbpp; si = 1; break; + case 8: masks = bdf_eightbpp; si = 0; break; + } + + /* + * Remove any unused bits from the value. + */ + val &= masks[si]; + + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + + /* + * Shift up the value to the appropriate place if necessary. + */ + if (di < si) + val <<= (si - di) * grid->bpp; + + /* + * Determine the bytes-per-row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + /* + * If the pixel is already set, simply return with an indication that + * nothing changed. + */ + if ((grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]) == val) + return set; + + /* + * Set the bit. + */ + set = 1; + + /* + * Clear the bits that will take the new value. + */ + grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di]; + grid->bitmap[(y * bpr) + (dx >> 3)] |= val; + + /* + * Adjust the glyph bounding box. + */ + if (x < grid->glyph_x) { + delta = grid->glyph_x - x; + grid->glyph_bbx.width += delta; + grid->glyph_bbx.x_offset -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + grid->glyph_x -= delta; + } else if (x >= grid->glyph_x + grid->glyph_bbx.width) { + delta = x - (grid->glyph_x + grid->glyph_bbx.width) + 1; + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + } + if (y < grid->glyph_y) { + delta = grid->glyph_y - y; + grid->glyph_bbx.ascent += delta; + grid->glyph_bbx.height += delta; + grid->glyph_y -= delta; + } else if (y >= grid->glyph_y + grid->glyph_bbx.height) { + delta = y - (grid->glyph_y + grid->glyph_bbx.height) + 1; + grid->glyph_bbx.descent += delta; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + } + + /* + * Indicate that the glyph was modified. + */ + grid->modified = 1; + + return set; +} + +int +bdf_grid_clear_pixel(bdf_glyph_grid_t *grid, short x, short y) +{ + int cleared, bpr; + short delta, maxx, minx, maxy, miny, wd, ht; + unsigned short di, dx; + unsigned char *masks; + + cleared = 0; + + if (grid == 0 || x < 0 || x >= grid->grid_width || + y < 0 || y >= grid->grid_height) + return cleared; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Determine the bytes-per-row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + + /* + * If the bit is already clear, simply return with an indication that + * nothing changed. + */ + if (!(grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di])) + return cleared; + + /* + * Clear the bit. + */ + cleared = 1; + grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di]; + + /* + * Determine the new min and max values. + */ + maxx = maxy = 0; + minx = miny = 32767; + + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + + for (y = grid->glyph_y; y < ht; y++) { + dx = grid->glyph_x * grid->bpp; + for (x = grid->glyph_x; x < wd; x++, dx += grid->bpp) { + di = (dx & 7) / grid->bpp; + if (grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]) { + minx = MIN(minx, x); + maxx = MAX(maxx, x); + miny = MIN(miny, y); + maxy = MAX(maxy, y); + } + } + } + + /* + * If this call clears the last bit in the image, set the glyph origin + * to the base and return. + */ + if (maxx == 0) { + grid->glyph_x = grid->base_x; + grid->glyph_y = grid->base_y; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = 0; + (void) memset((char *) &grid->glyph_bbx, 0, sizeof(grid->glyph_bbx)); + grid->modified = 1; + return cleared; + } + + /* + * Figure out the left and right bearing changes. + */ + if (minx > grid->glyph_x) { + delta = minx - grid->glyph_x; + grid->glyph_bbx.width -= delta; + grid->glyph_bbx.x_offset += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + grid->glyph_x += delta; + } else if (maxx < wd - 1) { + delta = (wd - 1) - maxx; + grid->glyph_bbx.width -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + } + + if (miny > grid->glyph_y) { + delta = miny - grid->glyph_y; + grid->glyph_bbx.ascent -= delta; + grid->glyph_bbx.height -= delta; + grid->glyph_y += delta; + } else if (maxy < ht - 1) { + delta = (ht - 1) - maxy; + grid->glyph_bbx.descent -= delta; + grid->glyph_bbx.height -= delta; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + } + + /* + * Indicate that the glyph was modified. + */ + grid->modified = 1; + + return cleared; +} + +int +bdf_grid_invert_pixel(bdf_glyph_grid_t *grid, short x, short y, int val) +{ + short bpr, di; + unsigned char *masks; + + if (grid == 0 || x < 0 || x >= grid->grid_width || + y < 0 || y >= grid->grid_height) + return 0; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Determine the bytes-per-row and mask index. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + di = ((x * grid->bpp) & 7) / grid->bpp; + + /* + * If the bit is set, then clear it, otherwise, set it. + */ + if (grid->bitmap[(y * bpr) + ((x * grid->bpp) >> 3)] & masks[di]) + return bdf_grid_clear_pixel(grid, x, y); + else + return bdf_grid_set_pixel(grid, x, y, val); +} + +/************************************************************************** + * + * Glyph grid bitmap transformation functions. + * + **************************************************************************/ + +short +_bdf_ceiling(double v) +{ + short val, neg; + + val = neg = 0; + if (v < 0) { + neg = 1; + while (v < -1.0) { + val++; + v += 1.0; + } + } else if (v > 0) { + while (v > 1.0) { + val++; + v -= 1.0; + } + if (v > 0.0) + val++; + } + return (!neg) ? val : -val; +} + +static int +_bdf_rotate_selection(bdf_glyph_grid_t *grid, int mul90, short degrees) +{ + int rotated, byte; + short wd, ht, nx, ny, cx, cy, x, y, col; + short ox, oy, shiftx, shifty, si, di; + double dx, dy; + unsigned short bytes, bpr; + unsigned char *scratch, *masks; + + rotated = 0; + + /* + * Check to see if the number of rotations would have no affect by + * checking if the count is a multiple of 4 (mod 4 == 0). + */ + if (grid == 0 || degrees == 0) + return rotated; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + bytes = grid->sel.bytes >> 1; + scratch = grid->sel.bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + cx = grid->sel.width >> 1; + cy = grid->sel.height >> 1; + + wd = ht = MAX(grid->sel.width, grid->sel.height); + cx = cy = wd >> 1; + + bpr = ((wd * grid->bpp) + 7) >> 3; + + for (shiftx = shifty = y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + dx = (double) (x - cx); + dy = (double) (y - cy); + if (mul90) { + nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } else { + nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } + + /* + * Wrap the coordinates around the edges if necessary. + */ + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += wd; + } else if (nx >= wd) { + ox = (nx - wd) + 1; + shiftx = MAX(shiftx, ox); + nx -= wd; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += ht; + } else if (ny >= ht) { + oy = (ny - ht) + 1; + shifty = MAX(shifty, oy); + ny -= ht; + } + + si = (col & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + rotated = 1; + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + + if (rotated) { + /* + * If a shift is required, then shift the scratch area back into + * the main bitmap. + */ + if (shiftx || shifty) { + (void) memset((char *) grid->sel.bitmap, 0, bytes); + for (y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = scratch[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + + if (nx < 0) + nx += wd; + else if (nx >= wd) + nx -= wd; + if (ny < 0) + ny += ht; + else if (ny >= ht) + ny -= ht; + + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->sel.bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + } else + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes); + + /* + * Determine the new selection width and height. + */ + ox = oy = 0; + nx = ny = 16384; + for (y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + if (grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + ox = MAX(ox, x); + nx = MIN(nx, x); + oy = MAX(oy, y); + ny = MIN(ny, y); + } + } + } + + /* + * Recalculate the center corrdinates so the selection will be + * positioned nicely once it is shifted to the upper left corner. + */ + cx = grid->sel.width >> 1; + cy = grid->sel.height >> 1; + + /* + * Set the new width and height. + */ + grid->sel.width = (ox - nx) + 1; + grid->sel.height = (oy - ny) + 1; + + /* + * Shift again to force the selection to the upper left corner. + */ + if (nx || ny) { + (void) memset((char *) scratch, 0, bytes); + for (y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & + masks[si]; + if (byte) { + oy = y - ny; + ox = (x - nx) * grid->bpp; + di = (ox & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(oy * bpr) + (ox >> 3)] |= byte; + } + } + } + (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes); + } + + /* + * Determine the new top left coordinates from the center coordinates. + */ + grid->sel.x = (grid->sel.x + cx) - (grid->sel.width >> 1); + grid->sel.y = (grid->sel.y + cy) - (grid->sel.height >> 1); + + /* + * If the rotation caused the selection rectangle to overlap the edges + * of the grid, shift it so it is completely visible again. + */ + if (grid->sel.x + grid->sel.width > grid->grid_width) + grid->sel.x -= (grid->sel.x + grid->sel.width) - grid->grid_width; + if (grid->sel.y + grid->sel.height > grid->grid_height) + grid->sel.y -= (grid->sel.y + grid->sel.height) - grid->grid_height; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return rotated; +} + +static void +_bdf_rotate_resize(bdf_glyph_grid_t *grid, int mul90, short degrees, + int *resize) +{ + unsigned short wd, ht; + short cx, cy, x1, y1, x2, y2; + double dx1, dy1, dx2, dy2; + bdf_metrics_t metrics; + + *resize = 0; + (void) memset((char *) &metrics, 0, sizeof(bdf_metrics_t)); + + metrics.x_offset = grid->font_bbx.x_offset; + metrics.width = grid->font_bbx.width; + metrics.ascent = grid->font_bbx.ascent; + metrics.descent = grid->font_bbx.descent; + metrics.height = grid->font_bbx.height; + metrics.y_offset = grid->font_bbx.y_offset; + + cx = grid->glyph_x + (grid->glyph_bbx.width >> 1); + cy = grid->glyph_y + (grid->glyph_bbx.height >> 1); + + /* + * Rotate the lower left and upper right corners and check for a potential + * resize. + */ + x1 = grid->glyph_x; + y1 = grid->glyph_y + grid->glyph_bbx.height; + x2 = grid->glyph_x + grid->glyph_bbx.width; + y2 = grid->glyph_y; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + if (ht > metrics.height) { + metrics.ascent += ht - grid->font_bbx.height; + metrics.height += ht - grid->font_bbx.height; + *resize = 1; + } + + /* + * Rotate the upper left and lower right corners and check for a potential + * resize. + */ + x1 = grid->glyph_x; + y1 = grid->glyph_y; + x2 = grid->glyph_x + grid->glyph_bbx.width; + y2 = grid->glyph_y + grid->glyph_bbx.height; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + if (ht > metrics.height) { + metrics.ascent += ht - grid->font_bbx.height; + metrics.height += ht - grid->font_bbx.height; + *resize = 1; + } + + if (*resize) + (void) bdf_grid_resize(grid, &metrics); +} + +static void +_bdf_shear_resize(bdf_glyph_grid_t *grid, short degrees, int neg, int *resize) +{ + unsigned short wd; + short x1, y1, x2, y2; + bdf_metrics_t metrics; + + *resize = 0; + (void) memset((char *) &metrics, 0, sizeof(bdf_metrics_t)); + + metrics.x_offset = grid->font_bbx.x_offset; + metrics.width = grid->font_bbx.width; + metrics.ascent = grid->font_bbx.ascent; + metrics.descent = grid->font_bbx.descent; + metrics.height = grid->font_bbx.height; + metrics.y_offset = grid->font_bbx.y_offset; + + /* + * Shear the lower left and upper right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = grid->glyph_bbx.height; + x2 = grid->glyph_bbx.width; + y2 = 0; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (grid->glyph_bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (grid->glyph_bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + + /* + * Shear the upper left and lower right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = 0; + x2 = grid->glyph_bbx.width; + y2 = grid->glyph_bbx.height; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (grid->glyph_bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (grid->glyph_bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + + if (*resize) + (void) bdf_grid_resize(grid, &metrics); +} + +/* + * Rotate the bitmap in the grid by some number of degrees. + */ +int +bdf_grid_rotate(bdf_glyph_grid_t *grid, short degrees, int *resize) +{ + int rotated, mul90; + short nx, ny, cx, cy, x, y, wd, ht; + short ox, oy, gx, gy, shiftx, shifty; + unsigned short si, di, col, byte; + double dx, dy; + unsigned short bytes, bpr; + unsigned char *scratch, *masks; + + rotated = 0; + + /* + * Make sure the number of degrees is between 0 and 359 and adjusted to a + * positive number of degrees if necessary. + */ + while (degrees < 0) + degrees += 360; + while (degrees >= 360) + degrees -= 360; + + if (grid == 0 || degrees == 0 || + (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0)) + return rotated; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + mul90 = ((degrees % 90) == 0) ? 1 : 0; + + /* + * Force the grid to resize if the rotation requires it. + */ + _bdf_rotate_resize(grid, mul90, degrees, resize); + + if (grid->sel.width != 0 && grid->sel.height != 0) + return _bdf_rotate_selection(grid, mul90, degrees); + + /* + * Halve the byte count in the grid for later use. + */ + bytes = grid->bytes >> 1; + + /* + * Point at the scratch buffer area and initialize it. + */ + scratch = grid->bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + /* + * Determine the bytes per row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + /* + * Determine the center coordinates of the glyph bitmap rectangle. + */ + cx = grid->glyph_x + (grid->glyph_bbx.width >> 1); + cy = grid->glyph_y + (grid->glyph_bbx.height >> 1); + + /* + * Only run over the rectangle containing the glyph itself. + */ + gx = grid->glyph_x; + gy = grid->glyph_y; + + wd = gx + grid->glyph_bbx.width; + ht = gy + grid->glyph_bbx.height; + + /* + * Initialize the adjustment counts used if the bitmap + * wraps around the edge. + */ + shiftx = shifty = 0; + + for (y = gy; y < ht; y++) { + col = gx * grid->bpp; + for (x = gx; x < wd; x++, col += grid->bpp) { + + /* + * Rotate the point. + */ + dx = (double) (x - cx); + dy = (double) (y - cy); + if (mul90) { + nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } else { + nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } + + /* + * Wrap the coordinates around the edges if necessary. + */ + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += grid->grid_width; + } else if (nx >= grid->grid_width) { + ox = (nx - grid->grid_width) + 1; + shiftx = MAX(shiftx, ox); + nx -= grid->grid_width; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += grid->grid_height; + } else if (ny >= grid->grid_height) { + oy = (ny - grid->grid_height) + 1; + shifty = MAX(shifty, oy); + ny -= grid->grid_height; + } + + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + rotated = 1; + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + + if (rotated) { + /* + * If a shift is required, then shift the scratch area back into + * the main bitmap. + */ + if (shiftx || shifty) { + (void) memset((char *) grid->bitmap, 0, bytes); + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; + x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = scratch[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + + if (nx < 0) + nx += grid->grid_width; + else if (nx >= grid->grid_width) + nx -= grid->grid_width; + if (ny < 0) + ny += grid->grid_height; + else if (ny >= grid->grid_height) + ny -= grid->grid_height; + + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + } else + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + /* + * Determine the new glyph bounding box and the top left coordinates. + */ + ox = oy = 0; + nx = ny = 16384; + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + if (grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + nx = MIN(nx, x); + ox = MAX(ox, x); + ny = MIN(ny, y); + oy = MAX(oy, y); + } + } + } + + /* + * Set the new top left corrdinates. + */ + grid->glyph_x = nx; + grid->glyph_y = ny; + + /* + * Set the new glyph bounding box. + */ + grid->glyph_bbx.width = (ox - nx) + 1; + grid->glyph_bbx.x_offset = nx - grid->base_x; + grid->glyph_bbx.height = (oy - ny) + 1; + grid->glyph_bbx.ascent = grid->base_y - ny; + grid->glyph_bbx.descent = grid->glyph_bbx.height - + grid->glyph_bbx.ascent; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return rotated; +} + +int +bdf_grid_shear(bdf_glyph_grid_t *grid, short degrees, int *resize) +{ + int sheared, neg; + short cx, cy, wd, ht, gx, gy, x, y; + short nx, ox, ny, oy, shiftx, shifty; + unsigned short bytes, bpr, si, di, col, byte; + unsigned char *scratch, *masks; + + sheared = 0; + + if (degrees == 0 || degrees < -45 || degrees > 45 || grid == 0 || + (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0)) + return sheared; + + if ((neg = (degrees < 0))) + degrees = -degrees; + + /* + * Check to see if the grid needs to be resized to hold the sheared glyph. + */ + _bdf_shear_resize(grid, degrees, neg, resize); + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Halve the byte count in the grid for later use. + */ + bytes = grid->bytes >> 1; + + /* + * Point at the scratch buffer area and initialize it. + */ + scratch = grid->bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + /* + * Determine the bytes per row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + /* + * Determine the center coordinates of the glyph bitmap rectangle. + */ + gx = grid->glyph_x; + gy = grid->glyph_y; + + cx = gx + (grid->glyph_bbx.width >> 1); + cy = gy + (grid->glyph_bbx.height >> 1); + + wd = gx + grid->glyph_bbx.width; + ht = gy + grid->glyph_bbx.height; + + shiftx = shifty = 0; + for (y = gy; y < ht; y++) { + col = gx * grid->bpp; + for (x = gx; x < wd; x++, col += grid->bpp) { + ny = y; + if (neg) + nx = x + (short) ((double) y * _bdf_tan_tbl[degrees]); + else + nx = x + (short) ((double) (gy + (ht - y)) * + _bdf_tan_tbl[degrees]); + + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += grid->grid_width; + } else if (nx >= grid->grid_width) { + ox = (nx - grid->grid_width) + 1; + shiftx = MAX(shiftx, ox); + nx -= grid->grid_width; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += grid->grid_height; + } else if (ny >= grid->grid_height) { + oy = (ny - grid->grid_height) + 1; + shifty = MAX(shifty, oy); + ny -= grid->grid_height; + } + + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + sheared = 1; + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(y * bpr) + (nx >> 3)] |= byte; + } + } + } + + if (sheared) { + /* + * If a shift is required, then shift the scratch area back into + * the main bitmap. + */ + if (shiftx || shifty) { + (void) memset((char *) grid->bitmap, 0, bytes); + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; + x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = scratch[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + + if (nx < 0) + nx += grid->grid_width; + else if (nx >= grid->grid_width) + nx -= grid->grid_width; + if (ny < 0) + ny += grid->grid_height; + else if (ny >= grid->grid_height) + ny -= grid->grid_height; + + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + } else + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + ox = oy = 0; + nx = ny = 16384; + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + if (grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + ox = MAX(ox, x); + nx = MIN(nx, x); + oy = MAX(oy, y); + ny = MIN(ny, y); + } + } + } + + /* + * Set the new top left corrdinates. + */ + grid->glyph_x = nx; + grid->glyph_y = ny; + + /* + * Set the new glyph bounding box. + */ + grid->glyph_bbx.width = (ox - nx) + 1; + grid->glyph_bbx.x_offset = nx - grid->base_x; + grid->glyph_bbx.height = (oy - ny) + 1; + grid->glyph_bbx.ascent = grid->base_y - ny; + grid->glyph_bbx.descent = grid->glyph_bbx.height - + grid->glyph_bbx.ascent; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return sheared; +} + +int +bdf_grid_embolden(bdf_glyph_grid_t *grid) +{ + int done; + short wd, ht, gx, gy, x, y; + unsigned short b1, b2, bpr, si, di, col; + unsigned char *masks; + + done = 0; + + if (grid == 0 || + (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0)) + return done; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + /* + * Determine the bytes per row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + gx = grid->glyph_x; + gy = grid->glyph_y; + + wd = gx + grid->glyph_bbx.width; + ht = gy + grid->glyph_bbx.height; + + if (grid->spacing == BDF_PROPORTIONAL || + (grid->spacing == BDF_MONOWIDTH && + grid->glyph_bbx.width < grid->font_bbx.width)) + /* + * Only allow horizontal expansion in the cases that make sense. + */ + wd++; + + for (y = gy; y < ht; y++) { + col = (wd - 1) * grid->bpp; + for (x = wd - 1; x > gx; x--, col -= grid->bpp) { + si = (col & 7) / grid->bpp; + di = ((col - grid->bpp) & 7) / grid->bpp; + b1 = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + b2 = grid->bitmap[(y * bpr) + ((col - grid->bpp) >> 3)] & + masks[di]; + if (!b1 && b2) { + if (di < si) + b2 >>= (si - di) * grid->bpp; + else if (di > si) + b2 <<= (di - si) * grid->bpp; + grid->bitmap[(y * bpr) + (col >> 3)] |= b2; + /* + * Mark the grid as being modified. + */ + done = grid->modified = 1; + } + } + } + + /* + * Adjust the glyph width so it will be reflected when the glyph is stored + * back in the font. + */ + grid->glyph_bbx.width = wd - gx; + + return done; +} + +/************************************************************************** + * + * Glyph grid selection functions. + * + **************************************************************************/ + +int +bdf_has_selection(bdf_glyph_grid_t *grid, short *x, short *y, + short *width, short *height) +{ + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return 0; + + if (x != 0) + *x = grid->sel.x; + if (y != 0) + *y = grid->sel.y; + if (width != 0) + *width = grid->sel.width; + if (height != 0) + *height = grid->sel.height; + + return 1; +} + +/* + * Select a rectangle on the grid. + */ +void +bdf_set_selection(bdf_glyph_grid_t *grid, short x, short y, + short width, short height) +{ + short nx, ny, wd, ht, ssize, dx, dy, col; + unsigned short bytes, bpr, sbpr, si, di, byte; + unsigned char *masks; + + if (grid == 0) + return; + + /* + * Make sure the specified rectangle is within reasonable bounds. + */ + if (x < 0 || x >= grid->grid_width) + x = 0; + if (y < 0 || y >= grid->grid_height) + y = 0; + + if (x + width > grid->grid_width) + width = (x + width) - grid->grid_width; + if (y + height > grid->grid_height) + height = (y + height) - grid->grid_height; + + grid->sel.x = x; + grid->sel.y = y; + grid->sel.width = width; + grid->sel.height = height; + + /* + * Allocate enough space to represent a square the size of the largest + * of the width and height of the selection. This allows rotation and + * flipping of the selected bitmap. + */ + ssize = MAX(width, height); + + bytes = ((((ssize * grid->bpp) + 7) >> 3) * ssize) << 1; + + /* + * If the selection is being removed (width and height are 0), then simply + * return. + */ + if (bytes == 0) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + if (bytes > grid->sel.bytes) { + if (grid->sel.bytes == 0) + grid->sel.bitmap = (unsigned char *) malloc(bytes); + else + grid->sel.bitmap = (unsigned char *) + realloc((char *) grid->sel.bitmap, bytes); + grid->sel.bytes = bytes; + } else + bytes = grid->sel.bytes; + + /* + * Initialize the selection bitmap and copy the selected bits to it. + */ + (void) memset((char *) grid->sel.bitmap, 0, bytes); + + wd = x + width; + ht = y + height; + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + for (ny = 0, dy = y; dy < ht; dy++, ny++) { + col = x * grid->bpp; + for (nx = 0, dx = x; dx < wd; + dx++, nx += grid->bpp, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->sel.bitmap[(ny * sbpr) + (nx >> 3)] |= byte; + } + } + } +} + +/* + * Detach a selection in preparation for moving it. What it does is clear the + * bits set in the selection from the main grid. Again, this is only used for + * move operations. + */ +void +bdf_detach_selection(bdf_glyph_grid_t *grid) +{ + short sx, sy, x, y, wd, ht, dx; + unsigned short bpr, sbpr, si, di, byte; + unsigned char *masks; + + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + wd = grid->sel.x + grid->sel.width; + ht = grid->sel.y + grid->sel.height; + + for (sy = 0, y = grid->sel.y; y < ht; y++, sy++) { + for (sx = 0, x = grid->sel.x; x < wd; x++, sx += grid->bpp) { + si = (sx & 7) / grid->bpp; + byte = grid->sel.bitmap[(sy * sbpr) + (sx >> 3)] & masks[si]; + if (byte) { + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di]; + } + } + } + + /* + * Crop the new image to determine the new bounds with the selection. + */ + (void) bdf_grid_crop(grid, 1); +} + +void +bdf_attach_selection(bdf_glyph_grid_t *grid) +{ + short sx, sy, x, y, wd, ht; + unsigned short bpr, sbpr, dx, di, si, byte; + unsigned char *masks; + + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + wd = grid->sel.x + grid->sel.width; + ht = grid->sel.y + grid->sel.height; + + for (sy = 0, y = grid->sel.y; y < ht; y++, sy++) { + for (sx = 0, x = grid->sel.x; x < wd; x++, sx += grid->bpp) { + si = (sx & 7) / grid->bpp; + byte = grid->sel.bitmap[(sy * sbpr) + (sx >> 3)] & masks[si]; + if (byte) { + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->bitmap[(y * bpr) + (dx >> 3)] |= byte; + } + } + } + + /* + * Crop the new image to determine the new bounds with the selection. + */ + (void) bdf_grid_crop(grid, 1); +} + +/* + * Indicate the selection no longer exists by setting the width and height to + * 0. + */ +void +bdf_lose_selection(bdf_glyph_grid_t *grid) +{ + if (grid == 0) + return; + grid->sel.width = grid->sel.height = 0; +} + +/* + * Delete the selection by first detaching it which will erase the rectangle + * on the grid and then losing the selection. + */ +void +bdf_delete_selection(bdf_glyph_grid_t *grid) +{ + bdf_detach_selection(grid); + bdf_lose_selection(grid); +} + +/* + * Check to see if a coordinate pair is in the selected region. + */ +int +bdf_in_selection(bdf_glyph_grid_t *grid, short x, short y, short *set) +{ + short wd, ht; + unsigned short bpr, si, di, byte; + unsigned char *masks; + + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return 0; + + di = 0; + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + bpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + wd = grid->sel.x + grid->sel.width; + ht = grid->sel.y + grid->sel.height; + + if ((x >= grid->sel.x && x < wd) && (y >= grid->sel.y && y < ht)) { + if (set) { + /* + * Adjust the byte back to an index value. + */ + x *= grid->bpp; + si = (x & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (x >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * grid->bpp; + *set = byte; + } + return 1; + } + + return 0; +} + +int +bdf_grid_shift(bdf_glyph_grid_t *grid, short xcount, short ycount) +{ + int sel, delta; + short xdir, ydir, x, y, wd, ht, dx, dy, nx, ny; + unsigned short bytes, bpr, si, di, byte, col; + unsigned char *scratch, *masks; + + if (grid == 0) + return 0; + + xdir = ydir = 1; + if (xcount < 0) { + xdir = -1; + xcount = -xcount; + } + + if (ycount < 0) { + ydir = -1; + ycount = -ycount; + } + + /* + * Adjust the shift counts if they are larger than they should be. + */ + if (xcount > grid->grid_width) + xcount -= grid->grid_width; + if (ycount > grid->grid_height) + ycount -= grid->grid_height; + + /* + * Adjust the counts to limit the shift to the boundaries of the grid. + */ + if (grid->sel.width != 0 && grid->sel.height != 0) { + /* + * The selection is being shifted. + */ + x = grid->sel.x; + y = grid->sel.y; + wd = grid->sel.width; + ht = grid->sel.height; + sel = 1; + } else { + x = grid->glyph_x; + y = grid->glyph_y; + wd = grid->glyph_bbx.width; + ht = grid->glyph_bbx.height; + sel = 0; + } + + /* + * If the width and height are 0, then simply return, because there + * is nothing to shift. + */ + if (wd == 0 && ht == 0) + return 0; + + if (xdir == 1 && x + wd + xcount > grid->grid_width) + xcount = grid->grid_width - (x + wd); + else if (xdir == -1 && xcount > x) + xcount = x; + + if (ydir == 1 && y + ht + ycount > grid->grid_height) + ycount = grid->grid_height - (y + ht); + else if (ydir == -1 && ycount > y) + ycount = y; + + if (xcount == 0 && ycount == 0) + return 0; + + /* + * If the selection is the one being shifted, adjust the X and Y + * coordinates and adjust the glyph metrics. + */ + if (sel) { + /* + * Determine the actual ink bounds of the selection so the + * glyph metrics can be adjusted if necessary. + */ + if (_bdf_grid_ink_bounds(grid, &x, &y, &wd, &ht)) { + /* + * Have to adjust the glyph metrics. + */ + x += xdir * xcount; + y += ydir * ycount; + if (x < grid->glyph_x) { + delta = grid->glyph_x - x; + grid->glyph_bbx.width += delta; + grid->glyph_bbx.x_offset -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + grid->glyph_x -= delta; + } else if (x >= grid->glyph_x + grid->glyph_bbx.width) { + delta = x - (grid->glyph_x + grid->glyph_bbx.width); + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } + + if (y < grid->glyph_y) { + delta = grid->glyph_y - y; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.ascent += delta; + grid->glyph_y -= delta; + } else if (y + ht >= grid->glyph_y + grid->glyph_bbx.height) { + delta = (y + ht) - (grid->glyph_y + grid->glyph_bbx.height); + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset -= delta; + grid->glyph_bbx.descent += delta; + } + + grid->modified = 1; + } + + /* + * Adjust the top-left coordinate of the selection rectangle. + */ + grid->sel.x += xdir * xcount; + grid->sel.y += ydir * ycount; + + return 1; + } + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + /* + * The glyph itself is being shifted. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + bytes = grid->bytes >> 1; + scratch = grid->bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + /* + * Shift just the glyph rectangle to keep things fast. + */ + wd += x; + ht += y; + for (dy = y; dy < ht; dy++) { + col = x * grid->bpp; + for (dx = x; dx < wd; dx++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = dx + (xdir * xcount); + ny = dy + (ydir * ycount); + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + /* + * Adjust the top-left coordinate of the glyph rectangle. + */ + grid->glyph_x += xdir * xcount; + grid->glyph_y += ydir * ycount; + + /* + * Adjust the glyph offsets relative to the baseline coordinates. + */ + grid->glyph_bbx.x_offset = grid->glyph_x - grid->base_x; + grid->glyph_bbx.y_offset = grid->base_y - + (grid->glyph_y + grid->glyph_bbx.height); + + /* + * Adjust the glyph ascent and descent. + */ + grid->glyph_bbx.ascent = grid->base_y - grid->glyph_y; + grid->glyph_bbx.descent = (grid->glyph_y + grid->glyph_bbx.height) - + grid->base_y; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + + return 1; +} + + +int +bdf_grid_flip(bdf_glyph_grid_t *grid, short dir) +{ + int flipped, sel, delta; + short dx, dy, x, y, nx, ny, wd, ht; + unsigned short bytes, bpr, si, di, col, colx, byte; + unsigned char *bmap, *scratch, *masks; + + flipped = 0; + + if (grid == 0) + return flipped; + + if (grid->sel.width != 0 && grid->sel.height != 0) { + sel = 1; + x = y = 0; + wd = grid->sel.width; + ht = grid->sel.height; + bpr = ((wd * grid->bpp) + 7) >> 3; + bytes = grid->sel.bytes >> 1; + bmap = grid->sel.bitmap; + } else { + sel = 0; + x = grid->glyph_x; + y = grid->glyph_y; + wd = grid->glyph_bbx.width; + ht = grid->glyph_bbx.height; + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + bytes = grid->bytes >> 1; + bmap = grid->bitmap; + } + + /* + * If the width or height is 0, don't do anything. + */ + if (wd == 0|| ht == 0) + return flipped; + + nx = 0; + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + /* + * Set and initialize the scratch area. + */ + scratch = bmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + wd += x; + ht += y; + + if (dir < 0) { + /* + * Flip horizontally. + */ + for (dy = y; dy < ht; dy++) { + col = x * grid->bpp; + for (nx = wd - 1, dx = x; dx < wd; dx++, nx--, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = bmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + flipped = 1; + colx = nx * grid->bpp; + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(dy * bpr) + (colx >> 3)] |= byte; + } + } + } + if (flipped) { + if (sel) + grid->sel.x += nx + 1; + else { + grid->glyph_x = nx + 1; + grid->glyph_bbx.x_offset = grid->glyph_x - grid->base_x; + } + } + } else { + /* + * Flip vertically. + */ + for (ny = ht - 1, dy = y; dy < ht; dy++, ny--) { + col = x * grid->bpp; + for (dx = x; dx < wd; dx++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = bmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + flipped = 1; + scratch[(ny * bpr) + (col >> 3)] |= byte; + } + } + } + if (flipped) { + if (sel) + grid->sel.y += ny + 1; + else { + grid->glyph_y = ny + 1; + grid->glyph_bbx.y_offset = grid->base_y - + (grid->glyph_y + grid->glyph_bbx.height); + grid->glyph_bbx.ascent = grid->base_y - grid->glyph_y; + grid->glyph_bbx.descent = + (grid->glyph_y + grid->glyph_bbx.height) - grid->base_y; + } + } + } + + if (flipped) { + /* + * Copy the scratch area back to the working area. + */ + if (sel) + (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes); + else + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + if (sel) { + /* + * Check to see if flipping the selection caused the glyph metrics + * to change. + */ + if (_bdf_grid_ink_bounds(grid, &x, &y, &wd, &ht)) { + if (x < grid->glyph_x) { + delta = grid->glyph_x - x; + grid->glyph_bbx.width += delta; + grid->glyph_bbx.x_offset -= delta; + grid->glyph_x -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } else if (x >= grid->glyph_x + grid->glyph_bbx.width) { + delta = x - (grid->glyph_x + grid->glyph_bbx.width); + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } + + if (y < grid->glyph_y) { + delta = grid->glyph_y - y; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.ascent += delta; + grid->glyph_y -= delta; + } else if (y >= grid->glyph_y + grid->glyph_bbx.height) { + delta = y - (grid->glyph_y + grid->glyph_bbx.height); + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset -= delta; + grid->glyph_bbx.descent += delta; + } + } + } + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return flipped; +} + +void +bdf_grid_origin(bdf_glyph_grid_t *grid, short *x, short *y) +{ + if (grid == 0) + return; + + *x = grid->base_x; + *y = grid->base_y; +} + +bdf_glyph_t * +bdf_grid_glyph(bdf_glyph_grid_t *grid) +{ + int len; + short x, y, nx, ny, wd, ht, gx, gy; + unsigned short bpr, nbpr, si, di, col, byte; + bdf_glyph_t *glyph; + unsigned char *masks; + double ps, dw, rx; + + if (grid == 0) + return 0; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + /* + * Create the new glyph. + */ + glyph = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t)); + (void) memset((char *) glyph, 0, sizeof(bdf_glyph_t)); + + gx = grid->glyph_x; + gy = grid->glyph_y; + + /* + * Copy the bounding box. + */ + (void) memcpy((char *) &glyph->bbx, (char *) &grid->glyph_bbx, + sizeof(bdf_bbx_t)); + + /* + * If the font has character-cell spacing, then make sure the bitmap is + * cropped to fit within the bounds of the font bbx. + */ + if (grid->spacing == BDF_CHARCELL) { + if (gx < grid->base_x) { + glyph->bbx.x_offset = 0; + glyph->bbx.width -= grid->base_x - gx; + gx += grid->base_x - gx; + } + if (glyph->bbx.width > grid->font_bbx.width) + glyph->bbx.width -= glyph->bbx.width - grid->font_bbx.width; + } + + /* + * Set up its bitmap. + */ + nbpr = ((glyph->bbx.width * grid->bpp) + 7) >> 3; + glyph->bytes = nbpr * glyph->bbx.height; + glyph->bitmap = (unsigned char *) malloc(glyph->bytes); + (void) memset((char *) glyph->bitmap, 0, glyph->bytes); + + /* + * Set the other values. + */ + if (grid->name != 0) { + len = strlen(grid->name) + 1; + glyph->name = (char *) malloc(len); + (void) memcpy(glyph->name, grid->name, len); + } + glyph->encoding = grid->encoding; + glyph->dwidth = grid->dwidth; + + /* + * Reset the glyph SWIDTH value. + */ + ps = (double) grid->point_size; + rx = (double) grid->resolution_x; + dw = (double) grid->dwidth; + glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + wd = gx + glyph->bbx.width; + ht = gy + glyph->bbx.height; + + /* + * Copy the bitmap from the grid into the glyph. + */ + for (ny = 0, y = gy; y < ht; y++, ny++) { + col = gx * grid->bpp; + for (nx = 0, x = gx; x < wd; x++, nx += grid->bpp, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + glyph->bitmap[(ny * nbpr) + (nx >> 3)] |= byte; + } + } + } + + /* + * Return the new glyph. + */ + return glyph; +} + +/* + * Create a bitmap with the glyph image as well as the selection. + */ +void +bdf_grid_image(bdf_glyph_grid_t *grid, bdf_bitmap_t *image) +{ + short x, y, ix, iy; + unsigned short bpr, ibpr, si, di, col, colx, byte; + unsigned char *masks; + + if (grid == 0 || image == 0) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + image->bpp = grid->bpp; + image->x = image->y = 0; + image->width = grid->grid_width; + image->height = grid->grid_height; + image->bytes = grid->bytes >> 1; + image->bitmap = (unsigned char *) malloc(image->bytes); + (void) memcpy((char *) image->bitmap, (char *) grid->bitmap, image->bytes); + + /* + * Add the selection to the bitmap if it exists. + */ + if (grid->sel.width != 0 && grid->sel.height != 0) { + ibpr = ((image->width * grid->bpp) + 7) >> 3; + bpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + for (iy = grid->sel.y, y = 0; y < grid->sel.height; y++, iy++) { + for (ix = grid->sel.x, col = x = 0; x < grid->sel.width; + x++, ix++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + colx = ix * grid->bpp; + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + image->bitmap[(iy * ibpr) + (colx >> 3)] |= byte; + } + } + } + } +} + +/* + * These values are intended to give pixels mapped from 1bpp to nbpp the + * darkest available index, which is 1. + */ +static unsigned char twobpp_ones[] = {0x40, 0x10, 0x04, 0x01}; +static unsigned char fourbpp_ones[] = {0x10, 0x01}; +static unsigned char eightbpp_ones[] = {0x01}; + +/* + * Routines for quick and dirty dithering. + */ +static void +_bdf_one_to_n(bdf_bitmap_t *bmap, int n) +{ + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *ones = 0; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + switch (n) { + case 1: ones = bdf_onebpp; break; + case 2: ones = twobpp_ones; break; + case 4: ones = fourbpp_ones; break; + case 8: ones = eightbpp_ones; break; + } + + sbpr = (bmap->width + 7) >> 3; + bpr = ((bmap->width * n) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += n) { + if (bmap->bitmap[(sy * sbpr) + (sx >> 3)] & (0x80 >> (sx & 7))) + nbmap[(sy * bpr) + (col >> 3)] |= ones[(col & 7) / n]; + } + } + free((char *) bmap->bitmap); + bmap->bpp = n; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_n_to_one(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = 0; + switch (bmap->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + sbpr = ((bmap->width * bmap->bpp) + 7) >> 3; + bpr = (bmap->width + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += bmap->bpp) { + if (bmap->bitmap[(sy * sbpr) + (col >> 3)] & + masks[(col & 7) / bmap->bpp]) + nbmap[(sy * bpr) + (sx >> 3)] |= (0x80 >> (sx & 7)); + } + } + free((char *) bmap->bitmap); + bmap->bpp = 1; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_two_to_four(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = bdf_twobpp; + + sbpr = ((bmap->width << 1) + 7) >> 3; + bpr = ((bmap->width << 2) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 2) { + si = (col & 7) >> 1; + byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to leave the index in the lowest 2 + * bits. + */ + if (si < 3) + byte >>= (3 - si) << 1; + + /* + * Break 16 into 4 groups of 4 and map the 2bpp index to one + * of those 4. + */ + bytes <<= 2; + if ((sx & 1) == 0) + byte <<= 4; + nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 4; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_four_to_two(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = bdf_fourbpp; + + sbpr = ((bmap->width << 2) + 7) >> 3; + bpr = ((bmap->width << 1) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 4) { + si = (col & 7) >> 2; + byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si == 0) + byte >>= 4; + + /* + * Scale the index to two bits per pixel and shift it into + * place if necessary. + */ + byte >>= 2; + if (byte == 0) + byte = 1; + + si = ((sx << 1) & 7) >> 1; + if (si < 3) + byte <<= (3 - si) << 1; + + nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 2; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_two_to_eight(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = bdf_twobpp; + + sbpr = ((bmap->width << 1) + 7) >> 3; + bpr = bmap->width; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 2) { + si = (col & 7) >> 1; + byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to leave the index in the lowest 2 + * bits. + */ + if (si < 3) + byte >>= (3 - si) << 1; + + /* + * Break 256 into 4 groups of 64 and map the 2bpp index to one + * of those 4. + */ + byte <<= 6; + nbmap[(sy * bpr) + sx] = byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 8; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_eight_to_two(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, si, byte, sx, sy; + unsigned char *nbmap; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + sbpr = bmap->width; + bpr = ((bmap->width << 1) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (sx = 0; sx < bmap->width; sx++) { + byte = bmap->bitmap[(sy * sbpr) + sx]; + if (byte) { + /* + * Scale the index to two bits per pixel and shift it into + * place if necessary. + */ + byte >>= 6; + if (byte == 0) + byte = 1; + + si = ((sx << 1) & 7) >> 1; + if (si < 3) + byte <<= (3 - si) << 1; + + nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 2; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_four_to_eight(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = bdf_fourbpp; + + sbpr = ((bmap->width << 2) + 7) >> 3; + bpr = bmap->width; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 4) { + si = (col & 7) >> 2; + byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si == 0) + byte >>= 4; + + /* + * Multiply by 16 to get the 8bpp index. + */ + byte <<= 4; + + nbmap[(sy * bpr) + sx] = byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 8; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +_bdf_eight_to_four(bdf_bitmap_t *bmap) +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + sbpr = bmap->width; + bpr = ((bmap->width << 2) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 4) { + byte = bmap->bitmap[(sy * sbpr) + sx]; + if (byte) { + /* + * Divide the index by 16 to determine which 4bpp index + * it will be. + */ + byte >>= 4; + if (byte == 0) + byte = 1; + + /* + * Shift the bits up by 4 if the index is even. + */ + si = (col & 7) >> 2; + if (si == 0) + byte <<= 4; + + nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 4; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +/* + * Add a bitmap to a grid as a selection. + */ +void +bdf_add_selection(bdf_glyph_grid_t *grid, bdf_bitmap_t *sel) +{ + unsigned short bytes, bpr; + + if (grid == 0 || sel == 0 || sel->width == 0 || sel->height == 0 || + sel->bytes == 0) + return; + + if (sel->bpp != grid->bpp) { + /* + * Dither the incoming bitmap to match the same bits per pixel as the + * grid it is being added to. + */ + if (sel->bpp == 1) + _bdf_one_to_n(sel, grid->bpp); + else if (grid->bpp == 1) + _bdf_n_to_one(sel); + else if (sel->bpp == 2) { + if (grid->bpp == 4) + _bdf_two_to_four(sel); + else + _bdf_two_to_eight(sel); + } else if (sel->bpp == 4) { + if (grid->bpp == 2) + _bdf_four_to_two(sel); + else + _bdf_four_to_eight(sel); + } else if (sel->bpp == 8) { + if (grid->bpp == 2) + _bdf_eight_to_two(sel); + else + _bdf_eight_to_four(sel); + } + } + + /* + * If the bitmap is too big then trim the right and/or the bottom to fit + * in the grid. + */ + if (sel->width > grid->grid_width) + sel->width = grid->grid_width; + if (sel->height > grid->grid_height) + sel->height = grid->grid_height; + + /* + * If the positioning puts the selection bitmap off one of the edges, + * adjust it so it is completely on the grid. + */ + if (sel->x + sel->width > grid->grid_width) + sel->x -= (sel->x + sel->width) - grid->grid_width; + if (sel->y + sel->height > grid->grid_height) + sel->y -= (sel->y + sel->height) - grid->grid_height; + + bpr = ((sel->width * grid->bpp) + 7) >> 3; + bytes = (bpr * sel->height) << 1; + + /* + * Resize the storage for the selection bitmap if necessary. + */ + if (bytes > grid->sel.bytes) { + if (grid->sel.bytes == 0) + grid->sel.bitmap = (unsigned char *) malloc(bytes); + else + grid->sel.bitmap = (unsigned char *) + realloc((char *) grid->sel.bitmap, bytes); + grid->sel.bytes = bytes; + } + + /* + * Copy the width and height values. + */ + grid->sel.x = sel->x; + grid->sel.y = sel->y; + grid->sel.width = sel->width; + grid->sel.height = sel->height; + + /* + * Copy the incoming bitmap to the new selection bitmap. + */ + (void) memcpy((char *) grid->sel.bitmap, (char *) sel->bitmap, + bytes >> 1); + + /* + * Crop the image to adjust the glyph bounding box. + */ + (void) bdf_grid_crop(grid, 1); +} + +int +bdf_grid_color_at(bdf_glyph_grid_t *grid, short x, short y) +{ + unsigned short bpr, si, di, byte; + unsigned char *masks = 0; + + if (grid->bpp == 1) + return -1; + + di = 0; + switch (grid->bpp) { + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + x *= grid->bpp; + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + si = (x & 7) / grid->bpp; + + byte = grid->bitmap[(y * bpr) + (x >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * grid->bpp; + return (int) byte; +} diff --git a/bdfotf.c b/bdfotf.c new file mode 100644 index 0000000..8725db1 --- /dev/null +++ b/bdfotf.c @@ -0,0 +1,746 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * Only compile this if the FreeType library is available. + */ +#ifdef HAVE_FREETYPE + +#include "bdfP.h" +#include FT_GLYPH_H +#include FT_SFNT_NAMES_H +#include FT_TRUETYPE_TABLES_H + +#undef MAX +#define MAX(h, i) ((h) > (i) ? (h) : (i)) + +#undef MIN +#define MIN(l, o) ((l) < (o) ? (l) : (o)) + +/************************************************************************** + * + * Local variables. + * + **************************************************************************/ + +static char *platform_names[] = { + "Apple Unicode", "Macintosh", "ISO", "Microsoft", "Unknown" +}; +static int nplatform_names = +sizeof(platform_names) / sizeof(platform_names[0]); + +/* + * Mac encoding names used when creating the BDF XLFD font name and when + * selecting an encoding from a font. + */ +static char *mac_encodings[] = { + "MacRoman", "MacJapanese", "MacChinese", "MacKorean", + "MacArabic", "MacHebrew", "MacGreek", "MacRussian", + "MacRSymbol", "MacDevanagari", "MacGurmukhi", "MacGujarati", + "MacOriya", "MacBengali", "MacTamil", "MacTelugu", + "MacKannada", "MacMalayalam", "MacSinhalese", "MacBurmese", + "MacKhmer", "MacThai", "MacLaotian", "MacGeorgian", + "MacArmenian", "MacMaldivian", "MacTibetan", "MacMongolian", + "MacGeez", "MacSlavic", "MacVietnamese","MacSindhi", + "MacUninterp" +}; +static int nmac_encodings = sizeof(mac_encodings) / sizeof(mac_encodings[0]); + +/* + * ISO encoding names used when creating the BDF XLFD font name and when + * selecting an encoding from a font. + */ +static char *iso_encodings[] = { + "ASCII", "ISO10646", "ISO8859-1" +}; +static int niso_encodings = sizeof(iso_encodings) / sizeof(iso_encodings[0]); + +/* + * Microsoft encoding names used when creating the BDF XLFD font name and + * when selecting an encoding from a font. + */ +static char *ms_encodings[] = { + "Symbol", "ISO10646", "ShiftJIS", "GB2312.1980", "Big5", + "KSC5601.1987", "KSC5601.1992" +}; +static int nms_encodings = sizeof(ms_encodings) / sizeof(ms_encodings[0]); + +/************************************************************************** + * + * API. + * + **************************************************************************/ + +/* + * Routine to get the platform name from the platform ID. + */ +char * +bdfotf_platform_name(short pid) +{ + return (pid < nplatform_names) ? + platform_names[pid] : platform_names[nplatform_names - 1]; +} + +/* + * Routine to get the encoding name from the platform and encoding IDs. + */ +char * +bdfotf_encoding_name(short pid, short eid) +{ + int nnames; + char **names; + + switch (pid) { + case 0: return "ISO10646"; + case 1: + nnames = nmac_encodings; + names = mac_encodings; + break; + case 2: + nnames = niso_encodings; + names = iso_encodings; + break; + case 3: + nnames = nms_encodings; + names = ms_encodings; + break; + default: return "Unknown"; + } + + return (eid < nnames) ? names[eid] : "Unknown"; +} + +/* + * A generic routine to get a name from the TT name table. This routine + * always looks for English language names and checks three possibilities: + * 1. English names with the MS Unicode encoding ID. + * 2. English names with the MS unknown encoding ID. + * 3. English names with the Apple Unicode encoding ID. + * + * The particular name ID mut be provided (e.g. nameID = 0 for copyright + * string, nameID = 6 for Postscript name, nameID = 1 for typeface name. + * + * If the `dash_to_space' flag is set, all dashes (-) in the name will be + * replaced with spaces. + * + * Returns the number of bytes added. + */ +int +bdfotf_get_english_string(FT_Face face, int nameID, int dash_to_space, + char *name) +{ + int j, encid; + FT_UInt i, nrec; + FT_SfntName sfntName; + unsigned char *s; + unsigned short slen; + + nrec = FT_Get_Sfnt_Name_Count(face); + + for (encid = 1, j = 0; j < 2; j++, encid--) { + /* + * Locate one of the MS English font names. + */ + for (i = 0; i < nrec; i++) { + FT_Get_Sfnt_Name(face, i, &sfntName); + if (sfntName.platform_id == 3 && + sfntName.encoding_id == encid && + sfntName.name_id == nameID && + (sfntName.language_id == 0x0409 || + sfntName.language_id == 0x0809 || + sfntName.language_id == 0x0c09 || + sfntName.language_id == 0x1009 || + sfntName.language_id == 0x1409 || + sfntName.language_id == 0x1809)) { + s = sfntName.string; + slen = sfntName.string_len; + break; + } + } + + if (i < nrec) { + /* + * Found one of the MS English font names. The name is by + * definition encoded in Unicode, so copy every second byte into + * the `name' parameter, assuming there is enough space. + */ + for (i = 1; i < slen; i += 2) { + if (dash_to_space) + *name++ = (s[i] != '-') ? s[i] : ' '; + else if (s[i] == '\r' || s[i] == '\n') { + if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') + i += 2; + *name++ = ' '; + *name++ = ' '; + } else + *name++ = s[i]; + } + *name = 0; + return (slen >> 1); + } + } + + /* + * No MS English name found, attempt to find an Apple Unicode English + * name. + */ + for (i = 0; i < nrec; i++) { + FT_Get_Sfnt_Name(face, i, &sfntName); + if (sfntName.platform_id == 0 && sfntName.language_id == 0 && + sfntName.name_id == nameID) { + s = sfntName.string; + slen = sfntName.string_len; + break; + } + } + + if (i < nrec) { + /* + * Found the Apple Unicode English name. The name is by definition + * encoded in Unicode, so copy every second byte into the `name' + * parameter, assuming there is enough space. + */ + for (i = 1; i < slen; i += 2) { + if (dash_to_space) + *name++ = (s[i] != '-') ? s[i] : ' '; + else if (s[i] == '\r' || s[i] == '\n') { + if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') + i += 2; + *name++ = ' '; + *name++ = ' '; + } else + *name++ = s[i]; + } + *name = 0; + return (slen >> 1); + } + + return 0; +} + +static int +_bdfotf_generate(FT_Face face, int nocmap, bdf_options_t *opts, + bdf_callback_t callback, void *data, bdf_font_t *fp) +{ + int ismono; + int awidth, code, idx; + short maxrb, maxlb, minlb, y, x; + short x_off, y_off, maxas, maxds; + int upm, bpr, wd, ht, sx, ex, sy, ey; + unsigned char *bmap; + bdf_glyph_t *gp; + double swscale; + bdf_callback_struct_t cb; + bdf_property_t prop; + + FT_Size_Metrics imetrics; + TT_HoriHeader *horizontal = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); + + /* + * Set the instance resolution and point size. + */ + FT_Set_Char_Size(face, 0, opts->point_size * 64, + opts->resolution_x, opts->resolution_y); + imetrics = face->size->metrics; + + /* + * Set up the initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = face->num_glyphs; + (*callback)(&cb, data); + } + + /* + * Get the units per em value. + */ + upm = face->units_per_EM; + + ismono = 1; + wd = 0xffff; + awidth = 0; + maxrb = maxlb = maxas = maxds = 0; + minlb = 32767; + + /* + * Calculate the SWIDTH scaling factor. + */ + swscale = ((double) opts->resolution_y) * ((double) opts->point_size); + + for (code = fp->glyphs_used = 0; code < 0xffff; code++) { + if (nocmap) { + /* + * No cmap is being used, so do each index in turn. + */ + if (code >= face->num_glyphs) + break; + idx = code; + } else + idx = FT_Get_Char_Index(face, code); + + if (idx <= 0 || + FT_Load_Glyph(face, idx, opts->otf_flags)) + continue; + + if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO)) + continue; + + /* + * Increase the amount of storage by 128 every time. + */ + if (fp->glyphs_used == fp->glyphs_size) { + fp->glyphs = (bdf_glyph_t *) + realloc((char *) fp->glyphs, + sizeof(bdf_glyph_t) * (fp->glyphs_size + 128)); + gp = fp->glyphs + fp->glyphs_size; + (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) << 7); + fp->glyphs_size += 128; + } + + /* + * Point at the next glyph. + */ + gp = fp->glyphs + fp->glyphs_used++; + gp->encoding = code; + gp->dwidth = face->glyph->metrics.horiAdvance >> 6; + gp->swidth = (unsigned short) + (((double) gp->dwidth) * 72000.0) / swscale; + + /* + * Determine the actual bounding box of the glyph bitmap. Do not + * forget that the glyph is rendered upside down! + */ + sx = sy = 0xffff; + ex = ey = 0; + bmap = face->glyph->bitmap.buffer; + for (y = 0; y < face->glyph->bitmap.rows; y++) { + for (x = 0; x < face->glyph->bitmap.width; x++) { + if (bmap[x >> 3] & (0x80 >> (x & 7))) { + if (x < sx) sx = x; + if (x > ex) ex = x; + if (y < sy) sy = y; + if (y > ey) ey = y; + } + } + bmap += face->glyph->bitmap.pitch; + } + + /* + * If the glyph is actually an empty bitmap, set the size to 0 all + * around. + */ + if (sx == 0xffff && sy == 0xffff && ex == 0 && ey == 0) + sx = ex = sy = ey = 0; + else { + /* + * Adjust the end points. + */ + ex++; + ey++; + } + + /* + * Test to see if the font is going to be monowidth or not by + * comparing the current glyph width against the last one. + */ + if (ismono && (ex - sx) + 1 != wd) + ismono = 0; + + /* + * Set the initial metrics. + */ + wd = ex - sx; + ht = ey - sy; + x_off = sx + face->glyph->bitmap_left; + y_off = sy + face->glyph->bitmap_top - face->glyph->bitmap.rows; + + /* + * Adjust the overall bounding box. + */ + maxas = MAX(maxas, ht + y_off); + maxds = MAX(maxds, -y_off); + maxrb = MAX(maxrb, wd + x_off); + minlb = MIN(minlb, x_off); + maxlb = MAX(maxlb, x_off); + + /* + * Accumulate the average width value. + */ + awidth += wd; + + /* + * Set the glyph metrics. + */ + gp->bbx.width = wd; + gp->bbx.height = ht; + gp->bbx.x_offset = x_off; + gp->bbx.y_offset = y_off; + gp->bbx.ascent = ht + y_off; + gp->bbx.descent = -y_off; + + /* + * Allocate the bitmap for the glyph. + */ + bpr = (wd + 7) >> 3; + gp->bytes = bpr * ht; + gp->bitmap = (unsigned char *) malloc(gp->bytes); + (void) memset((char *) gp->bitmap, 0, gp->bytes); + + /* + * Shift the bits into the glyph bitmap. + */ + bmap = face->glyph->bitmap.buffer + sy * face->glyph->bitmap.pitch; + for (y = 0; y < ey - sy; y++) { + for (x = 0; x < ex - sx; x++) { + if (bmap[(x+sx) >> 3] & (0x80 >> ((x+sx) & 7))) + gp->bitmap[(y * bpr) + (x >> 3)] |= (0x80 >> (x & 7)); + } + bmap += face->glyph->bitmap.pitch; + } + + /* + * Call the callback if it was provided. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.current = fp->glyphs_used; + cb.total = face->num_glyphs; + (*callback)(&cb, data); + } + } + + /* + * Calculate the font average width. + */ + awidth = + (int) ((((double) awidth / (double) fp->glyphs_used) + 0.5) * 10.0); + + /* + * Set the font bounding box. + */ + fp->bbx.width = maxrb - minlb; + fp->bbx.height = maxas + maxds; + fp->bbx.x_offset = minlb; + fp->bbx.y_offset = -maxds; + fp->bbx.ascent = maxas; + fp->bbx.descent = maxds; + + /* + * Set the font ascent and descent. + */ + fp->font_ascent = + (horizontal->Ascender * imetrics.y_ppem) / upm; + fp->font_descent = + -((horizontal->Descender * imetrics.y_ppem) / upm); + + /* + * Determine if the font is monowidth. + */ + if (ismono) { + fp->spacing = BDF_MONOWIDTH; + fp->monowidth = fp->bbx.width; + } + + /* + * Add the properties needed for the XLFD name. + */ + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = fp->point_size * 10; + bdf_add_font_property(fp, &prop); + + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) + ((((double) (fp->point_size * 10) * + (double) fp->resolution_y) / 722.7) + 0.5); + bdf_add_font_property(fp, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) fp->resolution_x; + bdf_add_font_property(fp, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) fp->resolution_y; + bdf_add_font_property(fp, &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = fp->font_ascent; + bdf_add_font_property(fp, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = fp->font_descent; + bdf_add_font_property(fp, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = awidth; + bdf_add_font_property(fp, &prop); + + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = "P"; + switch (fp->spacing) { + case BDF_PROPORTIONAL: prop.value.atom = "P"; break; + case BDF_MONOWIDTH: prop.value.atom = "M"; break; + case BDF_CHARCELL: prop.value.atom = "C"; break; + } + bdf_add_font_property(fp, &prop); + + return 1; +} + +int +bdfotf_load_font(FT_Face face, short pid, short eid, bdf_options_t *opts, + bdf_callback_t callback, void *data, bdf_font_t **font) +{ + int i, nocmap, res, slen; + bdf_font_t *fp; + char *np, str[256]; + bdf_property_t prop; + bdf_callback_struct_t cb; + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); + + /* + * First get the requested cmap from the font. + */ + for (nocmap = i = 0; i < face->num_charmaps; i++) { + if (face->charmaps[i]->platform_id == pid && + face->charmaps[i]->encoding_id == eid) + break; + } + + /* + * If the requested cmap was not found, attempt to fall back on the + * Microsoft Unicode cmap. + */ + if (i == face->num_charmaps) { + for (i = 0; i < face->num_charmaps; i++) { + if (face->charmaps[i]->platform_id == 3 && + face->charmaps[i]->encoding_id == 1) + break; + } + if (i == face->num_charmaps) { + /* + * No cmap was found. + */ + nocmap = 1; + pid = eid = -1; + } else { + /* + * Found the Microsoft Unicode cmap. + */ + pid = 3; + eid = 1; + FT_Set_Charmap(face, face->charmaps[i]); + } + } else + FT_Set_Charmap(face, face->charmaps[i]); + + /* + * Create the font. + */ + *font = fp = (bdf_font_t *) malloc(sizeof(bdf_font_t)); + (void) memset((char *) fp, 0, sizeof(bdf_font_t)); + + /* + * Do some initializations by defaulting to proportional spacing and + * allocate at least the reported number of glyphs so reallocations will + * be minimal. + */ + fp->bpp = 1; + fp->default_glyph = -1; + fp->spacing = BDF_PROPORTIONAL; + fp->glyphs_size = face->num_glyphs; + fp->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * face->num_glyphs); + (void) memset((char *) fp->glyphs, 0, + sizeof(bdf_glyph_t) * face->num_glyphs); + + /* + * Set the metrics. + */ + fp->point_size = opts->point_size; + fp->resolution_x = opts->resolution_x; + fp->resolution_y = opts->resolution_y; + + /* + * Actually generate the font. + */ + res = _bdfotf_generate(face, nocmap, opts, callback, data, fp); + + /* + * If the number of glyphs loaded is less than the reported number of + * glyphs, force a callback if one was provided. + */ + if (callback != 0 && fp->glyphs_used < face->num_glyphs) { + cb.reason = BDF_LOADING; + cb.total = cb.current = face->num_glyphs; + (*callback)(&cb, data); + } + + /* + * If the font did not load successfully, free up the font. + */ + if (!res) { + bdf_free_font(fp); + *font = 0; + } else { + /* + * Add other sundry properties so the XLFD name can be generated. + */ + prop.name = "FOUNDRY"; + prop.format = BDF_ATOM; + prop.value.atom = "FreeType"; + bdf_add_font_property(fp, &prop); + + /* + * Get the typeface name. + */ + slen = bdfotf_get_english_string(face, BDFOTF_FAMILY_STRING, 1, str); + prop.name = "FAMILY_NAME"; + prop.format = BDF_ATOM; + if (slen > 0) + prop.value.atom = str; + else + prop.value.atom = "Unknown"; + bdf_add_font_property(fp, &prop); + + /* + * Add the CHARSET_REGISTRY and CHARSET_ENCODING properties. + */ + np = bdfotf_encoding_name(pid, eid); + if (strcmp(np, "ISO8859-1") == 0) { + prop.name = "CHARSET_REGISTRY"; + prop.format = BDF_ATOM; + prop.value.atom = "ISO8859"; + bdf_add_font_property(fp, &prop); + prop.name = "CHARSET_ENCODING"; + prop.format = BDF_ATOM; + prop.value.atom = "1"; + bdf_add_font_property(fp, &prop); + } else if (strcmp(np, "ISO10646") == 0) { + prop.name = "CHARSET_REGISTRY"; + prop.format = BDF_ATOM; + prop.value.atom = np; + bdf_add_font_property(fp, &prop); + prop.name = "CHARSET_ENCODING"; + prop.format = BDF_ATOM; + prop.value.atom = "1"; + bdf_add_font_property(fp, &prop); + } else { + prop.name = "CHARSET_REGISTRY"; + prop.format = BDF_ATOM; + prop.value.atom = np; + bdf_add_font_property(fp, &prop); + prop.name = "CHARSET_ENCODING"; + prop.format = BDF_ATOM; + prop.value.atom = "0"; + bdf_add_font_property(fp, &prop); + } + + /* + * Determine the weight name. + */ + prop.name = "WEIGHT_NAME"; + prop.format = BDF_ATOM; + slen = bdfotf_get_english_string(face, BDFOTF_SUBFAMILY_STRING, + 1, str); + if (strcmp(str, "Regular") == 0) + prop.value.atom = "Medium"; + else if (os2->fsSelection & 0x20) + prop.value.atom = "Bold"; + else if (slen > 0) + prop.value.atom = str; + else + prop.value.atom = "Medium"; + bdf_add_font_property(fp, &prop); + + /* + * Determine the slant name. + */ + prop.name = "SLANT"; + prop.format = BDF_ATOM; + if (os2->fsSelection & 0x01) + prop.value.atom = "I"; + else + prop.value.atom = "R"; + bdf_add_font_property(fp, &prop); + + /* + * Add the default SETWIDTH_NAME. + */ + prop.name = "SETWIDTH_NAME"; + prop.format = BDF_ATOM; + prop.value.atom = "Normal"; + bdf_add_font_property(fp, &prop); + + /* + * Create the XLFD font name for the font. + */ + fp->name = bdf_make_xlfd_name(fp, 0, 0); + + /* + * Add the COPYRIGHT notice. + */ + slen = bdfotf_get_english_string(face, BDFOTF_COPYRIGHT_STRING, + 0, str); + if (slen > 0) { + prop.name = "COPYRIGHT"; + prop.format = BDF_ATOM; + prop.value.atom = str; + bdf_add_font_property(fp, &prop); + } + + /* + * Add the special _TTF_PSNAME atom with the font Postscript name. + */ + slen = bdfotf_get_english_string(face, BDFOTF_POSTSCRIPT_STRING, + 0, str); + if (slen > 0) { + prop.name = "_TTF_PSNAME"; + prop.format = BDF_ATOM; + prop.value.atom = str; + bdf_add_font_property(fp, &prop); + } + + /* + * Add a message indicating the font was converted. + */ + _bdf_add_comment(fp, "Font converted from OTF to BDF.", 31); + _bdf_add_acmsg(fp, "Font converted from OTF to BDF.", 31); + + /* + * Finally, mark the font as being modified. + */ + fp->modified = 1; + } + + return res; +} + +#endif /* HAVE_FREETYPE */ diff --git a/bdfpkgf.c b/bdfpkgf.c new file mode 100644 index 0000000..902d879 --- /dev/null +++ b/bdfpkgf.c @@ -0,0 +1,1495 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Routines to import GF and PK format bitmap font files. + * + * GF is the "generic font" file format for bitmap fonts; GF files are + * typically produced by Metafont. + * + * PK is the "packed file" font file format which is the de facto standard + * bitmap font format in the TeX world. It contains most of the information + * that's in a GF file, but much more compactly. PK fonts are typically + * generated from GF fonts by gftopk(1), or by a converter like gsftopk(1). + * + * Documentation for these file formats can be found in the literate programs + * GFtoPK, PKtoGF, GFtoDVI and GFtype which are included in a typical TeX + * distribution's source. + * + */ + +#include +#include +#include +#include +#include +#include + +#ifndef BDF_NO_X11 +#include +#include +#include +#endif /* !BDF_NO_X11 */ + +#include "bdfP.h" + +#undef MAX +#define MAX(h, i) ((h) > (i) ? (h) : (i)) + +#undef MIN +#define MIN(l, o) ((l) < (o) ? (l) : (o)) + +/* + * Symbolic names for the opcode bytes in GF files. + */ +#define GF_paint1 64 /* 0x40 */ +#define GF_paint2 65 /* 0x41 */ +#define GF_paint3 66 /* 0x42 */ +#define GF_boc 67 /* 0x43 */ +#define GF_boc1 68 /* 0x44 */ +#define GF_eoc 69 /* 0x45 */ +#define GF_skip0 70 /* 0x46 */ +#define GF_skip1 71 /* 0x47 */ +#define GF_skip2 72 /* 0x48 */ +#define GF_skip3 73 /* 0x49 */ +#define GF_newrow_0 74 /* 0x4A */ +#define GF_newrow_164 238 /* 0xEE */ +#define GF_xxx1 239 /* 0xEF */ +#define GF_xxx2 240 /* 0xF0 */ +#define GF_xxx3 241 /* 0xF1 */ +#define GF_xxx4 242 /* 0xF2 */ +#define GF_yyy 243 /* 0xF3 */ +#define GF_no_op 244 /* 0xF4 */ +#define GF_char_loc 245 /* 0xF5 */ +#define GF_char_loc0 246 /* 0xF6 */ +#define GF_pre 247 /* 0xF7 */ +#define GF_post 248 /* 0xF8 */ +#define GF_post_post 249 /* 0xF9 */ + +/* + * Symbolic names for the opcode bytes in PK files. + */ + +#define PK_id 89 /* 0x59 */ +#define PK_xxx1 240 /* 0xF0 */ +#define PK_xxx4 243 /* 0xF3 */ +#define PK_yyy 244 /* 0xF4 */ +#define PK_post 245 /* 0xF5 */ +#define PK_reserved1 248 /* 0xF8 */ +#define PK_pre 247 /* 0xF7 */ + +/* + * Structure used to track the state for various things when reading a font. + */ +typedef struct { + int top; + int mask; + int c; +} _bdf_mf_state_t; + +/* + * Routine to compare two glyphs by encoding so they can be sorted. + */ +static int +by_encoding(const void *a, const void *b) +{ + bdf_glyph_t *c1, *c2; + + c1 = (bdf_glyph_t *) a; + c2 = (bdf_glyph_t *) b; + if (c1->encoding < c2->encoding) + return -1; + else if (c1->encoding > c2->encoding) + return 1; + return 0; +} + +/* + * Routines for scanning numbers from GF and PK files. + */ +static int +_bdf_mf_get16(FILE *in) +{ + return (getc(in) << 8) | (getc(in) & 0xff); +} + +static int +_bdf_mf_get32(FILE *in) +{ + int hi = _bdf_mf_get16(in); + + if (hi > 32767) + hi -= 65536; + return (hi << 16) + _bdf_mf_get16(in); +} + +static void +printscaled(int s, unsigned char *buf) +{ + int delta; + + *buf++ = ' '; + *buf++ = '('; + if (s < 0) { + *buf++ = '-'; + s = -s; + } + sprintf((char *) buf, "%d", s >> 16); + buf += strlen((char *) buf); + s = 10 * ( s & 65535 ) + 5; + if (s != 5) { + delta = 10; + *buf++ = '.'; + do { + if (delta > 65536) + s = s + 32768 - (delta >> 1); + *buf++ = 0x30 + (s >> 16); + s = 10 * (s & 65535); + delta *= 10; + } while (s > delta); + } + sprintf((char *) buf, " scaled)"); +} + +/* + * Routine to scan the PK specials and add them as comments if necessary. + */ +static int +_bdf_pk_specials(FILE *in, bdf_font_t *font, bdf_options_t *opts, + unsigned char *glyphname) +{ + int c; + int i, n, num; + unsigned int comment_size; + unsigned char *comment, bytes[4]; + + /* + * Initialize the variable that keeps track of the storage allocated + * for the comments encountered. + */ + comment = 0; + comment_size = 0; + + while ((c = getc(in)) >= PK_xxx1 && c != GF_char_loc) { + /* + * Anything between PK_reserved1 and 0xff are bad values. PK_pre is + * the font preamble which is not expected here. + */ + if (c == PK_pre || (c >= PK_post && c <= 0xff)) + return -2; + + /* + * Anything between PK_xxx1 and PK_xxx4 are string specials which will + * be added with the comments if comments are being kept. + */ + if (c >= PK_xxx1 && c <= PK_xxx4) { + /* + * Determine the number of bytes that need to be read to determine + * the length of the string special. + */ + n = (c - PK_xxx1) + 1; + fread((char *) bytes, n, 1, in); + for (i = 0, num = 0; i < n; i++) + num = (num << 8) | bytes[i]; + + if (opts->keep_comments) { + /* + * Make sure there is enough space for the string. + */ + if (comment_size < num + 1) { + if (comment_size == 0) + comment = (unsigned char *) malloc(num + 1); + else + comment = (unsigned char *) + realloc((char *) comment, num + 1); + comment_size = num + 1; + } + /* + * Read the comment and add it to the font. + */ + fread((char *) comment, num, 1, in); + comment[num] = 0; + if (!strncmp((char *) comment, "title ", 6)) + /* + * The comment is the glyph's name/title; save it so it can + * be associated with the forthcoming glyph, rather than + * with the font as a whole. + */ + strcpy((char *) glyphname, (char *) comment + 6); + else + /* + * A regular comment. + */ + _bdf_add_comment(font, (char *) comment, num); + } else + /* + * Skip the string special. + */ + fseek(in, num, 1L); + } + + /* + * PK_yyy is a numeric special. Add the number as a comment if + * specified. + */ + if (c == PK_yyy) { + num = _bdf_mf_get32(in); + if (opts->keep_comments) { + if (comment_size < 64) { + if (comment_size == 0) + comment = (unsigned char *) malloc(64); + else + comment = (unsigned char *) + realloc((char *) comment, 64); + comment_size = 64; + } + sprintf((char *) comment, "%d", num); + printscaled(num, comment + strlen((char *) comment)); + _bdf_add_comment(font, (char *) comment, + strlen((char *) comment)); + } + } + } + + /* + * Free up the comment buffer if it was allocated. + */ + if (comment_size > 0) + free((char *) comment); + + /* + * Return the byte that caused the loop to terminate. This will be the + * postamble marker GF_post or the start of the glyphs. + */ + return c; +} + +/* + * Awkward little routine to collect packed bits from a PK file. + */ +static int +_bdf_pk_getbit(FILE *in, _bdf_mf_state_t *state) +{ + state->mask >>= 1; + if (state->mask == 0) { + state->c = getc(in); + state->mask = 0x80; + } + return (state->c & state->mask); +} + +/* + * Another awkward little routine to get 4 bits at a time from a PK file. + */ +static int +_bdf_pk_getnybble(FILE *in, _bdf_mf_state_t *state) +{ + int r; + + if (state->top == 0) { + state->c = getc(in); + state->top = 2; + } + r = (state->c >> ((state->top - 1) << 2)) & 0x0f; + state->top--; + return r; +} + +/* + * Yet another awkward routine to read a packed number and a repeat count from + * a PK file. + */ +static int +_bdf_pk_getpacked(FILE *in, int *rcount, int dyn, _bdf_mf_state_t *state) +{ + int i, j; + + if ((i = _bdf_pk_getnybble(in, state)) == 0) { + do { + j = _bdf_pk_getnybble(in, state); + i++; + } while (!j); + for (;i > 0; i--) + j = (j << 4) + _bdf_pk_getnybble(in, state); + return j - 15 + ((13 - dyn) << 4) + dyn; + } + + if (i <= dyn) + return i; + + if (i < 14) + return ((i - dyn - 1) << 4) + dyn + 1 + _bdf_pk_getnybble(in, state); + + *rcount = (i == 14) ? _bdf_pk_getpacked(in, rcount, dyn, state) : 1; + return _bdf_pk_getpacked(in, rcount, dyn, state); +} + +static int +_bdf_load_pk_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback, + void *data, bdf_font_t **font) +{ + int n, res, set, x, y, bpr, rcnt, ismono; + int num, plen, pend, row_size, awidth; + short rb, maxrb, minlb, maxlb; + double denom, dw; + bdf_font_t *f; + bdf_glyph_t *gp, g; + bdf_property_t prop; + bdf_callback_struct_t cb; + _bdf_mf_state_t state; + struct stat st; + unsigned char *row, bytes[256], glyphname[256]; + + row = 0; + glyphname[0] = 0; + + /* + * Create a font to work with. + */ + *font = f = (bdf_font_t *) malloc(sizeof(bdf_font_t)); + (void) memset((char *) f, 0, sizeof(bdf_font_t)); + + /* + * Set some defaults and allocate an initial amount of space. Make the + * initial assumption that the font is monowidth but determine if it + * should be proportional when the glyphs are loaded. Allocate space for + * at least 128 glyphs before loading the font. + */ + f->bpp = 1; + f->spacing = BDF_MONOWIDTH; + f->glyphs_size = 128; + f->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) << 7); + (void) memset((char *) f->glyphs, 0, sizeof(bdf_glyph_t) << 7); + + /* + * Initialize the horizontal and vertical offsets of the font to some + * large number. + */ + f->bbx.x_offset = f->bbx.y_offset = 32767; + + /* + * Initialize things. + */ + ismono = 1; + row_size = 0; + awidth = 0; + rb = maxrb = maxlb = 0; + minlb = 32767; + (void) memset((char *) &g, 0, sizeof(bdf_glyph_t)); + + /* + * Load the initial comment. + */ + fread((char *) bytes, 1, 1, in); + n = bytes[0]; + fread(bytes, n, 1, in); + bytes[n] = 0; + + /* + * Add the comment to the font if indicated. + */ + if (opts->keep_comments) + _bdf_add_comment(f, (char *) bytes, (unsigned int) n); + + /* + * Get the point size and scale it down + */ + f->point_size = (int) (((float) _bdf_mf_get32(in)) / + ((float) (1 << 20))); + + /* + * Skip the checksum. + */ + fread(bytes, 4, 1, in); + + /* + * Get the horizontal resolution. + */ + f->resolution_x = (int) + (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5); + + /* + * Get the vertical resolution. + */ + f->resolution_y = (int) + (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5); + + /* + * Determine the denominator used for scalable width calculations. + */ + denom = ((double) f->point_size) * ((double) f->resolution_x); + + /* + * Get the font info so we can set up the callback. The callback will + * update after every glyph is loaded and is based on file size instead of + * number of glyphs. This allows the font to be read all at once instead + * of twice. + */ + (void) fstat(fileno(in), &st); + + /* + * Set the callback up. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = st.st_size; + (*callback)(&cb, data); + } + + /* + * Load the glyphs. + */ + gp = f->glyphs; + while ((res = _bdf_pk_specials(in, f, opts, glyphname)) != PK_post && + res > 0) { + /* Set the glyph's name, if we've seen it */ + if (strlen((char *) glyphname)) { + g.name = malloc(strlen((char *) glyphname)+1); + strcpy((char *) g.name, (char *) glyphname); + } + if ((res & 7) == 7) { + /* + * Long glyph info. + */ + + /* + * Get the packet length and glyph encoding. + */ + plen = _bdf_mf_get32(in); + g.encoding = _bdf_mf_get32(in); + + pend = plen + ftell(in); + + /* + * Get the glyph metrics. + */ + + /* + * Ignore the TFM width. + */ + (void) _bdf_mf_get32(in); + + /* + * Get the device width (DWIDTH) from the horizontal escapement of + * the glyph and calculate the scalable width (SWIDTH) from it. + */ + num = _bdf_mf_get32(in); + g.dwidth = num >> 16; + dw = (double) g.dwidth; + g.swidth = (unsigned short) ((dw * 72000.0) / denom); + + /* + * Ignore the vertical escapement. + */ + (void) _bdf_mf_get32(in); + + /* + * Collect the remaining glyph metrics info and calculate the + * glyph ascent and descent from the height and vertical offset. + */ + g.bbx.width = (unsigned short) _bdf_mf_get32(in); + g.bbx.height = (unsigned short) _bdf_mf_get32(in); + g.bbx.x_offset = (short) _bdf_mf_get32(in); + g.bbx.y_offset = (short) _bdf_mf_get32(in); + } else if (res & 0x04) { + /* + * Extended glyph info. + */ + if (res & 0x08) + plen = (((res & 0x07) - 4) << 16) + _bdf_mf_get16(in); + else + plen = (((res & 0x0f) - 4) << 16) + _bdf_mf_get16(in); + + /* + * Load the encoding byte and the first byte of the TFM width. + */ + fread(bytes, 2, 1, in); + g.encoding = (int) bytes[0]; + + pend = plen + ftell(in) - 1; + + /* + * Get the glyph metrics. + */ + + /* + * Ignore the last two bytes of the TFM width. + */ + (void) _bdf_mf_get16(in); + + /* + * Get the device width (DWIDTH) from the horizontal escapement of + * the glyph and calculate the scalable width (SWIDTH) from it. + */ + g.dwidth = (unsigned short) _bdf_mf_get16(in); + dw = (double) g.dwidth; + g.swidth = (unsigned short) ((dw * 72000.0) / denom); + + /* + * Collect the remaining glyph metrics info and calculate the + * glyph ascent and descent from the height and vertical offset. + */ + g.bbx.width = (unsigned short) _bdf_mf_get16(in); + g.bbx.height = (unsigned short) _bdf_mf_get16(in); + if ((num = _bdf_mf_get16(in)) > 32767) + g.bbx.x_offset = num - 65536; + else + g.bbx.x_offset = (short) num; + if ((num = _bdf_mf_get16(in)) > 32767) + g.bbx.y_offset = num - 65536; + else + g.bbx.y_offset = (short) num; + } else { + /* + * Short glyph info. Read the next 10 bytes so they can be used + * as part of the glyph info calculations. + */ + fread(bytes, 10, 1, in); + + num = 0; + if (res & 0x08) + plen = ((res & 0x07) << 8) + bytes[num++]; + else + plen = ((res & 0x0f) << 8) + bytes[num++]; + + g.encoding = (int) bytes[num++]; + + pend = plen + ftell(in) - 8; + + /* + * Skip the TFM width. + */ + num += 3; + + /* + * Get the device width (DWIDTH) from the horizontal escapement of + * the glyph and calculate the scalable width (SWIDTH) from it. + */ + g.dwidth = (unsigned short) bytes[num++]; + dw = (double) g.dwidth; + g.swidth = (unsigned short) ((dw * 72000.0) / denom); + + /* + * Collect the remaining glyph metrics info and calculate the + * glyph ascent and descent from the height and vertical offset. + */ + g.bbx.width = (unsigned short) bytes[num++]; + g.bbx.height = (unsigned short) bytes[num++]; + /* + * The hoff value we're now to interpret gives the horizontal + * offset of the reference point compared to the character's + * pixels. i.e. a value of -2 means the pixels start at +2; see + * the discussion of the example character raster in the pktogf + * web. + */ + g.bbx.x_offset = (short) ((bytes[num] & 0x80) ? + 256 - bytes[num] : bytes[num]); + num++; + g.bbx.y_offset = (short) ((bytes[num] & 0x80) ? + bytes[num] - 256 : bytes[num]); + num++; + } + + /* + * Adjust the vertical metrics of the glyph. + */ + g.bbx.y_offset = (g.bbx.y_offset + 1) - g.bbx.height; + g.bbx.ascent = g.bbx.height + g.bbx.y_offset; + g.bbx.descent = -g.bbx.y_offset; + + /* + * Check to see if the font needs to be marked as proportional. + */ + if (f->glyphs_used > 0 && ismono && + (f->glyphs[0].bbx.width != g.bbx.width || + f->glyphs[0].bbx.height != g.bbx.height || + f->glyphs[0].bbx.x_offset != g.bbx.x_offset || + f->glyphs[0].bbx.y_offset != g.bbx.y_offset || + f->glyphs[0].bbx.ascent != g.bbx.ascent || + f->glyphs[0].bbx.descent != g.bbx.descent)) { + ismono = 0; + f->spacing = BDF_PROPORTIONAL; + } + + /* + * Now load the packed bits or the run length encoded image. + */ + bpr = (g.bbx.width + 7) >> 3; + g.bytes = bpr * g.bbx.height; + g.bitmap = (unsigned char *) malloc(g.bytes); + (void) memset((char *) g.bitmap, 0, g.bytes); + + /* + * Reset the state values. + */ + state.top = state.mask = state.c = 0; + if ((res & 0xf0) == 0xe0) { + /* + * Packed bit format. + */ + if ((g.bbx.width & 7) == 0) + /* + * The bits are on a boundary that is a multiple of 8, so the + * bitmap can be read all at once. + */ + fread(g.bitmap, g.bbx.height * bpr, 1, in); + else { + /* + * The width is not a multiple of 8, so the bitmap should be + * read one bit at a time. + */ + for (y = 0; y < g.bbx.height; y++) { + for (x = 0; x < g.bbx.width; x++) { + if (_bdf_pk_getbit(in, &state)) + g.bitmap[(y * bpr) + (x >> 3)] |= (0x80 >> (x & 7)); + } + } + } + } else { + /* + * Get the run length encoded image. + */ + + /* + * The glyph image is going to be run length encoded, so allocate + * a row to collect bits into. + */ + if (row_size < bpr) { + if (row_size == 0) + row = (unsigned char *) malloc(bpr); + else + row = (unsigned char *) realloc((char *) row, bpr); + row_size = bpr; + } + /* + * Initialize the row buffer so we don't get any extra bits in the + * image by accident. + */ + (void) memset((char *) row, 0, row_size); + + /* + * Determine if the run length encoding starts with bits that are + * to be set or cleared. + */ + set = res & 0x08; + for (rcnt = x = y = 0; y < g.bbx.height;) { + /* + * Get the next number and a repeat count. + */ + n = _bdf_pk_getpacked(in, &rcnt, (res >> 4) & 0x0f, &state); + while (n > 0) { + for (; n > 0 && x < g.bbx.width; x++, n--) { + if (set) + row[x >> 3] |= (0x80 >> (x & 7)); + } + if (x == g.bbx.width) { + /* + * Copy the row into the actual glyph bitmap as many + * times as called for by the repeat count, reset x to + * 0 so a new row can be started, and initialize the + * row buffer again. + */ + for (x = 0; rcnt >= 0; rcnt--, y++) + (void) memcpy((char *) (g.bitmap + (y * bpr)), + (char *) row, bpr); + (void) memset((char *) row, 0, bpr); + rcnt = 0; + } + } + /* + * Invert the flag that indicates whether bits need + * to be set or not. + */ + set = !set; + } + } + + /* + * Adjust the font bounding box. + */ + f->bbx.ascent = MAX(g.bbx.ascent, f->bbx.ascent); + f->bbx.descent = MAX(g.bbx.descent, f->bbx.descent); + + rb = g.bbx.width + g.bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(g.bbx.x_offset, minlb); + maxlb = MAX(g.bbx.x_offset, maxlb); + + /* + * Increase the average width count to be used later. + */ + awidth += g.bbx.width; + + if ((g.encoding < 0 || g.encoding > 65535) && opts->keep_unencoded) { + /* + * If the glyph is unencoded (encoding field < 0 or > 65535) and + * the unencoded glyphs should be kept, then add the glyph to the + * unencoded list of the font. + */ + if (f->unencoded_used == f->unencoded_size) { + if (f->unencoded_size == 0) + f->unencoded = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t)); + else + f->unencoded = (bdf_glyph_t *) + realloc((char *) f->unencoded, + sizeof(bdf_glyph_t) * (f->unencoded_size + 1)); + f->unencoded_size++; + } + (void) memcpy((char *) (f->unencoded + f->unencoded_used), + (char *) &g, sizeof(bdf_glyph_t)); + f->unencoded_used++; + } else if (g.encoding >= 0 && g.encoding <= 65535) { + /* + * Add the glyph to the encoded list. + */ + if (f->glyphs_used == f->glyphs_size) { + /* + * Expand by 128 glyphs at a time. + */ + if (f->glyphs_used == 0) + f->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) << 7); + else + f->glyphs = (bdf_glyph_t *) + realloc((char *) f->glyphs, + sizeof(bdf_glyph_t) * (f->glyphs_used + 128)); + gp = f->glyphs + f->glyphs_used; + (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) << 7); + f->glyphs_size += 128; + } + gp = f->glyphs + f->glyphs_used++; + (void) memcpy((char *) gp, (char *) &g, sizeof(bdf_glyph_t));; + } else { + /* + * Free up the memory allocated for the temporary glyph so it + * doesn't leak. + */ + if (g.bytes > 0) + free((char *) g.bitmap); + } + + /* + * Make sure the temporary glyph is reinitialized. + */ + (void) memset((char *) &g, 0, sizeof(bdf_glyph_t)); + + /* + * Call the callback if indicated. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = st.st_size; + cb.current = ftell(in); + (*callback)(&cb, data); + } + } + + /* + * Sort all the glyphs by encoding. + */ + qsort((char *) f->glyphs, f->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); + + /* + * Adjust the font bounding box from the values collected. + */ + f->bbx.width = maxrb - minlb; + f->bbx.height = f->bbx.ascent + f->bbx.descent; + f->bbx.x_offset = minlb; + f->bbx.y_offset = -f->bbx.descent; + + /* + * Set the default character as being undefined. + */ + f->default_glyph = -1; + + /* + * Set the font ascent and descent. + */ + f->font_ascent = f->bbx.ascent; + f->font_descent = f->bbx.descent; + + /* + * Now add the properties to the font. + */ + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->point_size * 10; + bdf_add_font_property(f, &prop); + + /* + * Calculate and add the pixel size. + */ + denom = (double) f->resolution_y; + dw = (double) (f->point_size * 10); + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) (((denom * dw) / 722.7) + 0.5); + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) f->resolution_x; + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) f->resolution_y; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->font_ascent; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->font_descent; + bdf_add_font_property(f, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = (awidth / (f->unencoded_used + f->glyphs_used)) * 10; + bdf_add_font_property(f, &prop); + + /* + * Default all PK fonts to proportional spacing. + */ + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = "P"; + switch (f->spacing) { + case BDF_PROPORTIONAL: prop.value.atom = "P"; break; + case BDF_MONOWIDTH: prop.value.atom = "M"; break; + case BDF_CHARCELL: prop.value.atom = "C"; break; + } + bdf_add_font_property(f, &prop); + + /* + * Free up the row buffer if it was allocated. + */ + if (row_size > 0) + free((char *) row); + + /* + * Call the callback one last time if necessary. + */ + if (callback != 0 && cb.current != cb.total) { + cb.reason = BDF_LOADING; + cb.total = cb.current = st.st_size; + (*callback)(&cb, data); + } + + /* + * Add a message indicating the font was converted. + */ + _bdf_add_comment(f, "Font converted from PK to BDF.", 30); + _bdf_add_acmsg(f, "Font converted from PK to BDF.", 30); + + return BDF_OK; +} + +static int +_bdf_gf_specials(FILE *in, bdf_font_t *font, bdf_options_t *opts, + unsigned char glyphname[]) +{ + int c; + int i, n, num; + unsigned int comment_size; + unsigned char *comment, bytes[4]; + + /* + * Initialize the variable that keeps track of the storage allocated + * for the comments encountered. + */ + comment = 0; + comment_size = 0; + + while ((c = getc(in)) >= GF_xxx1) { + /* + * GF_xxx1 .. GF_xxx4 are string specials which will be added with the + * comments if comments are being kept. + */ + if (c >= GF_xxx1 && c <= GF_xxx4) { + /* + * Determine the number of bytes that need to be read to determine + * the length of the string special. + */ + n = (c - GF_xxx1) + 1; + fread((char *) bytes, n, 1, in); + for (i = 0, num = 0; i < n; i++) + num = (num << 8) | bytes[i]; + + if (opts->keep_comments) { + /* + * Make sure there is enough space for the string. + */ + if (comment_size < num + 1) { + if (comment_size == 0) + comment = (unsigned char *) malloc(num + 1); + else + comment = (unsigned char *) + realloc((char *) comment, num + 1); + comment_size = num + 1; + } + /* + * Read the comment and add it to the font. + */ + fread((char *) comment, num, 1, in); + comment[num] = 0; + if (!strncmp((char *) comment, "title ", 6)) + /* + * The comment is the glyph's name/title; save it so + * it can be associated with the forthcoming glyph, + * rather than with the font as a whole. + */ + strcpy((char *) glyphname, (char *) comment + 6); + else + /* + * A regular comment + */ + _bdf_add_comment(font, (char *) comment, num); + } else + /* + * Skip the string special. + */ + fseek(in, num, 1L); + c = GF_no_op; + } + + /* + * GF_yyy is a numeric special. Add the number as a comment if + * specified. + */ + if (c == GF_yyy) { + num = _bdf_mf_get32(in); + if (opts->keep_comments) { + if (comment_size < 64) { + if (comment_size == 0) + comment = (unsigned char *) malloc(64); + else + comment = (unsigned char *) + realloc((char *) comment, 64); + comment_size = 64; + } + sprintf((char *) comment, "%d", num); + printscaled(num, comment + strlen((char *) comment)); + _bdf_add_comment(font, (char *) comment, + strlen((char *) comment)); + } + c = GF_no_op; + } + if (c != GF_no_op) + break; + } + + /* + * Free up the comment buffer if it was allocated. + */ + if (comment_size > 0) + free((char *) comment); + + /* + * Return the byte that caused the loop to terminate. + */ + return c; +} + +static int +_bdf_load_gf_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback, + void *data, bdf_font_t **font) +{ + int n, res, set, x, y, bpr, ismono; + int awidth, num; + short rb, maxrb, minlb, maxlb; + double denom, dw; + bdf_font_t *f; + bdf_glyph_t *gp, g; + bdf_property_t prop; + bdf_callback_struct_t cb; + struct stat st; + unsigned char bytes[256], glyphname[256]; + + glyphname[0] = 0; + + /* + * Create a font to work with. + */ + *font = f = (bdf_font_t *) malloc(sizeof(bdf_font_t)); + (void) memset((char *) f, 0, sizeof(bdf_font_t)); + + /* + * Set some defaults and allocate an initial amount of space. Make the + * initial assumption that the font is monowidth but determine if it + * should be proportional when the glyphs are loaded. Allocate space for + * at least 128 glyphs before loading the font. + */ + f->bpp = 1; + f->spacing = BDF_MONOWIDTH; + f->glyphs_size = 128; + f->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) << 7); + (void) memset((char *) f->glyphs, 0, sizeof(bdf_glyph_t) << 7); + + /* + * Initialize the horizontal and vertical offsets of the font to some + * large number. + */ + f->bbx.x_offset = f->bbx.y_offset = 32767; + + /* + * Initialize things. + */ + ismono = 1; + awidth = 0; + rb = maxrb = maxlb = 0; + minlb = 32767; + (void) memset((char *) &g, 0, sizeof(bdf_glyph_t)); + + /* + * Load the initial comment. + */ + fread((char *) bytes, 1, 1, in); + n = bytes[0]; + fread(bytes, n, 1, in); + bytes[n] = 0; + + /* + * Add the comment to the font if indicated. + */ + if (opts->keep_comments) + _bdf_add_comment(f, (char *) bytes, (unsigned int) n); + + /* + * Get the font info so we can set up the callback. The callback will + * update after every glyph is loaded and is based on file size instead of + * number of glyphs. This allows the font to be read all at once instead + * of twice. + */ + (void) fstat(fileno(in), &st); + + /* + * Set the callback up. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = st.st_size; + (*callback)(&cb, data); + } + + while ((res = _bdf_gf_specials(in, f, opts, glyphname)) != GF_post) { + /* Set the glyph's name, if we've seen it */ + if (((res == GF_boc) || (res == GF_boc1)) && + strlen((char *) glyphname)) { + g.name = malloc(strlen((char *) glyphname)+1); + strcpy(g.name, (char *) glyphname); + } + + if (res == GF_boc) { + /* 32-bit character code */ + if ((g.encoding = _bdf_mf_get32(in) & 0xff) < 0) + g.encoding += 256; + /* Skip navigation pointer that's not relevant for us */ + (void) _bdf_mf_get32(in); + /* 4 times 32-bit loose bounding box parameters */ + g.bbx.x_offset = -((short) _bdf_mf_get32(in)); + g.bbx.width = + (unsigned short) (_bdf_mf_get32(in) + g.bbx.x_offset); + g.bbx.y_offset = (short) _bdf_mf_get32(in); + g.bbx.height = + (unsigned short) (_bdf_mf_get32(in) - g.bbx.y_offset); + g.bbx.height++; + g.bbx.ascent = g.bbx.height + g.bbx.y_offset; + g.bbx.descent = -g.bbx.y_offset; + } else if (res == GF_boc1) { + g.encoding = getc(in); + g.bbx.width = getc(in); + g.bbx.x_offset = getc(in) - g.bbx.width; + g.bbx.height = getc(in); + g.bbx.y_offset = getc(in) - g.bbx.height; + g.bbx.height++; + g.bbx.width++; + g.bbx.ascent = g.bbx.height + g.bbx.y_offset; + g.bbx.descent = -g.bbx.y_offset; + } + + /* + * Check to see if the font needs to be marked as proportional. + */ + if (f->glyphs_used > 0 && ismono && + (f->glyphs[0].bbx.width != g.bbx.width || + f->glyphs[0].bbx.height != g.bbx.height || + f->glyphs[0].bbx.x_offset != g.bbx.x_offset || + f->glyphs[0].bbx.y_offset != g.bbx.y_offset || + f->glyphs[0].bbx.ascent != g.bbx.ascent || + f->glyphs[0].bbx.descent != g.bbx.descent)) { + ismono = 0; + f->spacing = BDF_PROPORTIONAL; + } + + bpr = (g.bbx.width + 7) >> 3; + g.bytes = g.bbx.height * bpr; + g.bitmap = (unsigned char *) malloc(g.bytes); + (void) memset((char *) g.bitmap, 0, g.bytes); + + /* + * Get the glyph. + */ + set = x = y = 0; + while ((res = getc(in)) < GF_xxx1) { + if (res == GF_eoc) + break; + if (res < GF_paint3) { + switch (res) { + case GF_paint1: + res = getc(in); + break; + case GF_paint2: + res = _bdf_mf_get16(in); + break; + case GF_paint3: + res = (_bdf_mf_get16(in) << 8) | (getc(in) & 0xff); + break; + } + for (; res > 0; x++, res--) { + if (set) + g.bitmap[(y * bpr) + (x >> 3)] |= (0x80 >> (x & 7)); + } + set = !set; + } else if (GF_skip0 <= res && res <= GF_skip3) { + switch (res) { + case GF_skip1: + res = getc(in); + break; + case GF_skip2: + res = _bdf_mf_get16(in); + break; + case GF_skip3: + res = (_bdf_mf_get16(in) << 8) | (getc(in) & 0xff); + break; + default: + res = 0; + } + x = 0; + y += res; + set = 0; + } else if (GF_newrow_0 <= res && res <= GF_newrow_164) { + y++; + x = res - GF_newrow_0; + set = 1; + } + } + + /* + * Adjust the font bounding box. + */ + f->bbx.ascent = MAX(g.bbx.ascent, f->bbx.ascent); + f->bbx.descent = MAX(g.bbx.descent, f->bbx.descent); + + rb = g.bbx.width + g.bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(g.bbx.x_offset, minlb); + maxlb = MAX(g.bbx.x_offset, maxlb); + + /* + * Increase the average width count to be used later. + */ + awidth += g.bbx.width; + + if ((g.encoding < 0 || g.encoding > 65535) && opts->keep_unencoded) { + /* + * If the glyph is unencoded (encoding field < 0 or > 65535) and + * the unencoded glyphs should be kept, then add the glyph to the + * unencoded list of the font. + */ + if (f->unencoded_used == f->unencoded_size) { + if (f->unencoded_size == 0) + f->unencoded = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t)); + else + f->unencoded = (bdf_glyph_t *) + realloc((char *) f->unencoded, + sizeof(bdf_glyph_t) * (f->unencoded_size + 1)); + f->unencoded_size++; + } + (void) memcpy((char *) (f->unencoded + f->unencoded_used), + (char *) &g, sizeof(bdf_glyph_t)); + f->unencoded_used++; + } else if (g.encoding >= 0 && g.encoding <= 65535) { + /* + * Add the glyph to the encoded list. + */ + if (f->glyphs_used == f->glyphs_size) { + /* + * Expand by 128 glyphs at a time. + */ + if (f->glyphs_used == 0) + f->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) << 7); + else + f->glyphs = (bdf_glyph_t *) + realloc((char *) f->glyphs, + sizeof(bdf_glyph_t) * (f->glyphs_used + 128)); + gp = f->glyphs + f->glyphs_used; + (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) << 7); + f->glyphs_size += 128; + } + gp = f->glyphs + f->glyphs_used++; + (void) memcpy((char *) gp, (char *) &g, sizeof(bdf_glyph_t));; + } else { + /* + * Free up the memory allocated for the temporary glyph so it + * doesn't leak. + */ + if (g.bytes > 0) + free((char *) g.bitmap); + } + + /* + * Make sure the temporary glyph is reinitialized. + */ + (void) memset((char *) &g, 0, sizeof(bdf_glyph_t)); + + /* + * We're done with this glyph; forget we've seen its name + */ + glyphname[0] = 0; + + /* + * Call the callback if indicated. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = st.st_size; + cb.current = ftell(in); + (*callback)(&cb, data); + } + + if (res == GF_post) + break; + } + + /* + * Sort all the glyphs by encoding. + */ + qsort((char *) f->glyphs, f->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); + + /* + * Adjust the font bounding box from the values collected. + */ + f->bbx.width = maxrb - minlb; + f->bbx.height = f->bbx.ascent + f->bbx.descent; + f->bbx.x_offset = minlb; + f->bbx.y_offset = -f->bbx.descent; + + /* + * Set the default character as being undefined. + */ + f->default_glyph = -1; + + /* + * Set the font ascent and descent. + */ + f->font_ascent = f->bbx.ascent; + f->font_descent = f->bbx.descent; + + /* + * Collect the remaining font info from the postamble. + */ + (void) _bdf_mf_get32(in); + + /* + * Get the point size and scale it down + */ + f->point_size = (int) (((float) _bdf_mf_get32(in)) / + ((float) (1 << 20))); + + /* + * Skip the checksum. + */ + fseek(in, 4, 1L); + + /* + * Get the horizontal resolution. + */ + f->resolution_x = (int) + (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5); + + /* + * Get the vertical resolution. + */ + f->resolution_y = (int) + (((((float) _bdf_mf_get32(in)) * 72.27) / ((float) (1 << 16))) + 0.5); + + /* + * Skip the overall font rows and columns because they have already + * been determined. + */ + fseek(in, 16, 1L); + + /* + * Determine the denominator used for scalable width calculations. + */ + denom = ((double) f->point_size) * + ((double) f->resolution_x); + + /* + * Cycle through the glyph specific info and set the device and scalable + * widths. + */ + while ((res = getc(in)) != GF_post_post) { + /* + * Get the encoding and locate it in the font. + */ + num = getc(in); + for (set = 0, gp = f->glyphs; + set < f->glyphs_used && gp->encoding != num; gp++, set++) ; + /* + * If the glyph is not found for some reason, make the glyph pointer + * point to the temporary glyph storage. + */ + if (set == f->glyphs_used) + gp = &g; + + if (res == GF_char_loc) { + /* + * Get both horizontal and vertical escapement, only keeping + * the horizontal for the device width. + */ + num = _bdf_mf_get32(in); + gp->dwidth = num >> 16; + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / denom); + (void) _bdf_mf_get32(in); + } else if (res == GF_char_loc0) { + gp->dwidth = (unsigned short) getc(in); + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / denom); + } + + /* + * Skip the TFM width and the glyph file offset. + */ + fseek(in, 8, 1L); + + /* + * Call the callback if indicated. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = st.st_size; + cb.current = ftell(in); + (*callback)(&cb, data); + } + } + + /* + * Now add the properties to the font. + */ + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->point_size * 10; + bdf_add_font_property(f, &prop); + + /* + * Calculate and add the pixel size. + */ + denom = (double) f->resolution_y; + dw = (double) (f->point_size * 10); + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = (int) (((denom * dw) / 722.7) + 0.5); + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) f->resolution_x; + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned int) f->resolution_y; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->font_ascent; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = f->font_descent; + bdf_add_font_property(f, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = (awidth / (f->unencoded_used + f->glyphs_used)) * 10; + bdf_add_font_property(f, &prop); + + /* + * Default all PK fonts to proportional spacing. + */ + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = "P"; + switch (f->spacing) { + case BDF_PROPORTIONAL: prop.value.atom = "P"; break; + case BDF_MONOWIDTH: prop.value.atom = "M"; break; + case BDF_CHARCELL: prop.value.atom = "C"; break; + } + bdf_add_font_property(f, &prop); + + /* + * Call the callback one last time if necessary. + */ + if (callback != 0 && cb.current != cb.total) { + cb.reason = BDF_LOADING; + cb.total = cb.current = st.st_size; + (*callback)(&cb, data); + } + + /* + * Add a message indicating the font was converted. + */ + _bdf_add_comment(f, "Font converted from GF to BDF.", 30); + _bdf_add_acmsg(f, "Font converted from GF to BDF.", 30); + + return BDF_OK; +} + +int +bdf_load_mf_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback, + void *data, bdf_font_t **font) +{ + unsigned char mfmag[2]; + + /* + * Load the header to see if this is a GF or PK font. + */ + fread((char *) mfmag, 2, 1, in); + if (mfmag[0] != GF_pre || (mfmag[1] != PK_id && mfmag[1] != 0x83)) + return BDF_NOT_MF_FONT; + return (mfmag[1] == PK_id) ? + _bdf_load_pk_font(in, opts, callback, data, font) : + _bdf_load_gf_font(in, opts, callback, data, font); +} diff --git a/bdfpsf.c b/bdfpsf.c new file mode 100644 index 0000000..66f8534 --- /dev/null +++ b/bdfpsf.c @@ -0,0 +1,971 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "bdfP.h" + +/************************************************************************** + * + * PSF1 Macros and values. + * + **************************************************************************/ + +/* + * Macros that specify the indexes of the mode and height fields of the header + * info passed to the PSF1 loader. + */ +#define _BDF_PSF1MODE 2 +#define _BDF_PSF1HEIGHT 3 + +/* + * Flags which can appear in the third byte of the header (PSF1 mode). HAS512 + * means the font contains up to 512 glyphs. Otherwise the font has 256 + * glyphs. HASTAB and HASSEQ indicate the glyphs are followed by a Unicode + * mapping table. + * + * The HASTAB and HASSEQ flags appear to be essentially equivalent. + */ +#define _BDF_PSF1_HAS512 0x01 +#define _BDF_PSF1_HASTAB 0x02 +#define _BDF_PSF1_HASSEQ 0x04 + +/************************************************************************** + * + * PSF2 Macros + * + **************************************************************************/ + +#define _BDF_PSF2_HASTAB 0x01 + +/* + * Little endian versions of the PSF magic numbers. + */ +unsigned char _bdf_psf1magic[] = {0x36, 0x04}; +unsigned char _bdf_psf2magic[] = {0x72, 0xb5, 0x4a, 0x86}; + +/* + * The special header for PSF fonts that specify a list of partial + * fonts. + */ +char _bdf_psfcombined[] = {'#', ' ', 'c', 'o'}; + +/* + * The PSF2 internal header. + */ +typedef struct { + unsigned int version; + unsigned int headersize; + unsigned int flags; + unsigned int length; + unsigned int bpc; + unsigned int height; + unsigned int width; +} _bdf_psfhdr_t; + +/************************************************************************** + * + * Support functions. + * + **************************************************************************/ + +#define _swap_endian(n) ((n) >> 16) | (((n) & 0xffff) << 16) + +static int +_bdf_psf_load_map(FILE *in, bdf_font_t *font, int psf2, int *res) +{ + int i, more, c0, c1, cnt; + unsigned int code; + unsigned char buf[4]; + bdf_glyph_t *gp; + + gp = font->glyphs; + + while ((c0 = getc(in)) >= 0) { + /* + * If we are still reading bytes after the end of the glyphs, + * the table is too int. + */ + if (gp == font->glyphs + font->glyphs_used) + return BDF_PSF_LONG_TABLE; + + cnt = 0; + *res = gp->encoding; + if (!psf2) { + if ((c1 = getc(in)) < 0) + return BDF_PSF_SHORT_TABLE; + if (bdf_little_endian()) + code = (c1 << 8) | (c0 & 0xff); + else + code = (c0 << 8) | (c1 & 0xff); + + /* + * Convert to UTF-8. + */ + if (code != 0xffff) { + if (code < 0x80) + buf[cnt++] = code & 0xff; + else if (code < 0x800) { + buf[cnt++] = 0xc0 | (code >> 6); + buf[cnt++] = 0x80 | (code & 0x3f); + } else if (code < 0x10000) { + buf[cnt++] = 0xe0 | (code >> 12); + buf[cnt++] = 0x80 | ((code >> 6) & 0x3f); + buf[cnt++] = 0x80 | (code & 0x3f); + } else if (code < 0x200000) { + buf[cnt++] = 0xf0 | (code >> 18); + buf[cnt++] = 0x80 | ((code >> 12) & 0x3f); + buf[cnt++] = 0x80 | ((code >> 6) & 0x3f); + buf[cnt++] = 0x80 | (code & 0x3f); + } else if (code < 0x4000000) { + buf[cnt++] = 0xf8 | (code >> 24); + buf[cnt++] = 0x80 | ((code >> 18) & 0x3f); + buf[cnt++] = 0x80 | ((code >> 12) & 0x3f); + buf[cnt++] = 0x80 | ((code >> 6) & 0x3f); + buf[cnt++] = 0x80 | (code & 0x3f); + } else if (code < 0x7fffffff) { + buf[cnt++] = 0xfc | (code >> 30); + buf[cnt++] = 0x80 | ((code >> 24) & 0x3f); + buf[cnt++] = 0x80 | ((code >> 18) & 0x3f); + buf[cnt++] = 0x80 | ((code >> 12) & 0x3f); + buf[cnt++] = 0x80 | ((code >> 6) & 0x3f); + buf[cnt++] = 0x80 | (code & 0x3f); + } + } else + buf[cnt++] = 0xff; + } else { + buf[cnt++] = c0; + if (c0 < 0xfd && (c0 & 0x80) != 0) { + /* + * Only look for more if the byte is not 0xfe or 0xff, + * PSF separators. + */ + more = 0; + if ((c0 & 0xe0) == 0xc0) + more = 1; + else if ((c0 & 0xf0) == 0xe0) + more = 2; + else if ((c0 & 0xf0) == 0xf0) + more = 3; + + for (i = 0; i < more; i++) { + if ((c0 = getc(in)) < 0) + return BDF_PSF_SHORT_TABLE; + else if (c0 > 0xfd) + return BDF_PSF_CORRUPT_UTF8; + buf[cnt++] = c0; + } + } + } + if (buf[0] != 0xff) { + if (gp->unicode.map_used + cnt > gp->unicode.map_size) { + more = ((cnt >> 2) + ((cnt & 3) ? 1 : 0)) << 2; + if (gp->unicode.map_size == 0) + gp->unicode.map = (unsigned char *) + malloc(sizeof(unsigned char) * more); + else + gp->unicode.map = (unsigned char *) + realloc((char *) gp->unicode.map, + sizeof(unsigned char) * + (gp->unicode.map_size + more)); + gp->unicode.map_size += more; + } + (void) memcpy((char *) (gp->unicode.map + gp->unicode.map_used), + (char *) buf, sizeof(unsigned char) * cnt); + gp->unicode.map_used += cnt; + } else + gp++; + + } + return BDF_OK; +} + +static int +_bdf_psf_dump_map(FILE *out, bdf_font_t *font, bdf_glyphlist_t *glyphs) +{ + int seq; + unsigned int i, nglyphs, n; + int code = -1; + unsigned char *map, *map_end; + bdf_glyph_t *gp; + + nglyphs = (glyphs->glyphs_used > 512) ? 512 : glyphs->glyphs_used; + for (i = 0, gp = glyphs->glyphs; i < nglyphs; i++, gp++) { + + if (nglyphs > 256) + fprintf(out, "0x%03x", i); + else + fprintf(out, "0x%02x", i); + + map = gp->unicode.map; + map_end = map + gp->unicode.map_used; + + seq = 0; + while (map < map_end) { + n = 1; + + if (*map == 0xfe) { + seq = 2; + map++; + continue; + } + + /* + * Convert from UTF-8 to UTF-32. + */ + if ((*map & 0x80) == 0) + /* + * One byte character. + */ + code = (unsigned short) *map; + else if (*map < 0xe0) { + /* + * Two byte character. + */ + if (map + 2 >= map_end) + return BDF_PSF_CORRUPT_UTF8; + code = ((*map & 0x1f) << 6) | (*(map + 1) & 0x3f); + n = 2; + } else if (*map < 0xf0) { + /* + * Three byte character. + */ + if (map + 3 >= map_end) + return BDF_PSF_CORRUPT_UTF8; + code = ((*map & 0x0f) << 12) | + ((*(map + 1) & 0x3f) << 6) | (*(map + 2) & 0x3f); + n = 3; + } else if (*map < 0xf8) { + /* + * Four byte character. + */ + if (map + 4 >= map_end) + return BDF_PSF_CORRUPT_UTF8; + code = ((*map & 0x07) << 18) | + ((*(map + 1) & 0x3f) << 12) | + ((*(map + 2) & 0x3f) << 6) | (*(map + 3) & 0x3f); + n = 4; + } else if (*map < 0xfc) { + /* + * Five byte character. + */ + if (map + 5 >= map_end) + return BDF_PSF_CORRUPT_UTF8; + code = ((*map & 0x03) << 24) | + ((*(map + 1) & 0x3f) << 18) | + ((*(map + 2) & 0x3f) << 12) | + ((*(map + 3) & 0x3f) << 6) | + (*(map + 4) & 0x3f); + n = 5; + } else if (*map < 0xfe) { + /* + * Six byte character. + */ + if (map + 6 >= map_end) + return BDF_PSF_CORRUPT_UTF8; + code = ((*map & 0x01) << 30) | + ((*(map + 1) & 0x3f) << 24) | + ((*(map + 2) & 0x3f) << 18) | + ((*(map + 3) & 0x3f) << 12) | + ((*(map + 4) & 0x3f) << 6) | + (*(map + 5) & 0x3f); + n = 6; + } + /* + * Print the code(s). If we are printing the first one, + * then print a tab, otherwise we are printing separating + * spaces. + */ + if (map == gp->unicode.map) + putc('\t', out); + else + putc(((seq == 1) ? ',' : ' '), out); + if (n < 5) + fprintf(out, "U+%04X", code); + else + fprintf(out, "U+%06X", code); + map += n; + seq -= (seq == 2); + } + + /* + * Print the line separator. + */ + putc('\n', out); + + /* + * Free the current glyph storage. + */ + if (gp->name != 0) + free(gp->name); + if (gp->bytes > 0) + free((char *) gp->bitmap); + if (gp->unicode.map_size > 0) + free((char *) gp->unicode.map); + } + + /* + * Free the storage for the glyph list. + */ + if (glyphs->glyphs_size > 0) + free((char *) glyphs->glyphs); + + return BDF_OK; +} + +/************************************************************************** + * + * Public functions. + * + **************************************************************************/ + +/* + * Return an array of strings with the Unicode encodings already formatted for + * use with the GUI. + */ +char ** +_bdf_psf_unpack_mapping(bdf_psf_unimap_t *unimap, int *num_seq) +{ + int ns, nc, sum, c; + int code = -1; + unsigned char *mp, *ep; + char **list, *lp; + + list = 0; + if (!num_seq) + return list; + *num_seq = 0; + + if (unimap == 0 || unimap->map_used == 0) + return list; + + /* + * This routine will calculate the amount of storage is needed to be + * allocated as one big block so an array of strings can be returned. + * That way it can be deallocated as a single item by the caller. + */ + + + /* + * Count the total number of characters and sequences at the same time. + */ + mp = unimap->map; + ep = mp + unimap->map_used; + sum = ns = nc = 0; + while (mp < ep) { + if (*mp == 0xfe) { + sum = 1; + ns++; + mp++; + continue; + } + + c = 1; + if (*mp < 0xe0) + c = 2; + else if (*mp < 0xf0) + c = 3; + else if (*mp < 0xf8) + c = 4; + else if (*mp < 0xfc) + c = 5; + else if (*mp < 0xfe) + c = 6; + + nc++; + + mp += c; + ns += !sum; + } + + *num_seq = ns; + + /* + * The block of storage will need this many bytes for pointers to the + * sequences. + */ + sum = sizeof(unsigned char *) * ns; + + /* + * Each character uses up to 8 bytes in U+XXXXXX form and each is followed + * by either space or a null, so each is basically nine bytes including + * the trailing NULL. + */ + sum += nc * 9; + + list = (char **) malloc(sum); + lp = (char *) (list + ns); + + /* + * Now generate the codes one at a time. + */ + list[0] = lp; + + ns = sum = 0; + mp = unimap->map; + while (mp < ep) { + if (*mp == 0xfe) { + if (sum == 1) + /* + * The last thing added was a sequence, so move up to the + * next sequence. + */ + list[++ns] = ++lp; + + sum = 1; + mp++; + continue; + } + + nc = 1; + + /* + * Convert from UTF-8 to UTF-32. + */ + if (*mp < 0x80) + /* + * One byte character. + */ + code = (unsigned short) *mp; + else if (*mp < 0xe0) { + /* + * Two byte character. + */ + code = ((*mp & 0x1f) << 6) | (*(mp + 1) & 0x3f); + nc = 2; + } else if (*mp < 0xf0) { + /* + * Three byte character. + */ + code = ((*mp & 0x0f) << 12) | + ((*(mp + 1) & 0x3f) << 6) | (*(mp + 2) & 0x3f); + nc = 3; + } else if (*mp < 0xf8) { + /* + * Four byte character. + */ + code = ((*mp & 0x07) << 18) | + ((*(mp + 1) & 0x3f) << 12) | + ((*(mp + 2) & 0x3f) << 6) | (*(mp + 3) & 0x3f); + nc = 4; + } else if (*mp < 0xfc) { + /* + * Five byte character. + */ + code = ((*mp & 0x03) << 24) | + ((*(mp + 1) & 0x3f) << 18) | + ((*(mp + 2) & 0x3f) << 12) | + ((*(mp + 3) & 0x3f) << 6) | + (*(mp + 4) & 0x3f); + nc = 5; + } else if (*mp < 0xfe) { + /* + * Six byte character. + */ + code = ((*mp & 0x01) << 30) | + ((*(mp + 1) & 0x3f) << 24) | + ((*(mp + 2) & 0x3f) << 18) | + ((*(mp + 3) & 0x3f) << 12) | + ((*(mp + 4) & 0x3f) << 6) | + (*(mp + 5) & 0x3f); + nc = 6; + } + + /* + * Add to the string. + */ + + if (lp > list[ns]) + *lp++ = ' '; + if (nc < 5) + sprintf(lp, "U+%04X", code); + else + sprintf(lp, "U+%06X", code); + lp += 7; + + mp += nc; + + if (mp < ep && !sum) + list[++ns] = ++lp; + } + return list; +} + +/* + * Routine used to insure the list of mappings is in ascending order + * by length of string. + */ +static int +cmplen(const void *a, const void *b) +{ + int n; + char *as = *((char **)a); + char *bs = *((char **)b); + + n = strlen(as) - strlen(bs); + return (n) ? n : strcmp(as, bs); +} + +/* + * Taking a list of strings, generate a packed UTF-8 representation to + * be stored back into a Unicode map. + */ +int +_bdf_psf_pack_mapping(char **list, int len, int encoding, + bdf_psf_unimap_t *map) +{ + int i, j, ncodes, bytes = 3; + char *lp, *elp; + unsigned int codes[128]; + + if (list == 0 || len == 0 || map == 0) + return 0; + + map->map_used = 0; + + /* + * First thing that needs to be done is to make sure the list is sorted by + * length so the single character mappings come first. + */ + qsort((char *) list, len, sizeof(char *), cmplen); + + for (i = 0; i < len; i++) { + lp = list[i]; + ncodes = 0; + while (*lp) { + /* + * Skip anything that isn't expected. + */ + while (*lp && *lp != 'U' && *lp != 'u' && *lp != '0') + lp++; + if (*lp == 0) + continue; + codes[ncodes] = _bdf_atoul(lp, &elp, 16); + + /* + * Determine how many UTF-8 bytes the current code will + * take. + */ + if (codes[ncodes] < 0x80) + bytes++; + else if (codes[ncodes] < 0x800) + bytes += 2; + else if (codes[ncodes] < 0x10000) + bytes += 3; + else if (codes[ncodes] < 0x200000) + bytes += 4; + else if (codes[ncodes] < 0x4000000) + bytes += 5; + else if (codes[ncodes] < 0x7fffffff) + bytes += 6; + ncodes++; + lp = elp; + } + /* + * Make sure there is enough room in the map for this number of bytes. + * The number includes the encoding and the 0xff at the end on the + * first pass. + */ + if (map->map_used + bytes > map->map_size) { + if (map->map_size == 0) + map->map = (unsigned char *) + malloc(sizeof(unsigned char *) * 128); + else + map->map = (unsigned char *) + realloc((char *) map->map, + sizeof(unsigned char) * (map->map_size + 128)); + map->map_size += 128; + } + + if (ncodes > 1) + /* + * Have to increment the number of bytes by 1 to include the + * PSF2 sequence marker. + */ + map->map[map->map_used++] = 0xfe; + + /* + * Go through the codes and convert to UTF-8. + */ + for (j = 0; j < ncodes; j++) { + if (codes[j] < 0x80) + map->map[map->map_used++] = (codes[j] & 0x7f); + else if (codes[j] < 0x800) { + map->map[map->map_used++] = 0xc0 | ((codes[j] >> 6) & 0xff); + map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f); + } else if (codes[j] < 0x10000) { + map->map[map->map_used++] = 0xe0 | ((codes[j] >> 12) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f); + map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f); + } else if (codes[j] < 0x200000) { + map->map[map->map_used++] = 0xf0 | ((codes[j] >> 18) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 12) & 0x3f); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f); + map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f); + } else if (codes[j] < 0x4000000) { + map->map[map->map_used++] = 0xf8 | ((codes[j] >> 24) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 18) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 12) & 0x3f); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f); + map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f); + } else if (codes[j] < 0x7fffffff) { + map->map[map->map_used++] = 0xfc | ((codes[j] >> 30) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 24) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 18) & 0xff); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 12) & 0x3f); + map->map[map->map_used++] = 0x80 | ((codes[j] >> 6) & 0x3f); + map->map[map->map_used++] = 0x80 | (codes[j] & 0x3f); + } + } + bytes = 0; + } + + return BDF_OK; +} + +bdf_font_t * +bdf_load_psf(FILE *in, unsigned char *magic, bdf_options_t *opts, + bdf_callback_t callback, void *data, int *awidth) +{ + int i, enc; + unsigned short dwidth, swidth; + bdf_glyph_t *gp; + bdf_font_t *fp; + _bdf_psfhdr_t hdr; + bdf_callback_struct_t cb; + char msgbuf[1024]; + + /* + * Check options for loading the Unicode table or not. + */ + + if (*magic == 0x36) { + /* + * PSF1 font. + */ + hdr.version = 0; + hdr.width = 8; + hdr.height = hdr.bpc = (int) magic[_BDF_PSF1HEIGHT]; + hdr.length = (magic[_BDF_PSF1MODE] & _BDF_PSF1_HAS512) ? 512 : 256; + hdr.flags = (magic[_BDF_PSF1MODE] & _BDF_PSF1_HASTAB) ? + _BDF_PSF2_HASTAB : 0; + } else { + /* + * PSF2 font. + */ + fread((char *) &hdr, sizeof(_bdf_psfhdr_t), 1, in); + if (!bdf_little_endian()) { + /* + * Need to convert all the integers to big endian. + */ + hdr.version = _swap_endian(hdr.version); + hdr.headersize = _swap_endian(hdr.headersize); + hdr.flags = _swap_endian(hdr.flags); + hdr.length = _swap_endian(hdr.length); + hdr.bpc = _swap_endian(hdr.bpc); + hdr.height = _swap_endian(hdr.height); + hdr.width = _swap_endian(hdr.width); + } + } + + /* + * The point size of the font will be the height, the resolution will + * default to 72dpi, and the spacing will default to character cell. + */ + fp = bdf_new_font(0, (int) hdr.height, 72, 72, BDF_CHARCELL, 1); + + /* + * Force the bits per pixel to be 1. + */ + fp->bpp = 1; + + /* + * Set the font width and average width. + */ + *awidth = fp->bbx.width = hdr.width; + + /* + * Set the rest of the font bounding box parameters. + */ + fp->font_ascent = fp->bbx.ascent; + fp->font_descent = fp->bbx.descent; + +#if 0 + /* + * MAY NOT BE NEEDED ANY MORE. + */ + + /* + * Adjust the ascent and descent by hand for point sizes other than 16. + */ + if (hdr.height != 16) { + fp->bbx.ascent++; + fp->bbx.descent--; + } +#endif + + /* + * Default the font ascent and descent to that of the bounding box. + */ + fp->font_ascent = fp->bbx.ascent; + fp->font_descent = fp->bbx.descent; + + /* + * Allocate the expected number of glyphs. + */ + fp->glyphs_size = hdr.length; + fp->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * fp->glyphs_size); + (void) memset((char *) fp->glyphs, 0, + sizeof(bdf_glyph_t) * fp->glyphs_size); + + /* + * Determine the default scalable and device width for each character. + */ + dwidth = fp->bbx.width; + swidth = (unsigned short) + (((double) dwidth) * 72000.0) / + ((double) fp->point_size * fp->resolution_x); + + /* + * Set up to call the callback. + */ + if (callback != 0) { + cb.reason = BDF_LOAD_START; + cb.current = 0; + cb.total = fp->glyphs_size; + (*callback)(&cb, data); + } + + /* + * Now load the glyphs, assigning a default encoding. + */ + for (i = 0, gp = fp->glyphs; i < fp->glyphs_size; i++, gp++) { + gp->encoding = i; + gp->dwidth = dwidth; + gp->swidth = swidth; + (void) memcpy((char *) &gp->bbx, (char *) &fp->bbx, sizeof(bdf_bbx_t)); + + gp->bytes = hdr.bpc; + gp->bitmap = (unsigned char *) malloc(hdr.bpc); + fread((char *) gp->bitmap, hdr.bpc, 1, in); + fp->glyphs_used++; + + /* + * Call the callback if indicated. + */ + if (callback != 0) { + cb.reason = BDF_LOADING; + cb.total = fp->glyphs_size; + cb.current = fp->glyphs_used; + (*callback)(&cb, data); + } + } + + /* + * Now load the Unicode mapping table if it exists. + */ + if (hdr.flags & _BDF_PSF2_HASTAB) { + msgbuf[0] = 0; + switch (_bdf_psf_load_map(in, fp, (*magic == 0x72), &enc)) { + case BDF_PSF_SHORT_TABLE: + sprintf(msgbuf, "PSF Unicode table too short at 0x%04X (%d).", + (unsigned short) (enc & 0xffff), enc); + break; + case BDF_PSF_LONG_TABLE: + strcpy(msgbuf, "PSF Unicode table too int."); + break; + case BDF_PSF_CORRUPT_UTF8: + sprintf(msgbuf, "PSF UTF-8 sequence corrupt at 0x%04X (%d).", + (unsigned short) (enc & 0xffff), enc); + break; + case BDF_PSF_BUFFER_OVRFL: + sprintf(msgbuf, "PSF mapping buffer overflow at 0x%04X (%d).", + (unsigned short) (enc & 0xffff), enc); + break; + } + if (msgbuf[0] != 0) + _bdf_add_acmsg(fp, msgbuf, strlen(msgbuf)); + } + + sprintf(msgbuf, "Font converted from PSF%c to BDF.", + (*magic == 0x36) ? '1' : '2'); + _bdf_add_comment(fp, msgbuf, 32); + _bdf_add_acmsg(fp, msgbuf, 32); + + return fp; +} + +/* + * Exports all PSF fonts in PSF2 format for now. start and end are + * supplied when a partial font needs to be created. + */ +int +bdf_export_psf(FILE *out, bdf_font_t *font, bdf_options_t *opts, int start, + int end) +{ + unsigned int i, nglyphs, flags; + _bdf_psfhdr_t hdr; + bdf_glyph_t *gp; + bdf_font_t tmpfont; + bdf_glyphlist_t glyphs; + bdf_glyph_t cell; + + if (font->glyphs_used == 0) + return BDF_EMPTY_FONT; + + /* + * This routine only exports from CHARCELL and MONOWIDTH fonts, padding + * the glyphs as it writes. + */ + if (font->spacing == BDF_PROPORTIONAL) + return BDF_EMPTY_FONT; + + if (start == end) + return BDF_BAD_RANGE; + + /* + * Make a copy of the glyphs so we can get the smallest bounding box for + * the glyphs being exported. This also does a bit of range checking. + */ + (void) memset((char *) &glyphs, 0, sizeof(bdf_glyphlist_t)); + bdf_copy_glyphs(font, start, end, &glyphs, 0); + + /* + * At this point, if only the Unicode table is desired, then + * call the routine that prints the plain text version. + */ + if ((opts->psf_flags == BDF_PSF_UNIMAP)) + return _bdf_psf_dump_map(out, font, &glyphs); + + /* + * Set up the temporary font so glyph padding will happen like it is + * supposed to. + */ + tmpfont.bpp = glyphs.bpp; + (void) memcpy((char *) &tmpfont.bbx, (char *) &glyphs.bbx, + sizeof(bdf_bbx_t)); + + /* + * Create the header. The extra 4 on the header size account + * for the magic number. + * + * Number of glyphs and flags have to be calculated properly before writing + * so it isn't necessary to go back and rewrite the header after the font + * has been written. That causes havoc when writing to stdout. + */ + hdr.version = hdr.flags = 0; + hdr.headersize = sizeof(_bdf_psfhdr_t) + 4; + hdr.length = (glyphs.glyphs_used > 512) ? 512 : glyphs.glyphs_used; + hdr.width = glyphs.bbx.width; + hdr.height = glyphs.bbx.height; + hdr.bpc = hdr.height * ((hdr.width + 7) >> 3); + + /* + * Determine if the font will have a Unicode mapping table. + */ + for (i = 0; i < hdr.length; i++) { + if (glyphs.glyphs[i].unicode.map_used > 0) { + hdr.flags |= _BDF_PSF2_HASTAB; + break; + } + } + + /* + * Save these values so it doesn't get whacked in an endian conversion. + */ + nglyphs = hdr.length; + flags = hdr.flags; + + /* + * Set up a structure for padding glyphs to cell boundaries. + */ + cell.bytes = hdr.bpc; + cell.bitmap = (unsigned char *) malloc(cell.bytes); + (void) memcpy((char *) &cell.bbx, (char *) &glyphs.bbx, sizeof(bdf_bbx_t)); + + if (!bdf_little_endian()) { + /* + * Swap the integers into little endian order before writing. + */ + hdr.version = _swap_endian(hdr.version); + hdr.headersize = _swap_endian(hdr.headersize); + hdr.flags = _swap_endian(hdr.flags); + hdr.length = _swap_endian(hdr.length); + hdr.bpc = _swap_endian(hdr.bpc); + hdr.height = _swap_endian(hdr.height); + hdr.width = _swap_endian(hdr.width); + } + + /* + * Write the header. + */ + fwrite((char *) _bdf_psf2magic, sizeof(unsigned char), 4, out); + fwrite((char *) &hdr, sizeof(_bdf_psfhdr_t), 1, out); + + /* + * Generate the glyphs, padding them out to the dimensions of the + * font. + */ + for (i = 0, gp = glyphs.glyphs; i < nglyphs; i++, gp++) { + /* + * We only need to do cropping on CHARCELL glyphs because MONOWIDTH + * glyphs are already cropped to their minimum dimensions. + */ + if (font->spacing == BDF_CHARCELL) + _bdf_crop_glyph(&tmpfont, gp); + _bdf_pad_cell(&tmpfont, gp, &cell); + fwrite((char *) cell.bitmap, sizeof(unsigned char), cell.bytes, out); + } + + /* + * Now generate the Unicode table if called for. + */ + if ((opts->psf_flags & BDF_PSF_UNIMAP) && (flags & _BDF_PSF2_HASTAB)) { + for (gp = glyphs.glyphs, i = 0; i < nglyphs; i++, gp++) { + if (gp->unicode.map_used > 0) + fwrite((char *) gp->unicode.map, sizeof(unsigned char), + gp->unicode.map_used, out); + putc(0xff, out); + } + } + + /* + * Finally, dispose of the glyph copies. + */ + for (i = 0, gp = glyphs.glyphs; i < glyphs.glyphs_used; i++, gp++) { + if (gp->name != 0) + free(gp->name); + if (gp->bytes > 0) + free((char *) gp->bitmap); + if (gp->unicode.map_size > 0) + free((char *) gp->unicode.map); + } + if (glyphs.glyphs_size > 0) + free((char *) glyphs.glyphs); + + return BDF_OK; +} + +#undef _swap_endian diff --git a/configure b/configure new file mode 100755 index 0000000..3c5c22f --- /dev/null +++ b/configure @@ -0,0 +1,7086 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.61. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &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 +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#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 if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + 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 +#include +#include +#include +/* 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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 +#include +#include +#include + +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 + +_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 + +_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 +#include +#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 +#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 +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 +_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 +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 declares gethostbyname. + For example, HP-UX 11i declares gettimeofday. */ +#define gethostbyname innocuous_gethostbyname + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostbyname (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares connect. + For example, HP-UX 11i declares gettimeofday. */ +#define connect innocuous_connect + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char connect (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares remove. + For example, HP-UX 11i declares gettimeofday. */ +#define remove innocuous_remove + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char remove (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares shmat. + For example, HP-UX 11i declares gettimeofday. */ +#define shmat innocuous_shmat + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shmat (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 ." + +_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 >$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..b0d7830 --- /dev/null +++ b/configure.in @@ -0,0 +1,67 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(gbdfed.c) + +AC_PROG_CC + +dnl Get Compiler flags right. + +if test "x$CC" = xgcc; then + XX_CFLAGS="-Wall -pedantic" +else + case "$host" in + alpha-dec-osf*) + XX_CFLAGS="-std1 -O2 -g3" + ;; + *) + XX_CFLAGS= + ;; + esac +fi +AC_SUBST(XX_CFLAGS) + +AC_CHECK_PROG(RM, rm, rm) +AC_CHECK_PROG(CP, cp, cp) + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([libintl.h stddef.h stdlib.h string.h unistd.h]) + +AC_C_CONST + +dnl Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_FUNC_REALLOC +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([memmove memset strchr strdup strrchr strstr]) + +dnl These use the pkgconfig macro (in aclocal.m4) to check on libraries. +PKG_CHECK_MODULES(FREETYPE, freetype2 >= 2.0,DEFINES="-DHAVE_FREETYPE" CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" LIBS="$LIBS $FREETYPE_LIBS",) +PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.6,CPPFLAGS="$CPPFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS",) + +AC_CHECK_FILE(hbf.c, DEFINES="$DEFINES -DHAVE_HBF" HBFSRC="hbf.c" HBFOBJ="hbf.o",) + +AC_PATH_XTRA + +if test "$have_x" != yes; then + AC_MSG_NOTICE(X11 not found. Disabling server font grabbing.) +else + DEFINES="$DEFINES -DHAVE_XLIB" + BDFGRABSRC="bdfgrab.c" + BDFGRABOBJ="bdfgrab.o" +fi + +# +# Fix for implicit DSO linking issue. +# +AC_CHECK_LIB(X11, XCreatePixmap) + +AC_SUBST(DEFINES) +AC_SUBST(HBFSRC) +AC_SUBST(HBFOBJ) +AC_SUBST(BDFGRABSRC) +AC_SUBST(BDFGRABOBJ) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/fontgrid.c b/fontgrid.c new file mode 100644 index 0000000..b3222d5 --- /dev/null +++ b/fontgrid.c @@ -0,0 +1,4846 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "fontgrid.h" +#include +#include + +#ifdef HAVE_XLIB +#include +#endif + +#ifdef ENABLE_NLS +#include +#define _(s) dgettext(GETTEXT_PACKAGE,s) +#else +#define _(s) (s) +#endif + +/* + * Macros that represent the properties used by this type of object. + */ +#define FONTGRID_CLIPBOARD gdk_atom_intern("FONTGRID_CLIPBOARD", FALSE) +#define FONTGRID_GLYPHLIST gdk_atom_intern("FONTGRID_GLYPHLIST", FALSE) + +/* + * Set several defaults. + */ +#define FGRID_MAX_COLS 16 +#define FGRID_MAX_ROWS 16 +#define FGRID_DEFAULT_COLS 16 +#define FGRID_DEFAULT_ROWS 8 +#define FGRID_DEFAULT_CELL_HEIGHT 18 + +/* + * Enums used for identifying properties. + */ +enum { + PROP_0 = 0, + PROP_CODE_BASE, + PROP_POWER2, + PROP_ORIENTATION, + PROP_FONT, + PROP_POINT_SIZE, + PROP_SPACING, + PROP_SKIP_BLANKS, + PROP_OVERWRITE, + PROP_COLORS, + PROP_INITIAL_GLYPH, + PROP_BPP, + PROP_HRES, + PROP_VRES +}; + +/************************************************************************** + * + * Selection macros for toggling & testing glyph selected state. + * + **************************************************************************/ + +/* + * Macros for dealing with the selected state of glyphs in the font grid. + */ +#define IsSelected(code, map) (map[(code) >> 5] & (1 << ((code) & 31))) +#define Select(code, map) (map[(code) >> 5] |= (1 << ((code) & 31))) +#define Unselect(code, map) (map[(code) >> 5] &= ~(1 << ((code) & 31))) + +/************************************************************************** + * + * Signals. + * + **************************************************************************/ + +/* + * Enums that represent the signals these objects send out. + */ +enum { + SELECTION_START = 0, + SELECTION_EXTEND, + SELECTION_END, + ACTIVATE, + MODIFIED, + TURN_TO_PAGE +}; + +static GtkWidgetClass *parent_class = 0; +static guint fontgrid_signals[TURN_TO_PAGE + 1]; + +static bdf_glyph_t empty_glyph; + +/************************************************************************** + * + * Digits for displaying the cell encoding. + * + **************************************************************************/ + +/* + * Lists of points that describe the encoding digits. + */ +typedef struct { + GdkPoint *points; + guint npoints; +} fontgrid_digit; + +static GdkPoint digit00[] = {{2, 0}, {1, 1}, {3, 1}, {0, 2}, {4, 2}, {0, 3}, + {4, 3}, {0, 4}, {4, 4}, {1, 5}, {3, 5}, {2, 6}}; + +static GdkPoint digit01[] = {{2, 0}, {1, 1}, {2, 1}, {0, 2}, {2, 2}, {2, 3}, + {2, 4}, {2, 5}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, + {4, 6}}; + +static GdkPoint digit02[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {4, 2}, + {2, 3}, {3, 3}, {1, 4}, {0, 5}, {0, 6}, {1, 6}, + {2, 6}, {3, 6}, {4, 6}}; + +static GdkPoint digit03[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {4, 1}, + {3, 2}, {2, 3},{3, 3}, {4, 4}, {0, 5}, {4, 5}, + {1, 6}, {2, 6}, {3, 6}}; + +static GdkPoint digit04[] = {{3, 0}, {2, 1}, {3, 1}, {1, 2}, {3, 2}, {0, 3}, + {3, 3}, {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, + {3, 5}, {3, 6}}; + +static GdkPoint digit05[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1}, + {0, 2}, {2, 2}, {3, 2}, {0, 3}, {1, 3}, {4, 3}, + {4, 4}, {0, 5}, {4, 5}, {1, 6}, {2, 6}, {3, 6}}; + +static GdkPoint digit06[] = {{2, 0}, {3, 0}, {1, 1}, {0, 2}, {0, 3}, {2, 3}, + {3, 3}, {0, 4}, {1, 4}, {4, 4}, {0, 5}, {4, 5}, + {1, 6}, {2, 6}, {3, 6}}; + +static GdkPoint digit07[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {4, 1}, + {3, 2}, {3, 3}, {2, 4}, {1, 5}, {1, 6}}; + +static GdkPoint digit08[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {0, 2}, + {4, 2}, {1, 3}, {2, 3}, {3, 3}, {0, 4}, {4, 4}, + {0, 5}, {4, 5}, {1, 6}, {2, 6}, {3, 6}}; + +static GdkPoint digit09[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {0, 2}, + {3, 2}, {4, 2}, {1, 3}, {2, 3}, {4, 3}, {4, 4}, + {3, 5}, {1, 6}, {2, 6}}; + +static GdkPoint digit10[] = {{2, 0}, {1, 1}, {3, 1}, {0, 2}, {4, 2}, {0, 3}, + {4, 3}, {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, + {0, 5}, {4, 5}, {0, 6}, {4, 6}}; + +static GdkPoint digit11[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {1, 1}, {4, 1}, + {1, 2}, {4, 2}, {1, 3}, {2, 3}, {3, 3}, {1, 4}, + {4, 4}, {1, 5}, {4, 5}, {0, 6}, {1, 6}, {2, 6}, + {3, 6}}; + +static GdkPoint digit12[] = {{1, 0}, {2, 0}, {3, 0}, {0, 1}, {4, 1}, {0, 2}, + {0, 3}, {0, 4},{0, 5}, {4, 5}, {1, 6}, {2, 6}, + {3, 6}}; + +static GdkPoint digit13[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {1, 1}, {4, 1}, + {1, 2}, {4, 2}, {1, 3}, {4, 3}, {1, 4}, {4, 4}, + {1, 5}, {4, 5}, {0, 6}, {1, 6}, {2, 6}, {3, 6}}; + +static GdkPoint digit14[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1}, + {0, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {0, 4}, + {0, 5}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}}; + +static GdkPoint digit15[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1}, + {0, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {0, 4}, + {0, 5}, {0, 6}}; +static GdkPoint minus[] = {{1, 3}, {2, 3}, {3, 3}, {4,3}}; + +static fontgrid_digit digits[] = { + {digit00, sizeof(digit00)/sizeof(GdkPoint)}, + {digit01, sizeof(digit01)/sizeof(GdkPoint)}, + {digit02, sizeof(digit02)/sizeof(GdkPoint)}, + {digit03, sizeof(digit03)/sizeof(GdkPoint)}, + {digit04, sizeof(digit04)/sizeof(GdkPoint)}, + {digit05, sizeof(digit05)/sizeof(GdkPoint)}, + {digit06, sizeof(digit06)/sizeof(GdkPoint)}, + {digit07, sizeof(digit07)/sizeof(GdkPoint)}, + {digit08, sizeof(digit08)/sizeof(GdkPoint)}, + {digit09, sizeof(digit09)/sizeof(GdkPoint)}, + {digit10, sizeof(digit10)/sizeof(GdkPoint)}, + {digit11, sizeof(digit11)/sizeof(GdkPoint)}, + {digit12, sizeof(digit12)/sizeof(GdkPoint)}, + {digit13, sizeof(digit13)/sizeof(GdkPoint)}, + {digit14, sizeof(digit14)/sizeof(GdkPoint)}, + {digit15, sizeof(digit15)/sizeof(GdkPoint)}, + {minus, sizeof(minus)/sizeof(GdkPoint)}, +}; + +/* + * This array is used to hold a set of digits that will be displayed. It + * provides for a max of 19 points per digit and a max of 6 digits. + */ +static GdkPoint encoding_digits[19*6]; + +/* + * Used to determine spacing between digits when displaying. + */ +#define FONTGRID_DIGIT_WIDTH 6 +#define FONTGRID_DIGIT_HEIGHT 10 + +/* + * A macro for getting the current foreground GC. + */ +#define WIDGET_FG_GC(w) ((w)->style->fg_gc[GTK_WIDGET_STATE(w)]) + +#define HMARGINS(fw) ((fw)->hmargin << 1) +#define VMARGINS(fw) ((fw)->vmargin << 1) + +static void +fontgrid_set_cell_geometry(Fontgrid *fw) +{ + bdf_font_t *font; + gint lw; + + font = fw->font; + + lw = FONTGRID_DIGIT_WIDTH * 7; + + /* + * The labels will always be numbers in base 8, 10, or 16, so we are only + * interested in the max ascent. Add a 2-pixel margin on top and bottom. + */ + fw->label_height = FONTGRID_DIGIT_HEIGHT + 4; + + /* + * We want a minumum padding of 3 pixels on each side of the glyph bitmap + * in each cell. Thus the addition of 6 to each dimension. + */ + if (font != 0) { + fw->cell_width = font->bbx.width + 6; + fw->cell_height = font->bbx.height + 6; + } else { + /* + * Hard-code a minimum size for NULL fonts. The initial height of + * an empty cell is 20 to give it a better visual appearance. + */ + fw->cell_width = lw + 6; + fw->cell_height = FGRID_DEFAULT_CELL_HEIGHT + 6; + } + + fw->cell_width = MAX(fw->cell_width, lw); + fw->cell_height = MAX(fw->cell_height, fw->label_height); + + /* + * Now add the label size into the picture. + */ + fw->cell_height += fw->label_height - 1; +} + +static void +fontgrid_set_rows_cols(Fontgrid *fw, GtkAllocation *core) +{ + gint i; + guint16 dw, dh, wd, ht; + + /* + * Limit the window size to 7/8 of the actual screen dimensions. + */ + dw = (gdk_screen_width() * 7) >> 3; + dh = (gdk_screen_height() * 7) >> 3; + + if (!core) { + /* + * Adjust the rows and columns based on the preferred geometry. + */ + wd = (fw->cell_width * fw->cell_cols) + HMARGINS(fw); + ht = (fw->cell_height * fw->cell_rows) + VMARGINS(fw); + + if (wd > dw) + fw->cell_cols = (dw - HMARGINS(fw)) / fw->cell_width; + + if (ht > dh) + fw->cell_rows = (dh - VMARGINS(fw)) / fw->cell_height; + } else { + /* + * Adjust the rows and columns based on the current geometry. + */ + fw->cell_cols = (core->width - HMARGINS(fw)) / fw->cell_width; + fw->cell_rows = (core->height - VMARGINS(fw)) / fw->cell_height; + } + + /* + * Adjust rows and columns to powers of two if necessary. + */ + if (fw->power2) { + /* + * Make sure the columns are a power of 2. + */ + for (i = 15; i >= 0; i--) { + if (fw->cell_cols & (1 << i)) { + fw->cell_cols = 1 << i; + break; + } + } + + /* + * Make sure the rows are a power of 2. + */ + for (i = 15; i >= 0; i--) { + if (fw->cell_rows & (1 << i)) { + fw->cell_rows = 1 << i; + break; + } + } + } + + /* + * Fall back to a minimum of two rows. + */ + if (fw->cell_rows == 0) + fw->cell_rows = 2; + + /* + * Fall back to a minimum of two columns. + */ + if (fw->cell_cols == 0) + fw->cell_cols = 2; + + /* + * Make sure the number of rows and cols are within the max limits. + */ + if (fw->cell_cols > FGRID_MAX_COLS) + fw->cell_cols = FGRID_MAX_COLS; + + if (fw->cell_rows > FGRID_MAX_ROWS) + fw->cell_rows = FGRID_MAX_ROWS; + + /* + * Set the new page size based on the calculated rows and columns. + */ + fw->pagesize = fw->cell_rows * fw->cell_cols; +} + +/************************************************************************** + * + * GObjectClass functions. + * + **************************************************************************/ + +static void +fontgrid_set_property(GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GtkWidget *widget; + Fontgrid *fw; + + widget = GTK_WIDGET(obj); + fw = FONTGRID(obj); + + switch (prop_id) { + case PROP_CODE_BASE: + fw->base = g_value_get_uint(value); + /* + * Force the encodings to be redisplayed here? + */ + break; + case PROP_POWER2: + fw->power2 = g_value_get_boolean(value); + break; + case PROP_ORIENTATION: + fontgrid_set_orientation(fw, g_value_get_enum(value)); + break; + case PROP_FONT: + /* + * Need to set the rows and columns back to their defaults when + * a new font is passed in case it is NULL. + */ + fw->font = (bdf_font_t *) g_value_get_pointer(value); + + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + break; + case PROP_POINT_SIZE: + fw->point_size = g_value_get_uint(value); + break; + case PROP_SPACING: + fw->spacing = g_value_get_int(value); + break; + case PROP_SKIP_BLANKS: + fw->noblanks = g_value_get_boolean(value); + break; + case PROP_OVERWRITE: + fw->overwrite = g_value_get_boolean(value); + break; + case PROP_COLORS: + fw->colors = (guint16 *) g_value_get_pointer(value); + break; + case PROP_INITIAL_GLYPH: + fw->initial_glyph = g_value_get_int(value); + break; + case PROP_BPP: + fw->bpp = g_value_get_int(value); + break; + case PROP_HRES: + fw->hres = g_value_get_int(value); + break; + case PROP_VRES: + fw->vres = g_value_get_int(value); + break; + } +} + +static void +fontgrid_get_property(GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + Fontgrid *f; + + f = FONTGRID(obj); + + switch (prop_id) { + case PROP_CODE_BASE: + g_value_set_uint(value, f->base); + break; + case PROP_POWER2: + g_value_set_boolean(value, f->power2); + break; + case PROP_ORIENTATION: + g_value_set_enum(value, f->orientation); + break; + case PROP_FONT: + g_value_set_pointer(value, f->font); + break; + case PROP_POINT_SIZE: + g_value_set_uint(value, f->point_size); + break; + case PROP_SPACING: + g_value_set_int(value, f->spacing); + break; + case PROP_SKIP_BLANKS: + g_value_set_boolean(value, f->noblanks); + break; + case PROP_COLORS: + g_value_set_pointer(value, f->colors); + break; + case PROP_INITIAL_GLYPH: + g_value_set_int(value, f->initial_glyph); + break; + case PROP_BPP: + g_value_set_int(value, f->bpp); + break; + case PROP_HRES: + g_value_set_int(value, f->hres); + break; + case PROP_VRES: + g_value_set_int(value, f->vres); + break; + } +} + +/************************************************************************** + * + * GtkObjectClass functions. + * + **************************************************************************/ + +static void +fontgrid_destroy(GtkObject *obj) +{ + Fontgrid *f; + guint32 i; + bdf_glyphlist_t *gl; + + /* + * Do some checks to make sure the incoming object exists and is the right + * kind. + */ + g_return_if_fail(obj != 0); + g_return_if_fail(IS_FONTGRID(obj)); + + f = FONTGRID(obj); + + /* + * Clean up this object instance. + */ + if (f->font) + bdf_free_font(f->font); + f->font = 0; + + if (f->xor_gc != 0) + g_object_unref(G_OBJECT(f->xor_gc)); + f->xor_gc = 0; + + if (f->points_size > 0) + g_free(f->points); + f->points_size = f->points_used = 0; + + if (f->rgb_size > 0) + g_free(f->rgb); + f->rgb_used = f->rgb_size = 0; + + /* + * Remove all ownership of selections. + */ + gtk_selection_remove_all(GTK_WIDGET(obj)); + + /* + * Free up the clipboard contents if there are any. + */ + gl = &f->clipboard; + for (i = 0; i < gl->glyphs_used; i++) { + if (gl->glyphs[i].name) + free(gl->glyphs[i].name); + if (gl->glyphs[i].bytes > 0) + free((char *) gl->glyphs[i].bitmap); + } + if (gl->glyphs_size > 0) + free((char *) gl->glyphs); + gl->glyphs_size = gl->glyphs_used = 0; + + /* + * Follow the class chain back up to free up resources allocated in the + * parent classes. + */ + GTK_OBJECT_CLASS(parent_class)->destroy(obj); +} + +static void +fontgrid_finalize(GObject *obj) +{ + /* + * Do some checks to make sure the incoming object exists and is the right + * kind. + */ + g_return_if_fail(obj != 0); + g_return_if_fail(IS_FONTGRID(obj)); + + /* + * Follow the class chain back up to free up resources allocated in the + * parent classes. + */ + G_OBJECT_CLASS(parent_class)->finalize(obj); +} + +/************************************************************************** + * + * GtkWidgetClass functions. + * + **************************************************************************/ + +static void +fontgrid_preferred_size(GtkWidget *widget, GtkRequisition *preferred) +{ + Fontgrid *fw; + + fw = FONTGRID(widget); + preferred->width = (fw->cell_width * fw->cell_cols) + HMARGINS(fw); + preferred->height = (fw->cell_height * fw->cell_rows) + VMARGINS(fw); +} + +static void +fontgrid_actual_size(GtkWidget *widget, GtkAllocation *actual) +{ + Fontgrid *fw; + + fw = FONTGRID(widget); + + widget->allocation = *actual; + + /* + * Make sure the rows and columns are adjusted to fit the actual allocated + * size. + */ + fontgrid_set_rows_cols(fw, actual); + + if (GTK_WIDGET_REALIZED(widget)) + gdk_window_move_resize(widget->window, actual->x, actual->y, + actual->width, actual->height); +} + +static void +fontgrid_realize(GtkWidget *widget) +{ + Fontgrid *fw; + GdkWindowAttr attributes; + GdkGCValues values; + gint attributes_mask; + + g_return_if_fail(widget != NULL); + g_return_if_fail(IS_FONTGRID(widget)); + + fw = FONTGRID(widget); + GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); + attributes.colormap = gtk_widget_get_colormap(widget); + attributes.event_mask = gtk_widget_get_events(widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK| + GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK| + GDK_POINTER_MOTION_MASK| + GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK| + GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK| + GDK_PROPERTY_CHANGE_MASK); + + attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP; + + widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), + &attributes, attributes_mask); + gdk_window_set_user_data(widget->window, widget); + + widget->style = gtk_style_attach(widget->style, widget->window); + gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL); + + if (fw->xor_gc != 0) + g_object_unref(G_OBJECT(fw->xor_gc)); + + /* + * Create the GC used to display selected cells. + */ + values.foreground.pixel = + widget->style->fg[GTK_WIDGET_STATE(widget)].pixel ^ + widget->style->bg[GTK_WIDGET_STATE(widget)].pixel; + (void) memset((char *) &values.background, 0, sizeof(GdkColor)); + values.function = GDK_XOR; + fw->xor_gc = gdk_gc_new_with_values(widget->window, &values, + GDK_GC_FOREGROUND| + GDK_GC_BACKGROUND|GDK_GC_FUNCTION); +} + +static bdf_glyph_t * +fontgrid_locate_glyph(bdf_glyph_t *glyphs, guint32 nglyphs, gint32 code, + gboolean exact_match) +{ + gint32 l, r, m = 0; + + if (code < 0 || glyphs == 0 || nglyphs == 0) + return 0; + + for (l = 0, r = (gint32) (nglyphs - 1); l <= r; ) { + m = (l + r) >> 1; + if (glyphs[m].encoding < code) + l = m + 1; + else if (glyphs[m].encoding > code) + r = m - 1; + else { + if (exact_match) + return glyphs + m; + break; + } + } + + if (exact_match) + return 0; + + /* + * Adjust to the beginning or end if nothing was found in the search. + */ + while (m > 0 && glyphs[m].encoding > code) + m--; + while (m < (gint32) nglyphs && glyphs[m].encoding < code) + m++; + + return (m < (gint32) nglyphs) ? glyphs + m : 0; +} + +static void +fontgrid_get_glyph_points(Fontgrid *fw, gint x, gint y, gint rx, gint by, + bdf_glyph_t *glyph) +{ + gint i, j, bpr, col; + unsigned char *bmap, *masks = 0; + + switch (fw->bpp) { + case 1: masks = bdf_onebpp; break; + case 2: masks = bdf_twobpp; break; + case 4: masks = bdf_fourbpp; break; + case 8: masks = bdf_eightbpp; break; + } + + fw->points_used = 0; + bmap = glyph->bitmap; + bpr = ((glyph->bbx.width * fw->bpp) + 7) >> 3; + + for (i = 0; i + y - glyph->bbx.ascent < by && i < glyph->bbx.height; i++) { + for (col = j = 0; j + x < rx && j < glyph->bbx.width; + j++, col += fw->bpp) { + if (bmap[(i * bpr) + (col >> 3)] & + masks[(col & 7) / fw->bpp]) { + if (fw->points_used == fw->points_size) { + if (fw->points_size == 0) + fw->points = + (GdkPoint *) g_malloc(sizeof(GdkPoint) << 7); + else + fw->points = + (GdkPoint *) g_realloc(fw->points, + sizeof(GdkPoint) * + (fw->points_size + 128)); + fw->points_size += 128; + } + + fw->points[fw->points_used].x = j + x; + fw->points[fw->points_used].y = i + y - glyph->bbx.ascent; + fw->points_used++; + } + } + } +} + +#if 0 +static void +fontgrid_get_glyph_points_color(Fontgrid *fw, gint x, gint y, gint rx, gint by, + gint color_index, bdf_glyph_t *glyph) +{ + gint i, j, bpr, col, byte, di = 0, si, cidx = 0; + unsigned char *bmap, *masks = 0; + + switch (fw->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + fw->points_used = 0; + bmap = glyph->bitmap; + bpr = ((glyph->bbx.width * fw->bpp) + 7) >> 3; + + for (i = 0; i + y - glyph->bbx.ascent < by && i < glyph->bbx.height; i++) { + for (col = j = 0; j + x < rx && j < glyph->bbx.width; + j++, col += fw->bpp) { + si = (col & 7) / fw->bpp; + byte = bmap[(i * bpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Check to see if the byte matches the color index being + * collected. + */ + if (di > si) + byte >>= (di - si) * fw->bpp; + + if (byte == cidx) { + if (fw->points_used == fw->points_size) { + if (fw->points_size == 0) + fw->points = (GdkPoint *) + g_malloc(sizeof(GdkPoint) << 6); + else + fw->points = (GdkPoint *) + g_realloc(fw->points, sizeof(GdkPoint) * + (fw->points_size + 64)); + fw->points_size += 64; + } + + fw->points[fw->points_used].x = j + x; + fw->points[fw->points_used].y = i + y - glyph->bbx.ascent; + fw->points_used++; + } + } + } + } +} +#endif + +/* + * This routine creates a 24 bits per pixel image of a glyph so it can be + * drawn using GtkRGB. This is less complicated than the old method of + * collecting and drawing individual pixels of each different color. + */ +static void +fontgrid_make_rgb_image(Fontgrid *fw, bdf_glyph_t *glyph) +{ + GtkWidget *w = GTK_WIDGET(fw); + gint x, y, bpr, rgb_bpr, col, byte, di = 0, si; + guchar bg[4], pix[4], *bmap, *masks = 0; + + /* + * Figure out the background color. + */ + bg[0] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].red; + bg[1] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].green; + bg[2] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].blue; + + switch (fw->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + bmap = glyph->bitmap; + bpr = ((glyph->bbx.width * fw->bpp) + 7) >> 3; + + rgb_bpr = glyph->bbx.width * 3; + fw->rgb_used = rgb_bpr * glyph->bbx.height; + + if (fw->rgb_size < fw->rgb_used) { + if (fw->rgb_size == 0) + fw->rgb = g_malloc(fw->rgb_used); + else + fw->rgb = g_realloc(fw->rgb, fw->rgb_used); + fw->rgb_size = fw->rgb_used; + } + + for (y = 0; y < glyph->bbx.height; y++) { + for (col = x = 0; x < glyph->bbx.width; x++, col += fw->bpp) { + si = (col & 7) / fw->bpp; + + byte = bmap[(y * bpr) + (col >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * fw->bpp; + if (byte) { + /* + * Look up the color. + */ + switch (fw->bpp) { + case 1: memset(pix, 0, 3); break; + case 2: memset(pix, fw->colors[byte-1], 3); break; + case 4: memset(pix, fw->colors[byte-1+4], 3); break; + case 8: memset(pix, byte, 3); break; + } + } else + /* + * Set the pixel to the background color. + */ + memcpy(pix, bg, 3); + + memcpy(&fw->rgb[(y * rgb_bpr) + (x * 3)], pix, 3); + } + } +} + +static void +fontgrid_draw_encoding(GtkWidget *w, GdkGC *gc, gint x, gint y, gchar *num, + gint numlen) +{ + gint i, j, d; + GdkPoint *dp; + + if (!GTK_WIDGET_REALIZED(w)) + return; + + dp = encoding_digits; + for (i = 0; i < numlen; i++) { + if (num[i] == '-') + d = 16; + else if (num[i] <= '9') + d = num[i] - '0'; + else + d = (num[i] - 'A') + 10; + + /* + * Copy the next digit into the display array. + */ + (void) memcpy((char *) dp, (char *) digits[d].points, + sizeof(GdkPoint) * digits[d].npoints); + /* + * Position the points. + */ + for (j = 0; j < digits[d].npoints; j++) { + dp[j].x += x; + dp[j].y += y; + } + dp += digits[d].npoints; + x += 6; + } + + /* + * Draw the points. + */ + gdk_draw_points(w->window, gc, encoding_digits, dp - encoding_digits); +} + +static void +fontgrid_draw_cells(GtkWidget *widget, gint32 start, gint32 end, + gboolean labels, gboolean glyphs) +{ + Fontgrid *fw; + gint x, y, wd, as, ds, len, lx, ly; + gint32 i, n, r, c; + guint32 nglyphs, ng; + gboolean mod; + bdf_font_t *font; + bdf_glyph_t *glyph, *gp; + FontgridInternalPageInfo *pi; + GdkGC *gc; + GdkRectangle rect; + gchar nbuf[16]; + + if (!GTK_WIDGET_REALIZED(widget) || (labels == FALSE && glyphs == FALSE)) + return; + + fw = FONTGRID(widget); + + font = fw->font; + + glyph = 0; + nglyphs = 0; + + if (!fw->unencoded) { + pi = &fw->npage; + if (font) { + glyph = font->glyphs; + nglyphs = font->glyphs_used; + } + } else { + /* + * When viewing the unencoded glyph pages, all glyphs are labelled + * with an encoding of -1. + */ + strcpy(nbuf, "-1"); + + pi = &fw->upage; + if (font) { + glyph = font->unencoded; + nglyphs = font->unencoded_used; + } + } + + /* + * The initial code to work from. + */ + n = pi->bcode; + + /* + * Locate the glyph closest to the starting code. + */ + if ((glyph = fontgrid_locate_glyph(glyph, nglyphs, start, FALSE)) == 0) + nglyphs = 0; + + gp = glyph; + + gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)]; + + for (ng = 0, i = start; i <= end; i++) { + /* + * Only draw those cells that are on the current page. + */ + if (i < pi->bcode || i >= pi->bcode + fw->pagesize) + continue; + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) { + r = (i - n) / fw->cell_cols; + c = (i - n) % fw->cell_cols; + } else { + c = (i - n) / fw->cell_rows; + r = (i - n) % fw->cell_rows; + } + + x = fw->xoff + (c * fw->cell_width); + y = fw->yoff + (r * fw->cell_height); + + if (labels) { + if (!fw->unencoded) { + switch (fw->base) { + case 8: sprintf(nbuf, "%o", i); break; + case 10: sprintf(nbuf, "%d", i); break; + case 16: sprintf(nbuf, "%X", i); break; + } + } + rect.x = x + 1; + rect.y = y + 1; + rect.width = fw->cell_width - 2; + rect.height = fw->label_height - 2; + gdk_draw_rectangle(widget->window, gc, FALSE, + rect.x, rect.y, rect.width, rect.height); + + len = strlen(nbuf); + wd = len * 6; + as = 8; + ds = 0; + + lx = (x + ((fw->cell_width >> 1) - (wd >> 1))) + 1; + ly = (y + ((fw->label_height >> 1) - ((as + ds) >> 1))) + 1; + + mod = FALSE; + if (i <= 0xffff) + mod = (!fw->unencoded) ? bdf_glyph_modified(font, i, 0) : + bdf_glyph_modified(font, i, 1); + + gdk_window_clear_area(widget->window, rect.x + 1, rect.y + 1, + rect.width - 1, rect.height - 1); + + if (!fw->unencoded && mod) { + gdk_draw_rectangle(widget->window, gc, TRUE, + rect.x + 2, rect.y + 2, + rect.width - 3, rect.height - 3); + fontgrid_draw_encoding(widget, fw->xor_gc, lx, ly, nbuf, len); + if (gp && gp->encoding == i) { + ng++; + gp++; + if (ng == nglyphs) + gp = 0; + } + } else { + /* + * If the glyph exists, then darken the rectangle to indicate + * this. + */ + if (gp && gp->encoding == i) { + gdk_draw_rectangle(widget->window, gc, FALSE, + rect.x + 1, rect.y + 1, + rect.width - 2, rect.height - 2); + ng++; + gp++; + if (ng == nglyphs) + gp = 0; + } + fontgrid_draw_encoding(widget, gc, lx, ly, nbuf, len); + } + } + + if (glyphs) { + rect.x = x + 1; + rect.y = y + fw->label_height + 1; + rect.width = fw->cell_width - 2; + rect.height = (fw->cell_height - fw->label_height) - 2; + + if (i <= 0xffff && nglyphs > 0 && glyph->encoding == i) { + /* + * Draw the glyph. + */ + + /* + * Set the right and left limits for generating points. + */ + lx = x + fw->cell_width - 2; + ly = y + fw->cell_height - 2; + + /* + * Adjust the X,Y coordinate pair so the bitmap points will + * be generated to center the glyphs horizontally and align + * them to the BDF font's baseline vertically. + */ + x += (fw->cell_width >> 1) - + ((font->bbx.width + font->bbx.x_offset) >> 1) + 1; + y += fw->label_height + font->bbx.ascent + 3; + + if (IsSelected(glyph->encoding, pi->selmap)) { + gdk_draw_rectangle(widget->window, gc, TRUE, + rect.x + 1, rect.y + 1, + rect.width - 1, rect.height - 1); + if (glyph->bytes > 0) { + fontgrid_get_glyph_points(fw, x, y, lx, ly, glyph); + if (fw->points_used > 0) + gdk_draw_points(widget->window, fw->xor_gc, + fw->points, fw->points_used); + } + } else { + /* + * The glyph is not selected, so draw it according to + * the bytes-per-pixel of the font. + */ + gdk_window_clear_area(widget->window, rect.x, rect.y, + rect.width, rect.height); + if (glyph->bytes > 0) { + fontgrid_make_rgb_image(fw, glyph); + gdk_draw_rgb_image(widget->window, gc, + x, y - glyph->bbx.ascent, + glyph->bbx.width, + glyph->bbx.height, + GDK_RGB_DITHER_NONE, + fw->rgb, glyph->bbx.width * 3); + } + } + glyph++; + if (ng == nglyphs) { + nglyphs = 0; + glyph = 0; + } + } else { + /* + * Clear the empty cell. + */ + if (i <= 0xffff && IsSelected(i, pi->selmap)) + gdk_draw_rectangle(widget->window, gc, TRUE, + rect.x + 1, rect.y + 1, + rect.width - 1, rect.height - 1); + else { + gdk_window_clear_area(widget->window, rect.x, rect.y, + rect.width, rect.height); + if (i > 0xffff) { + gdk_draw_line(widget->window, gc, rect.x, rect.y, + rect.x + rect.width, + rect.y + rect.height); + gdk_draw_line(widget->window, gc, + rect.x + rect.width, rect.y, + rect.x, rect.y + rect.height); + } + } + } + } + } +} + +static void +fontgrid_draw(GtkWidget *widget, GdkRegion *region) +{ + Fontgrid *fw; + gint x, y, i; + guint16 wd, ht, gw, gh; + gint32 start, end; + GdkGC *gc; + + g_return_if_fail(widget != NULL); + g_return_if_fail(IS_FONTGRID(widget)); + + fw = FONTGRID(widget); + + gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)]; + + gw = fw->cell_width * fw->cell_cols; + gh = fw->cell_height * fw->cell_rows; + wd = widget->allocation.width; + ht = widget->allocation.height; + x = fw->xoff = ((wd >> 1) - (gw >> 1)) - 1; + y = fw->yoff = ((ht >> 1) - (gh >> 1)) - 1; + + /* + * Draw the horizontal lines. + */ + for (i = 0; i <= fw->cell_rows; i++) { + gdk_draw_line(widget->window, gc, x, y, x + gw, y); + + /* + * Only draw the second line if this is not the last line. + */ + if (i < fw->cell_rows) + gdk_draw_line(widget->window, gc, x, y + fw->label_height, + x + gw, y + fw->label_height); + + y += fw->cell_height; + } + + /* + * Draw the vertical lines. + */ + x = fw->xoff; + y = fw->yoff; + + for (i = 0; i <= fw->cell_cols; i++) { + gdk_draw_line(widget->window, gc, x, y, x, y + gh); + x += fw->cell_width; + } + + start = (!fw->unencoded) ? fw->npage.bcode : fw->upage.bcode; + end = start + (gint32) (fw->pagesize - 1); + + fontgrid_draw_cells(widget, start, end, TRUE, TRUE); +} + +static void +fontgrid_select_range(Fontgrid *fw, gint32 start, gint32 end) +{ + gint32 i, tmp; + FontgridInternalPageInfo *pi; + + if (start > end) { + tmp = start; + start = end; + end = tmp; + } + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + for (i = start; i <= end; i++) + Select(i, pi->selmap); + + /* + * Adjust the start and end values to the current page to determine which + * cells need to be redrawn. + */ + tmp = pi->bcode + (fw->pagesize - 1); + if (start >= tmp || end < pi->bcode) + return; + + if (start < pi->bcode) + start = pi->bcode; + if (end > tmp) + end = tmp; + fontgrid_draw_cells(GTK_WIDGET(fw), start, end, FALSE, TRUE); +} + +static void +fontgrid_deselect_range(Fontgrid *fw, gint32 start, gint32 end) +{ + gint32 i, tmp; + FontgridInternalPageInfo *pi; + + if (start > end) { + tmp = start; + start = end; + end = tmp; + } + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + for (i = start; i <= end; i++) { + if (IsSelected(i, pi->selmap)) { + Unselect(i, pi->selmap); + if (i >= pi->bcode && i <= pi->bcode + (fw->pagesize - 1)) + fontgrid_draw_cells(GTK_WIDGET(fw), i, i, FALSE, TRUE); + } + } +} + +static void +fontgrid_deselect_all(Fontgrid *fw) +{ + FontgridInternalPageInfo *pi, *opi; + + if (!fw->unencoded) { + pi = &fw->npage; + opi = &fw->upage; + } else { + pi = &fw->upage; + opi = &fw->npage; + } + + if (pi->sel_start != -1 || pi->sel_end != -1) + fontgrid_deselect_range(fw, pi->bcode, pi->bcode + fw->pagesize - 1); + else if (opi->sel_start != -1 || opi->sel_end != -1) + fontgrid_deselect_range(fw, opi->bcode, opi->bcode + fw->pagesize - 1); + + /* + * Now clear the selected bitmaps. + */ + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); + (void) memset((char *) opi->selmap, 0, sizeof(guint32) * 2048); + + /* + * Reset the selection start and end points. + */ + pi->sel_start = pi->sel_end = opi->sel_start = opi->sel_end = -1; +} + +static void +fontgrid_draw_focus(GtkWidget *widget, GdkRectangle *area) +{ + GdkGC *gc; + gint x, y, wd, ht, fwidth, fpad; + + /* + * Do something with this later to make sure the focus line width + * is set in the GC's. + */ + gtk_widget_style_get(widget, + "focus-line-width", &fwidth, + "focus-padding", &fpad, NULL); + + gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)]; + + x = (widget->style->xthickness + fwidth + fpad) - 1; + y = (widget->style->ythickness + fwidth + fpad) - 1; + wd = (widget->allocation.width - (x * 2)); + ht = (widget->allocation.height - (y * 2)); + + if (GTK_WIDGET_HAS_FOCUS(widget)) + gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget), + area, widget, "fontgrid", x, y, wd, ht); + else { + gdk_gc_set_clip_rectangle(gc, area); + gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1); + gdk_gc_set_clip_rectangle(gc, 0); + } +} + +static gint +fontgrid_expose(GtkWidget *widget, GdkEventExpose *event) +{ + /* + * Paint the shadow first. + */ + if (GTK_WIDGET_DRAWABLE(widget)) + gtk_paint_shadow(widget->style, widget->window, + GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT, + &event->area, widget, "fontgrid", + 0, 0, + widget->allocation.width, + widget->allocation.height); + + fontgrid_draw(widget, event->region); + + fontgrid_draw_focus(widget, &event->area); + + return FALSE; +} + +static gint +fontgrid_focus_in(GtkWidget *widget, GdkEventFocus *event) +{ + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(IS_FONTGRID(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); + fontgrid_draw_focus(widget, 0); + + return FALSE; +} + +static gint +fontgrid_focus_out(GtkWidget *widget, GdkEventFocus *event) +{ + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(IS_FONTGRID(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); + fontgrid_draw_focus(widget, 0); + + return FALSE; +} + +static gint +fontgrid_lose_selection(GtkWidget *widget, GdkEventSelection *event) +{ + Fontgrid *fw; + FontgridInternalPageInfo *pi; + gint32 code; + + fw = FONTGRID(widget); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (pi->sel_start != pi->sel_end) { + code = pi->sel_start; + fontgrid_deselect_all(fw); + pi->sel_end = pi->sel_start = code; + Select(pi->sel_start, pi->selmap); + fontgrid_draw_cells(widget, code, code, FALSE, TRUE); + } + + return TRUE; +} + +/************************************************************************** + * + * Paging routines. + * + **************************************************************************/ + +static void +fontgrid_neighbor_pages(Fontgrid *fw, gint32 page, gint32 *prev, gint32 *next) +{ + gint32 bcode, l, r, m; + guint32 nglyphs; + bdf_glyph_t *glyphs; + FontgridInternalPageInfo *pip; + + pip = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (fw->noblanks == FALSE || + (fw->unencoded == FALSE && + (fw->font == 0 || fw->font->glyphs_used == 0))) { + *prev = page - 1; + *next = (page < pip->maxpage) ? page + 1 : -1; + return; + } + + bcode = page * fw->pagesize; + + if (!fw->unencoded) { + glyphs = fw->font->glyphs; + nglyphs = fw->font->glyphs_used; + } else { + glyphs = fw->font->unencoded; + nglyphs = fw->font->unencoded_used; + } + + /* + * Do a binary search to find the the preceding page number. + */ + for (l = m = 0, r = nglyphs - 1; l < r; ) { + m = (l + r) >> 1; + if (glyphs[m].encoding < bcode) + l = m + 1; + else if (glyphs[m].encoding > bcode) + r = m - 1; + else { + /* + * Exact match. + */ + l = r = m - 1; + break; + } + } + + /* + * In case the search ends on a code in the specified page. + */ + while (r >= 0 && glyphs[r].encoding >= bcode) + r--; + + /* + * Set the previous page code. + */ + *prev = (r >= 0) ? glyphs[r].encoding / fw->pagesize : -1; + + /* + * Determine the following page code. + */ + if (r < 0) + r = 0; + while (r < nglyphs && glyphs[r].encoding < bcode + fw->pagesize) + r++; + + *next = (r < nglyphs) ? glyphs[r].encoding / fw->pagesize : -1; +} + +/************************************************************************** + * + * Selection routines. + * + **************************************************************************/ + +static void +start_selection(GtkWidget *widget, GdkEventButton *event) +{ + Fontgrid *fw; + gint16 x, y, row, col; + gint32 code; + bdf_glyph_t *gp; + FontgridInternalPageInfo *pi, *opi; + FontgridSelectionInfo sinfo; + + fw = FONTGRID(widget); + + /* + * Deal with the focus issue first. + */ + if (!GTK_WIDGET_HAS_FOCUS(widget)) { + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); + (void) fontgrid_draw_focus(widget, NULL); + } + + x = (gint16) event->x; + y = (gint16) event->y; + + col = fw->xoff + (fw->cell_width * fw->cell_cols); + row = fw->yoff + (fw->cell_height * fw->cell_rows); + + /* + * If the button press is not in the font grid proper, just return. + */ + if (x < fw->xoff || x >= col || y < fw->yoff || y >= row) + return; + + /* + * Calculate the row and column that was clicked. + */ + row = (y - fw->yoff) / fw->cell_height; + col = (x - fw->xoff) / fw->cell_width; + + if (!fw->unencoded) { + pi = &fw->npage; + opi = &fw->upage; + } else { + pi = &fw->upage; + opi = &fw->npage; + } + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) + code = pi->bcode + (row * fw->cell_cols) + col; + else + code = pi->bcode + (col * fw->cell_rows) + row; + + /* + * Any code greater than the maximum is ignored. + */ + if (code > 0xffff) + return; + + gp = 0; + if (fw->font) { + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + code, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + code, TRUE); + } + + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + + if (code != pi->sel_start || code != pi->sel_end) { + /* + * Clear any existing selection. + */ + if (pi->sel_start != -1 || pi->sel_end != -1 || + opi->sel_start != -1 || opi->sel_end != -1) + fontgrid_deselect_all(fw); + + Select(code, pi->selmap); + + fontgrid_draw_cells(widget, code, code, FALSE, TRUE); + + pi->sel_start = pi->sel_end = code; + + /* + * Clear the last click time to avoid situations where the second + * click on a different cell will cause the select callback to be + * called. + */ + fw->last_click = 0; + } + + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + if (event->type == GDK_BUTTON_PRESS && + event->time - fw->last_click >= fw->mclick_time) { + sinfo.reason = FONTGRID_START_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + } else if (event->type == GDK_2BUTTON_PRESS) { + sinfo.reason = FONTGRID_ACTIVATE; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[ACTIVATE], 0, + &sinfo); + } + fw->last_click = event->time; +} + +static void +extend_selection(GtkWidget *widget, gint16 x, gint16 y) +{ + Fontgrid *fw; + gint16 row, col; + gint32 code; + bdf_glyph_t *gp; + gboolean call_extend; + FontgridInternalPageInfo *pi; + FontgridSelectionInfo sinfo; + + fw = FONTGRID(widget); + + /* + * Deal with the focus issue first. + */ + if (!GTK_WIDGET_HAS_FOCUS(widget)) { + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); + (void) fontgrid_draw_focus(widget, NULL); + } + + col = fw->xoff + (fw->cell_width * fw->cell_cols); + row = fw->yoff + (fw->cell_height * fw->cell_rows); + + /* + * If the button press is not in the font grid proper, just return. + */ + if (x < fw->xoff || x >= col || y < fw->yoff || y >= row) + return; + + /* + * Calculate the row and column that was clicked. + */ + row = (y - fw->yoff) / fw->cell_height; + col = (x - fw->xoff) / fw->cell_width; + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) + code = pi->bcode + (row * fw->cell_cols) + col; + else + code = pi->bcode + (col * fw->cell_rows) + row; + + /* + * Any code greater than the maximum is ignored. + */ + if (code > 0xffff) + return; + + gp = 0; + if (fw->font) { + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + code, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + code, TRUE); + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + } + + call_extend = FALSE; + if (code > pi->sel_end) { + call_extend = TRUE; + if (code <= pi->sel_start) + fontgrid_deselect_range(fw, pi->sel_end, code - 1); + else { + if (pi->sel_end < pi->sel_start) { + fontgrid_deselect_range(fw, pi->sel_end, pi->sel_start - 1); + fontgrid_select_range(fw, pi->sel_start + 1, code); + } else + fontgrid_select_range(fw, pi->sel_end, code); + } + } else if (code < pi->sel_end) { + call_extend = TRUE; + if (code < pi->sel_start) { + if (pi->sel_end > pi->sel_start) { + fontgrid_deselect_range(fw, pi->sel_start + 1, pi->sel_end); + fontgrid_select_range(fw, code, pi->sel_start); + } else + fontgrid_select_range(fw, code, pi->sel_end); + } else + fontgrid_deselect_range(fw, code + 1, pi->sel_end); + } + + pi->sel_end = code; + + if (call_extend == TRUE) { + if (pi->sel_start == pi->sel_end) { + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + } else { + sinfo.glyphs = 0; + sinfo.num_glyphs = 0; + } + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + sinfo.reason = FONTGRID_EXTEND_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_EXTEND], 0, + &sinfo); + } +} + +static void +end_selection(GtkWidget *widget, GdkEventButton *event) +{ + Fontgrid *fw; + bdf_glyph_t *gp; + FontgridInternalPageInfo *pi; + FontgridSelectionInfo sinfo; + + fw = FONTGRID(widget); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (pi->sel_start != pi->sel_end) { + /* + * Assert ownership of the clipboard if there is a selection of + * more than one glyph. + */ + gdk_selection_owner_set(widget->window, FONTGRID_CLIPBOARD, + GDK_CURRENT_TIME, FALSE); + sinfo.glyphs = 0; + sinfo.num_glyphs = 0; + } else { + gp = 0; + if (fw->font) { + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, + fw->font->glyphs_used, + pi->sel_start, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + pi->sel_start, TRUE); + if (gp == 0) { + empty_glyph.encoding = pi->sel_start; + gp = &empty_glyph; + } + } + + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + } + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + sinfo.reason = FONTGRID_END_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_END], 0, + &sinfo); +} + +static void +paste_selection(GtkWidget *widget, GdkEventButton *event) +{ + start_selection(widget, event); + + if (event->state & GDK_SHIFT_MASK) + fontgrid_paste_selection(FONTGRID(widget), FONTGRID_INSERT_PASTE); + else if (event->state & GDK_CONTROL_MASK) + fontgrid_paste_selection(FONTGRID(widget), FONTGRID_MERGE_PASTE); + else + fontgrid_paste_selection(FONTGRID(widget), FONTGRID_NORMAL_PASTE); +} + +static void +copy_selection(GtkWidget *widget, GdkEventButton *event) +{ + fontgrid_copy_selection(FONTGRID(widget)); +} + +/************************************************************************** + * + * Button, pointer motion, and keyboard handling routines. + * + **************************************************************************/ + +static gint +fontgrid_button_press(GtkWidget *widget, GdkEventButton *event) +{ + switch (event->button) { + case 1: + if (event->state & GDK_SHIFT_MASK) + extend_selection(widget, (gint16) event->x, (gint16) event->y); + else + start_selection(widget, event); + break; + case 2: paste_selection(widget, event); break; + case 3: copy_selection(widget, event); break; + } + + return FALSE; +} + +static gint +fontgrid_button_release(GtkWidget *widget, GdkEventButton *event) +{ + switch (event->button) { + case 1: end_selection(widget, event); break; + case 2: break; + case 3: break; + } + return FALSE; +} + +static gint +fontgrid_motion_notify(GtkWidget *widget, GdkEventMotion *event) +{ + if (event->state & GDK_BUTTON1_MASK) + extend_selection(widget, (gint16) event->x, (gint16) event->y); +#if 0 + /* + * Don't need these at the moment. + */ + if (event->state & GDK_BUTTON2_MASK) { + } + if (event->state & GDK_BUTTON3_MASK) { + } +#endif + return FALSE; +} + +static gint +fontgrid_shift_key_press(GtkWidget *widget, GdkEventKey *event) +{ + Fontgrid *fw; + bdf_glyph_t *gp; + guint keyval; + gint32 code, pageno; + guint32 count; + gboolean signal_extend, activate; + FontgridInternalPageInfo *pi, *opi; + FontgridSelectionInfo sinfo; + + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(IS_FONTGRID(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + fw = FONTGRID(widget); + + /* + * For number keys, use them to add up a count that will effect the + * behavior of the other keys. + */ + if (event->keyval >= GDK_0 && event->keyval <= GDK_9) { + fw->count = (fw->count * 10) + (event->keyval - GDK_0); + return FALSE; + } + + if (!fw->unencoded) { + pi = &fw->npage; + opi = &fw->upage; + gp = (fw->font && fw->font->glyphs_used) ? + (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0; + } else { + pi = &fw->upage; + opi = &fw->npage; + gp = (fw->font && fw->font->unencoded_used) ? + (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0; + } + + activate = FALSE; + + code = pi->sel_end; + + if ((count = fw->count) == 0) + count = 1; + + keyval = event->keyval; + switch (event->keyval) { + case GDK_Page_Up: + case GDK_KP_Page_Up: + count *= fw->pagesize; + keyval = GDK_Left; + break; + case GDK_Page_Down: + case GDK_KP_Page_Down: + count *= fw->pagesize; + keyval = GDK_Right; + break; + case GDK_Home: + case GDK_KP_Home: + count = (pi->pageno - pi->minpage) * fw->pagesize; + keyval = GDK_Left; + break; + case GDK_End: + case GDK_KP_End: + count = (pi->maxpage - pi->pageno) * fw->pagesize; + keyval = GDK_Right; + break; + } + + switch (keyval) { + case GDK_Left: + case GDK_KP_Left: + if (code == 0) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_VERTICAL) + code -= (fw->cell_rows * count); + else + code -= count; + + if (code < 0) + code = 0; + + break; + case GDK_Right: + case GDK_KP_Right: + /* + * Make sure that when on the unencoded pages, the final glyph is + * the limit unlike the encoded pages where the max value is 0xffff. + */ + if ((fw->unencoded && + (gp == 0 || code == gp->encoding)) || + code == 0xffff) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_VERTICAL) + code += (fw->cell_rows * count); + else + code += count; + + if (fw->unencoded && code > gp->encoding) + code = gp->encoding; + else if (code > 0xffff) + code = 0xffff; + + break; + case GDK_Up: + case GDK_KP_Up: + if (code == 0) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) + code -= (fw->cell_cols * count); + else + code -= count; + + if (code < 0) + code = 0; + + break; + case GDK_Down: + case GDK_KP_Down: + /* + * Make sure that when on the unencoded pages, the final glyph is + * the limit unlike the encoded pages where the max value is 0xffff. + */ + if ((fw->unencoded && + (gp == 0 || code == gp->encoding)) || + code == 0xffff) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) + code += (fw->cell_cols * count); + else + code += count; + + if (fw->unencoded && code > gp->encoding) + code = gp->encoding; + else if (code > 0xffff) + code = 0xffff; + + break; + case GDK_Return: + case GDK_KP_Enter: + pi->sel_end = pi->sel_start; + activate = TRUE; + break; + default: + return FALSE; + } + + signal_extend = FALSE; + if (code > pi->sel_end) { + signal_extend = TRUE; + if (code <= pi->sel_start) + fontgrid_deselect_range(fw, pi->sel_end, code - 1); + else { + if (pi->sel_end < pi->sel_start) { + fontgrid_deselect_range(fw, pi->sel_end, pi->sel_start - 1); + fontgrid_select_range(fw, pi->sel_start + 1, code); + } else + fontgrid_select_range(fw, pi->sel_end, code); + } + } else if (code < pi->sel_end) { + signal_extend = TRUE; + if (code < pi->sel_start) { + if (pi->sel_end > pi->sel_start) { + fontgrid_deselect_range(fw, pi->sel_start + 1, pi->sel_end); + fontgrid_select_range(fw, code, pi->sel_start); + } else + fontgrid_select_range(fw, code, pi->sel_end); + } else + fontgrid_deselect_range(fw, code + 1, pi->sel_end); + } + + pi->sel_end = code; + + /* + * If the selection endpoint is on some page other than the current + * page, make sure the page holding the end point is made visible. + */ + pageno = code / fw->pagesize; + if (pageno != pi->pageno) { + fw->no_sel_callback = TRUE; + fontgrid_goto_page(fw, pageno); + } + + /* + * Reset the count. + */ + fw->count = 0; + + if (signal_extend) { + if (pi->sel_start == pi->sel_end) { + /* + * Set up and emit the selection start signal. + */ + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, + fw->font->glyphs_used, + code, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + code, TRUE); + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + } else { + sinfo.glyphs = 0; + sinfo.num_glyphs = 0; + } + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + + if (!activate) { + sinfo.reason = FONTGRID_EXTEND_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_EXTEND], + 0, &sinfo); + } else { + sinfo.reason = FONTGRID_ACTIVATE; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[ACTIVATE], 0, + &sinfo); + } + } + + return TRUE; +} + +static gint +fontgrid_key_press(GtkWidget *widget, GdkEventKey *event) +{ + Fontgrid *fw; + bdf_glyph_t *gp; + gint32 code, pageno; + guint32 count; + gboolean activate; + FontgridInternalPageInfo *pi, *opi; + FontgridSelectionInfo sinfo; + + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(IS_FONTGRID(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + if (event->state & GDK_SHIFT_MASK) + return fontgrid_shift_key_press(widget, event); + + fw = FONTGRID(widget); + + /* + * For number keys, use them to add up a count that will effect the + * behavior of the other keys. + */ + if (event->keyval >= GDK_0 && event->keyval <= GDK_9) { + fw->count = (fw->count * 10) + (event->keyval - GDK_0); + return FALSE; + } + + if (!fw->unencoded) { + pi = &fw->npage; + opi = &fw->upage; + gp = (fw->font && fw->font->glyphs_used) ? + (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0; + } else { + pi = &fw->upage; + opi = &fw->npage; + gp = (fw->font && fw->font->unencoded_used) ? + (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0; + } + + activate = FALSE; + + code = pi->sel_start; + + if ((count = fw->count) == 0) + count = 1; + + switch (event->keyval) { + case GDK_Left: + case GDK_KP_Left: + if (code == 0) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_VERTICAL) + code -= (fw->cell_rows * count); + else + code -= count; + + if (code < 0) + code = 0; + + break; + case GDK_Right: + case GDK_KP_Right: + /* + * Make sure that when on the unencoded pages, the final glyph is + * the limit unlike the encoded pages where the max value is 0xffff. + */ + if ((fw->unencoded && + (gp == 0 || code == gp->encoding)) || + code == 0xffff) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_VERTICAL) + code += (fw->cell_rows * count); + else + code += count; + + if (fw->unencoded && code > gp->encoding) + code = gp->encoding; + else if (code > 0xffff) + code = 0xffff; + + break; + case GDK_Up: + case GDK_KP_Up: + if (code == 0) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) + code -= (fw->cell_cols * count); + else + code -= count; + + if (code < 0) + code = 0; + + break; + case GDK_Down: + case GDK_KP_Down: + /* + * Make sure that when on the unencoded pages, the final glyph is + * the limit unlike the encoded pages where the max value is 0xffff. + */ + if ((fw->unencoded && + (gp == 0 || code == gp->encoding)) || + code == 0xffff) { + gdk_beep(); + return TRUE; + } + + if (fw->orientation == GTK_ORIENTATION_HORIZONTAL) + code += (fw->cell_cols * count); + else + code += count; + + if (fw->unencoded && code > gp->encoding) + code = gp->encoding; + else if (code > 0xffff) + code = 0xffff; + + break; + case GDK_Page_Up: + case GDK_KP_Page_Up: + fw->from_keyboard = TRUE; + fontgrid_goto_previous_page(fw); + return TRUE; + break; + case GDK_Page_Down: + case GDK_KP_Page_Down: + fw->from_keyboard = TRUE; + fontgrid_goto_next_page(fw); + return TRUE; + break; + case GDK_Home: + case GDK_KP_Home: + fw->from_keyboard = TRUE; + fontgrid_goto_first_page(fw); + return TRUE; + break; + case GDK_End: + case GDK_KP_End: + fw->from_keyboard = TRUE; + fontgrid_goto_last_page(fw); + return TRUE; + break; + case GDK_Return: + case GDK_KP_Enter: + pi->sel_end = pi->sel_start; + activate = TRUE; + break; + case GDK_BackSpace: + case GDK_Delete: + case GDK_KP_Delete: + fontgrid_cut_selection(fw); + return TRUE; + default: + return FALSE; + } + + /* + * This turns off the selection which means the cursor is effectively + * turned off even for the fontgrid_goto_page() call. The reason is that + * for keyboard navigation, the cursor should move up and down by rows and + * not whole pages when a page change occurs. + */ + fontgrid_deselect_all(fw); + + pageno = code / fw->pagesize; + if (pageno != pi->pageno) { + fw->no_sel_callback = TRUE; + fontgrid_goto_page(fw, pageno); + } + + pi->sel_start = pi->sel_end = code; + Select(code, pi->selmap); + fontgrid_draw_cells(widget, code, code, FALSE, TRUE); + + /* + * Reset the count. + */ + fw->count = 0; + + /* + * Set up and emit the selection start signal. + */ + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + code, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, fw->font->unencoded_used, + code, TRUE); + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + + if (!activate) { + sinfo.reason = FONTGRID_START_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + } else { + sinfo.reason = FONTGRID_ACTIVATE; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[ACTIVATE], 0, &sinfo); + } + + return TRUE; +} + +/************************************************************************** + * + * Class and instance setup. + * + **************************************************************************/ + +static void +fontgrid_class_init(gpointer g_class, gpointer class_data) +{ + GObjectClass *gocp = G_OBJECT_CLASS(g_class); + GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class); + GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class); + + /* + * Set the class global variables. + */ + parent_class = g_type_class_peek_parent(g_class); + + /* + * GObject class functions. + */ + gocp->set_property = fontgrid_set_property; + gocp->get_property = fontgrid_get_property; + gocp->finalize = fontgrid_finalize; + + /* + * GtkObjectClass functions. + */ + ocp->destroy = fontgrid_destroy; + + /* + * Instance functions. + */ + wcp->size_request = fontgrid_preferred_size; + wcp->size_allocate = fontgrid_actual_size; + wcp->realize = fontgrid_realize; + wcp->expose_event = fontgrid_expose; + wcp->focus_in_event = fontgrid_focus_in; + wcp->focus_out_event = fontgrid_focus_out; + wcp->button_press_event = fontgrid_button_press; + wcp->button_release_event = fontgrid_button_release; + wcp->motion_notify_event = fontgrid_motion_notify; + wcp->key_press_event = fontgrid_key_press; + wcp->selection_clear_event = fontgrid_lose_selection; + + /* + * Add parameters (a.k.a. resource) types. + */ + g_object_class_install_property(gocp, PROP_CODE_BASE, + g_param_spec_uint("codeBase", + _("Code base"), + _("Override for the code base (oct, dec, hex) for glyph codes."), + 8, + 16, + 16, + G_PARAM_READWRITE)); + g_object_class_install_property(gocp, PROP_POWER2, + g_param_spec_boolean("powersOfTwo", + _("Powers of two"), + _("Indicate whether the grid display should be a power-of-two rows."), + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_ORIENTATION, + g_param_spec_enum("orientation", + _("Orientation"), + _("Should the grid display vertically or horizontally."), + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_FONT, + g_param_spec_pointer("font", + _("Font"), + _("Font to be displayed."), + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_POINT_SIZE, + g_param_spec_uint("pointSize", + _("Point size"), + _("Set the default point size for new fonts."), + 2, + 256, + 12, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_SPACING, + g_param_spec_int("spacing", + _("Spacing"), + _("Set the default glyph spacing."), + BDF_PROPORTIONAL, + BDF_CHARCELL, + BDF_PROPORTIONAL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_SKIP_BLANKS, + g_param_spec_boolean("skipBlankPages", + _("Skip blank pages"), + _("Avoid displaying pages with no glyphs."), + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_OVERWRITE, + g_param_spec_boolean("overwriteMode", + _("Overwrite mode"), + _("Pasting the selection overwrites."), + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_COLORS, + g_param_spec_pointer("colorList", + _("Color list"), + _("Colors to be used for glyphs having bits-per-pixel > 1."), + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_INITIAL_GLYPH, + g_param_spec_int("initialGlyph", + _("Initial glyph"), + _("Code of the glyph to be displayed first."), + -1, + 0xffff, + -1, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_BPP, + g_param_spec_int("bitsPerPixel", + _("Bits per pixel"), + _("Number of bits per pixel for grayscale glyphs."), + 1, + 4, + 1, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_HRES, + g_param_spec_int("horizontalResolution", + _("Horizontal resolution"), + _("Set the default horizontal resolution for new fonts."), + 1, + 2400, + 100, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_VRES, + g_param_spec_int("verticalResolution", + _("Vertical resolution"), + _("Set the default vertical resolution for new fonts."), + 1, + 2400, + 100, + G_PARAM_READWRITE)); + + /* + * Add the signals these objects emit. + */ + fontgrid_signals[SELECTION_START] = + g_signal_new("selection-start", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(FontgridClass, selection_start), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + fontgrid_signals[SELECTION_EXTEND] = + g_signal_new("selection-extend", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(FontgridClass, selection_extend), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + fontgrid_signals[SELECTION_END] = + g_signal_new("selection-end", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(FontgridClass, selection_end), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + fontgrid_signals[ACTIVATE] = + g_signal_new("activate", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(FontgridClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + fontgrid_signals[MODIFIED] = + g_signal_new("modified", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(FontgridClass, modified), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + fontgrid_signals[TURN_TO_PAGE] = + g_signal_new("turn_to_page", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(FontgridClass, page), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); +} + +static void +fontgrid_init(GTypeInstance *obj, gpointer g_class) +{ + Fontgrid *fw = FONTGRID(obj); + FontgridInternalPageInfo *pi; + GdkScreen *screen; + gint fwidth, fpad; + + GTK_WIDGET_SET_FLAGS(fw, GTK_CAN_FOCUS); + + gtk_widget_style_get(GTK_WIDGET(fw), + "focus-line-width", &fwidth, + "focus-padding", &fpad, + NULL); + + fw->base = 16; + fw->power2 = TRUE; + fw->overwrite = TRUE; + fw->noblanks = TRUE; + fw->orientation = GTK_ORIENTATION_HORIZONTAL; + fw->point_size = 12; + fw->spacing = BDF_CHARCELL; + fw->colors = 0; + fw->initial_glyph = 0; + fw->bpp = 1; + + screen = + gdk_drawable_get_screen(GDK_DRAWABLE(gdk_get_default_root_window())); + fw->hres = (gint32) ((((double) gdk_screen_get_width(screen)) * 25.4) / + ((double) gdk_screen_get_width_mm(screen)) + 0.5); + fw->vres = (gint32) ((((double) gdk_screen_get_height(screen)) * 25.4) / + ((double) gdk_screen_get_height_mm(screen)) + 0.5); + + fw->cell_rows = FGRID_DEFAULT_ROWS; + fw->cell_cols = FGRID_DEFAULT_COLS; + fw->border = 4; + fw->hmargin = fw->widget.style->xthickness + fwidth + fpad + fw->border; + fw->vmargin = fw->widget.style->ythickness + fwidth + fpad + fw->border; + + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + + /* + * Private variables. + */ + fw->unencoded = FALSE; + fw->debug = FALSE; + fw->xor_gc = 0; + fw->points_used = 0; + fw->points_size = 0; + fw->rgb_used = 0; + fw->rgb_size = 0; + + fw->last_click = 0; + fw->mclick_time = 0; + + fw->count = 0; + memset((char *) &fw->clipboard, 0, sizeof(bdf_glyphlist_t)); + + /* + * Initialize the page information. + */ + pi = &fw->upage; + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->npage = pi->ppage = -1; + pi->pageno = pi->bcode = 0; + pi->sel_start = pi->sel_end = -1; + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); + pi = &fw->npage; + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->npage = pi->ppage = -1; + pi->pageno = pi->bcode = 0; + pi->sel_start = pi->sel_end = -1; + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); +} + +/************************************************************************** + * + * API. + * + **************************************************************************/ + +/* + * Type instantiation routines. + */ +GType +fontgrid_get_type(void) +{ + static GType fontgrid_type = 0; + + if (!fontgrid_type) { + static const GTypeInfo fontgrid_info = { + sizeof (FontgridClass), /* class_size */ + 0, /* base_init */ + 0, /* base_finalize */ + fontgrid_class_init, /* class_init */ + 0, /* class_finalize */ + 0, /* class_data */ + sizeof(Fontgrid), /* instance_size */ + 0, /* n_preallocs */ + fontgrid_init, /* instance_init */ + 0, /* value_table */ + }; + fontgrid_type = g_type_register_static(GTK_TYPE_WIDGET, + "Fontgrid", &fontgrid_info, 0); + } + + return fontgrid_type; +} + +GtkWidget * +fontgrid_new(const gchar *prop1, ...) +{ + GtkWidget *w; + va_list var_args; + + va_start(var_args, prop1); + w = GTK_WIDGET(g_object_new_valist(fontgrid_get_type(), prop1, var_args)); + va_end(var_args); + + return w; +} + +GtkWidget * +fontgrid_newv(bdf_font_t *font, guint32 pointSize, gint32 spacing, + gboolean skipBlankPages, gboolean overwriteMode, + gboolean powersOfTwo, guint16 *colorList, gint32 initialGlyph, + guint codeBase, GtkOrientation orientation, + gint32 bitsPerPixel, gint32 horizontalResolution, + gint32 verticalResolution, FontgridPageInfo *initialPageInfo) +{ + Fontgrid *fw = FONTGRID(g_object_new(fontgrid_get_type(), NULL)); + gint32 i, boundary; + FontgridInternalPageInfo *pi; + + fw->font = font; + fw->point_size = pointSize; + fw->spacing = spacing; + fw->colors = colorList; + fw->noblanks = skipBlankPages; + fw->overwrite = overwriteMode; + fw->power2 = powersOfTwo; + fw->initial_glyph = initialGlyph; + fw->base = codeBase; + fw->orientation = orientation; + fw->bpp = (font) ? font->bpp : bitsPerPixel; + fw->hres = horizontalResolution; + fw->vres = verticalResolution; + + /* + * If no font has been provided, make sure a default is created. + * Too many other things depend on a font existing. + */ + if (font == 0) { + fw->font = bdf_new_font(0, fw->point_size, fw->hres, fw->vres, + fw->spacing, fw->bpp); + if (fw->font->name == 0) + fw->font->name = bdf_make_xlfd_name(fw->font, g_get_prgname(), + "Unknown"); + } + + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + + /* + * Initialize the page information. + */ + pi = &fw->upage; + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->npage = pi->ppage = -1; + pi->pageno = pi->bcode = 0; + pi->sel_start = pi->sel_end = -1; + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); + pi = &fw->npage; + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->npage = pi->ppage = -1; + pi->pageno = pi->bcode = 0; + pi->sel_start = pi->sel_end = -1; + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); + + /* + * Determine the page info from the initial glyph setting. + */ + if (font != 0) { + if (fw->initial_glyph == -1) + fw->initial_glyph = (font->glyphs_used > 0) ? + font->glyphs->encoding : 0; + + pi = &fw->npage; + pi->pageno = fw->initial_glyph / fw->pagesize; + pi->bcode = pi->pageno * fw->pagesize; + pi->sel_start = pi->sel_end = fw->initial_glyph; + Select(fw->initial_glyph, pi->selmap); + fontgrid_neighbor_pages(fw, pi->pageno, &pi->ppage, &pi->npage); + + /* + * Set the min/max page numbers for the encoded glyphs. + */ + if (font->glyphs_used > 0) { + if (fw->noblanks) { + pi->minpage = font->glyphs->encoding / fw->pagesize; + pi->maxpage = + font->glyphs[font->glyphs_used-1].encoding / fw->pagesize; + } else { + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + } + } + + /* + * Set the min/max page numbers for the unencoded glyphs. + */ + if (font->unencoded_used > 0) { + pi = &fw->upage; + + if (fw->noblanks) { + pi->pageno = pi->minpage = + font->glyphs->encoding / fw->pagesize; + pi->maxpage = + font->unencoded[font->unencoded_used-1].encoding / + fw->pagesize; + pi->bcode = pi->pageno * fw->pagesize; + pi->ppage = -1; + + /* + * Lower boundary for the next page. + */ + boundary = pi->bcode + fw->pagesize; + for (i = 0; i < font->unencoded_used && + font->unencoded[i].encoding < boundary; i++) ; + pi->npage = (i == font->unencoded_used) ? + -1 : font->unencoded[i].encoding / fw->pagesize; + + } else { + pi->pageno = pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->ppage = -1; + pi->npage = pi->pageno + 1; + } + } + } + + /* + * Provide the initial page info the calling application will need + * to set up the page changing labels. + */ + initialPageInfo->unencoded_page = fw->unencoded; + initialPageInfo->encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0; + initialPageInfo->unencoded_glyphs = + (fw->font) ? fw->font->unencoded_used : 0; + + if (!fw->unencoded) { + initialPageInfo->previous_page = fw->npage.ppage; + initialPageInfo->current_page = fw->npage.pageno; + initialPageInfo->next_page = fw->npage.npage; + } else { + initialPageInfo->previous_page = fw->upage.ppage; + initialPageInfo->current_page = fw->upage.pageno; + initialPageInfo->next_page = fw->upage.npage; + } + + return GTK_WIDGET(fw); +} + +gboolean +fontgrid_has_selection(Fontgrid *fw, FontgridSelectionInfo *sinfo) +{ + FontgridInternalPageInfo *pi; + + g_return_val_if_fail(fw != 0, FALSE); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + /* + * Set up the selection info to alert the application that the + * base changed. + */ + if (sinfo != 0) { + /* + * Initialize the selection info structure. + */ + (void) memset((char *) sinfo, 0, sizeof(FontgridSelectionInfo)); + + if (pi->sel_start == pi->sel_end) { + if (fw->font) { + if (!fw->unencoded) + sinfo->glyphs = + fontgrid_locate_glyph(fw->font->glyphs, + fw->font->glyphs_used, + pi->sel_start, TRUE); + else + sinfo->glyphs = + fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + pi->sel_start, TRUE); + if (sinfo->glyphs == 0) { + empty_glyph.encoding = pi->sel_start; + sinfo->glyphs = &empty_glyph; + } + sinfo->num_glyphs = 1; + } + } else { + sinfo->glyphs = 0; + sinfo->num_glyphs = 0; + } + + sinfo->start = pi->sel_start; + sinfo->end = pi->sel_end; + sinfo->base = fw->base; + sinfo->unencoded = fw->unencoded; + sinfo->reason = FONTGRID_START_SELECTION; + } + + return (pi->sel_start == -1) ? FALSE : TRUE; +} + +bdf_font_t * +fontgrid_get_font(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, 0); + + return fw->font; +} + +void +fontgrid_set_font(Fontgrid *fw, bdf_font_t *font, gint32 initial_glyph) +{ + GtkWidget *w; + gint32 i, boundary; + FontgridInternalPageInfo *pi; + FontgridPageInfo pageinfo; + + g_return_if_fail(fw != 0); + + if (font == fw->font) + return; + + w = GTK_WIDGET(fw); + + /* + * Free up the existing font. + */ + if (fw->font != 0) + bdf_free_font(fw->font); + fw->font = font; + + /* + * Make sure the encoded pages are the default for newly loaded fonts. + */ + fw->unencoded = FALSE; + + /* + * Set the bits-per-pixel from the font. + */ + fw->bpp = (font != 0) ? font->bpp : 1; + + /* + * Set the initial glyph code. + */ + fw->initial_glyph = initial_glyph; + + /* + * Calculate the cell geometry and the rows and columns. + */ + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + + /* + * Initialize the page information. + */ + pi = &fw->upage; + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->npage = pi->ppage = -1; + pi->pageno = pi->bcode = 0; + pi->sel_start = pi->sel_end = -1; + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); + pi = &fw->npage; + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->npage = pi->ppage = -1; + pi->pageno = pi->bcode = 0; + pi->sel_start = pi->sel_end = -1; + (void) memset((char *) pi->selmap, 0, sizeof(guint32) * 2048); + + /* + * Determine the page info from the initial glyph setting. + */ + if (font != 0) { + if (fw->initial_glyph == -1) + fw->initial_glyph = (font->glyphs_used > 0) ? + font->glyphs->encoding : 0; + + pi = &fw->npage; + pi->pageno = fw->initial_glyph / fw->pagesize; + pi->bcode = pi->pageno * fw->pagesize; + pi->sel_start = pi->sel_end = fw->initial_glyph; + Select(fw->initial_glyph, pi->selmap); + fontgrid_neighbor_pages(fw, pi->pageno, &pi->ppage, &pi->npage); + + /* + * Set the min/max page numbers for the encoded glyphs. + */ + if (font->glyphs_used > 0) { + if (fw->noblanks) { + pi->minpage = font->glyphs->encoding / fw->pagesize; + pi->maxpage = + font->glyphs[font->glyphs_used-1].encoding / fw->pagesize; + } else { + pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + } + } + + /* + * Set the min/max page numbers for the unencoded glyphs. + */ + if (font->unencoded_used > 0) { + pi = &fw->upage; + + if (fw->noblanks) { + pi->pageno = pi->minpage = + font->glyphs->encoding / fw->pagesize; + pi->maxpage = + font->unencoded[font->unencoded_used-1].encoding / + fw->pagesize; + pi->bcode = pi->pageno * fw->pagesize; + pi->ppage = -1; + + /* + * Lower boundary for the next page. + */ + boundary = pi->bcode + fw->pagesize; + for (i = 0; i < font->unencoded_used && + font->unencoded[i].encoding < boundary; i++) ; + pi->npage = (i == font->unencoded_used) ? + -1 : font->unencoded[i].encoding / fw->pagesize; + + } else { + pi->pageno = pi->minpage = 0; + pi->maxpage = 0xffff / fw->pagesize; + pi->ppage = -1; + pi->npage = pi->pageno + 1; + } + } + } + + /* + * Signal that a page change has taken place so the application can do + * setup that it needs. + */ + pageinfo.unencoded_page = fw->unencoded; + pageinfo.encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0; + pageinfo.unencoded_glyphs = (fw->font) ? fw->font->unencoded_used : 0; + + if (!fw->unencoded) { + pageinfo.previous_page = fw->npage.ppage; + pageinfo.current_page = fw->npage.pageno; + pageinfo.next_page = fw->npage.npage; + } else { + pageinfo.previous_page = fw->upage.ppage; + pageinfo.current_page = fw->upage.pageno; + pageinfo.next_page = fw->upage.npage; + } + + g_signal_emit(G_OBJECT(fw), fontgrid_signals[TURN_TO_PAGE], 0, &pageinfo); + + /* + * Queue up a resize so the grid will change size. + */ + gtk_widget_queue_resize(w); +} + +gchar * +fontgrid_get_font_messages(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, 0); + + return (fw->font) ? fw->font->acmsgs : 0; +} + +guint +fontgrid_get_code_base(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, 0); + + return fw->base; +} + +void +fontgrid_set_code_base(Fontgrid *fw, guint base) +{ + FontgridInternalPageInfo *pi; + FontgridSelectionInfo sinfo; + + g_return_if_fail(fw != 0); + + switch (base) { + case 8: case 10: case 16: + if (fw->base != base) { + fw->base = base; + if (!fw->unencoded) { + pi = &fw->npage; + fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode, + fw->npage.bcode + fw->pagesize, + TRUE, FALSE); + } else + pi = &fw->upage; + + /* + * Set up the selection info to alert the application that the + * base changed. + */ + if (pi->sel_start == pi->sel_end) { + if (fw->font) { + if (!fw->unencoded) + sinfo.glyphs = + fontgrid_locate_glyph(fw->font->glyphs, + fw->font->glyphs_used, + pi->sel_start, TRUE); + else + sinfo.glyphs = + fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + pi->sel_start, TRUE); + if (sinfo.glyphs == 0) { + empty_glyph.encoding = pi->sel_start; + sinfo.glyphs = &empty_glyph; + } + sinfo.num_glyphs = 1; + } + } else { + sinfo.glyphs = 0; + sinfo.num_glyphs = 0; + } + + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + sinfo.reason = FONTGRID_BASE_CHANGE; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + } + break; + } +} + +GtkOrientation +fontgrid_get_orientation(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, GTK_ORIENTATION_HORIZONTAL); + + return fw->orientation; +} + +void +fontgrid_set_orientation(Fontgrid *fw, GtkOrientation dir) +{ + guint16 tmp; + + g_return_if_fail(fw != 0); + + if (dir != fw->orientation) { + fw->orientation = dir; + + /* + * Need to swap rows and cols and attempt a resize if the object + * has been constructed. + */ + tmp = fw->cell_rows; + fw->cell_rows = fw->cell_cols; + fw->cell_cols = tmp; + + gtk_widget_queue_resize(GTK_WIDGET(fw)); + } +} + +void +fontgrid_get_page_info(Fontgrid *fw, FontgridPageInfo *pageinfo) +{ + g_return_if_fail(fw != 0); + g_return_if_fail(pageinfo != 0); + + pageinfo->unencoded_page = fw->unencoded; + pageinfo->encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0; + pageinfo->unencoded_glyphs = (fw->font) ? fw->font->unencoded_used : 0; + + if (!fw->unencoded) { + pageinfo->previous_page = fw->npage.ppage; + pageinfo->current_page = fw->npage.pageno; + pageinfo->next_page = fw->npage.npage; + } else { + pageinfo->previous_page = fw->upage.ppage; + pageinfo->current_page = fw->upage.pageno; + pageinfo->next_page = fw->upage.npage; + } +} + +/* + * This is the routine that does the majority of the work for updating + * page changes. + */ +static void +fontgrid_page_change_update(Fontgrid *fw, FontgridInternalPageInfo *pi) +{ + gint32 code; + FontgridPageInfo pageinfo; + FontgridSelectionInfo selinfo; + + code = pi->sel_start - pi->bcode; + pi->bcode = pi->pageno * fw->pagesize; + + if (fw->from_keyboard) { + fontgrid_deselect_all(fw); + code += pi->bcode; + pi->sel_start = pi->sel_end = code; + Select(code, pi->selmap); + fw->from_keyboard = FALSE; + } + fontgrid_neighbor_pages(fw, pi->pageno, &pi->ppage, &pi->npage); + + fontgrid_draw_cells(GTK_WIDGET(fw), pi->bcode, + pi->bcode + (fw->pagesize - 1), TRUE, TRUE); + + pageinfo.unencoded_page = fw->unencoded; + pageinfo.encoded_glyphs = (fw->font) ? fw->font->glyphs_used : 0; + pageinfo.unencoded_glyphs = (fw->font) ? fw->font->unencoded_used : 0; + + pageinfo.previous_page = pi->ppage; + pageinfo.current_page = pi->pageno; + pageinfo.next_page = pi->npage; + + g_signal_emit(G_OBJECT(fw), fontgrid_signals[TURN_TO_PAGE], 0, &pageinfo); + + /* + * If this was called from the keyboard, then indicate the changed + * selection. + */ + if (!fw->no_sel_callback && fw->from_keyboard) { + selinfo.glyphs = 0; + selinfo.num_glyphs = 1; + if (fw->font) { + selinfo.glyphs = (!fw->unencoded) ? + fontgrid_locate_glyph(fw->font->glyphs, + fw->font->glyphs_used, + code, TRUE) : + fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + code, TRUE); + } + if (selinfo.glyphs == 0) { + empty_glyph.encoding = code; + selinfo.glyphs = &empty_glyph; + } + + selinfo.reason = FONTGRID_START_SELECTION; + selinfo.start = pi->sel_start; + selinfo.end = pi->sel_end; + selinfo.base = fw->base; + selinfo.unencoded = fw->unencoded; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &selinfo); + } +} + +void +fontgrid_goto_page(Fontgrid *fw, gint32 pageno) +{ + guint32 mpage; + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + mpage = 0xffff / fw->pagesize; + + if (pageno < 0) + pageno = 0; + if (pageno > mpage) + pageno = mpage; + + if (pageno != pi->pageno) { + pi->pageno = pageno; + fontgrid_page_change_update(fw, pi); + } +} + +void +fontgrid_goto_code(Fontgrid *fw, gint32 code) +{ + gint32 pageno; + FontgridInternalPageInfo *pi; + FontgridSelectionInfo selinfo; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (code < 0) + code = 0; + if (code > 0xffff) + code = 0xffff; + + pageno = code / fw->pagesize; + + if (pageno != pi->pageno) { + fw->no_sel_callback = TRUE; + pi->pageno = pageno; + fontgrid_page_change_update(fw, pi); + } + + fontgrid_deselect_all(fw); + Select(code, pi->selmap); + pi->sel_start = pi->sel_end = code; + fontgrid_draw_cells(GTK_WIDGET(fw), code, code, FALSE, TRUE); + + selinfo.glyphs = 0; + selinfo.num_glyphs = 1; + if (fw->font) { + selinfo.glyphs = (!fw->unencoded) ? + fontgrid_locate_glyph(fw->font->glyphs, + fw->font->glyphs_used, + code, TRUE) : + fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, + code, TRUE); + } + if (selinfo.glyphs == 0) { + empty_glyph.encoding = code; + selinfo.glyphs = &empty_glyph; + } + + selinfo.reason = FONTGRID_START_SELECTION; + selinfo.start = pi->sel_start; + selinfo.end = pi->sel_end; + selinfo.base = fw->base; + selinfo.unencoded = fw->unencoded; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &selinfo); +} + +void +fontgrid_goto_first_page(Fontgrid *fw) +{ + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (pi->pageno == pi->minpage) + return; + + pi->pageno = pi->minpage; + fontgrid_page_change_update(fw, pi); +} + +void +fontgrid_goto_last_page(Fontgrid *fw) +{ + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (pi->pageno == pi->maxpage) + return; + + pi->pageno = pi->maxpage; + fontgrid_page_change_update(fw, pi); +} + +void +fontgrid_goto_next_page(Fontgrid *fw) +{ + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (pi->pageno == pi->maxpage) + return; + + pi->pageno = pi->npage; + fontgrid_page_change_update(fw, pi); +} + +void +fontgrid_goto_previous_page(Fontgrid *fw) +{ + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + + if (pi->pageno == pi->minpage) + return; + + pi->pageno = pi->ppage; + fontgrid_page_change_update(fw, pi); +} + +gboolean +fontgrid_viewing_unencoded(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, FALSE); + + return fw->unencoded; +} + +void +fontgrid_switch_encoding_view(Fontgrid *fw) +{ + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + fw->unencoded = !fw->unencoded; + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + fontgrid_draw_cells(GTK_WIDGET(fw), pi->bcode, pi->bcode + fw->pagesize, + TRUE, TRUE); +} + +gchar * +fontgrid_get_font_name(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, 0); + + return (fw->font) ? fw->font->name : ""; +} + +void +fontgrid_set_font_name(Fontgrid *fw, gchar *name) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + g_return_if_fail(fw->font != 0); + + if (fw->font->name != 0) + free(fw->font->name); + + if (name == 0 || *name == 0) + fw->font->name = bdf_make_xlfd_name(fw->font, g_get_prgname(), + "Unknown"); + else + fw->font->name = g_strdup(name); + + bdf_set_modified(fw->font, 1); + + minfo.reason = FONTGRID_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); +} + +gboolean +fontgrid_get_font_modified(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, FALSE); + + return (fw->font) ? ((fw->font->modified) ? TRUE : FALSE) : FALSE; +} + +void +fontgrid_set_font_modified(Fontgrid *fw, gboolean mod) +{ + FontgridInternalPageInfo *pi; + + g_return_if_fail(fw != 0); + + if (fw->font && fw->font->modified != mod) { + bdf_set_modified(fw->font, mod); + + if (mod == FALSE) { + /* + * Redraw all the labels to clear those that were showing as + * modified. + */ + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + fontgrid_draw_cells(GTK_WIDGET(fw), pi->bcode, + (pi->bcode + fw->pagesize) - 1, TRUE, FALSE); + } else { + /* + * If the font is being marked as modified, then signal the + * application of this state. + */ + fprintf(stderr, "MOD\n"); + } + } +} + +void +fontgrid_set_unicode_glyph_names(Fontgrid *fw, FILE *in) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + g_return_if_fail(in != 0); + + if (bdf_set_unicode_glyph_names(in, fw->font, 0)) { + /* + * Redraw the labels. + */ + fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode, + fw->npage.bcode + fw->pagesize, TRUE, FALSE); + minfo.reason = FONTGRID_GLYPH_NAMES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_set_adobe_glyph_names(Fontgrid *fw, FILE *in) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + g_return_if_fail(in != 0); + + if (bdf_set_adobe_glyph_names(in, fw->font, 0)) { + /* + * Redraw the labels. + */ + fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode, + fw->npage.bcode + fw->pagesize, TRUE, FALSE); + minfo.reason = FONTGRID_GLYPH_NAMES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_set_code_glyph_names(Fontgrid *fw, gint ch) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + if (bdf_set_glyph_code_names(ch, fw->font, 0)) { + /* + * Redraw the labels. + */ + fontgrid_draw_cells(GTK_WIDGET(fw), fw->npage.bcode, + fw->npage.bcode + fw->pagesize, TRUE, FALSE); + minfo.reason = FONTGRID_GLYPH_NAMES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_make_xlfd_font_name(Fontgrid *fw) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + if ((minfo.name = bdf_make_xlfd_name(fw->font, "Foundry", + "FaceName")) != 0) { + minfo.reason = FONTGRID_NAME_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_update_font_name_from_properties(Fontgrid *fw) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + if (bdf_has_xlfd_name(fw->font)) { + bdf_update_name_from_properties(fw->font); + + minfo.reason = FONTGRID_NAME_MODIFIED; + minfo.name = fw->font->name; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_update_properties_from_font_name(Fontgrid *fw) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + if (bdf_update_properties_from_name(fw->font)) { + minfo.reason = FONTGRID_PROPERTIES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_set_font_property(Fontgrid *fw, bdf_property_t *prop) +{ + FontgridModificationInfo minfo; + gboolean changed; + bdf_property_t *p; + + g_return_if_fail(fw != 0); + g_return_if_fail(prop != 0); + + changed = FALSE; + + if ((p = bdf_get_font_property(fw->font, prop->name)) == 0) + changed = TRUE; + else if (p->format == prop->format) { + switch (p->format) { + case BDF_ATOM: + /* + * If the atoms are different or one is NULL and the other isn't, + * then the property will be changed. + */ + if ((p->value.atom && prop->value.atom && + strcmp(p->value.atom, prop->value.atom) != 0) || + p->value.atom != prop->value.atom) + changed = TRUE; + break; + case BDF_INTEGER: + if (p->value.int32 != prop->value.int32) + changed = TRUE; + break; + case BDF_CARDINAL: + if (p->value.card32 != prop->value.card32) + changed = TRUE; + break; + } + } + + /* + * If this causes no change, just return. + */ + if (changed == FALSE) + return; + + bdf_add_font_property(fw->font, prop); + minfo.reason = FONTGRID_PROPERTIES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); +} + +void +fontgrid_delete_font_property(Fontgrid *fw, gchar *prop_name) +{ + FontgridModificationInfo minfo; + bdf_property_t *p; + + g_return_if_fail(fw != 0); + g_return_if_fail(prop_name != 0); + + /* + * If the property doesn't exist, then just return. + */ + if ((p = bdf_get_font_property(fw->font, prop_name)) == 0) + return; + + bdf_delete_font_property(fw->font, prop_name); + minfo.reason = FONTGRID_PROPERTIES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); +} + +guint32 +fontgrid_get_font_comments(Fontgrid *fw, gchar **comments) +{ + g_return_val_if_fail(fw != 0, 0); + + if (comments != 0) + *comments = fw->font->comments; + + return fw->font->comments_len; +} + +void +fontgrid_set_font_comments(Fontgrid *fw, gchar *comments) +{ + FontgridModificationInfo minfo; + unsigned int len; + + g_return_if_fail(fw != 0); + + len = (comments) ? (unsigned int) strlen(comments) : 0; + if (bdf_replace_comments(fw->font, comments, len)) { + minfo.reason = FONTGRID_COMMENTS_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +gint +fontgrid_get_font_spacing(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, -1); + + return fw->font->spacing; +} + +void +fontgrid_set_font_spacing(Fontgrid *fw, gint spacing) +{ + FontgridModificationInfo minfo; + bdf_property_t p; + + g_return_if_fail(fw != 0); + + if (spacing < BDF_PROPORTIONAL || spacing > BDF_CHARCELL || + fw->font->spacing == spacing) + return; + + p.name = "SPACING"; + p.format = BDF_ATOM; + switch (spacing) { + case BDF_PROPORTIONAL: p.value.atom = "P"; break; + case BDF_MONOWIDTH: p.value.atom = "M"; break; + case BDF_CHARCELL: p.value.atom = "C"; break; + } + + bdf_add_font_property(fw->font, &p); + minfo.reason = FONTGRID_PROPERTIES_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); +} + +guint16 +fontgrid_get_font_device_width(Fontgrid *fw) +{ + g_return_val_if_fail(fw != 0, 0); + + return fw->font->monowidth; +} + +void +fontgrid_set_font_device_width(Fontgrid *fw, guint16 dwidth) +{ + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + /* + * Only set the global device width if this is not a proportional font or + * if there the device width changed. + */ + if (fw->font->spacing == BDF_PROPORTIONAL || + fw->font->monowidth == dwidth) + return; + + fw->font->monowidth = dwidth; + fw->font->modified = 1; + + minfo.reason = FONTGRID_DEVICE_WIDTH_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); +} + +void +fontgrid_get_font_info(Fontgrid *fw, FontgridFontInfo *info) +{ + g_return_if_fail(fw != 0); + g_return_if_fail(fw->font != 0); + g_return_if_fail(info != 0); + + info->name = fw->font->name; + info->comments = fw->font->comments; + info->messages = fw->font->acmsgs; + info->default_char = fw->font->default_glyph; + info->monowidth = fw->font->monowidth; + info->spacing = (guint16) fw->font->spacing; + info->font_ascent = fw->font->font_ascent; + info->font_descent = fw->font->font_descent; + info->font_descent = fw->font->font_descent; + info->resolution_x = fw->font->resolution_x; + info->resolution_y = fw->font->resolution_y; + info->bits_per_pixel = fw->font->bpp; + memcpy((char *) &info->bbx, (char *) &fw->font->bbx, sizeof(bdf_bbx_t)); +} + +void +fontgrid_set_font_info(Fontgrid *fw, FontgridFontInfo *info) +{ + int mod; + bdf_font_t *f; + bdf_property_t prop; + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + g_return_if_fail(fw->font != 0); + g_return_if_fail(info != 0); + + f = fw->font; + + minfo.reason = FONTGRID_MODIFIED; + + /* + * Do some special stuff with the modified field so we know whether to + * call the modified callback or not. + */ + mod = f->modified; + f->modified = 0; + + /* + * Handle the default character field. If it happens to be -1, then + * delete the font property. Otherwise add it. + */ + if (info->default_char < 0) + bdf_delete_font_property(f, "DEFAULT_CHAR"); + else { + prop.name = "DEFAULT_CHAR"; + prop.format = BDF_CARDINAL; + prop.value.card32 = info->default_char; + bdf_add_font_property(f, &prop); + } + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = info->font_ascent; + bdf_add_font_property(f, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = info->font_descent; + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.int32 = info->resolution_x; + bdf_add_font_property(f, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.int32 = info->resolution_y; + bdf_add_font_property(f, &prop); + + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = 0; + switch (info->spacing) { + case BDF_PROPORTIONAL: prop.value.atom = "P"; break; + case BDF_MONOWIDTH: prop.value.atom = "M"; break; + case BDF_CHARCELL: prop.value.atom = "C"; break; + } + if (prop.value.atom != 0) + bdf_add_font_property(f, &prop); + + /* + * If the font was modified, and has an XLFD name, make sure the XLFD name + * gets updated from the properties and the appropriate callback is + * called. + */ + if (f->modified && bdf_has_xlfd_name(f)) + fontgrid_update_font_name_from_properties(fw); + + /* + * Now determine if the monowidth field will have a resize affect on + * things. + */ + if (f->spacing != BDF_PROPORTIONAL) { + if (f->monowidth == 0) { + /* + * Handle the special case of a proportional font being changed to + * some other spacing. + */ + f->monowidth = f->bbx.width; + f->modified = 1; + } + if (info->monowidth != f->monowidth) { + /* + * Go ahead and queue up a resize in case the monowidth + * really does change the size. + */ + gtk_widget_queue_resize(GTK_WIDGET(fw)); + f->monowidth = f->bbx.width = info->monowidth; + f->modified = 1; + } + } + if (f->modified) + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + f->modified |= mod; +} + +void +fontgrid_translate_glyphs(Fontgrid *fw, gint16 dx, gint16 dy, + gboolean all_glyphs) +{ + GtkWidget *w = (GtkWidget *) fw; + gint32 start, end; + FontgridInternalPageInfo *pi; + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage: &fw->upage; + + if (all_glyphs) { + start = pi->minpage * fw->pagesize; + end = (pi->maxpage * fw->pagesize) + fw->pagesize; + } else { + start = pi->sel_start; + end = pi->sel_end; + } + + if (bdf_translate_glyphs(fw->font, dx, dy, start, end, 0, 0, + fw->unencoded)) { + gtk_widget_queue_resize(w); + if (GTK_WIDGET_REALIZED(w)) + gdk_window_clear(w->window); + + gtk_widget_queue_resize(w); + if (GTK_WIDGET_REALIZED(w)) + gdk_window_clear(w->window); + + minfo.reason = FONTGRID_GLYPHS_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_rotate_glyphs(Fontgrid *fw, gint16 degrees, gboolean all_glyphs) +{ + GtkWidget *w = (GtkWidget *) fw; + gint32 start, end; + FontgridInternalPageInfo *pi; + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage: &fw->upage; + + if (all_glyphs) { + start = pi->minpage * fw->pagesize; + end = (pi->maxpage * fw->pagesize) + fw->pagesize; + } else { + start = pi->sel_start; + end = pi->sel_end; + } + + if (bdf_rotate_glyphs(fw->font, degrees, start, end, 0, 0, + fw->unencoded)) { + gtk_widget_queue_resize(w); + if (GTK_WIDGET_REALIZED(w)) + gdk_window_clear(w->window); + + minfo.reason = FONTGRID_GLYPHS_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_shear_glyphs(Fontgrid *fw, gint16 degrees, gboolean all_glyphs) +{ + GtkWidget *w = (GtkWidget *) fw; + gint32 start, end; + FontgridInternalPageInfo *pi; + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage: &fw->upage; + + if (all_glyphs) { + start = pi->minpage * fw->pagesize; + end = (pi->maxpage * fw->pagesize) + fw->pagesize; + } else { + start = pi->sel_start; + end = pi->sel_end; + } + + if (bdf_shear_glyphs(fw->font, degrees, start, end, 0, 0, + fw->unencoded)) { + gtk_widget_queue_resize(w); + if (GTK_WIDGET_REALIZED(w)) + gdk_window_clear(w->window); + + minfo.reason = FONTGRID_GLYPHS_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +void +fontgrid_embolden_glyphs(Fontgrid *fw, gboolean all_glyphs) +{ + GtkWidget *w = (GtkWidget *) fw; + gint resize; + gint32 start, end; + FontgridInternalPageInfo *pi; + FontgridModificationInfo minfo; + + g_return_if_fail(fw != 0); + + pi = (!fw->unencoded) ? &fw->npage: &fw->upage; + + if (all_glyphs) { + start = pi->minpage * fw->pagesize; + end = (pi->maxpage * fw->pagesize) + fw->pagesize; + } else { + start = pi->sel_start; + end = pi->sel_end; + } + + resize = 0; + if (bdf_embolden_glyphs(fw->font, start, end, 0, 0, + fw->unencoded, &resize)) { + if (resize) { + gtk_widget_queue_resize(w); + if (GTK_WIDGET_REALIZED(w)) + gdk_window_clear(w->window); + } else + /* + * Just redisplay the selection. + */ + fontgrid_draw_cells(w, start, end, TRUE, TRUE); + + minfo.reason = FONTGRID_GLYPHS_MODIFIED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + } +} + +gboolean +fontgrid_clipboard_empty(Fontgrid *fw) +{ + GdkWindow *owner; + gboolean empty = TRUE; + GdkAtom atype; + gint aformat, nitems; + guchar *data; + + g_return_val_if_fail(fw != 0, empty); + + if ((owner = gdk_selection_owner_get(FONTGRID_CLIPBOARD)) == 0) + return empty; + + /* + * Check to see if the clipboard contents are empty or not. + * + * This is handled specially to allow determination of this without + * using up what might be a lot of memory to get the whole contents. It + * will have to be changed for Windows. + */ + if (gdk_property_get(owner, FONTGRID_CLIPBOARD, FONTGRID_GLYPHLIST, + 0, 4, FALSE, &atype, &aformat, &nitems, &data)) { + if (nitems > 0) { + empty = FALSE; + free((char *) data); + } + } + + return empty; +} + +static unsigned char * +fontgrid_encode_selection(Fontgrid *fw, guint32 *bytes) +{ + FontgridInternalPageInfo *pi; + bdf_glyph_t *gp; + bdf_glyphlist_t *gl; + guint16 a; + guint32 i, nlen, bcount; + guchar *sel, *sp; + + *bytes = 0; + + gl = &fw->clipboard; + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + bdf_copy_glyphs(fw->font, pi->sel_start, pi->sel_end, gl, fw->unencoded); + + /* + * Calculate the number of bytes that will be needed for everything except + * the name strings and the bitmap data. + */ + bcount = (sizeof(unsigned int) << 1) + (6 * sizeof(unsigned short)) + + (((6 * sizeof(unsigned short)) + sizeof(unsigned int)) * + gl->glyphs_used); + + /* + * Figure out how much extra will be needed for the names, bitmaps, and + * PSF Unicode mappings. + */ + for (i = 0, gp = gl->glyphs; i < gl->glyphs_used; i++, gp++) { + nlen = (gp->name) ? (guint32) (strlen(gp->name) + 1) : 0; + /* + * The extra 2 bytes is for encoding the number of bytes used for the + * Unicode mappings, even if it is 0. This could be a problem later + * if a set of mappings legitimately exceeds 2^16 in length. + */ + bcount += nlen + gp->bytes + 2 + gp->unicode.map_used; + } + + /* + * Allocate the storage space needed for the encoded form. + */ + sel = sp = g_malloc(bcount); + + /* + * Set the returned byte count. + */ + *bytes = bcount; + + /* + * Encode the 20-byte header. + */ + a = (guint16) gl->bpp; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + nlen = (guint32) gl->start; + *sp++ = (nlen >> 24) & 0xff; + *sp++ = (nlen >> 16) & 0xff; + *sp++ = (nlen >> 8) & 0xff; + *sp++ = nlen & 0xff; + + nlen = (guint32) gl->end; + *sp++ = (nlen >> 24) & 0xff; + *sp++ = (nlen >> 16) & 0xff; + *sp++ = (nlen >> 8) & 0xff; + *sp++ = nlen & 0xff; + + a = (guint16) gl->glyphs_used; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gl->bbx.width; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gl->bbx.x_offset; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gl->bbx.ascent; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gl->bbx.descent; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + /* + * Go through each glyph entry and encode the data. + */ + for (i = 0, gp = gl->glyphs; i < gl->glyphs_used; i++, gp++) { + /* + * Encode the glyph encoding. + */ + nlen = (guint32) gp->encoding; + *sp++ = (nlen >> 24) & 0xff; + *sp++ = (nlen >> 16) & 0xff; + *sp++ = (nlen >> 8) & 0xff; + *sp++ = nlen & 0xff; + + /* + * Encode the glyph device width. + */ + a = (guint16) gp->dwidth; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + /* + * Encode the glyph name length. + */ + nlen = (gp->name) ? (guint32) (strlen(gp->name) + 1) : 0; + a = (guint16) nlen; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + /* + * Encode the four bounding box values needed. + */ + a = (guint16) gp->bbx.width; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gp->bbx.x_offset; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gp->bbx.ascent; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + a = (guint16) gp->bbx.descent; + *sp++ = (a >> 8) & 0xff; + *sp++ = a & 0xff; + + /* + * Encode the name if it exists. + */ + if (nlen > 0) { + (void) memcpy((char *) sp, gp->name, nlen); + sp += nlen; + } + + /* + * Encode the bitmap. + */ + if (gp->bytes > 0) { + (void) memcpy((char *) sp, (char *) gp->bitmap, gp->bytes); + sp += gp->bytes; + } + + /* + * Encode the PSF Unicode mappings. Even if there aren't any, add + * the encoding. + */ + *sp++ = (gp->unicode.map_used >> 8) & 0xff; + *sp++ = gp->unicode.map_used & 0xff; + if (gp->unicode.map_used > 0) { + (void) memcpy((char *) sp, (char *) gp->unicode.map, + sizeof(unsigned char) * gp->unicode.map_used); + sp += gp->unicode.map_used; + } + } + + /* + * Return the selection encoded as a byte stream. + */ + return sel; +} + +#define GETSHORT(s) ((s[0] << 8) | s[1]) +#define GETLONG(s) ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]) + +static void +fontgrid_decode_selection(Fontgrid *fw, guchar *sel) +{ + guint32 i, range, nlen; + bdf_glyph_t *gp; + bdf_glyphlist_t *gl; + + if (sel == 0) + return; + + gl = &fw->clipboard; + + /* + * Clear out the bitmaps and names from the existing glyphs. + */ + for (gp = gl->glyphs, i = 0; i < gl->glyphs_size; i++, gp++) { + if (gp->name != 0) + free(gp->name); + if (gp->bytes > 0) + free((char *) gp->bitmap); + } + + /* + * Extract the glyph list bits per pixel. + */ + gl->bpp = GETSHORT(sel); + sel += 2; + + /* + * Extract the glyph list starting and ending encodings. + */ + gl->start = (int) GETLONG(sel); + sel += 4; + + gl->end = (int) GETLONG(sel); + sel += 4; + + /* + * Extract the number of encoded glyphs. + */ + range = (guint32) GETSHORT(sel); + sel += 2; + + /* + * Resize the internal glyph list clipboard if necessary. + */ + if (range > gl->glyphs_size) { + if (gl->glyphs_size == 0) + gl->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * range); + else + gl->glyphs = (bdf_glyph_t *) realloc((char *) gl->glyphs, + sizeof(bdf_glyph_t) * range); + gl->glyphs_size = range; + } + + /* + * Initialize the glyph list. + */ + (void) memset((char *) &gl->bbx, 0, sizeof(bdf_bbx_t)); + (void) memset((char *) gl->glyphs, 0, + sizeof(bdf_glyph_t) * gl->glyphs_size); + + gl->glyphs_used = range; + + /* + * Decode the overall metrics of the glyph list. + */ + gl->bbx.width = GETSHORT(sel); + sel += 2; + gl->bbx.x_offset = GETSHORT(sel); + sel += 2; + gl->bbx.ascent = GETSHORT(sel); + sel += 2; + gl->bbx.descent = GETSHORT(sel); + sel += 2; + gl->bbx.height = gl->bbx.ascent + gl->bbx.descent; + gl->bbx.y_offset = -gl->bbx.descent; + + /* + * Decode the glyphs. + */ + for (i = 0, gp = gl->glyphs; i < range; i++, gp++) { + /* + * Get the glyph encoding. + */ + gp->encoding = (int) GETLONG(sel); + sel += 4; + + /* + * Get the device width. + */ + gp->dwidth = GETSHORT(sel); + sel += 2; + + /* + * Get the name length. + */ + nlen = GETSHORT(sel); + sel += 2; + + /* + * Get the bounding box. + */ + gp->bbx.width = GETSHORT(sel); + sel += 2; + gp->bbx.x_offset = GETSHORT(sel); + sel += 2; + gp->bbx.ascent = GETSHORT(sel); + sel += 2; + gp->bbx.descent = GETSHORT(sel); + sel += 2; + gp->bbx.height = gp->bbx.ascent + gp->bbx.descent; + gp->bbx.y_offset = -gp->bbx.descent; + + /* + * Get the name. + */ + if (nlen > 0) { + gp->name = (char *) malloc(nlen); + (void) memcpy(gp->name, (char *) sel, nlen); + sel += nlen; + } + + /* + * Get the bitmap. + */ + + switch (gl->bpp) { + case 1: + gp->bytes = ((gp->bbx.width + 7) >> 3) * gp->bbx.height; + break; + case 2: + gp->bytes = (((gp->bbx.width << 1) + 7) >> 3) * gp->bbx.height; + break; + case 4: + gp->bytes = (((gp->bbx.width << 2) + 7) >> 3) * gp->bbx.height; + break; + case 8: + gp->bytes = gp->bbx.width * gp->bbx.height; + break; + } + + if (gp->bytes > 0) { + gp->bitmap = (unsigned char *) malloc(gp->bytes); + (void) memcpy((char *) gp->bitmap, (char *) sel, gp->bytes); + sel += gp->bytes; + } + + /* + * Get the Unicode mappings. + */ + gp->unicode.map_used = GETSHORT(sel); + sel += 2; + if (gp->unicode.map_used > 0) { + gp->unicode.map_size = ((gp->unicode.map_used >> 2) + + ((gp->unicode.map_used & 3) ? 1 : 0)) << 2; + gp->unicode.map = (unsigned char *) malloc(gp->unicode.map_size); + (void) memcpy((char *) gp->unicode.map, (char *) sel, + gp->unicode.map_used); + sel += gp->unicode.map_used; + } + } +} + +/* + * This function assumes the fontgrid is realized so a GdkWindow exists. + */ +void +fontgrid_copy_selection(Fontgrid *fw) +{ + GtkWidget *w; + GdkWindow *win; + guint32 bytes; + guchar *sel; + + g_return_if_fail(fw != 0); + + w = GTK_WIDGET(fw); + + /* + * Make sure the widget owns the clipboard property. + */ + if ((win = gdk_selection_owner_get(FONTGRID_CLIPBOARD)) == 0 || + win != w->window) + gdk_selection_owner_set(w->window, FONTGRID_CLIPBOARD, GDK_CURRENT_TIME, + FALSE); + + /* + * Encode the selection as a byte stream for the clipboard. + */ + if ((sel = fontgrid_encode_selection(fw, &bytes)) == 0) + return; + + gdk_property_change(w->window, FONTGRID_CLIPBOARD, FONTGRID_GLYPHLIST, + 8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes); + + /* + * Free the data because the property now has control over it. + */ + g_free(sel); +} + +void +fontgrid_cut_selection(Fontgrid *fw) +{ + gint32 code, start, end; + bdf_glyph_t *gp; + FontgridInternalPageInfo *pi; + FontgridModificationInfo minfo; + FontgridSelectionInfo sinfo; + + g_return_if_fail(fw != 0); + + fontgrid_copy_selection(fw); + + pi = (!fw->unencoded) ? &fw->npage : &fw->upage; + code = pi->sel_start; + + if (bdf_delete_glyphs(fw->font, pi->sel_start, pi->sel_end, + fw->unencoded)) { + start = pi->sel_start; + end = pi->sel_end; + + fontgrid_deselect_all(fw); + Select(code, pi->selmap); + pi->sel_start = pi->sel_end = code; + fontgrid_draw_cells(GTK_WIDGET(fw), start, end, TRUE, TRUE); + + /* + * Set up and emit the modified signal. + */ + minfo.reason = FONTGRID_GLYPHS_DELETED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + + /* + * Set up and call the selection start signal. + */ + gp = (!fw->unencoded) ? + fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + code, TRUE) : + fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, code, TRUE); + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + sinfo.start = sinfo.end = code; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + sinfo.reason = FONTGRID_START_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + } +} + +void +fontgrid_paste_selection(Fontgrid *fw, FontgridPasteType paste_type) +{ + GtkWidget *w = GTK_WIDGET(fw); + GdkWindow *win; + GdkAtom atype; + gint afmt, nitems, unenc, doresize; + gint32 i; + unsigned int ng; + guchar *data; + bdf_font_t *font; + bdf_glyph_t *gp; + bdf_glyphlist_t *gl; + FontgridInternalPageInfo *pi; + bdf_glyphlist_t overflow; + FontgridModificationInfo minfo; + FontgridSelectionInfo sinfo; + + g_return_if_fail(fw != 0); + g_return_if_fail(GTK_WIDGET_REALIZED(w)); + + if ((win = gdk_selection_owner_get(FONTGRID_CLIPBOARD)) == 0) { + gdk_selection_owner_set(w->window, FONTGRID_CLIPBOARD, + GDK_CURRENT_TIME, FALSE); + /* + * Return here because there was no owner of the selection. + */ + return; + } + + doresize = 0; + unenc = fw->unencoded; + + pi = (!unenc) ? &fw->npage : &fw->upage; + + nitems = 0; + (void) gdk_property_get(win, FONTGRID_CLIPBOARD, FONTGRID_GLYPHLIST, + 0, 102400, FALSE, &atype, &afmt, &nitems, &data); + + /* + * Attempt to own the clipboard after getting the value if this widget + * does not own it. + */ + if (win != w->window) + gdk_selection_owner_set(w->window, FONTGRID_CLIPBOARD, GDK_CURRENT_TIME, + FALSE); + + if (nitems > 0) { + font = fw->font; + gl = &fw->clipboard; + + /* + * Convert the encoded selection into a glyph list in the internal + * glyph list clipboard. + */ + fontgrid_decode_selection(fw, data); + + /* + * If the paste is occuring in the unencoded section, make sure the + * paste is appended as opposed to being inserted. Also turn off + * the selected cell before doing the paste. + */ + if (unenc) { + fontgrid_deselect_all(fw); + pi->sel_start = font->unencoded_used; + gl->start = 0; + gl->end = gl->glyphs_used - 1; + } + + /* + * Set the end point of the selection. + */ + pi->sel_end = pi->sel_start + (gl->end - gl->start); + + /* + * First, check to see if pasting the glyphs will exceed the maximum + * encoding value of 0xffff. If some of them do, then transfer the + * extra glyphs to the unencoded area before doing anything else. + * This means that a new glyph list needs to be constructed to do the + * insert into the unencoded area. + */ + if (!unenc && pi->sel_end > 0xffff) { + /* + * Determine if any of the glyphs would actually get encoded after + * 0xffff or if those are all empty glyphs. + */ + for (ng = 0, gp = gl->glyphs; ng < gl->glyphs_used; ng++, gp++) { + if (pi->sel_start + (gp->encoding - gl->start) > 0xffff) + /* + * The glyph list does contain glyphs that will overflow. + */ + break; + } + + if (ng < gl->glyphs_used) { + /* + * Construct a new glyph list containing only the glyphs that + * overflow the 0xffff boundary. There is no need to + * recalculate the bounding box for the new glyph list. Any + * resize will be handled correctly anyway. + */ + (void) memcpy((char *) &overflow.bbx, (char *) &gl->bbx, + sizeof(bdf_bbx_t)); + overflow.bpp = font->bpp; + overflow.glyphs_used = gl->glyphs_used - ng; + overflow.glyphs = gp; + overflow.start = 0; + overflow.end = overflow.glyphs_used - 1; + + /* + * Add the glyphs to the unencoded area. + */ + doresize = bdf_replace_glyphs(font, font->unencoded_used, + &overflow, 1); + } + + /* + * Adjust the glyph list and selection to fit within the 0xffff + * limit before pasting the glyphs into the font. + */ + gl->glyphs_used = ng; + gl->end -= pi->sel_end - 0xffff; + pi->sel_end = 0xffff; + } + + /* + * If the grid is in insert mode, then determine if moving glyphs + * forward from the insert location would cause an overflow. + */ + if (!unenc && + (!fw->overwrite || paste_type == FONTGRID_INSERT_PASTE)) { + doresize += bdf_insert_glyphs(font, pi->sel_start, gl); + /* + * Force a page recalculation to be done so the application can + * update if needed. + */ + fontgrid_goto_page(fw, fw->npage.pageno); + } else if (paste_type == FONTGRID_MERGE_PASTE) + doresize += bdf_merge_glyphs(font, pi->sel_start, gl, unenc); + else + doresize += bdf_replace_glyphs(font, pi->sel_start, gl, unenc); + + /* + * If the paste has more than one glyph, make sure the whole + * range is selected. + */ + for (i = pi->sel_start; i <= pi->sel_end; i++) + Select(i, pi->selmap); + + /* + * If the incoming glyphs changed the font bounding box, then + * determine the new geometry and attempt a resize. + */ + if (doresize) { + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + gtk_widget_queue_resize(w); + } else + fontgrid_draw_cells(w, pi->sel_start, pi->sel_end, TRUE, TRUE); + + /* + * Update the number of pages used. + */ + if (unenc) { + if (fw->noblanks) { + if (font->unencoded_used == 0) + pi->maxpage = 0; + else { + gp = font->unencoded + (font->unencoded_used - 1); + pi->maxpage = gp->encoding / fw->pagesize; + } + } + } else { + if (fw->noblanks) { + if (font->glyphs_used == 0) + pi->maxpage = 0; + else { + gp = font->glyphs + (font->glyphs_used - 1); + pi->maxpage = gp->encoding / fw->pagesize; + } + } + } + + /* + * Set up and call the modified callback. + */ + /* + * Set up and emit the modified signal. + */ + minfo.reason = FONTGRID_GLYPHS_PASTED; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &minfo); + + if (pi->sel_start == pi->sel_end) { + /* + * Set up and call the selection start signal. + */ + gp = (!fw->unencoded) ? + fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + pi->sel_start, TRUE) : + fontgrid_locate_glyph(fw->font->unencoded, + fw->font->unencoded_used, pi->sel_start, + TRUE); + if (gp == 0) { + empty_glyph.encoding = pi->sel_start; + gp = &empty_glyph; + } + + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + } else { + sinfo.glyphs = 0; + sinfo.num_glyphs = 0; + } + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + sinfo.reason = FONTGRID_START_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + + /* + * And last, since the change of the selection owner caused the + * clipboard to lose its data, add the data to it again so + * it can be pasted in some other font editor. + */ + gdk_property_change(w->window, FONTGRID_CLIPBOARD, + FONTGRID_GLYPHLIST, 8, GDK_PROP_MODE_REPLACE, + data, (gint) nitems); + + g_free((char *) data); + } +} + +void +fontgrid_update_metrics(Fontgrid *fw, bdf_metrics_t *metrics) +{ + FontgridModificationInfo mi; + + g_return_if_fail(fw != 0); + g_return_if_fail(fw->font != 0); + + if (bdf_set_font_bbx(fw->font, metrics)) { + /* + * Need to resize. + */ + + /* + * Calculate the cell geometry and the rows and columns. + */ + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + + gtk_widget_queue_resize(GTK_WIDGET(fw)); + + mi.reason = FONTGRID_FONT_METRICS_MODIFIED; + mi.name = 0; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi); + } +} + +void +fontgrid_update_glyph(Fontgrid *fw, bdf_glyph_t *glyph, gboolean unencoded) +{ + FontgridInternalPageInfo *pi; + bdf_glyph_t *gp; + bdf_glyphlist_t gl; + FontgridModificationInfo mi; + + g_return_if_fail(fw != 0); + g_return_if_fail(fw->font != 0); + + gl.bpp = fw->font->bpp; + gl.start = gl.end = glyph->encoding; + gl.glyphs = glyph; + gl.glyphs_used = 1; + memcpy((char *) &gl.bbx, (char *) &glyph->bbx, sizeof(bdf_bbx_t)); + + if (bdf_replace_glyphs(fw->font, glyph->encoding, &gl, unencoded)) { + /* + * The font geometry was changed by the glyph being pasted. + * A resize will be needed. + */ + + /* + * Calculate the cell geometry and the rows and columns. + */ + fontgrid_set_cell_geometry(fw); + fontgrid_set_rows_cols(fw, 0); + + gtk_widget_queue_resize(GTK_WIDGET(fw)); + + mi.reason = FONTGRID_FONT_METRICS_MODIFIED; + mi.name = 0; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi); + } else + /* + * Simply redraw the cells that were modified. + */ + fontgrid_draw_cells(GTK_WIDGET(fw), glyph->encoding, glyph->encoding, + TRUE, TRUE); + + pi = (fw->unencoded) ? &fw->upage : &fw->npage; + if (unencoded) { + if (fw->noblanks) { + if (fw->font->unencoded_used == 0) + pi->maxpage = 0; + else { + gp = fw->font->unencoded + (fw->font->unencoded_used - 1); + pi->maxpage = gp->encoding / fw->pagesize; + } + } + } else { + if (fw->noblanks) { + if (fw->font->glyphs_used == 0) + pi->maxpage = 0; + else { + gp = fw->font->glyphs + (fw->font->glyphs_used - 1); + pi->maxpage = gp->encoding / fw->pagesize; + } + } + } + + mi.reason = FONTGRID_GLYPHS_MODIFIED; + mi.name = 0; + mi.start = mi.end = glyph->encoding; + mi.unencoded = fw->unencoded; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi); +} + +void +fontgrid_update_psf_mappings(Fontgrid *fw, gint32 encoding, + bdf_psf_unimap_t *mappings) +{ + FontgridModificationInfo mi; + + g_return_if_fail(fw != 0); + g_return_if_fail(fw->font != 0); + + if (bdf_replace_mappings(fw->font, encoding, mappings, fw->unencoded)) { + mi.reason = FONTGRID_PSF_MAPPINGS_MODIFIED; + mi.name = 0; + mi.start = mi.end = encoding; + mi.unencoded = fw->unencoded; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[MODIFIED], 0, &mi); + } +} + +gboolean +fontgrid_select_next_glyph(Fontgrid *fw, gint32 code) +{ + bdf_glyph_t *gp; + gint32 pageno; + guint32 count; + FontgridInternalPageInfo *pi; + FontgridSelectionInfo sinfo; + + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(IS_FONTGRID(fw), FALSE); + + if (!fw->unencoded) { + pi = &fw->npage; + gp = (fw->font && fw->font->glyphs_used) ? + (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0; + } else { + pi = &fw->upage; + gp = (fw->font && fw->font->unencoded_used) ? + (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0; + } + + if ((count = fw->count) == 0) + count = 1; + + /* + * Make sure that when on the unencoded pages, the final glyph is + * the limit unlike the encoded pages where the max value is 0xffff. + */ + if ((fw->unencoded && + (gp == 0 || code == gp->encoding)) || + code == 0xffff) { + gdk_beep(); + return FALSE; + } + + if (fw->orientation == GTK_ORIENTATION_VERTICAL) + code += (fw->cell_rows * count); + else + code += count; + + if (fw->unencoded && code > gp->encoding) + code = gp->encoding; + else if (code > 0xffff) + code = 0xffff; + + fontgrid_deselect_all(fw); + + pageno = code / fw->pagesize; + if (pageno != pi->pageno) { + fw->no_sel_callback = TRUE; + fontgrid_goto_page(fw, pageno); + } + + pi->sel_start = pi->sel_end = code; + Select(code, pi->selmap); + fontgrid_draw_cells(GTK_WIDGET(fw), code, code, FALSE, TRUE); + + /* + * Reset the count. + */ + fw->count = 0; + + /* + * Set up and emit the selection start signal. + */ + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + code, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, fw->font->unencoded_used, + code, TRUE); + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + + sinfo.reason = FONTGRID_START_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + return TRUE; +} + +gboolean +fontgrid_select_previous_glyph(Fontgrid *fw, gint32 code) +{ + bdf_glyph_t *gp; + gint32 pageno; + guint32 count; + FontgridInternalPageInfo *pi; + FontgridSelectionInfo sinfo; + + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(IS_FONTGRID(fw), FALSE); + + if (!fw->unencoded) { + pi = &fw->npage; + gp = (fw->font && fw->font->glyphs_used) ? + (fw->font->glyphs + (fw->font->glyphs_used - 1)) : 0; + } else { + pi = &fw->upage; + gp = (fw->font && fw->font->unencoded_used) ? + (fw->font->unencoded + (fw->font->unencoded_used - 1)) : 0; + } + + if ((count = fw->count) == 0) + count = 1; + + if (code == 0) { + gdk_beep(); + return FALSE; + } + + if (fw->orientation == GTK_ORIENTATION_VERTICAL) + code -= (fw->cell_rows * count); + else + code -= count; + + if (code < 0) + code = 0; + + fontgrid_deselect_all(fw); + + pageno = code / fw->pagesize; + if (pageno != pi->pageno) { + fw->no_sel_callback = TRUE; + fontgrid_goto_page(fw, pageno); + } + + pi->sel_start = pi->sel_end = code; + Select(code, pi->selmap); + fontgrid_draw_cells(GTK_WIDGET(fw), code, code, FALSE, TRUE); + + /* + * Reset the count. + */ + fw->count = 0; + + /* + * Set up and emit the selection start signal. + */ + if (!fw->unencoded) + gp = fontgrid_locate_glyph(fw->font->glyphs, fw->font->glyphs_used, + code, TRUE); + else + gp = fontgrid_locate_glyph(fw->font->unencoded, fw->font->unencoded_used, + code, TRUE); + if (gp == 0) { + empty_glyph.encoding = code; + gp = &empty_glyph; + } + sinfo.glyphs = gp; + sinfo.num_glyphs = 1; + sinfo.start = pi->sel_start; + sinfo.end = pi->sel_end; + sinfo.base = fw->base; + sinfo.unencoded = fw->unencoded; + + sinfo.reason = FONTGRID_START_SELECTION; + g_signal_emit(G_OBJECT(fw), fontgrid_signals[SELECTION_START], 0, + &sinfo); + return TRUE; +} diff --git a/fontgrid.h b/fontgrid.h new file mode 100644 index 0000000..cbabe3c --- /dev/null +++ b/fontgrid.h @@ -0,0 +1,359 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_fontgrid +#define _h_fontgrid + +#include +#include +#include +#include "bdfP.h" +#include "gtkcompat.h" + +G_BEGIN_DECLS + +/* + * The macros for accessing various parts of the widget class. + */ +#define FONTGRID(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), fontgrid_get_type(), Fontgrid)) + +#define FONTGRID_CLASS(c) \ + (G_TYPE_CHECK_CLASS_CAST((c), fontgrid_get_type(), FontgridClass)) + +#define IS_FONTGRID(o) G_TYPE_CHECK_INSTANCE_TYPE((o), fontgrid_get_type()) + +#define IS_FONTGRID_CLASS(c) \ + (G_TYPE_CHECK_CLASS_TYPE((c), fontgrid_get_type())) + +#define FONTGRID_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), fontgrid_get_type(), FontgridClass)) + +typedef struct _Fontgrid Fontgrid; +typedef struct _FontgridClass FontgridClass; + +typedef struct { + gint32 minpage; + gint32 maxpage; + gint32 npage; + gint32 ppage; + + gint32 pageno; + gint32 bcode; + gint32 sel_start; + gint32 sel_end; + + guint32 selmap[2048]; +} FontgridInternalPageInfo; + +struct _Fontgrid { + GtkWidget widget; + + bdf_font_t *font; + guint base; + gboolean power2; + gboolean overwrite; + gboolean noblanks; + GtkOrientation orientation; + guint32 point_size; + gint32 spacing; + guint16 *colors; + gint32 initial_glyph; + gint32 bpp; + gint32 hres; + gint32 vres; + + guint16 cell_rows; + guint16 cell_cols; + guint16 border; + guint16 hmargin; + guint16 vmargin; + + /* + * Private variables. + */ + gboolean init; + + gboolean resizing; + gboolean from_keyboard; + gboolean no_sel_callback; + + guint16 label_height; + guint16 cell_width; + guint16 cell_height; + guint16 pagesize; + gint16 xoff; + gint16 yoff; + + gboolean unencoded; + gboolean debug; + + GdkGC *xor_gc; + + GdkPoint *points; + guint32 points_used; + guint32 points_size; + + /* + * For creating RGB glyph images. + */ + guchar *rgb; + guint32 rgb_used; + guint32 rgb_size; + + /* + * Stuff related to the timer between clicks. + */ + guint32 last_click; + guint32 mclick_time; + + /* + * The count accumulated from pressing number keys. + */ + guint32 count; + + /* + * The clipboard used to store selections among other things. + */ + bdf_glyphlist_t clipboard; + + /* + * Page information necessary for paging an drawing. + */ + FontgridInternalPageInfo npage; + FontgridInternalPageInfo upage; +}; + +struct _FontgridClass { + GtkWidgetClass parent_class; + + void (*selection_start)(GtkWidget *, gpointer, gpointer); + void (*selection_extend)(GtkWidget *, gpointer, gpointer); + void (*selection_end)(GtkWidget *, gpointer, gpointer); + void (*page)(GtkWidget *, gpointer, gpointer); + void (*activate)(GtkWidget *, gpointer, gpointer); + void (*modified)(GtkWidget *, gpointer, gpointer); +}; + +/************************************************************************** + * + * Structures used for the API. + * + **************************************************************************/ + +typedef struct { + gint32 previous_page; + gint32 current_page; + gint32 next_page; + gint32 encoded_glyphs; + gint32 unencoded_glyphs; + gboolean unencoded_page; +} FontgridPageInfo; + +typedef struct { + gchar *name; + gchar *comments; + gchar *messages; + glong bits_per_pixel; + glong default_char; + guint16 monowidth; + guint16 spacing; + gulong font_ascent; + gulong font_descent; + gulong resolution_x; + gulong resolution_y; + bdf_bbx_t bbx; +} FontgridFontInfo; + +/* + * Enum representing the callback reasons. + */ +typedef enum { + FONTGRID_START_SELECTION = 0, + FONTGRID_EXTEND_SELECTION, + FONTGRID_END_SELECTION, + FONTGRID_ACTIVATE, + FONTGRID_BASE_CHANGE +} FontgridSelectionReason; + +typedef struct { + FontgridSelectionReason reason; + gint32 start; + gint32 end; + gint base; + bdf_glyph_t *glyphs; + guint32 num_glyphs; + gboolean unencoded; +} FontgridSelectionInfo; + +typedef enum { + FONTGRID_MODIFIED = 0, + FONTGRID_GLYPH_NAMES_MODIFIED, + FONTGRID_NAME_MODIFIED, + FONTGRID_PROPERTIES_MODIFIED, + FONTGRID_COMMENTS_MODIFIED, + FONTGRID_DEVICE_WIDTH_MODIFIED, + FONTGRID_GLYPHS_MODIFIED, + FONTGRID_GLYPHS_DELETED, + FONTGRID_GLYPHS_PASTED, + FONTGRID_FONT_METRICS_MODIFIED, + FONTGRID_PSF_MAPPINGS_MODIFIED +} FontgridModificationReason; + +typedef struct { + FontgridModificationReason reason; + gchar *name; + gint32 start; + gint32 end; + gboolean unencoded; +} FontgridModificationInfo; + +/************************************************************************** + * + * General API + * + **************************************************************************/ + +extern GType fontgrid_get_type(void); +extern GtkWidget *fontgrid_new(const gchar *prop1, ...); +extern GtkWidget *fontgrid_newv(bdf_font_t *font, guint32 pointSize, + gint32 spacing, gboolean skipBlankPages, + gboolean overwriteMode, gboolean powersOfTwo, + guint16 *colorList, + gint32 initialGlyph, guint codeBase, + GtkOrientation orientation, + gint32 bitsPerPixel, + gint32 horizontalResolution, + gint32 verticalResolution, + FontgridPageInfo *initialPageInfo); + +/* + * A routine to force initialization before the widget is realized. This is + * needed to get some fields filled in for apps using the widget. + */ +extern void fontgrid_force_init(Fontgrid *); + +/* + * Selection information. + */ +extern gboolean fontgrid_clipboard_empty(Fontgrid *); +extern gboolean fontgrid_has_selection(Fontgrid *, FontgridSelectionInfo *); + +/* + * Getting and setting widget values. + */ +extern bdf_font_t *fontgrid_get_font(Fontgrid *); +extern void fontgrid_set_font(Fontgrid *, bdf_font_t *, gint32); + +extern gchar *fontgrid_get_font_messages(Fontgrid *); + +extern GtkOrientation fontgrid_get_orientation(Fontgrid *); +extern void fontgrid_set_orientation(Fontgrid *, GtkOrientation); + +extern gboolean fontgrid_viewing_unencoded(Fontgrid *); +extern void fontgrid_switch_encoding_view(Fontgrid *); + +extern guint fontgrid_get_code_base(Fontgrid *); +extern void fontgrid_set_code_base(Fontgrid *, guint); + +extern gchar *fontgrid_get_font_name(Fontgrid *); +extern void fontgrid_set_font_name(Fontgrid *, gchar *); + +extern gboolean fontgrid_get_font_modified(Fontgrid *); +extern void fontgrid_set_font_modified(Fontgrid *, gboolean); + +extern void fontgrid_set_unicode_glyph_names(Fontgrid *, FILE *); +extern void fontgrid_set_adobe_glyph_names(Fontgrid *, FILE *); +extern void fontgrid_set_code_glyph_names(Fontgrid *, gint); + +extern void fontgrid_set_font_property(Fontgrid *, bdf_property_t *); +extern void fontgrid_delete_font_property(Fontgrid *, gchar *); + +extern guint32 fontgrid_get_font_comments(Fontgrid *, gchar **); +extern void fontgrid_set_font_comments(Fontgrid *, gchar *); + +extern gint fontgrid_get_font_spacing(Fontgrid *); +extern void fontgrid_set_font_spacing(Fontgrid *, gint); + +extern guint16 fontgrid_get_font_device_width(Fontgrid *); +extern void fontgrid_set_font_device_width(Fontgrid *, guint16); + +extern void fontgrid_get_font_info(Fontgrid *, FontgridFontInfo *); +extern void fontgrid_set_font_info(Fontgrid *, FontgridFontInfo *); + +/* + * Navigation and page information. + */ +extern void fontgrid_goto_page(Fontgrid *fw, gint32 pageno); +extern void fontgrid_goto_code(Fontgrid *, gint32 pageno); +extern void fontgrid_goto_first_page(Fontgrid *fw); +extern void fontgrid_goto_last_page(Fontgrid *fw); +extern void fontgrid_goto_next_page(Fontgrid *fw); +extern void fontgrid_goto_previous_page(Fontgrid *fw); +extern void fontgrid_get_page_info(Fontgrid *fw, FontgridPageInfo *pageinfo); +extern gboolean fontgrid_select_next_glyph(Fontgrid *fw, gint32 code); +extern gboolean fontgrid_select_previous_glyph(Fontgrid *fw, gint32 code); + +/* + * Font name functions. + */ +extern void fontgrid_make_xlfd_font_name(Fontgrid *); +extern void fontgrid_update_font_name_from_properties(Fontgrid *); +extern void fontgrid_update_properties_from_font_name(Fontgrid *); + +/* + * Graphical operations. + */ +extern void fontgrid_translate_glyphs(Fontgrid *fw, gint16 dx, gint16 dy, + gboolean all_glyphs); +extern void fontgrid_rotate_glyphs(Fontgrid *fw, gint16 degrees, + gboolean all_glyphs); +extern void fontgrid_shear_glyphs(Fontgrid *fw, gint16 degrees, + gboolean all_glyphs); +extern void fontgrid_embolden_glyphs(Fontgrid *fw, gboolean all_glyphs); + +/* + * Clipboard operations. MERGE and OVERLAY are the same operation. + */ +typedef enum { + FONTGRID_NORMAL_PASTE = 0, + FONTGRID_INSERT_PASTE, + FONTGRID_MERGE_PASTE, + FONTGRID_OVERLAY_PASTE +} FontgridPasteType; + +extern void fontgrid_copy_selection(Fontgrid *fw); +extern void fontgrid_cut_selection(Fontgrid *fw); +extern void fontgrid_paste_selection(Fontgrid *fw, + FontgridPasteType paste_type); + +/* + * Metrics, glyph, and PSF mappings updates. + */ +extern void fontgrid_update_metrics(Fontgrid *fw, bdf_metrics_t *metrics); +extern void fontgrid_update_glyph(Fontgrid *fw, bdf_glyph_t *glyph, + gboolean unencoded); + +extern void fontgrid_update_psf_mappings(Fontgrid *fw, gint32 encoding, + bdf_psf_unimap_t *mappings); +G_END_DECLS + +#endif /* _h_fontgrid */ diff --git a/gbdfed.c b/gbdfed.c new file mode 100644 index 0000000..d0343f7 --- /dev/null +++ b/gbdfed.c @@ -0,0 +1,2503 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" +#include "labcon.h" + +/************************************************************************** + * + * Application globals. + * + **************************************************************************/ + +gchar buffer1[BUFSIZ]; +gchar buffer2[BUFSIZ]; +gbdfed_options_t options; + +/* + * The list of editors that exist. + */ +gbdfed_editor_t *editors; +guint num_editors; + +/************************************************************************** + * + * Forward declarations and local variables. + * + **************************************************************************/ + +/* + * These are formats that can appear in the editor for importing/loading and + * exporting fonts. + */ +#define XMBDFED_BDF_FORMAT 1 +#define XMBDFED_CONSOLE_FORMAT 2 +#define XMBDFED_PKGF_FORMAT 3 +#define XMBDFED_FNT_FORMAT 4 +#define XMBDFED_HBF_FORMAT 5 +#define XMBDFED_TTF_FORMAT 6 +#define XMBDFED_PSF_FORMAT 7 +#define XMBDFED_HEX_FORMAT 8 + +/************************************************************************** + * + * Application icons. + * + **************************************************************************/ + +static const gchar *gbdfed_16x16[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #dcdcdc", +"# c #ff0000", +"a c #ffffff", +"b c #000000", +"c c #b2c0dc", +/* pixels */ +"..#.............", +"a.#.a.a.a.a.a.a.", +"..#.............", +"a.#bbba.a.a.a.b.", +"..#.bbb......b..", +"a.#.abbba.a.b.a.", +"..#...bbb..b....", +"a.#.a.abb.b.a.a.", +"..#....b.bb.....", +"a.#.a.b.abbba.a.", +"..#..b....bbb...", +"a.#.b.a.a.abbba.", +"..#b........bbb.", +"################", +"..#.............", +"c.#.c.c.c.c.c.c." +}; + +static const gchar *gbdfed_32x32[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 4 1", +/* colors */ +". c #d9d9d9", +"# c #ffffff", +"a c #ff0000", +"b c #000000", +/* pixels */ +".##a##.##.##.##.##.##.##.##.##.#", +".##a##.##.##.##.##.##.##.##.##.#", +"...a............................", +".##a##.##.##.##.##.##.##.##.##.#", +".##a##.##.##.##.##.##.##.##.##.#", +"...a............................", +".##abb.bb.bb.##.##.##.##.##.bb.#", +".##abb.bb.bb.##.##.##.##.##.bb.#", +"...a............................", +".##a##.bb.bb.bb.##.##.##.bb.##.#", +".##a##.bb.bb.bb.##.##.##.bb.##.#", +"...a............................", +".##a##.##.bb.bb.bb.##.bb.##.##.#", +".##a##.##.bb.bb.bb.##.bb.##.##.#", +"...a............................", +".##a##.##.##.bb.##.bb.##.##.##.#", +".##a##.##.##.bb.##.bb.##.##.##.#", +"...a............................", +".##a##.##.bb.##.bb.bb.bb.##.##.#", +".##a##.##.bb.##.bb.bb.bb.##.##.#", +"...a............................", +".##a##.bb.##.##.##.bb.bb.bb.##.#", +".##a##.bb.##.##.##.bb.bb.bb.##.#", +"...a............................", +".##abb.##.##.##.##.##.bb.bb.bb.#", +".##abb.##.##.##.##.##.bb.bb.bb.#", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +".##a##.##.##.##.##.##.##.##.##.#", +".##a##.##.##.##.##.##.##.##.##.#", +"...a............................", +".##a##.##.##.##.##.##.##.##.##.#", +".##a##.##.##.##.##.##.##.##.##.#" +}; + +static const gchar *gbdfed_48x48[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 4 1", +/* colors */ +". c #dcdcdc", +"# c #ff0000", +"a c #ffffff", +"b c #000000", +/* pixels */ +"....#...........................................", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa..aa.aaa.aaa", +"....#...........................................", +".aaa#bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb", +".aaa#bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb", +".aaa#bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb", +"....#...........................................", +".aaa#aaa.bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.bbb.aaa", +".aaa#aaa.bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.bbb.aaa", +".aaa#aaa.bbb.bbb.bbb.aaa.aaa.aaa.aaa.aaa.bbb.aaa", +"....#...........................................", +".aaa#aaa.aaa.bbb.bbb.bbb.aaa.aaa.aaa.bbb.aaa.aaa", +".aaa#aaa.aaa.bbb.bbb.bbb.aaa.aaa.aaa.bbb.aaa.aaa", +".aaa#aaa.aaa.bbb.bbb.bbb.aaa.aaa.aaa.bbb.aaa.aaa", +"....#...........................................", +".aaa#aaa.aaa.aaa.bbb.bbb.bbb.aaa.bbb.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.bbb.bbb.bbb.aaa.bbb.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.bbb.bbb.bbb.aaa.bbb.aaa.aaa.aaa", +"....#...........................................", +".aaa#aaa.aaa.aaa.aaa.bbb.aaa.bbb.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.bbb.aaa.bbb.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.bbb.aaa.bbb.aaa.aaa.aaa.aaa", +"....#...........................................", +".aaa#aaa.aaa.aaa.bbb.aaa.bbb.bbb.bbb.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.bbb.aaa.bbb.bbb.bbb.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.bbb.aaa.bbb.bbb.bbb.aaa.aaa.aaa", +"....#...........................................", +".aaa#aaa.aaa.bbb.aaa.aaa.aaa.bbb.bbb.bbb.aaa.aaa", +".aaa#aaa.aaa.bbb.aaa.aaa.aaa.bbb.bbb.bbb.aaa.aaa", +".aaa#aaa.aaa.bbb.aaa.aaa.aaa.bbb.bbb.bbb.aaa.aaa", +"....#...........................................", +".aaa#aaa.bbb.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb.aaa", +".aaa#aaa.bbb.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb.aaa", +".aaa#aaa.bbb.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb.aaa", +"....#...........................................", +".aaa#bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb", +".aaa#bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb", +".aaa#bbb.aaa.aaa.aaa.aaa.aaa.aaa.aaa.bbb.bbb.bbb", +"################################################", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +"....#...........................................", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa", +".aaa#aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa.aaa" +}; + +static gboolean icons_set = FALSE; + +/************************************************************************** + * + * GTK signal handlers. + * + **************************************************************************/ + +static gboolean +quit_application(GtkWidget *w, GdkEvent *ev, gpointer data) +{ + guint i, modified; + + for (i = modified = 0; i < num_editors; i++) { + if (fontgrid_get_font_modified(FONTGRID(editors[i].fgrid))) + modified++; + } + + if (modified) { + if (modified == 1) + sprintf(buffer1, "Save Font: One font was modified. Save?"); + else + sprintf(buffer1, "Save Font: %d fonts were modified. Save?", + modified); + if (guiutil_yes_or_no(editors[0].shell, buffer1, TRUE)) { + + /* + * Go through each editor and ask if the font should be saved if + * it has been modified. + */ + for (i = 0; i < num_editors; i++) { + if (fontgrid_get_font_modified(FONTGRID(editors[i].fgrid))) { + /* + * Ask if this font should be saved. + */ + if (editors[i].file) + sprintf(buffer1, "Save Font: Save %s?", editors[i].file); + else + sprintf(buffer1, "Save Font: Save unnamed%d.bdf?", i); + + /* + * Always ask this question using the shell window of the + * first editor so the dialog box doesn't move around. + */ + if (guiutil_yes_or_no(editors[0].shell, buffer1, TRUE)) + guifile_save_as_wait(w, GUINT_TO_POINTER(i)); + } + } + } + } + + /* + * Ask if the user really wants to exit if their preferences specify this + * question should be asked. + */ + if (options.really_exit && + !guiutil_yes_or_no(editors[0].shell, "Really Quit?", TRUE)) + return TRUE; + + /* + * Call all the cleanup routines in case something really needs to be + * deallocated. + */ + guigedit_cleanup(); + guiedit_preference_cleanup(); + guiutil_cursor_cleanup(); + guihelp_cleanup(); + + bdf_cleanup(); + gtk_main_quit(); + exit(0); +} + +static void +show_editor(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + /* + * Nothing seems to force the original window to the top of the stack + * on the screen, but this supposedly does everything necessary. + */ + gtk_widget_show_all(ed->shell); + gtk_window_present(GTK_WINDOW(ed->shell)); +} + +static void +goto_other_page(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + gint32 opage; + FontgridPageInfo pi; + + fontgrid_get_page_info(FONTGRID(ed->fgrid), &pi); + + if (!pi.unencoded_page) { + opage = ed->last_pageno; + ed->last_pageno = pi.current_page; + } else { + opage = ed->last_upageno; + ed->last_upageno = pi.current_page; + } + + if (opage != -1 && opage != pi.current_page) + fontgrid_goto_page(FONTGRID(ed->fgrid), opage); +} + +static void +toggle_encoding_view(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + gchar *label; + GtkLabel *lw; + + if (fontgrid_viewing_unencoded(FONTGRID(ed->fgrid))) + label = "Unencoded"; + else + label = "Encoded"; + lw = GTK_LABEL(GTK_BIN(ed->view_unencoded)->child); + gtk_label_set_text(lw, label); + + fontgrid_switch_encoding_view(FONTGRID(ed->fgrid)); +} + +static void +toggle_view_orientation(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + gchar *label; + GtkLabel *lw; + + if (fontgrid_get_orientation(FONTGRID(ed->fgrid)) == + GTK_ORIENTATION_VERTICAL) { + fontgrid_set_orientation(FONTGRID(ed->fgrid), + GTK_ORIENTATION_HORIZONTAL); + label = "Vertical View"; + } else { + fontgrid_set_orientation(FONTGRID(ed->fgrid), + GTK_ORIENTATION_VERTICAL); + label = "Horizontal View"; + } + lw = GTK_LABEL(GTK_BIN(ed->view_orientation)->child); + gtk_label_set_text(lw, label); +} + +static void +set_code_base(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + gint base; + + if (w == ed->view_oct) + base = 8; + else if (w == ed->view_dec) + base = 10; + else + base = 16; + + fontgrid_set_code_base(FONTGRID(ed->fgrid), base); + + /* + * Make sure the font info editor is updated when the code base + * changes. + */ + guiedit_update_code_base(ed); + guigedit_set_code_base(base); +} + +static void +page_change(GtkWidget *w, gpointer pinfo, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + FontgridPageInfo *pi = (FontgridPageInfo *) pinfo; + gchar *label; + GtkLabel *lw; + + if (pi->previous_page < 0) { + gtk_widget_set_sensitive(ed->prev, FALSE); + gtk_widget_set_sensitive(ed->first, FALSE); + } else { + gtk_widget_set_sensitive(ed->prev, TRUE); + gtk_widget_set_sensitive(ed->first, TRUE); + } + + if (pi->next_page < 0) { + gtk_widget_set_sensitive(ed->next, FALSE); + gtk_widget_set_sensitive(ed->last, FALSE); + } else { + gtk_widget_set_sensitive(ed->next, TRUE); + gtk_widget_set_sensitive(ed->last, TRUE); + } + + /* + * Update the page number field with the current page. + */ + sprintf(buffer1, "%d", pi->current_page); + gtk_entry_set_text(GTK_ENTRY(ed->pageno), buffer1); + gtk_editable_set_position(GTK_EDITABLE(ed->pageno), -1); + + /* + * Finally, modify the label on the Encoded/Unencoded view menu item. + */ + label = (pi->unencoded_page) ? "Encoded" : "Unencoded"; + lw = GTK_LABEL(GTK_BIN(ed->view_unencoded)->child); + gtk_label_set_text(lw, label); +} + +static void +first_page(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + + fontgrid_goto_first_page(FONTGRID(ed->fgrid)); + + /* + * Force the focus back to the Fontgrid. + */ + gtk_widget_grab_focus(ed->fgrid); +} + +static void +previous_page(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + + fontgrid_goto_previous_page(FONTGRID(ed->fgrid)); + + /* + * Force the focus back to the Fontgrid. + */ + gtk_widget_grab_focus(ed->fgrid); +} + +static void +next_page(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + + fontgrid_goto_next_page(FONTGRID(ed->fgrid)); + + /* + * Force the focus back to the Fontgrid. + */ + gtk_widget_grab_focus(ed->fgrid); +} + +static void +last_page(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + + fontgrid_goto_last_page(FONTGRID(ed->fgrid)); + + /* + * Force the focus back to the Fontgrid. + */ + gtk_widget_grab_focus(ed->fgrid); +} + +static void +goto_page_or_code(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + const gchar *text; + gint32 code; + FontgridPageInfo pi; + + fontgrid_get_page_info(FONTGRID(ed->fgrid), &pi); + + if (!pi.unencoded_page) + ed->last_pageno = pi.current_page; + else + ed->last_upageno = pi.current_page; + + if (w == ed->pageno) { + text = gtk_entry_get_text(GTK_ENTRY(ed->pageno)); + fontgrid_goto_page(FONTGRID(ed->fgrid), + _bdf_atol((char *) text, 0, 10)); + } else { + text = gtk_entry_get_text(GTK_ENTRY(ed->charno)); + code = _bdf_atol((char *) text, 0, + fontgrid_get_code_base(FONTGRID(ed->fgrid))); + fontgrid_goto_code(FONTGRID(ed->fgrid), code); + } +} + +static void +update_selection_info(GtkWidget *w, gpointer sinfo, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + FontgridSelectionInfo *si = (FontgridSelectionInfo *) sinfo; + short as, ds, rt, lt; + gint32 start, end; + guint b1 = 0, b2 = 0, b3, b4; + + as = ds = rt = lt = 0; + if (si->start == si->end) { + if (si->num_glyphs != 0 && si->glyphs != 0) { + b1 = (si->start >> 8) & 0xff; + b2 = si->start & 0xff; + + as = si->glyphs->bbx.ascent; + ds = si->glyphs->bbx.descent; + lt = si->glyphs->bbx.x_offset; + rt = si->glyphs->bbx.width + lt; + if (si->glyphs->name != 0) + (void) strcpy(buffer1, si->glyphs->name); + else + sprintf(buffer1, "char%d", si->glyphs->encoding); + + /* + * If the glyph test dialog is active, send it the glyph if this + * is an end selection event. + */ + if (si->reason == FONTGRID_END_SELECTION && glyphtest != 0) + glyphtest_add_glyph(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid)), + si->glyphs); + } else + sprintf(buffer1, "char%d", si->start); + + switch (si->base) { + case 8: + sprintf(buffer2, "\"%s\" %o (%o, %o)", buffer1, + si->start, b1, b2); + break; + case 10: + sprintf(buffer2, "\"%s\" %d (%d, %d)", buffer1, + si->start, b1, b2); + break; + case 16: + sprintf(buffer2, "\"%s\" %04X (%02X, %02X)", buffer1, + si->start, b1, b2); + break; + } + + } else { + /* + * A range of glyphs has been selected. + */ + if (si->end < si->start) { + start = si->end; + end = si->start; + } else { + start = si->start; + end = si->end; + } + b1 = (start >> 8) & 0xff; + b2 = start & 0xff; + b3 = (end >> 8) & 0xff; + b4 = end & 0xff; + + switch (si->base) { + case 8: + sprintf(buffer2, "Selection %o (%o, %o) - %o (%o, %o)", + start, b1, b2, end, b3, b4); + break; + case 10: + sprintf(buffer2, "Selection %d (%d, %d) - %d (%d, %d)", + start, b1, b2, end, b3, b4); + break; + case 16: + sprintf(buffer2, "Selection %04X (%02X, %02X) - %04X (%02X, %02X)", + start, b1, b2, end, b3, b4); + break; + } + } + + /* + * Update the glyph info label. + */ + gtk_label_set_text(GTK_LABEL(ed->charinfo), buffer2); + + /* + * Update the metrics label. + */ + sprintf(buffer1, "ascent %hd descent %hd right %hd left %hd", + as, ds, rt, lt); + gtk_label_set_text(GTK_LABEL(ed->metrics), buffer1); + + switch (si->reason) { + case FONTGRID_START_SELECTION: + break; + case FONTGRID_EXTEND_SELECTION: + break; + case FONTGRID_END_SELECTION: + break; + case FONTGRID_ACTIVATE: + guigedit_edit_glyph(ed, si); + break; + case FONTGRID_BASE_CHANGE: + break; + } +} + +static void +handle_modified_signal(GtkWidget *w, gpointer minfo, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + gchar *prgname = g_get_prgname(); + FontgridModificationInfo *mi; + + mi = (FontgridModificationInfo *) minfo; + + /* + * Check to see what change was made so updates can be handled + * appropriately. + */ + if (mi->reason == FONTGRID_NAME_MODIFIED) + gtk_entry_set_text(GTK_ENTRY(ed->fontname), mi->name); + else if (mi->reason == FONTGRID_PROPERTIES_MODIFIED) + /* + * Make sure the font info editing dialog knows that the list of + * font properties changed. + */ + guiedit_update_font_properties(ed); + else if (mi->reason == FONTGRID_GLYPHS_DELETED || + mi->reason == FONTGRID_GLYPHS_PASTED) + guiedit_update_font_details(ed); + else if ((mi->reason == FONTGRID_DEVICE_WIDTH_MODIFIED || + mi->reason == FONTGRID_GLYPHS_MODIFIED) && glyphtest != 0) + /* + * Update the glyph test widget with the new device width. + */ + glyphtest_update_device_width(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + /* + * Update the title. + */ + if (ed->file == 0) + sprintf(buffer1, "%s - (unnamed%d) [modified]", prgname, ed->id); + else + sprintf(buffer1, "%s - %s [modified]", prgname, ed->file); + + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); +} + +static void +view_font_messages(GtkWidget *w, gpointer editor) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(editor); + GtkWidget *dialog, *frame, *button, *label, *vbox, *sw; + GtkTextBuffer *text; + gchar *msgs; + + if (ed->messages_dialog == 0) { + /* + * Special case of no messages and the menu item hasn't been + * checked for sensitivity yet. + */ + if (fontgrid_get_font_messages(FONTGRID(ed->fgrid)) == 0) + return; + + dialog = ed->messages_dialog = gtk_dialog_new(); + (void) g_signal_connect(G_OBJECT(dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + frame = gtk_frame_new(0); + label = ed->messages_label = gtk_label_new(""); + gtk_container_add(GTK_CONTAINER(frame), label); + + vbox = GTK_DIALOG(dialog)->vbox; + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, + GTK_POLICY_ALWAYS); + gtk_box_pack_start(GTK_BOX(vbox), sw, FALSE, FALSE, 0); + + /* + * Add the text widget. + */ + text = gtk_text_buffer_new(NULL); + ed->messages_text = gtk_text_view_new_with_buffer(text); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ed->messages_text), + FALSE); + gtk_widget_set_size_request(ed->messages_text, 500, 200); + + gtk_text_view_set_editable(GTK_TEXT_VIEW(ed->messages_text), FALSE); + gtk_container_add(GTK_CONTAINER(sw), ed->messages_text); + + button = gtk_button_new_with_label("Close"); + (void) g_signal_connect_object(G_OBJECT(button), "clicked", + G_CALLBACK(gtk_widget_hide), + (gpointer) dialog, + G_CONNECT_SWAPPED); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), + button); + + gtk_widget_show_all(GTK_DIALOG(ed->messages_dialog)->vbox); + gtk_widget_show_all(GTK_DIALOG(ed->messages_dialog)->action_area); + } else + text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->messages_text)); + + /* + * Update the label with the font name in case it changed since the last + * time the messages were shown. + */ + if (ed->file != 0) + sprintf(buffer1, "%s: Messages", ed->file); + else + sprintf(buffer1, "unnamed%d.bdf: Messages", ed->id); + gtk_label_set_text(GTK_LABEL(ed->messages_label), buffer1); + + /* + * Now change the text itself. + */ + if ((msgs = fontgrid_get_font_messages(FONTGRID(ed->fgrid))) == 0) + msgs = ""; + gtk_text_buffer_set_text(text, msgs, -1); + + guiutil_show_dialog_centered(ed->messages_dialog, ed->shell); +} + +/************************************************************************** + * + * Menu construction. + * + **************************************************************************/ + +static GtkWidget * +make_accel_menu_item(GtkWidget *menu, const gchar *text, const gchar *accel, + GtkAccelGroup *ag) +{ + GtkWidget *mi; + guint key; + GdkModifierType mods; + + mi = gtk_menu_item_new_with_mnemonic(text); + + gtk_accelerator_parse(accel, &key, &mods); + gtk_widget_add_accelerator(mi, "activate", ag, key, mods, + GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); + + return mi; +} + +/* + * Handle all the menu checking needed to enable or disable menu items on + * all the menus. + */ +static gint +menu_popup(GtkWidget *w, GdkEvent *event, gpointer data) +{ + gbdfed_editor_t *ed; + bdf_font_t *font; + GtkWidget *mitem; + GList *kids, *label; + gboolean flag; + guint i; + + /* + * Get a pointer to the editor. + */ + ed = editors + GPOINTER_TO_UINT(data); + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + if (event->type == GDK_MAP) { + if (w == ed->file_menu) { + if (font && font->modified) + gtk_widget_set_sensitive(ed->file_save, TRUE); + else + gtk_widget_set_sensitive(ed->file_save, FALSE); + + if (font && font->glyphs_used > 0) + gtk_widget_set_sensitive(ed->file_export, TRUE); + else + gtk_widget_set_sensitive(ed->file_export, FALSE); + } else if (w == ed->edit_menu) { + /* + * If the fontgrid clipboard is empty, then disable the paste, + * overlay, and insert menu options. + */ + flag = fontgrid_clipboard_empty(FONTGRID(ed->fgrid)) ? + FALSE : TRUE; + gtk_widget_set_sensitive(ed->edit_paste, flag); + gtk_widget_set_sensitive(ed->edit_overlay, flag); + gtk_widget_set_sensitive(ed->edit_insert, flag); + + /* + * If there is no selection, disable the cut and copy menu + * options. + */ + flag = fontgrid_has_selection(FONTGRID(ed->fgrid), 0) ? + TRUE : FALSE; + gtk_widget_set_sensitive(ed->edit_cut, flag); + gtk_widget_set_sensitive(ed->edit_copy, flag); + + flag = (font && font->glyphs_used > 0) ? TRUE : FALSE; + + /* + * Disable glyph rename when viewing unecoded glyphs because their + * encoding values are not actual. + */ + if (fontgrid_viewing_unencoded(FONTGRID(ed->fgrid)) || !flag) + gtk_widget_set_sensitive(ed->edit_rename_glyphs, FALSE); + else + gtk_widget_set_sensitive(ed->edit_rename_glyphs, TRUE); + + gtk_widget_set_sensitive(ed->edit_test_glyphs, flag); + } else if (w == ed->edit_rename_menu) { + if (options.adobe_name_file != 0) + gtk_widget_set_sensitive(ed->edit_adobe_names, TRUE); + else + gtk_widget_set_sensitive(ed->edit_adobe_names, FALSE); + if (options.unicode_name_file != 0) + gtk_widget_set_sensitive(ed->edit_unicode_names, TRUE); + else + gtk_widget_set_sensitive(ed->edit_unicode_names, FALSE); + } else if (w == ed->name_submenu) { + flag = (!font || bdf_has_xlfd_name(font)) ? FALSE : TRUE; + gtk_widget_set_sensitive(ed->edit_make_xlfd, flag); + } else if (w == ed->view_menu) { + if (fontgrid_get_font_messages(FONTGRID(ed->fgrid))) + gtk_widget_set_sensitive(ed->view_messages, TRUE); + else + gtk_widget_set_sensitive(ed->view_messages, FALSE); + } else if (w == ed->win_menu) { + /* + * Go through and update the file names that might have changed + * since the last time this menu popped up. + */ + for (i = 0, kids = GTK_MENU(w)->menu_shell.children; kids != 0; + kids = kids->next, i++) { + if (editors[i].file == 0) + sprintf(buffer1, "(unnamed%d)", i); + else + strcpy(buffer1, editors[i].file); + label = gtk_container_get_children(GTK_CONTAINER(kids->data)); + gtk_label_set_text(GTK_LABEL(label->data), buffer1); + } + + /* + * Add any new editors that were created since the last time this + * menu was shown. + */ + for (; i < num_editors; i++) { + if (editors[i].file == 0) + sprintf(buffer1, "(unnamed%d)", editors[i].id); + else + strcpy(buffer1, editors[i].file); + + mitem = gtk_menu_item_new_with_label(buffer1); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(show_editor), + GUINT_TO_POINTER(i)); + gtk_menu_shell_append(GTK_MENU_SHELL(ed->win_menu), mitem); + gtk_widget_show(mitem); + } + + /* + * Disable the item for this editor. + */ + gtk_widget_set_sensitive(ed->win_menu_item, FALSE); + } + } else if (event->type == GDK_UNMAP) { + /* + * Enable everything again, so it doesn't get forgotten. + */ + gtk_widget_set_sensitive(ed->file_save, TRUE); + gtk_widget_set_sensitive(ed->file_export, TRUE); + gtk_widget_set_sensitive(ed->edit_paste, TRUE); + gtk_widget_set_sensitive(ed->edit_overlay, TRUE); + gtk_widget_set_sensitive(ed->edit_insert, TRUE); + gtk_widget_set_sensitive(ed->edit_cut, TRUE); + gtk_widget_set_sensitive(ed->edit_copy, TRUE); + gtk_widget_set_sensitive(ed->edit_rename_glyphs, TRUE); + gtk_widget_set_sensitive(ed->edit_adobe_names, TRUE); + gtk_widget_set_sensitive(ed->edit_unicode_names, TRUE); + gtk_widget_set_sensitive(ed->edit_test_glyphs, TRUE); + gtk_widget_set_sensitive(ed->edit_make_xlfd, TRUE); + gtk_widget_set_sensitive(ed->win_menu_item, TRUE); + gtk_widget_set_sensitive(ed->view_messages, TRUE); + } + + return FALSE; +} + +#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10) +static void +open_recent_font(GtkWidget *w, gpointer data) +{ + gchar *p, *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(w)); + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (uri == NULL) + return; + + /* + * Skip the URI prefix to get to the path. The URI's are strictly local + * at the moment. + */ + for (p = uri; *p && *p != ':'; p++); + if (*p != ':') + return; + p++; + if (*p == '/' && *(p + 1) == '/') + p += 2; + guifile_load_bdf_font(ed, (const gchar *) p); + g_free(uri); +} + +static GtkWidget * +make_recent_menu(guint ed_id) +{ + GtkWidget *menu; + GtkRecentFilter *filter; + GtkRecentManager *manager; + + manager = gtk_recent_manager_get_default(); + menu = gtk_recent_chooser_menu_new_for_manager(manager); + gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(menu), 10); + gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(menu), + GTK_RECENT_SORT_MRU); + gtk_recent_chooser_set_show_tips(GTK_RECENT_CHOOSER(menu), TRUE); + gtk_recent_chooser_menu_set_show_numbers(GTK_RECENT_CHOOSER_MENU(menu), + TRUE); + gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(menu), TRUE); + filter = gtk_recent_filter_new(); + gtk_recent_filter_add_pattern(filter, "*.[Bb][Dd][Ff]"); + gtk_recent_chooser_set_filter(GTK_RECENT_CHOOSER(menu), filter); + + (void) g_signal_connect(G_OBJECT(menu), "item_activated", + G_CALLBACK(open_recent_font), + GUINT_TO_POINTER(ed_id)); + + return menu; +} +#endif + +static GtkWidget * +make_file_menu(gbdfed_editor_t *ed, GtkWidget *menubar) +{ + GtkWidget *file, *menu, *submenu, *mitem, *sep; + + /* + * Create the File menu. + */ + file = gtk_menu_item_new_with_mnemonic("_File"); + + ed->file_menu = menu = gtk_menu_new(); + (void) g_signal_connect(G_OBJECT(menu), "map_event", + G_CALLBACK(menu_popup), + GUINT_TO_POINTER(ed->id)); + + mitem = make_accel_menu_item(menu, "_New", "N", ed->ag); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guifile_new_editor), 0); + + mitem = make_accel_menu_item(menu, "_Open", "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", "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...", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", + "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", + "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", "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", "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", "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", + "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", "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", "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", "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", "Q", ed->ag); + else + ed->view_orientation = mitem = + make_accel_menu_item(menu, "Vertical View", "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", "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", "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", "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", "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", "B", + ed->ag); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guiops_show_embolden), + GUINT_TO_POINTER(ed->id)); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(ops), menu); + + return ops; +} + +static GtkWidget * +make_windows_menu(gbdfed_editor_t *ed, GtkWidget *menubar) +{ + GtkWidget *win, *menu, *mitem; + guint i; + + win = gtk_menu_item_new_with_mnemonic("_Windows"); + + ed->win_menu = menu = gtk_menu_new(); + (void) g_signal_connect(G_OBJECT(menu), "map_event", + G_CALLBACK(menu_popup), + GUINT_TO_POINTER(ed->id)); + + for (i = 0; i < num_editors; i++) { + if (editors[i].file == 0) + sprintf(buffer1, "(unnamed%d)", editors[i].id); + else + strcpy(buffer1, editors[i].file); + + ed->win_menu_item = mitem = gtk_menu_item_new_with_label(buffer1); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(show_editor), + GUINT_TO_POINTER(ed->id)); + gtk_menu_shell_append(GTK_MENU_SHELL(ed->win_menu), mitem); + } + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(win), menu); + + return win; +} + +static GtkWidget * +make_help_menu(gbdfed_editor_t *ed, GtkWidget *menubar) +{ + GtkWidget *help, *menu, *mitem, *sep; + + /* + * Create the Edit menu. + */ + help = gtk_menu_item_new_with_mnemonic("_Help"); + gtk_menu_item_set_right_justified(GTK_MENU_ITEM(help), TRUE); + + menu = gtk_menu_new(); + + mitem = gtk_menu_item_new_with_mnemonic("The _Program"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_PROGRAM)); + + mitem = gtk_menu_item_new_with_mnemonic("_Font Grid"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_FONTGRID)); + + mitem = gtk_menu_item_new_with_mnemonic("_Glyph Editor"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_GLYPH_EDITOR)); + + mitem = gtk_menu_item_new_with_mnemonic("_Configuration File"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_CONFIG_FILE)); + + mitem = gtk_menu_item_new_with_mnemonic("P_references Dialog"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_PREFERENCES)); + + /* + * Separator. + */ + sep = gtk_menu_item_new(); + gtk_widget_set_sensitive(sep, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); + + mitem = gtk_menu_item_new_with_mnemonic("_Windows Font Notes"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_FNT)); + + mitem = gtk_menu_item_new_with_mnemonic("_OpenType Font Notes"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_OTF)); + + mitem = gtk_menu_item_new_with_mnemonic("P_SF Font Notes"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_PSF)); + + mitem = gtk_menu_item_new_with_mnemonic("_HEX Font Notes"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_HEX)); + + /* + * Separator. + */ + sep = gtk_menu_item_new(); + gtk_widget_set_sensitive(sep, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); + + mitem = gtk_menu_item_new_with_mnemonic("C_olor Notes"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_COLOR)); + + /* + * Separator. + */ + sep = gtk_menu_item_new(); + gtk_widget_set_sensitive(sep, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); + + mitem = gtk_menu_item_new_with_mnemonic("T_ips"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_TIPS)); + + /* + * Separator. + */ + sep = gtk_menu_item_new(); + gtk_widget_set_sensitive(sep, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); + + sprintf(buffer1, "_About %s", g_get_prgname()); + mitem = gtk_menu_item_new_with_mnemonic(buffer1); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + (void) g_signal_connect(G_OBJECT(mitem), "activate", + G_CALLBACK(guihelp_show_help), + GINT_TO_POINTER(GUIHELP_ABOUT)); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), menu); + + return help; +} + +#define UPDMSG "Font Name: XLFD name has been modified.\nDo you want to update the font properties from the name?" + +static void +update_font_name(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + bdf_font_t *font; + gchar *name; + + name = (gchar *) gtk_entry_get_text(GTK_ENTRY(ed->fontname)); + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + if (font && strcmp(name, font->name) == 0) + /* + * There was no actual change to the name. + */ + return; + + fontgrid_set_font_name(FONTGRID(ed->fgrid), name); + + /* + * If the new name is an XLFD name, then offer to update the + * font properties from the name. + */ + if (font && bdf_has_xlfd_name(font)) { + if (guiutil_yes_or_no(ed->shell, UPDMSG, TRUE)) { + fontgrid_update_properties_from_font_name(FONTGRID(ed->fgrid)); + + /* + * If the font info dialog is up, make sure it is updated with + * the font property changes. + */ + guiedit_update_font_properties(ed); + } + } +} + +guint +gbdfed_make_editor(gchar *filename, gboolean cmdline) +{ + FILE *in; + GtkWidget *mb, *mitem, *table; + GtkWidget *vbox, *hbox, *bbox, *frame; + gbdfed_editor_t *ed; + bdf_font_t *font; + gchar *path; + GList *icon_list = 0; + FontgridPageInfo pageinfo; + + font = 0; + + /* + * Attempt to load the specified font before doing anything else. + */ + if (filename != 0) { + /* + * Change code to put up an error dialog if the font won't load. + */ + if ((in = fopen(filename, "r")) == 0) { + fprintf(stderr, "%s: unable to open BDF font '%s'.\n", + g_get_prgname(), filename); + if (num_editors > 0 && !cmdline) + return ~0; + filename = 0; + } else { + font = bdf_load_font(in, &options.font_opts, 0, 0); + fclose(in); + if (font == 0) + fprintf(stderr, "%s: problem loading BDF font '%s'.\n", + g_get_prgname(), filename); + } + } + + /* + * Create the icon list if it hasn't already been created. + */ + if (icons_set == FALSE) { + /* + * Create the 16x16 icon. + */ + icon_list = g_list_append(icon_list, + gdk_pixbuf_new_from_xpm_data(gbdfed_48x48)); + icon_list = g_list_append(icon_list, + gdk_pixbuf_new_from_xpm_data(gbdfed_32x32)); + icon_list = g_list_append(icon_list, + gdk_pixbuf_new_from_xpm_data(gbdfed_16x16)); + + gtk_window_set_default_icon_list(icon_list); + + icons_set = TRUE; + } + + /* + * Create a new editor structure. + */ + if (num_editors == 0) + editors = g_malloc(sizeof(gbdfed_editor_t)); + else + editors = g_realloc(editors, + sizeof(gbdfed_editor_t) * (num_editors + 1)); + + ed = editors + num_editors; + (void) memset(ed, 0, sizeof(gbdfed_editor_t)); + ed->id = num_editors++; + + /* + * Construct the path and filename from the one passed. This makes copies + * of the strings. + */ + if (font != 0 && filename != 0) { + if (filename[0] != G_DIR_SEPARATOR) { + path = g_get_current_dir(); + sprintf(buffer1, "%s%c%s", path, G_DIR_SEPARATOR, filename); + g_free(path); + } else + strcpy(buffer1, filename); + + ed->file = g_path_get_basename(buffer1); + ed->path = g_path_get_dirname(buffer1); + } + + /* + * Create the top level window for the editor. + */ + ed->shell = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + /* + * This is a bit risky, but it allows the shell to shrink to fit new + * fonts when they are added. + */ + gtk_window_set_resizable(GTK_WINDOW(ed->shell), TRUE); + + if (ed->id == 0) { + (void) g_signal_connect(G_OBJECT(ed->shell), "destroy_event", + G_CALLBACK(quit_application), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->shell), "delete_event", + G_CALLBACK(quit_application), + GUINT_TO_POINTER(ed->id)); + } else { + (void) g_signal_connect(G_OBJECT(ed->shell), "destroy_event", + G_CALLBACK(gtk_widget_hide), 0); + (void) g_signal_connect(G_OBJECT(ed->shell), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + } + + /* + * Determine the name for the window. + */ + if (filename == 0) + sprintf(buffer1, "%s - (unnamed%d)", g_get_prgname(), ed->id); + else { + if (font && font->modified) + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file); + else + sprintf(buffer1, "%s - %s", g_get_prgname(), ed->file); + } + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Create the vertical box that will contain the widgets. + */ + table = gtk_table_new(5, 1, FALSE); + gtk_container_add(GTK_CONTAINER(ed->shell), table); + + ed->ag = gtk_accel_group_new(); + + mb = gtk_menu_bar_new(); + gtk_table_attach(GTK_TABLE(table), mb, 0, 1, 0, 1, GTK_FILL, GTK_FILL, + 0, 0); + + /* + * Create the File menu. + */ + mitem = make_file_menu(ed, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + /* + * Create the Edit menu. + */ + mitem = make_edit_menu(ed, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + /* + * Create the View menu. + */ + mitem = make_view_menu(ed, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + /* + * Create the Operations menu. + */ + mitem = make_ops_menu(ed, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + /* + * Create the Windows menu. + */ + mitem = make_windows_menu(ed, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + /* + * Create the Help menu. + */ + mitem = make_help_menu(ed, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + /* + * Attach the accelerators to the editor. + */ + gtk_window_add_accel_group(GTK_WINDOW(ed->shell), ed->ag); + + /* + * 1. Add the font name widget. + */ + frame = gtk_frame_new(0); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); + gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 1, 2, GTK_FILL, GTK_FILL, + 0, 0); + + ed->fontname = gtk_widget_new(gtk_entry_get_type(), + "max_length", 128, NULL); + mitem = labcon_new_label_defaults("Font:", ed->fontname, 0); + gtk_container_add(GTK_CONTAINER(frame), mitem); + g_signal_connect(G_OBJECT(ed->fontname), "activate", + G_CALLBACK(update_font_name), GUINT_TO_POINTER(ed->id)); + + /* + * 2. Add the glyph information widgets. + */ + frame = gtk_frame_new(0); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); + gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 2, 3, GTK_FILL, GTK_FILL, + 0, 0); + + vbox = gtk_vbox_new(TRUE, 0); + ed->charinfo = gtk_label_new("\"None\" (0000) (00,00)"); + ed->metrics = gtk_label_new("ascent 0 descent 0 right 0 left 0"); + gtk_misc_set_alignment(GTK_MISC(ed->charinfo), 0.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(ed->metrics), 0.0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), ed->charinfo, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), ed->metrics, TRUE, TRUE, 0); + + mitem = labcon_new_label_defaults("Glyph:", vbox, mitem); + gtk_container_add(GTK_CONTAINER(frame), mitem); + + frame = gtk_frame_new(0); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); + gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 3, 4, GTK_FILL, GTK_FILL, + 0, 0); + + hbox = gtk_hbox_new(FALSE, 2); + + bbox = gtk_hbox_new(FALSE, 0); + + ed->first = gtk_button_new_with_label("First Page"); + (void) g_signal_connect(G_OBJECT(ed->first), "released", + G_CALLBACK(first_page), + GUINT_TO_POINTER(ed->id)); + + ed->prev = gtk_button_new_with_label("Previous Page"); + (void) g_signal_connect(G_OBJECT(ed->prev), "released", + G_CALLBACK(previous_page), + GUINT_TO_POINTER(ed->id)); + + ed->next = gtk_button_new_with_label("Next Page"); + (void) g_signal_connect(G_OBJECT(ed->next), "released", + G_CALLBACK(next_page), + GUINT_TO_POINTER(ed->id)); + + ed->last = gtk_button_new_with_label("Last Page"); + (void) g_signal_connect(G_OBJECT(ed->last), "released", + G_CALLBACK(last_page), + GUINT_TO_POINTER(ed->id)); + + gtk_box_pack_start(GTK_BOX(bbox), ed->first, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(bbox), ed->prev, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(bbox), ed->next, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(bbox), ed->last, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0); + + mitem = gtk_label_new("Page:"); + ed->pageno = gtk_widget_new(gtk_entry_get_type(), + "max_length", 6, + "width-request", 70, + NULL); + (void) g_signal_connect(G_OBJECT(ed->pageno), "activate", + G_CALLBACK(goto_page_or_code), + GUINT_TO_POINTER(ed->id)); + + gtk_box_pack_start(GTK_BOX(hbox), mitem, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), ed->pageno, FALSE, FALSE, 0); + + mitem = gtk_label_new("Code:"); + ed->charno = gtk_widget_new(gtk_entry_get_type(), + "max_length", 6, + "width-request", 70, + NULL); + (void) g_signal_connect(G_OBJECT(ed->charno), "activate", + G_CALLBACK(goto_page_or_code), + GUINT_TO_POINTER(ed->id)); + + gtk_box_pack_start(GTK_BOX(hbox), mitem, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), ed->charno, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + /* + * Now add the font grid itself. + */ + ed->fgrid = fontgrid_newv(font, + options.font_opts.point_size, + options.font_opts.font_spacing, + options.no_blanks, + options.overwrite_mode, + options.power2, + options.colors, + options.initial_glyph, + options.code_base, + options.orientation, + options.font_opts.bits_per_pixel, + options.font_opts.resolution_x, + options.font_opts.resolution_y, + &pageinfo); + (void) g_signal_connect(G_OBJECT(ed->fgrid), "turn_to_page", + G_CALLBACK(page_change), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->fgrid), "selection-start", + G_CALLBACK(update_selection_info), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->fgrid), "selection-extend", + G_CALLBACK(update_selection_info), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->fgrid), "selection-end", + G_CALLBACK(update_selection_info), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->fgrid), "activate", + G_CALLBACK(update_selection_info), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->fgrid), "modified", + G_CALLBACK(handle_modified_signal), + GUINT_TO_POINTER(ed->id)); + gtk_table_attach(GTK_TABLE(table), ed->fgrid, 0, 1, 4, 5, + GTK_FILL|GTK_EXPAND|GTK_SHRINK, + GTK_FILL|GTK_EXPAND|GTK_SHRINK, 0, 0); + + /* + * Set up the initial page information. + */ + page_change(ed->fgrid, &pageinfo, GUINT_TO_POINTER(ed->id)); + + /* + * Show the editor if it the first one created or is not being created + * from the command line. + */ + if (ed->id == 0) + gtk_widget_show_all(ed->shell); + + { + /* + * Get the initial selection info to set up the glyph info labels. + * + * NOTE: This has to be done here because for some reason labels + * do not display properly if changed before the editor is fully + * realized. + */ + FontgridSelectionInfo sinfo; + if (fontgrid_has_selection(FONTGRID(ed->fgrid), &sinfo) == TRUE) + update_selection_info(ed->fgrid, (gpointer) &sinfo, + GUINT_TO_POINTER(ed->id)); + } + + /* + * Set the font name in the entry field if the font exists. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); + + /* + * Change the focus to the Fontgrid. + */ + gtk_widget_grab_focus(ed->fgrid); + + return ed->id; +} + +static int +handle_unknown_options(bdf_options_t *opts, char **params, + unsigned int nparams, void *client_data) +{ + gint idx; + gbdfed_options_t *op; + + op = (gbdfed_options_t *) client_data; + + if (nparams == 0) + return 0; + + /* + * Handle the 2 bits per pixel color list. + */ + if (strncmp(params[0], "2bpp_grays", 10) == 0) { + for (idx = 1; idx < 5; idx++) + op->colors[idx] = (unsigned short) _bdf_atos(params[idx], 0, 10); + return 1; + } + + /* + * Handle the 4 bits per pixel color list. + */ + if (strncmp(params[0], "4bpp_grays", 10) == 0) { + for (idx = 1; idx < 17; idx++) + op->colors[idx-1+4] = (unsigned short) _bdf_atos(params[idx], 0, 10); + return 1; + } + + if (params[0][0] == 'a' && + strncmp(params[0], "adobe_name_file", 15) == 0) { + if (op->adobe_name_file != 0) + g_free(op->adobe_name_file); + if (params[1] == 0 || params[1][0] == 0) + op->adobe_name_file = 0; + else + op->adobe_name_file = g_strdup(params[1]); + return 1; + } + + if (params[0][0] == 'c') { + if (strncmp(params[0], "close_accelerator", 17) == 0) { + if (params[0][17] == 0) { + /* + * We have the accelerator itself. Add code to convert Xt key + * bindings to GTK. + */ + if (op->accelerator != 0) + g_free(op->accelerator); + if (params[1] == 0 || params[1][0] == 0) + op->accelerator = 0; + else + op->accelerator = g_strdup(params[1]); + } + + /* + * Ignore instances of 'close_accelerator_text'. GTK+ figures this + * out already. + */ + + return 1; + } + + if (strncmp(params[0], "code_base", 9) == 0) { + switch (params[1][0]) { + case 'o': case 'O': op->code_base = 8; break; + case 'd': case 'D': op->code_base = 10; break; + case 'h': case 'H': op->code_base = 16; break; + default: op->code_base = 16; + } + return 1; + } + + /* + * Ignore the old grayscale specification keywords. + */ + if (strncmp(params[0], "color", 5) == 0) { + /* + * Quietly ignore the old color list. + */ + return 1; + } + } + + if (params[0][0] == 'f' && + strncmp(params[0], "font_grid_horizontal", 20) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->orientation = GTK_ORIENTATION_VERTICAL; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->orientation = GTK_ORIENTATION_HORIZONTAL; + break; + } + return 1; + } + + if (params[0][0] == 'g') { + if (strncmp(params[0], "grid_overwrite_mode", 19) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->overwrite_mode = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->overwrite_mode = TRUE; + break; + } + return 1; + } + + if (strncmp(params[0], "generate_sbit_metrics", 21) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->sbit = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->sbit = TRUE; + break; + } + return 1; + } + } + + if (params[0][0] == 'm' && strncmp(params[0], "make_backups", 12) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->backups = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->backups = TRUE; + break; + } + return 1; + } + + if ((params[0][0] == 'n' && strncmp(params[0], "name_file", 9) == 0) || + (params[0][0] == 'u' && + strncmp(params[0], "unicode_name_file", 17) == 0)) { + if (op->unicode_name_file != 0) + g_free(op->unicode_name_file); + if (params[1] == 0 || params[1][0] == 0) + op->unicode_name_file = 0; + else + op->unicode_name_file = g_strdup(params[1]); + return 1; + } + + if (params[0][0] == 'o' && strncmp(params[0], "orientation", 11) == 0) { + switch (params[1][0]) { + case 'H': case 'h': + op->orientation = GTK_ORIENTATION_HORIZONTAL; + break; + case 'V': case 'v': + op->orientation = GTK_ORIENTATION_VERTICAL; + break; + } + return 1; + } + + if (params[0][0] == 'p') { + if (strncmp(params[0], "pixel_size", 10) == 0) { + op->pixel_size = _bdf_atoul(params[1], 0, 10); + if (op->pixel_size < 2) + op->pixel_size = 2; + else if (op->pixel_size > 20) + op->pixel_size = 20; + return 1; + } + + if (strncmp(params[0], "power2", 6) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->power2 = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->power2 = TRUE; + break; + } + return 1; + } + + if (strncmp(params[0], "percentage_only", 15) == 0) + /* + * Ignore this option. No longer needed. + */ + return 1; + + if (strncmp(params[0], "progress_bar", 12) == 0) + /* + * The progress bar is no longer being used as of version 5.0. + */ + return 1; + } + + if (params[0][0] == 'r') { + if (strncmp(params[0], "resolution", 10) == 0) { + op->resolution = _bdf_atoul(params[1], 0, 10); + return 1; + } + + if (strncmp(params[0], "really_exit", 11) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->really_exit = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->really_exit = TRUE; + break; + } + return 1; + } + } + + if (params[0][0] == 's') { + if (strncmp(params[0], "skip_blank_pages", 16) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->no_blanks = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->no_blanks = TRUE; + break; + } + return 1; + } + + if (strncmp(params[0], "show_cap_height", 15) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->show_cap_height = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->show_cap_height = TRUE; + break; + } + return 1; + } + + if (strncmp(params[0], "show_x_height", 13) == 0) { + switch (params[1][0]) { + case '0': case 'F': case 'f': case 'N': case 'n': + op->show_x_height = FALSE; + break; + case '1': case 'T': case 't': case 'Y': case 'y': + op->show_x_height = TRUE; + break; + } + return 1; + } + } + + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [font1.bdf .....]\n", g_get_prgname()); + exit(1); +} + +static void +editor_setup(int argc, char *argv[]) +{ + int base = 0; + gchar *ap; + FILE *in; + gboolean need_editor; + GdkScreen *screen; + + /* + * Get the default BDF options. + */ + bdf_default_options(&options.font_opts); + + options.accelerator = options.accelerator_text = + options.unicode_name_file = options.adobe_name_file = 0; + options.pixel_size = 10; + options.resolution = 0; + options.no_blanks = options.really_exit = + options.overwrite_mode = options.power2 = + options.backups = TRUE; + options.show_cap_height = options.show_x_height = + options.sbit = options.gbdfed_opts = FALSE; + options.initial_glyph = -1; + options.code_base = 16; + + options.orientation = GTK_ORIENTATION_HORIZONTAL; + + /* + * Set the default colors for 2 bits per pixel. + */ + options.colors[0] = 0; + options.colors[1] = 175; + options.colors[2] = 207; + options.colors[3] = 239; + + /* + * Set the default colors for 4 bits per pixel. + */ + options.colors[4] = 0; + options.colors[5] = 31; + options.colors[6] = 63; + options.colors[7] = 95; + options.colors[8] = 127; + options.colors[9] = 159; + options.colors[10] = 167; + options.colors[11] = 175; + options.colors[12] = 183; + options.colors[13] = 191; + options.colors[14] = 199; + options.colors[15] = 207; + options.colors[16] = 215; + options.colors[17] = 223; + options.colors[18] = 231; + options.colors[19] = 239; + + /* + * Attempt to load the user config file. + */ + if ((ap = getenv("HOME")) != 0) { + sprintf(buffer1, "%s/.gbdfedrc", ap); + if ((in = fopen(buffer1, "r")) != 0) { + bdf_load_options(in, &options.font_opts, handle_unknown_options, + (void *) &options); + fclose(in); + } else { + /* + * Try reading the older ".xmbdfedrc". + */ + sprintf(buffer1, "%s/.xmbdfedrc", ap); + if ((in = fopen(buffer1, "r")) != 0) { + bdf_load_options(in, &options.font_opts, + handle_unknown_options, (void *) &options); + fclose(in); + } + } + } + + /* + * Determine the horizontal and vertical resolutions if they have not been + * set on the command line or from the config file. + */ + if (options.resolution != 0) + options.font_opts.resolution_x = options.font_opts.resolution_y = + options.resolution; + + screen = gdk_drawable_get_screen(GDK_DRAWABLE(gdk_get_default_root_window())); + if (options.font_opts.resolution_x == 0) + options.font_opts.resolution_x = + (unsigned int) ((((double) gdk_screen_get_width(screen)) * 25.4) / + ((double) gdk_screen_get_width_mm(screen)) + 0.5); + + if (options.font_opts.resolution_y == 0) + options.font_opts.resolution_y = + (unsigned int) ((((double) gdk_screen_get_height(screen)) * 25.4) / + ((double) gdk_screen_get_height_mm(screen)) + 0.5); + + /* + * Handle the case of no command line options here. + */ + if (argc == 0) { + (void) gbdfed_make_editor(0, TRUE); + return; + } + + /* + * If a filename is not present on the command line, make sure an + * editor is created after the command line is parsed. + */ + need_editor = TRUE; + + /* + * Now parse the options from the command line. + */ + while (argc > 0) { + ap = *argv; + if (*ap == '-') { + if (strcmp(ap + 1, "nm") == 0) + options.font_opts.correct_metrics = 0; + else if (strcmp(ap + 1, "nu") == 0) + options.font_opts.keep_unencoded = 0; + else if (strcmp(ap + 1, "nc") == 0) + options.font_opts.keep_comments = 0; + else if (strcmp(ap + 1, "np") == 0) + options.font_opts.pad_cells = 0; + else if (strcmp(ap + 1, "bp") == 0) + options.no_blanks = FALSE; + else if (strcmp(ap + 1, "ed") == 0) + options.really_exit = FALSE; + else if (strcmp(ap + 1, "im") == 0) + options.overwrite_mode = FALSE; + else if (strcmp(ap + 1, "o") == 0) { + argc--; + argv++; + options.orientation = + (argv[0][0] == 'v' || argv[0][0] == 'V') ? + GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL; + } else if (strcmp(ap + 1, "ps") == 0) { + argc--; + argv++; + options.font_opts.point_size = _bdf_atoul(*argv, 0, 10); + } else if (strcmp(ap + 1, "res") == 0) { + argc--; + argv++; + options.resolution = _bdf_atoul(*argv, 0, 10); + options.font_opts.resolution_x = + options.font_opts.resolution_y = options.resolution; + } else if (strcmp(ap + 1, "hres") == 0) { + argc--; + argv++; + options.font_opts.resolution_x = _bdf_atoul(*argv, 0, 10); + } else if (strcmp(ap + 1, "vres") == 0) { + argc--; + argv++; + options.font_opts.resolution_y = _bdf_atoul(*argv, 0, 10); + } else if (strcmp(ap + 1, "bpp") == 0) { + argc--; + argv++; + options.font_opts.bits_per_pixel = _bdf_atoul(*argv, 0, 10); + } else if (strcmp(ap + 1, "g") == 0) { + argc--; + argv++; + ap = *argv; + if (*ap == '\\') { + switch (*(ap + 1)) { + case 'u': case 'x': base = 16; ap += 2; break; + case 'o': base = 8; ap += 2; break; + case 'd': base = 10; ap += 2; break; + } + } else if ((*ap == 'U' && (*(ap + 1) == '+' || + *(ap + 1) == '-')) || + (*ap == '0' && (*(ap + 1) == 'x' || + *(ap + 1) == 'X'))) { + base = 16; + ap += 2; + } else if (*ap == '0') + base = 8; + + options.initial_glyph = _bdf_atol(ap, 0, base); + } else if (strcmp(ap + 1, "sp") == 0) { + argc--; + argv++; + switch (argv[0][0]) { + case 'c': case 'C': + options.font_opts.font_spacing = BDF_CHARCELL; break; + case 'm': case 'M': + options.font_opts.font_spacing = BDF_MONOWIDTH; break; + case 'p': case 'P': + options.font_opts.font_spacing = BDF_PROPORTIONAL; break; + } + } else if (strcmp(ap + 1, "eol") == 0) { + argc--; + argv++; + switch (argv[0][0]) { + case 'd': case 'D': + options.font_opts.eol = BDF_DOS_EOL; break; + case 'm': case 'M': + options.font_opts.eol = BDF_MAC_EOL; break; + case 'u': case 'U': + options.font_opts.eol = BDF_UNIX_EOL; break; + } + } else if (strcmp(ap + 1, "cb") == 0) { + argc--; + argv++; + switch (argv[0][0]) { + case 'd': case 'D': + options.code_base = 10; break; + case 'h': case 'H': + options.code_base = 16; break; + case 'o': case 'O': + options.code_base = 8; break; + } + } else { + if (ap[1] != 'h') + fprintf(stderr, "%s: unknown parameter '%s'.\n", + g_get_prgname(), ap); + usage(); + } + } else { + need_editor = FALSE; + (void) gbdfed_make_editor(argv[0], TRUE); + } + + argc--; + argv++; + } + + if (need_editor == TRUE) + /* + * If an editor was not created, make one here. + */ + (void) gbdfed_make_editor(0, TRUE); +} + +int +main(int argc, char* argv[]) +{ + num_editors = 0; + + /* + * Make sure the BDF library is initialized. + */ + bdf_setup(); + + gtk_init(&argc, &argv); + + argc--; + argv++; + + editor_setup(argc, argv); + + gtk_main(); + + return 0; +} diff --git a/gbdfed.h b/gbdfed.h new file mode 100644 index 0000000..0b521da --- /dev/null +++ b/gbdfed.h @@ -0,0 +1,425 @@ +#ifndef _h_gbdfed +#define _h_gbdfed + +#include +#include +#include "bdf.h" + +G_BEGIN_DECLS + +#define GBDFED_VERSION "1.6" + +/************************************************************************* + * + * Types. + * + *************************************************************************/ + +typedef struct { + gboolean gbdfed_opts; + + gchar *accelerator; + gchar *accelerator_text; + gchar *unicode_name_file; + gchar *adobe_name_file; + gboolean no_blanks; + gboolean really_exit; + gboolean overwrite_mode; + gint initial_glyph; + unsigned int pixel_size; + unsigned int resolution; + gboolean show_cap_height; + gboolean show_x_height; +#if 0 + gboolean vertical; +#endif + gboolean power2; +#if 0 + gboolean colors_allocated; +#endif + unsigned short colors[20]; +#if 0 + guint32 pixels[20]; +#endif + gboolean sbit; + gboolean backups; + guint code_base; + GtkOrientation orientation; + bdf_options_t font_opts; +} gbdfed_options_t; + +typedef struct { + /* + * Numeric ID for the editor. + */ + guint id; + + /* + * Path and filenames of the font in question. + */ + gchar *path; + gchar *file; + + GtkAccelGroup *ag; + GtkWidget *shell; + + /* + * The Open/Save file selection dialogs that will be used for related + * operations as well as the original. + */ + GtkWidget *open_dialog; + GtkWidget *save_dialog; + + /* + * The menus that need to have item sensitivity checked on popup and + * popdown. + */ + GtkWidget *file_menu; + GtkWidget *edit_menu; + GtkWidget *name_submenu; + GtkWidget *view_menu; + GtkWidget *ops_menu; + GtkWidget *win_menu; + + /* + * This editor's item on the Windows menu. Used to toggle sensitivity. + */ + GtkWidget *win_menu_item; + + /* + * The font name field. + */ + GtkWidget *fontname; + + /* + * The encoding and metrics labels. + */ + GtkWidget *charinfo; + GtkWidget *metrics; + + /* + * The paging buttons and page tracking variables. + */ + GtkWidget *first; + GtkWidget *prev; + GtkWidget *next; + GtkWidget *last; + gint32 last_upageno; + gint32 last_pageno; + + /* + * The Page number and character code input fields. + */ + GtkWidget *pageno; + GtkWidget *charno; + + /* + * The FontGrid widget. + */ + GtkWidget *fgrid; + + /* + * Widgets that may change sensitivity depending on the state of the font + * being edited. + */ + GtkWidget *file_save; + GtkWidget *file_export; + + GtkWidget *edit_cut; + GtkWidget *edit_copy; + GtkWidget *edit_paste; + GtkWidget *edit_overlay; + GtkWidget *edit_insert; + GtkWidget *edit_rename_glyphs; + GtkWidget *edit_rename_menu; + GtkWidget *edit_make_xlfd; + GtkWidget *edit_unicode_names; + GtkWidget *edit_adobe_names; + GtkWidget *edit_test_glyphs; + + /* + * These are the toggle widgets that change the code base displayed + * in the Fontgrid to octal, decimal, or hex. + */ + GtkWidget *view_oct; + GtkWidget *view_dec; + GtkWidget *view_hex; + + /* + * The widget that toggles the view between the encoded and unencoded + * sections of the font. + */ + GtkWidget *view_unencoded; + + /* + * The orientation label which changes depending on the current + * orientation of the Fontgrid. + */ + GtkWidget *view_orientation; + + /* + * This is the font messages menu item that may or may not be + * enabled. + */ + GtkWidget *view_messages; + + /* + * These are the widgets that make up the font messages dialog which is + * used to record the font I/O and modification messages. + */ + GtkWidget *messages_label; + GtkWidget *messages_text; + GtkWidget *messages_dialog; + + /* + * These fields specify the font format being imported or exported. + */ + guint import_format; + guint export_format; + + /* + * These are the preference widgets. + */ + GtkWidget *pref_dialog; + + /* + * These are the widgets for editing information about the font. + */ + GtkWidget *finfo_notebook; + GtkWidget *finfo_dialog; + GtkWidget *finfo_comments; + GtkWidget *finfo_erase_comments; + GtkWidget *finfo_apply_comments; + GtkWidget *finfo_font_props; + GtkWidget *finfo_all_props; + GtkWidget *finfo_prop_name; + GtkWidget *finfo_prop_value; + GtkWidget *finfo_prop_format[4]; + GtkWidget *finfo_apply_prop; + GtkWidget *finfo_delete_prop; + + GtkWidget *finfo_enc_count; + GtkWidget *finfo_unenc_count; + + GtkWidget *finfo_default_char; + + GtkWidget *finfo_spacing[4]; + + GtkWidget *finfo_hres; + GtkWidget *finfo_vres; + GtkWidget *finfo_bpp; + + GtkWidget *finfo_dwidth; + GtkWidget *finfo_ascent; + GtkWidget *finfo_descent; + + GtkWidget *finfo_apply_info; + gboolean finfo_xlfd_props_modified; + + /* + * A flag to indicate if the font was just imported from a non-BDF format + * file. It is set to FALSE whenever a save of any kind is done. + */ + gboolean imported; + +} gbdfed_editor_t; + +/************************************************************************* + * + * Globals. + * + *************************************************************************/ + +#include "fontgrid.h" +#if 0 +#include "colorswatch.h" +#endif +#include "glyphtest.h" + +/* + * List of editors and their number. + */ +extern gbdfed_editor_t *editors; +extern guint num_editors; + +/* + * Buffers used for making messages and filenames. + */ +extern gchar buffer1[]; +extern gchar buffer2[]; + +/* + * Options for loading/saving fonts and other application options. + */ +extern gbdfed_options_t options; + +/************************************************************************* + * + * The glyph test widget used by all editors in one session. + * + *************************************************************************/ + +extern GtkWidget *glyphtest; + +/************************************************************************* + * + * Other functions: gbdfed.c + * + *************************************************************************/ + +extern guint gbdfed_make_editor(gchar *, gboolean); + +/************************************************************************* + * + * File menu functions: guifile.c + * + *************************************************************************/ + +/* + * New editor creation callback. + */ +extern void guifile_new_editor(GtkWidget *, gpointer); + +/* + * File import callbacks. + */ +extern void guifile_import_bdf_font(GtkWidget *, gpointer); +extern void guifile_import_console_font(GtkWidget *, gpointer); +extern void guifile_import_pkgf_font(GtkWidget *, gpointer); +extern void guifile_import_windows_font(GtkWidget *, gpointer); + +#ifdef HAVE_HBF +extern void guifile_import_hbf_font(GtkWidget *, gpointer); +#endif + +#ifdef HAVE_FREETYPE +extern void guifile_import_otf_font(GtkWidget *, gpointer); +#endif /* HAVE_FREETYPE */ + +#ifdef HAVE_XLIB +extern void guifile_import_xserver_font(GtkWidget *, gpointer); +#endif + +/* + * File save callbacks. + */ +extern void guifile_save_as(GtkWidget *, gpointer); +extern void guifile_save_as_wait(GtkWidget *, gpointer); +extern void guifile_save(GtkWidget *, gpointer); +extern void guifile_export_psf_font(GtkWidget *, gpointer); +extern void guifile_export_hex_font(GtkWidget *, gpointer); + +/* + * Special direct BDF font load call for recent fonts menu. + */ +extern void guifile_load_bdf_font(gbdfed_editor_t *ed, const gchar *filename); + +/************************************************************************* + * + * Edit functions: guiedit.c + * + *************************************************************************/ + +extern void guiedit_set_unicode_glyph_names(GtkWidget *, gpointer); +extern void guiedit_set_adobe_glyph_names(GtkWidget *, gpointer); +extern void guiedit_set_uni_glyph_names(GtkWidget *, gpointer); +extern void guiedit_set_zerox_glyph_names(GtkWidget *, gpointer); +extern void guiedit_set_uplus_glyph_names(GtkWidget *, gpointer); +extern void guiedit_set_bslashu_glyph_names(GtkWidget *, gpointer); +extern void guiedit_show_font_info(GtkWidget *, gpointer); +extern void guiedit_show_font_comments(GtkWidget *, gpointer); +extern void guiedit_show_font_properties(GtkWidget *, gpointer); +extern void guiedit_update_font_info(gbdfed_editor_t *); +extern void guiedit_update_font_properties(gbdfed_editor_t *); +extern void guiedit_update_font_details(gbdfed_editor_t *); +extern void guiedit_update_code_base(gbdfed_editor_t *); +extern void guiedit_copy_selection(GtkWidget *, gpointer); +extern void guiedit_cut_selection(GtkWidget *, gpointer); +extern void guiedit_paste_selection(GtkWidget *, gpointer); +extern void guiedit_overlay_selection(GtkWidget *, gpointer); +extern void guiedit_insert_selection(GtkWidget *, gpointer); +extern void guiedit_show_glyphtest(GtkWidget *, gpointer); + +/************************************************************************* + * + * Glyph edit functions: guigedit.c + * + *************************************************************************/ + +extern void guigedit_edit_glyph(gbdfed_editor_t *ed, + FontgridSelectionInfo *si); + +extern void guigedit_show_cap_height(gboolean show); +extern void guigedit_show_x_height(gboolean show); +extern void guigedit_set_pixel_size(guint pixel_size); +extern void guigedit_set_font_spacing(gint spacing, guint16 monowidth); +extern void guigedit_set_font_spacing(gint spacing, guint16 monowidth); +extern void guigedit_set_code_base(gint code_base); + +extern void guigedit_cleanup(void); + + +/************************************************************************* + * + * Preference functions: guipref.c + * + *************************************************************************/ + +extern void guiedit_show_preferences(GtkWidget *, gpointer); +extern void guiedit_preference_cleanup(void); + +/************************************************************************* + * + * Help functions: guihelp.c + * + *************************************************************************/ + +/* + * Macros that specify the help text to be used. + */ +#define GUIHELP_ABOUT 0 +#define GUIHELP_PROGRAM 1 +#define GUIHELP_FONTGRID 2 +#define GUIHELP_GLYPH_EDITOR 3 +#define GUIHELP_CONFIG_FILE 4 +#define GUIHELP_PREFERENCES 5 +#define GUIHELP_FNT 6 +#define GUIHELP_OTF 7 +#define GUIHELP_PSF 8 +#define GUIHELP_HEX 9 +#define GUIHELP_COLOR 10 +#define GUIHELP_TIPS 11 + +extern void guihelp_show_help(GtkWidget *w, gpointer data); + +extern void guigedit_cleanup(void); +extern void guihelp_cleanup(void); + +/************************************************************************* + * + * Glyph operation functions: guiops.c + * + *************************************************************************/ + +extern void guiops_show_translate(GtkWidget *, gpointer); +extern void guiops_show_rotate(GtkWidget *, gpointer); +extern void guiops_show_shear(GtkWidget *, gpointer); +extern void guiops_show_embolden(GtkWidget *, gpointer); + +/************************************************************************* + * + * Util functions: guiutil.c + * + *************************************************************************/ + +extern void guiutil_show_dialog_centered(GtkWidget *dialog, GtkWidget *parent); +extern void guiutil_cursor_cleanup(void); +extern void guiutil_busy_cursor(GtkWidget *w, gboolean on); +extern void guiutil_error_message(GtkWidget *parent, gchar *text); +extern gboolean guiutil_yes_or_no(GtkWidget *parent, gchar *text, + gboolean default_answer); +extern void guiutil_util_set_tooltip(GtkWidget *, gchar *); + +G_END_DECLS + +#endif /* _h_gbdfed */ diff --git a/gbdfed.man b/gbdfed.man new file mode 100644 index 0000000..a3e4839 --- /dev/null +++ b/gbdfed.man @@ -0,0 +1,711 @@ +.TH GBDFED 1 "23 February 2010" "GTK2" +.SH NAME +gbdfed \- GTK-based BDF font editor + +.SH SYNOPSIS +.B gbdfed +[\fIoptions\fP] [\fIfonts ...\fP] + +.SH DESCRIPTION +.I gbdfed +lets you interactively create new bitmap font files or modify +existing ones. It allows editing multiple fonts and multiple +glyphs, it allows cut and paste operations between fonts and +glyphs and editing font properties. +.I gbdfed +can import Metafont PK/GF fonts, Han Bitmap Font Format (HBF) fonts, Linux +console fonts (PSF, CP, and EGA/VGA) fonts, Sun VF fonts, OpenType/TrueType +(OTF/TTF) fonts, or grab a font from the X server (when running under X11). +.I gbdfed +can export PSF2 Linux console fonts and HEX fonts (see online help). + +.I gbdfed +works on X Window System Version 11 (X11), Release 5 or 6, with GTK+ 2.6 or +greater. It may work on Windows, but hasn't been tested yet. + +.SH OPTIONS +.I gbdfed +accepts the following command line arguments: + +.PP +.TP 8 +.I -nc +do not preserve comments (by default, +.I gbdfed +automatically collects comments that are saved with the font). +.PP +.TP 8 +.I -nu +do not preserve unencoded glyphs (by default, +.I gbdfed +preserves the unencoded glyphs). +.PP +.TP 8 +.I -nm +do not make metrics corrections (by default, +.I gbdfed +attempts to make metrics corrections automatically). +.PP +.TP 8 +.I -np +do not pad character-cell bitmaps (by default, +.I gbdfed +pads character-cell bitmaps with 0's to the cell dimensions when the font is +saved). +.PP +.TP 8 +.I -bp +allow blank pages (by default, +.I gbdfed +skips blank pages). +.PP +.TP 8 +.I -ed +do not present the "Really Exit?" dialog (by default, this dialog always +presented). +.PP +.TP 8 +.I -ps n +set default point size (if unspecified, +.I gbdfed +sets it to 12). +.PP +.TP 8 +.I -hres n +set default horizontal resolution. +.PP +.TP 8 +.I -vres n +set default vertical resolution. +.PP +.TP 8 +.I -res n +set both default resolutions (if unspecified, +.I gbdfed +sets both horizontal and vertical resolution to that of display, +(e.g. 90x90 dpi for Sun workstations). +.PP +.TP 8 +.I -sp s +set the default font spacing ("p" for Proportional, "m" for Monowidth, or "c" +for Character Cell). +.PP +.TP 8 +.I -eol e +set the default end-of-line type ("u" for Unix LF, "d" for DOS/Windows CRLF, +or "m" for Macintosh CR). +CR) +.PP +.TP 8 +.I -g glyph-code +specify the initial glyph code at startup. The glyph code can be specified in +decimal, octal, or hex. Octal numbers must be prefixed with the digit 0, and +hex numbers must be prefixed with one of: \fI0x, 0X, U+, U-, \\u\fP. +.PP +.TP 8 +.I -cb code-base +specify the code base used to display the glyphs encodings (can be "octal", +"decimal", or "hexadecimal"). + +.SH FONT GRID + +At the top of each editor window there are some +fields and buttons. These are: +.IP +The "Font" text field is where the font name +is set so it can be edited. + +The "Glyph" field is a label that provides +some information about glyph name, encoding, and +metrics when a glyph is selected. When a range +of glyphs are selected, this field displays the +start and end codes of the range. + +The push buttons are used to navigate through the +glyph pages. The "Previous Page" and "Next Page" +buttons normally skip glyph pages that are empty, +but that can be changed using the "Preferences" dialog. + +The "Page" field indicates the current glyph page +and also allows a specific page number to be entered. +Once a page number is entered, pressing the Return +key will cause the Font Grid to shift to that page. +The page number entered is assumed to be a decimal +number. + +The "Code" field is provided for situations where +the page number is not known, but the encoding is +known. The encoding entered in this field must be +in the base (8, 10, or 16) that is currently being +used to display glyph encodings (see the "View" +menu below). Once the encoding is entered, pressing +the Return key will cause the Font Grid to shift to +the page containing the encoding. +.PP +The main window of each font editor is called the +.I Font Grid. +Each Font Grid has a clipboard used for passing glyphs around. +This clipboard is called +.I FONTGRID_CLIPBOARD. +The format of the data stored to this clipboard is not documented yet. +.sp +When a glyph has been modified either by the user or +by automatic metrics corrections when the font is loaded, +the glyph code above the glyph cell will be highlighted. + +.SH Font Grid Menus +The +.I File +menu has the following entries: +.PP +.TP 4 +.I New +This creates a new font using the current defaults for point size, horizontal +and vertical resolution, and font spacing. +.PP +.TP 4 +.I Open +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 +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 +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 +Import a Metafont PK or GF font. +.PP +.TP 8 +.in 4 +.I Console Font +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 +Import an HBF font. Only available if HBF support is compiled into gbdfed. +.PP +.TP 8 +.in 4 +.I Windows Font +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 +Import an OpenType/TrueType font (.otf or .ttf extension) or a TrueType +collection (.ttc extension). +.PP +.TP 8 +.in 4 +.I Server Font +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 +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 +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 or +This copies the current selection to the Font Grid clipboard. +.PP +.TP 4 +.I Cut or Delete or BackSpace +This copies the current selection to the Font Grid clipboard and +then deletes the selection. +.PP +.TP 4 +.I Paste or +This replaces the glyphs starting at the currently selected position with the +Font Grid clipboard. +.PP +.TP 4 +.I Overlay or Ctrl +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 or Shift +This inserts the glyphs on the Font Grid clipboard in front of the currently +selected position. +.PP +.TP 4 +.I Properties +This invokes the font property editor. +.PP +.TP 4 +.I Comments +This invokes the font comments editor. +.PP +.TP 4 +.I Font Info +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 +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 +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 +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 +This will toggle between the current page and the last page +that was viewed. +.PP +.TP 4 +.I Vertical View +This will toggle the FontGrid between showing the glyphs +horizontally (default) and vertically. +.PP +.TP 4 +.I Messages +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 +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 +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 +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 +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 +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 +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 +This will update the FontGrid with the modified glyph and move to the next +glyph. +.PP +.TP 4 +.I Update and Previous +This will update the FontGrid with the modified glyph and move to the previous +glyph. +.PP +.TP 4 +.I Close +This will close the Glyph Editor. +.PP +The +.I Edit +menu has the following entries: +.PP +.TP 4 +.I Reload +This will reload the glyph and discard any changes made in the GlyphEditor. +.PP +.TP 4 +.I Copy +This will copy the currently selected portion of the bitmap to the Glyph +Editor clipboard. +.PP +.TP 4 +.I Cut +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 +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 +This will select the whole glyph bitmap. +.PP +.TP 4 +.I Next Glyph +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 +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 +Change the Glyph Editor into Draw mode. +.PP +.TP 4 +.I Move +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 +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 +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 +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 +This will embolden the glyph in a simple manner. +.PP +.TP 4 +.I Resize BBX +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 +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 for patches. +.br +Primoz Peterlin for this manual page. +.br +Danny Backx for the LessTif Imakefile. +.br +Donald Page for patches. +.br +Michal Szymanski for problem reports. +.br +Werner Lemberg for problem reports. +.br +William F. Maton for problem reports. +.br +Ivan Nejgebauer for problem reports. +.br +Solofo for problem reports. +.br +Dave Bodenstab for patches. +.br +W. Chao for Makefile changes and problem report. +.br +Andreas Reuter for problem reports. +.br +Leonard Dickens for IRIX 6.3 Makefile changes. +.br +Markus Kuhn for suggestions. +.br +Jim Knoble for dialog geometry fixes. +.br +Darren Stuart Embry for HP/UX 10.20 X11R6 +Makefile additions. +.br +Vladimir Volovich for pointing out something I forgot to +test. +.br +Ben Fry for IRIX 6.5.2 variables for the Makefile. +.br +J.H.M. Dassen (Ray) for bug fixes. +.br +Robert Brady for pointing out a problem. +.br +Stefan Monnier for a bug report. +.br +Humphrey Clerx for a bug report. +.br +Rudolf Cejka for bug fixes and a suggestion. +.br +Baruch Even for a bug fix. +.br +Sergey Vlasov for bug fixes. +.br +Daniel Neuburger for bug fixes. +.br +Pierre HANSER for a bug fix. +.br +Patrick Hagglund for FreeType 2 support. +.br +James Cloos for pointing out problems. +.br +Ming Hua for pointing out problems. +.br +Viktor Urban for pointing out problems. +.br +Jiri "BlueBear" Dluhos for providing 64-bit fixes. +.br +Jan Engelhardt help text improvements and missing +prototype. +.br +Daniel Richard G. for help on 64-bit architectures. +.br +Baruch Even for help on 64-bit architectures. +.br +Ming Hua for an unsuspected warning. +.br +Ryan Hill 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 for discovering glyph and font spacing bugs. +.br +Daniel Quarras for discovering a PSF unicode map editing +problem. +.br +Bertrand Janin for improving the GlyphEditor user +interface. +.br +Peter Volkov for fixing a name collision. +.br +Tom "spot" Callaway for fixing a linking problem. + +.SH AUTHOR +Mark Leisher diff --git a/gectrl.c b/gectrl.c new file mode 100644 index 0000000..96d4765 --- /dev/null +++ b/gectrl.c @@ -0,0 +1,1655 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "gectrl.h" +#include "gectrlbmaps.h" + +#ifdef ENABLE_NLS +#include +#define _(s) dgettext(GETTEXT_PACKAGE,s) +#else +#define _(s) (s) +#endif + +/* + * All the pixmaps are 16x16. + */ +#define BMAP_DIM 16 + +#define GEC_TOGGLE_SIZE 38 +#define GEC_BUTTON_SIZE 33 + +/* + * Properties of this widget. + */ +enum { + PROP_0 = 0, + TIP_LABEL, + GLYPH_IMAGE, + COLOR_LIST +}; + +/* + * Signals this widget emits. + */ +enum { + ACTIVATE = 0 +}; + +static GtkDrawingAreaClass *parent_class = 0; +static guint gecontrol_signals[ACTIVATE + 1]; + +#define GEC_DRAW_TOGGLE 0 +#define GEC_MOVE_TOGGLE 1 +#define GEC_COPY_TOGGLE 2 +#define GEC_FLIPH_BUTTON 3 +#define GEC_FLIPV_BUTTON 4 +#define GEC_SHEAR_BUTTON 5 +#define GEC_RLEFT_BUTTON 6 +#define GEC_RRIGHT_BUTTON 7 +#define GEC_ROTATE_BUTTON 8 +#define GEC_ULEFT_BUTTON 9 +#define GEC_UP_BUTTON 10 +#define GEC_URIGHT_BUTTON 11 +#define GEC_LEFT_BUTTON 12 +#define GEC_RIGHT_BUTTON 13 +#define GEC_DLEFT_BUTTON 14 +#define GEC_DOWN_BUTTON 15 +#define GEC_DRIGHT_BUTTON 16 +#define GEC_GLYPH_IMAGE 17 + +/* + * These are encoded in UTF-8. + */ +static guchar *help_strings[18] = { + (guchar *) "Draw", + (guchar *) "Move", + (guchar *) "Copy", + (guchar *) "Flip Horizontally", + (guchar *) "Flip Vertically", + (guchar *) "Shear ±45°", + (guchar *) "Rotate -90°", + (guchar *) "Rotate +90°", + (guchar *) "Rotate ±359°", + (guchar *) "Shift Up+Left", + (guchar *) "Shift Up", + (guchar *) "Shift Up+Right", + (guchar *) "Shift Left", + (guchar *) "Shift Right", + (guchar *) "Shift Down+Left", + (guchar *) "Shift Down", + (guchar *) "Shift Down+Right", + (guchar *) "Glyph Image", +}; + +/* + * Position all the buttons in the space provided. + */ +static void +gecontrol_position_buttons(GtkWidget *w) +{ + GEControl *ge = GECONTROL(w); + gint x, y, sx, sy, ix, dx, i, j, v, wd, ht; + GdkPoint points[5]; + + dx = 0; + + /* + * Determine the starting x and y coordinates, centered in the + * window. Modify later to make room for the color strip on the left side + * of the window. + */ + + sx = (w->allocation.width >> 1) - (((GEC_TOGGLE_SIZE * 3) + 6) >> 1); + v = (GEC_TOGGLE_SIZE + (GEC_BUTTON_SIZE * 5)) + 15; + + if (ge->gimage != 0) { + /* + * The addition of 7 includes a space of 3 between the glyph image and + * the row of toggles, and 2 pixels on the top and bottom of the glyph + * image for a box that will be drawn around it and a single pixel + * between the edge of the box and the actual glyph image. + */ + v += ge->gimage->height + 7; + + /* + * Calculate a horizontal offset for the toggles and buttons in the + * case of 8 bits per pixel. The color selection square is 128x128 so + * everything else needs to move over to accomodate it. + */ + if (ge->gimage->bpp == 8 && sx < 128 + 8) + dx = (128 + 8) - sx; + } + + sx += dx; + sy = (w->allocation.height >> 1) - (v >> 1); + + /* + * Position the glyph image first if one is present. + */ + if (ge->gimage != 0) { + /* + * The addition of 2 is for the box and an empty row around the glyph + * image. + */ + ix = ((w->allocation.width >> 1)-((ge->gimage->width + 2) >> 1)) + dx; + + if (ge->buttons[GEC_GLYPH_IMAGE].region == NULL) { + /* Top left. */ + points[0].x = points[4].x = ix; + points[0].y = points[4].y = sy; + /* Top right. */ + points[1].x = ix + ge->gimage->width + 4; + points[1].y = points[0].y; + /* Bottom right. */ + points[2].x = points[1].x; + points[2].y = sy + ge->gimage->height + 4; + /* Bottom left. */ + points[3].x = points[0].x; + points[3].y = points[2].y; + ge->buttons[GEC_GLYPH_IMAGE].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + } else + gdk_region_offset(ge->buttons[GEC_GLYPH_IMAGE].region, + ix - ge->buttons[GEC_GLYPH_IMAGE].x, + sy - ge->buttons[GEC_GLYPH_IMAGE].y); + + ge->gimage->x = ge->buttons[GEC_GLYPH_IMAGE].x = ix; + ge->gimage->y = ge->buttons[GEC_GLYPH_IMAGE].y = sy; + + sy += ge->gimage->height + 7; + } + + x = sx; + y = sy; + + /* + * Prep the points for creating regions for the toggle buttons. + */ + points[0].x = points[4].x = x; + points[0].y = points[4].y = y + (GEC_TOGGLE_SIZE >> 1); + points[1].x = x + (GEC_TOGGLE_SIZE >> 1); + points[1].y = y; + points[2].x = x + GEC_TOGGLE_SIZE; + points[2].y = points[0].y; + points[3].x = points[1].x; + points[3].y = y + GEC_TOGGLE_SIZE; + + /* + * Position the toggle buttons. + */ + for (i = 0; i < GEC_FLIPH_BUTTON; i++) { + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + + ge->buttons[i].x = x; + ge->buttons[i].y = y; + + x += GEC_TOGGLE_SIZE + 3; + + for (j = 0; j < 5; j++) + points[j].x += GEC_TOGGLE_SIZE + 3; + } + + /* + * Recalculate the starting x position based on the button size instead + * of the toggle size. + */ + sx = ((w->allocation.width >> 1)-(((GEC_BUTTON_SIZE * 3) + 6) >> 1)) + dx; + + y += GEC_TOGGLE_SIZE + 3; + + /* + * Now set up the points for the buttons. + */ + points[0].x = sx; + points[0].y = y; + points[1].x = sx + GEC_BUTTON_SIZE; + points[1].y = points[0].y; + points[2].x = points[1].x; + points[2].y = y + GEC_BUTTON_SIZE; + points[3].x = points[0].x; + points[3].y = points[2].y; + + /* + * Position the first row of buttons. + */ + for (x = sx; i < GEC_RLEFT_BUTTON; i++) { + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + ge->buttons[i].x = x; + ge->buttons[i].y = y; + + x += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].x += GEC_BUTTON_SIZE + 3; + } + + /* + * Reset the x coordinate for the regions. + */ + points[0].x = points[3].x = sx; + points[1].x = points[2].x = sx + GEC_BUTTON_SIZE; + + y += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].y += GEC_BUTTON_SIZE + 3; + + /* + * Position second row of buttons. + */ + for (x = sx; i < GEC_ULEFT_BUTTON; i++) { + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + ge->buttons[i].x = x; + ge->buttons[i].y = y; + + x += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].x += GEC_BUTTON_SIZE + 3; + } + + /* + * Reset the x coordinate for the regions. + */ + points[0].x = points[3].x = sx; + points[1].x = points[2].x = sx + GEC_BUTTON_SIZE; + + y += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].y += GEC_BUTTON_SIZE + 3; + + /* + * Position third row of buttons. + */ + for (x = sx; i < GEC_LEFT_BUTTON; i++) { + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + ge->buttons[i].x = x; + ge->buttons[i].y = y; + + x += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].x += GEC_BUTTON_SIZE + 3; + } + + /* + * Reset the x coordinate for the regions. + */ + points[0].x = points[3].x = sx; + points[1].x = points[2].x = sx + GEC_BUTTON_SIZE; + + x = sx; + y += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].y += GEC_BUTTON_SIZE + 3; + + /* + * Set the coordinates of the LEFT and RIGHT buttons. + */ + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + ge->buttons[i].x = x; + ge->buttons[i++].y = y; + + x += (GEC_BUTTON_SIZE + 3) * 2; + + for (j = 0; j < 4; j++) + points[j].x += (GEC_BUTTON_SIZE + 3) * 2; + + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + ge->buttons[i].x = x; + ge->buttons[i++].y = y; + + /* + * Reset the x coordinate for the regions. + */ + points[0].x = points[3].x = sx; + points[1].x = points[2].x = sx + GEC_BUTTON_SIZE; + + y += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].y += GEC_BUTTON_SIZE + 3; + + for (x = sx; i < GEC_GLYPH_IMAGE; i++) { + if (ge->buttons[i].region == NULL) + ge->buttons[i].region = + gdk_region_polygon(points, 4, GDK_WINDING_RULE); + else + gdk_region_offset(ge->buttons[i].region, + x - ge->buttons[i].x, y - ge->buttons[i].y); + ge->buttons[i].x = x; + ge->buttons[i].y = y; + + x += GEC_BUTTON_SIZE + 3; + + for (j = 0; j < 4; j++) + points[j].x += GEC_BUTTON_SIZE + 3; + } + + /* + * Now position the color spots if they are needed. + */ + + if (ge->gimage && ge->gimage->bpp > 1) { + if (ge->gimage->bpp == 2 || ge->gimage->bpp == 4) { + /* + * The starting horizontal position is 1/2 way between the left + * edge of the window and the left edge of the buttons. The + * starting vertical position is centered on the left edge of the + * buttons. + */ + sx = (sx >> 1) - (8 >> 1); + y = (8 * (1 << ge->gimage->bpp)) + ((1 << ge->gimage->bpp) - 1); + sy = ge->buttons[GEC_FLIPH_BUTTON].y + + ((w->allocation.height-ge->buttons[GEC_FLIPH_BUTTON].y)>>1) - + (y >> 1); + wd = 8; + ht = 8 * (1 << ge->gimage->bpp); + } else { + sx = (sx >> 1) - (128 >> 1); + sy = ge->buttons[GEC_FLIPH_BUTTON].y + + ((w->allocation.height-ge->buttons[GEC_FLIPH_BUTTON].y)>>1) - + (128 >> 1); + wd = ht = 128; + } + + /* + * Initialize the points for the spot region. + */ + + /* Top left. */ + points[0].x = points[4].x = sx; + points[0].y = points[4].y = sy; + /* Top right. */ + points[1].x = points[0].x + wd; + points[1].y = points[0].y; + /* Bottom right. */ + points[2].x = points[1].x; + points[2].y = points[1].y + ht; + /* Bottom left. */ + points[3].x = points[0].x; + points[3].y = points[2].y; + + if (ge->spot_region == NULL) + ge->spot_region = gdk_region_polygon(points, 4, + GDK_WINDING_RULE); + else + gdk_region_offset(ge->spot_region, + sx - ge->spot.x, sy - ge->spot.y); + + ge->spot.x = sx; + ge->spot.y = sy; + ge->spot.width = wd; + ge->spot.height = ht; + } +} + +/********************************************************************** + * + * Initialization routines. + * + **********************************************************************/ + +static void +gecontrol_finalize(GObject *obj) +{ + gint i; + GEControl *ge; + GEControlClass *gwc; + + g_return_if_fail(obj != 0); + g_return_if_fail(IS_GECONTROL(obj)); + + /* + * Destroy all the regions for the buttons. + */ + ge = GECONTROL(obj); + for (i = 0; i < 18; i++) { + if (ge->buttons[i].region != 0) + gdk_region_destroy(ge->buttons[i].region); + ge->buttons[i].region = 0; + } + + /* + * Make sure the image is removed if it exists. + */ + if (ge->gimage != 0) { + if (ge->gimage->bytes > 0) + g_free(ge->gimage->bitmap); + g_free(ge->gimage); + ge->gimage = 0; + } + + gwc = GECONTROL_GET_CLASS(obj); + + if (gwc->selgc != 0) + g_object_unref(G_OBJECT(gwc->selgc)); + gwc->selgc = 0; + + /* + * Unreference all the pixbufs that were created. + */ + if (gwc->draw != 0) { + g_object_unref(G_OBJECT(gwc->draw)); + g_object_unref(G_OBJECT(gwc->move)); + g_object_unref(G_OBJECT(gwc->copy)); + g_object_unref(G_OBJECT(gwc->fliph)); + g_object_unref(G_OBJECT(gwc->flipv)); + g_object_unref(G_OBJECT(gwc->shear)); + g_object_unref(G_OBJECT(gwc->rleft)); + g_object_unref(G_OBJECT(gwc->rright)); + g_object_unref(G_OBJECT(gwc->rotate)); + g_object_unref(G_OBJECT(gwc->uleft)); + g_object_unref(G_OBJECT(gwc->up)); + g_object_unref(G_OBJECT(gwc->uright)); + g_object_unref(G_OBJECT(gwc->left)); + g_object_unref(G_OBJECT(gwc->right)); + g_object_unref(G_OBJECT(gwc->dleft)); + g_object_unref(G_OBJECT(gwc->down)); + g_object_unref(G_OBJECT(gwc->dright)); + + gwc->draw = gwc->move = gwc->copy = + gwc->fliph = gwc->flipv = gwc->shear = + gwc->rleft = gwc->rright = gwc->rotate = + gwc->uleft = gwc->up = gwc->uright = + gwc->left = gwc->right = + gwc->dleft = gwc->down = gwc->dright = 0; + } +} + +static void +gecontrol_preferred_size(GtkWidget *widget, GtkRequisition *preferred) +{ + GEControl *gw = GECONTROL(widget); + gint ht; + + preferred->width = 50 + (3 * (GEC_TOGGLE_SIZE + 4)) + 4; + preferred->height = (GEC_TOGGLE_SIZE + 6) + ((5 * GEC_BUTTON_SIZE) + 8); + + if (gw->gimage != 0) { + /* + * The addition of 10 includes a box around the glyph, a line of empty + * pixels between the box and the glyph image, and a space of 3 pixels + * above and below the glyph image. + */ + preferred->height += gw->gimage->height + 10; + + /* + * Determine the height of the color list. Each color spot is 8x8 and + * there is a buffer of two pixels on each side, and 2 pixels in + * between them vertically. + */ + if (gw->gimage->bpp == 2 || gw->gimage->bpp == 4) { + preferred->width += 8 + 4; + ht = 8 * (1 << gw->gimage->bpp); + preferred->height = MAX(preferred->height, ht); + } else if (gw->gimage->bpp == 8) { + /* + * For 8 bits per pixel, the square is 8x8 spots with 16 spots per + * row and 16 rows. This gives 64 + 4 pixels which includes the 2 + * empties on each side. + */ + preferred->width += 128 + 4; + ht = 128 + 4; + preferred->height = MAX(preferred->height, ht); + } + } +} + +static void +gecontrol_actual_size(GtkWidget *widget, GtkAllocation *actual) +{ + widget->allocation = *actual; + + gecontrol_position_buttons(widget); + + if (GTK_WIDGET_REALIZED(widget)) + gdk_window_move_resize(widget->window, actual->x, actual->y, + actual->width, actual->height); +} + +/* + * Use our own painting routines to get the look and behavior we want. + */ +static void +gecontrol_paint_diamond(GtkStyle *style, GdkWindow *win, GtkStateType state, + GdkRectangle *area, gint x, gint y, + gint width, gint height) +{ + GdkSegment left[6], right[6]; + + /* + * Start the left segments at the top. Go from outer to inner. + */ + + /* Outer. */ + left[0].x1 = x + (width >> 1); + left[0].y1 = y; + left[0].x2 = x; + left[0].y2 = y + (width >> 1); + left[1].x1 = left[0].x2; + left[1].y1 = left[0].y2; + left[1].x2 = x + (width >> 1); + left[1].y2 = y + height; + + /* Middle. */ + left[2].x1 = left[0].x1; + left[2].y1 = left[0].y1 + 1; + left[2].x2 = left[0].x2 + 1; + left[2].y2 = left[0].y2; + left[3].x1 = left[2].x2; + left[3].y1 = left[2].y2; + left[3].x2 = left[1].x2; + left[3].y2 = left[1].y2 - 1; + + /* Inner. */ + left[4].x1 = left[2].x1; + left[4].y1 = left[2].y1 + 1; + left[4].x2 = left[2].x2 + 1; + left[4].y2 = left[2].y2; + left[5].x1 = left[4].x2; + left[5].y1 = left[4].y2; + left[5].x2 = left[3].x2; + left[5].y2 = left[3].y2 - 1; + + /* Outer. */ + right[0].x1 = x + (width >> 1); + right[0].y1 = y; + right[0].x2 = x + width; + right[0].y2 = y + (width >> 1); + right[1].x1 = right[0].x2; + right[1].y1 = right[0].y2; + right[1].x2 = x + (width >> 1); + right[1].y2 = y + height; + + /* Middle. */ + right[2].x1 = right[0].x1; + right[2].y1 = right[0].y1 + 1; + right[2].x2 = right[0].x2 - 1; + right[2].y2 = right[0].y2; + right[3].x1 = right[2].x2; + right[3].y1 = right[2].y2; + right[3].x2 = right[1].x2; + right[3].y2 = right[1].y2 - 1; + + /* Inner. */ + right[4].x1 = right[2].x1; + right[4].y1 = right[2].y1 + 1; + right[4].x2 = right[2].x2 - 1; + right[4].y2 = right[2].y2; + right[5].x1 = right[4].x2; + right[5].y1 = right[4].y2; + right[5].x2 = right[3].x2; + right[5].y2 = right[3].y2 - 1; + + if (area) { + gdk_gc_set_clip_rectangle(style->bg_gc[state], area); + gdk_gc_set_clip_rectangle(style->light_gc[state], area); + gdk_gc_set_clip_rectangle(style->dark_gc[state], area); + gdk_gc_set_clip_rectangle(style->black_gc, area); + } + + if (state != GTK_STATE_ACTIVE) { + gdk_draw_segments(win, style->light_gc[state], left, 4); + gdk_draw_segments(win, style->bg_gc[state], &left[4], 2); + gdk_draw_segments(win, style->black_gc, right, 2); + gdk_draw_segments(win, style->dark_gc[state], &right[2], 4); + } else { + gdk_draw_segments(win, style->dark_gc[state], left, 4); + gdk_draw_segments(win, style->black_gc, &left[4], 2); + gdk_draw_segments(win, style->light_gc[state], right, 4); + gdk_draw_segments(win, style->bg_gc[state], &right[4], 2); + } + + if (area) { + gdk_gc_set_clip_rectangle(style->bg_gc[state], NULL); + gdk_gc_set_clip_rectangle(style->light_gc[state], NULL); + gdk_gc_set_clip_rectangle(style->dark_gc[state], NULL); + gdk_gc_set_clip_rectangle(style->black_gc, NULL); + } +} + +static void +gecontrol_button_normal(GEControl *ge, gint button) +{ + gint v; + GtkWidget *w = GTK_WIDGET(ge); + GdkPoint points[4]; + + if (button == GEC_GLYPH_IMAGE) + return; + + if (button < 3) { + gecontrol_paint_diamond(w->style, w->window, GTK_STATE_NORMAL, 0, + ge->buttons[button].x, ge->buttons[button].y, + GEC_TOGGLE_SIZE, GEC_TOGGLE_SIZE); + + points[0].x = ge->buttons[button].x + (GEC_TOGGLE_SIZE >> 1); + points[0].y = ge->buttons[button].y + 3; + points[1].x = ge->buttons[button].x + 3; + points[1].y = ge->buttons[button].y + (GEC_TOGGLE_SIZE >> 1); + points[2].x = points[0].x; + points[2].y = ge->buttons[button].y + GEC_TOGGLE_SIZE - 3; + points[3].x = ge->buttons[button].x + GEC_TOGGLE_SIZE - 3; + points[3].y = points[1].y; + + gdk_draw_polygon(w->window, w->style->bg_gc[GTK_STATE_NORMAL], TRUE, + points, 4); + + v = (GEC_TOGGLE_SIZE >> 1) - (BMAP_DIM >> 1); + + } else { + gtk_paint_box(w->style, w->window, GTK_STATE_NORMAL, + GTK_SHADOW_OUT, 0, GTK_WIDGET(ge), "gectrl", + ge->buttons[button].x, ge->buttons[button].y, + GEC_BUTTON_SIZE, GEC_BUTTON_SIZE); + + v = (GEC_BUTTON_SIZE >> 1) - (BMAP_DIM >> 1); + } + + gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)], + ge->buttons[button].image, 0, 0, + ge->buttons[button].x + v, ge->buttons[button].y + v, + BMAP_DIM, BMAP_DIM, GDK_RGB_DITHER_NONE, 0, 0); +} + +static void +gecontrol_button_prelight(GEControl *ge, gint button) +{ + gint v; + GtkWidget *w = GTK_WIDGET(ge); + GdkPoint points[4]; + + if (button == GEC_GLYPH_IMAGE) + return; + + if (button < 3) { + gecontrol_paint_diamond(w->style, w->window, GTK_STATE_PRELIGHT, 0, + ge->buttons[button].x, ge->buttons[button].y, + GEC_TOGGLE_SIZE, GEC_TOGGLE_SIZE); + + points[0].x = ge->buttons[button].x + (GEC_TOGGLE_SIZE >> 1); + points[0].y = ge->buttons[button].y + 3; + points[1].x = ge->buttons[button].x + 3; + points[1].y = ge->buttons[button].y + (GEC_TOGGLE_SIZE >> 1); + points[2].x = points[0].x; + points[2].y = ge->buttons[button].y + GEC_TOGGLE_SIZE - 3; + points[3].x = ge->buttons[button].x + GEC_TOGGLE_SIZE - 3; + points[3].y = points[1].y; + + gdk_draw_polygon(w->window, w->style->bg_gc[GTK_STATE_PRELIGHT], TRUE, + points, 4); + v = (GEC_TOGGLE_SIZE >> 1) - (BMAP_DIM >> 1); + } else { + gtk_paint_box(w->style, w->window, GTK_STATE_PRELIGHT, + GTK_SHADOW_OUT, 0, GTK_WIDGET(ge), "gectrl", + ge->buttons[button].x, ge->buttons[button].y, + GEC_BUTTON_SIZE, GEC_BUTTON_SIZE); + v = (GEC_BUTTON_SIZE >> 1) - (BMAP_DIM >> 1); + } + + gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)], + ge->buttons[button].image, 0, 0, + ge->buttons[button].x + v, ge->buttons[button].y + v, + BMAP_DIM, BMAP_DIM, GDK_RGB_DITHER_NONE, 0, 0); +} + +static void +gecontrol_button_active(GEControl *ge, gint button) +{ + gint v; + GtkWidget *w = GTK_WIDGET(ge); + GdkPoint points[4]; + + if (button == GEC_GLYPH_IMAGE) + return; + + if (button < 3) { + gecontrol_paint_diamond(w->style, w->window, GTK_STATE_ACTIVE, 0, + ge->buttons[button].x, ge->buttons[button].y, + GEC_TOGGLE_SIZE, GEC_TOGGLE_SIZE); + + points[0].x = ge->buttons[button].x + (GEC_TOGGLE_SIZE >> 1); + points[0].y = ge->buttons[button].y + 3; + points[1].x = ge->buttons[button].x + 3; + points[1].y = ge->buttons[button].y + (GEC_TOGGLE_SIZE >> 1); + points[2].x = points[0].x; + points[2].y = ge->buttons[button].y + GEC_TOGGLE_SIZE - 3; + points[3].x = ge->buttons[button].x + GEC_TOGGLE_SIZE - 3; + points[3].y = points[1].y; + + gdk_draw_polygon(w->window, w->style->bg_gc[GTK_STATE_ACTIVE], TRUE, + points, 4); + v = (GEC_TOGGLE_SIZE >> 1) - (BMAP_DIM >> 1); + } else { + gtk_paint_box(w->style, w->window, GTK_STATE_ACTIVE, + GTK_SHADOW_IN, 0, GTK_WIDGET(ge), "gectrl", + ge->buttons[button].x, ge->buttons[button].y, + GEC_BUTTON_SIZE, GEC_BUTTON_SIZE); + v = (GEC_BUTTON_SIZE >> 1) - (BMAP_DIM >> 1); + } + + gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)], + ge->buttons[button].image, 0, 0, + ge->buttons[button].x + v, ge->buttons[button].y + v, + BMAP_DIM, BMAP_DIM, GDK_RGB_DITHER_NONE, 0, 0); +} + +#if 0 +static void +gecontrol_get_image_pixels(GEControl *ge, gint color) +{ + gint byte; + guint16 x, y, bpr, si, di, nx; + guchar *masks; + bdf_bitmap_t *im; + + im = ge->gimage; + ge->points_used = 0; + + di = 0; + masks = 0; + switch (im->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + bpr = ((im->width * im->bpp) + 7) >> 3; + for (y = 0; y < im->height; y++) { + for (nx = x = 0; x < im->width; x++, nx += im->bpp) { + si = (nx & 7) / im->bpp; + + byte = im->bitmap[(y * bpr) + (nx >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * im->bpp; + if (byte == color) { + if (ge->points_used == ge->points_size) { + if (ge->points_size == 0) + ge->points = + (GdkPoint *) g_malloc(sizeof(GdkPoint) * 64); + else + ge->points = (GdkPoint *) + g_realloc(ge->points, + sizeof(GdkPoint) * + (ge->points_size + 64));; + ge->points_size += 64; + } + ge->points[ge->points_used].x = x + im->x + 2; + ge->points[ge->points_used].y = y + im->y + 2; + ge->points_used++; + } + } + } +} +#endif + +static void +gecontrol_make_rgb_glyph(GEControl *ge) +{ + GtkWidget *w = GTK_WIDGET(ge); + gint byte = 0; + guint16 x, y, bpr, rgb_bpr, si, di, nx; + guchar bg[4], pix[4], *masks, *img; + bdf_bitmap_t *im; + + /* + * First, get the background color of the widget for the empty + * pixels. + */ + bg[0] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].red; + bg[1] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].green; + bg[2] = (guchar) w->style->bg[GTK_WIDGET_STATE(w)].blue; + + im = ge->gimage; + + di = 0; + masks = 0; + switch (im->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + bpr = ((im->width * im->bpp) + 7) >> 3; + + rgb_bpr = im->width * 3; + ge->rgb_used = rgb_bpr * im->height; + + /* + * Make sure there is enough storage space for the image. + */ + if (ge->rgb_size < ge->rgb_used) { + if (ge->rgb_size == 0) + ge->rgb = g_malloc(ge->rgb_used); + else + ge->rgb = g_realloc(ge->rgb, ge->rgb_used); + ge->rgb_size = ge->rgb_used; + } + + img = ge->rgb; + + for (y = 0; y < im->height; y++) { + for (nx = x = 0; x < im->width; x++, nx += im->bpp) { + si = (nx & 7) / im->bpp; + + byte = im->bitmap[(y * bpr) + (nx >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * im->bpp; + if (byte) { + switch (im->bpp) { + case 1: memset(pix, 0, 3); break; + case 2: memset(pix, ge->colors[byte-1], 3); break; + case 4: memset(pix, ge->colors[byte-1+4], 3); break; + case 8: memset(pix, byte-1, 3); break; + } + } else + memcpy(pix, bg, 3); + + memcpy(&ge->rgb[(y * rgb_bpr) + (x * 3)], pix, 3); + } + } +} + +static void +gecontrol_highlight_selected_spot(GEControl *ge) +{ + GtkWidget *w = GTK_WIDGET(ge); + gint x, y; + GEControlClass *gec = GECONTROL_GET_CLASS(ge); + + if (!GTK_WIDGET_REALIZED(w) || ge->gimage == 0 || ge->gimage->bpp == 1) + return; + + if (ge->gimage->bpp != 8) { + x = ge->spot.x; + y = ge->spot.y + (8 * ge->cidx); + } else { + x = ge->spot.x + ((ge->cidx % 16) * 8); + y = ge->spot.y + ((ge->cidx / 16) * 8); + } + gdk_draw_rectangle(GTK_WIDGET(ge)->window, gec->selgc, FALSE, x, y, 7, 7); +} + +static void +gecontrol_make_color_spots(GEControl *ge, gint bpp) +{ + gint i, j, c, wd, ht, bytes; + + if (bpp < 2 || bpp > 8) + return; + + bytes = wd = ht = 0; + + switch (bpp) { + case 2: + case 4: + wd = 8; + ht = 8 * (1 << bpp); + break; + case 8: + wd = ht = 128; + break; + } + bytes = wd * ht; + + if (ge->rgb_size < bytes) { + if (ge->rgb_size == 0) + ge->rgb = g_malloc(bytes); + else + ge->rgb = g_realloc(ge->rgb, bytes); + ge->rgb_size = bytes; + } + ge->rgb_used = bytes; + + /* + * Now create the color spots image. + */ + if (bpp != 8) { + for (i = 0; i < (1 << bpp); i++) { + if (bpp == 2) + memset(ge->rgb+(i*64), ge->colors[i], 64); + else + memset(ge->rgb+(i*64), ge->colors[i+4], 64); + } + } else { + for (c = i = 0; i < 128; i += 8) { + for (j = 0; j < 128; j += 8, c++) + memset(ge->rgb+((i*128)+j), c, 8); + memcpy(ge->rgb+((i+1)*128), ge->rgb+(i*128), 128); + memcpy(ge->rgb+((i+2)*128), ge->rgb+(i*128), 256); + memcpy(ge->rgb+((i+4)*128), ge->rgb+(i*128), 512); + } + } +} + +static void +gecontrol_draw_glyph_image(GEControl *ge) +{ + GtkWidget *w = GTK_WIDGET(ge); + + if (ge->gimage == 0 || !GTK_WIDGET_REALIZED(w)) + return; + + /* + * 1. Draw the box around the image. + */ + gdk_draw_rectangle(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)], + FALSE, ge->gimage->x, ge->gimage->y, + ge->gimage->width + 4, ge->gimage->height + 4); + + /* + * 2. Clear the space inside the rectangle. + */ + gdk_window_clear_area(w->window, ge->gimage->x + 1, ge->gimage->y + 1, + ge->gimage->width - 2, ge->gimage->height - 2); + + /* + * 3. Draw the points. + */ + gecontrol_make_rgb_glyph(ge); + gdk_draw_rgb_image(w->window, + w->style->bg_gc[GTK_WIDGET_STATE(w)], + ge->gimage->x + 2, ge->gimage->y + 2, + ge->gimage->width, ge->gimage->height, + GDK_RGB_DITHER_NONE, ge->rgb, + ge->gimage->width * 3); +} + +static gboolean +gecontrol_expose(GtkWidget *w, GdkEventExpose *ev) +{ + gint i; + GEControl *ge = GECONTROL(w); + GEControlClass *gec = GECONTROL_GET_CLASS(w); + GdkGCValues gcv; + + /* + * Draw the glyph image if one was provided. + */ + gecontrol_draw_glyph_image(ge); + + for (i = 0; i < GEC_GLYPH_IMAGE; i++) { + if (ge->buttons[i].set) + gecontrol_button_active(ge, i); + else + gecontrol_button_normal(ge, i); + } + + /* + * Draw the color spots if called for. + */ + if (ge->gimage && ge->gimage->bpp > 1) { + /* + * Make sure the selection GC has been created. + */ + if (gec->selgc == 0) { + gcv.foreground.pixel = w->style->fg[GTK_WIDGET_STATE(w)].pixel; + gcv.background.pixel = w->style->bg[GTK_WIDGET_STATE(w)].pixel; + gcv.foreground.pixel ^= gcv.background.pixel; + gcv.function = GDK_XOR; + gec->selgc = gdk_gc_new_with_values(w->window, &gcv, + GDK_GC_FOREGROUND|GDK_GC_BACKGROUND|GDK_GC_FUNCTION); + } + + gecontrol_make_color_spots(ge, ge->gimage->bpp); + + gdk_draw_gray_image(w->window, + w->style->fg_gc[GTK_WIDGET_STATE(w)], + ge->spot.x, ge->spot.y, + ge->spot.width, ge->spot.height, + GDK_RGB_DITHER_NONE, ge->rgb, ge->spot.width); + + /* + * Draw the box around the active color. + */ + gecontrol_highlight_selected_spot(ge); + } + return FALSE; +} + +static gboolean +gecontrol_motion_notify(GtkWidget *w, GdkEventMotion *ev) +{ + gint i, x, y; + GEControl *ge; + gchar buf[24]; + + ge = GECONTROL(w); + for (i = 0; i < 18; i++) { + if (ge->buttons[i].region != NULL && + gdk_region_point_in(ge->buttons[i].region, ev->x, ev->y)) { + if (i != ge->last_button) { + + /* + * Turn of the prelight on the previous button. + */ + if (ge->last_button >= 0) { + if (ge->buttons[ge->last_button].set == FALSE) + gecontrol_button_normal(ge, ge->last_button); + } + + if (ge->buttons[i].set == FALSE) + gecontrol_button_prelight(ge, i); + + if (ge->tip_label != 0) + gtk_label_set_text(GTK_LABEL(ge->tip_label), + (gchar *) ge->buttons[i].help); + ge->last_button = i; + } + break; + } + } + if (i == 18) { + /* + * Now check to see if the pointer is in the color spot. Only + * if the tip label exists. No reason to know this info other than + * to inform the user. + */ + if (ge->tip_label) { + if (ge->spot_region != NULL && + gdk_region_point_in(ge->spot_region, ev->x, ev->y)) { + /* + * Determine which color this is and it's value. Mask + * the coordinates so they can't overflow the text buffer + * if they somehow get too large. + */ + x = (((guint) ev->x) - ge->spot.x) & 0xff; + y = (((guint) ev->y) - ge->spot.y) & 0xff; + + if (ge->gimage->bpp == 2) + sprintf(buf, "Color: %03d Gray: %03d", (y>>3)+1, + ge->colors[y>>3]); + else if (ge->gimage->bpp == 4) + sprintf(buf, "Color: %03d Gray: %03d", (y>>3)+1, + ge->colors[(y>>3) + 4]); + else { + /* + * Divide x and y by 4 (spots are 4x4 in the 8bpp image) + * to get row and column. + */ + x >>= 3; + y >>= 3; + sprintf(buf, "Color: %03d Gray: %03d", + ((y<<4)+x)+1, (y<<4)+x); + } + gtk_label_set_text(GTK_LABEL(ge->tip_label), buf); + return FALSE; + } + } + + if (ge->tip_label != 0) + gtk_label_set_text(GTK_LABEL(ge->tip_label), ""); + if (ge->last_button >= 0) { + if (ge->buttons[ge->last_button].set == FALSE) + gecontrol_button_normal(ge, ge->last_button); + } + ge->last_button = -1; + } + return FALSE; +} + +static gboolean +handle_timeout(gpointer data) +{ + GEControl *ge = GECONTROL(data); + GEControlActivateInfo ai; + + if (ge->timer_button < 0 || ge->buttons[ge->timer_button].set == FALSE) { + ge->timer_button = -1; + return FALSE; + } + + /* + * Emit the operation signal here. + */ + ai.operation = (GEControlOperation) ge->timer_button; + g_signal_emit(G_OBJECT(ge), gecontrol_signals[ACTIVATE], 0, &ai); + ge->timer_count++; + return TRUE; +} + +static gboolean +gecontrol_button_press(GtkWidget *w, GdkEventButton *ev) +{ + gint i, o; + GEControl *ge = GECONTROL(w); + + for (i = 0; i < 17; i++) { + if (ge->buttons[i].region != 0 && + gdk_region_point_in(ge->buttons[i].region, ev->x, ev->y)) { + if (i < 3) { + if (ge->buttons[i].set == TRUE) + /* + * The toggle button is already set. Simply return. + */ + return FALSE; + + /* + * Clear the button that is set. + */ + o = (ge->buttons[ge->buttons[i].other_toggles[0]].set) ? + ge->buttons[i].other_toggles[0] : + ge->buttons[i].other_toggles[1]; + gecontrol_button_normal(ge, o); + ge->buttons[o].set = FALSE; + } + gecontrol_button_active(ge, i); + ge->buttons[i].set = TRUE; + + /* + * If this is any of the shift buttons, add a timer so it + * will be handled multiple times. + */ + if (i >= GEC_ULEFT_BUTTON && i <= GEC_DRIGHT_BUTTON) { + ge->timer_count = 0; + ge->timer_button = i; + ge->timer = g_timeout_add(100, handle_timeout, + (gpointer) ge); + } + break; + } + } + + return FALSE; +} + +static gboolean +gecontrol_button_release(GtkWidget *w, GdkEventButton *ev) +{ + gint i, x, y; + GEControl *ge = GECONTROL(w); + GEControlActivateInfo ai; + + for (i = 0; i < 17; i++) { + if (ge->buttons[i].region != 0 && + gdk_region_point_in(ge->buttons[i].region, ev->x, ev->y)) { + if (i >= 3) { + gecontrol_button_prelight(ge, i); + ge->buttons[i].set = FALSE; + } + if (ge->timer_count == 0) { + ai.operation = (GEControlOperation) i; + g_signal_emit(G_OBJECT(ge), gecontrol_signals[ACTIVATE], 0, + &ai); + } else + /* + * Simply reset the timer count because the signal was emitted + * in the timeout handler, probably more than once. + */ + ge->timer_count = 0; + break; + } + } + if (i == 17) { + /* + * Check to see if one of the colors was selected. + */ + if (ge->gimage || ge->gimage->bpp > 1) { + if (ge->spot_region != NULL && + gdk_region_point_in(ge->spot_region, ev->x, ev->y)) { + x = (((guint) ev->x) - ge->spot.x) & 0xff; + y = (((guint) ev->y) - ge->spot.y) & 0xff; + if (ge->gimage->bpp != 8) + i = y >> 3; + else { + x >>= 3; + y >>= 3; + i = (y << 4) + x; + } + gecontrol_highlight_selected_spot(ge); + ge->cidx = i; + gecontrol_highlight_selected_spot(ge); + + ai.operation = GECONTROL_COLOR_CHANGE; + ai.color = ge->cidx + 1; + g_signal_emit(G_OBJECT(ge), gecontrol_signals[ACTIVATE], + 0, &ai); + } + } + } + return FALSE; +} + +static void +gecontrol_set_property(GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GEControl *ge; + + ge = GECONTROL(obj); + + switch (prop_id) { + case TIP_LABEL: + ge->tip_label = (GtkWidget *) g_value_get_object(value); + break; + case GLYPH_IMAGE: + gecontrol_set_glyph_image(ge, + (bdf_bitmap_t *) g_value_get_pointer(value)); + break; + case COLOR_LIST: + gecontrol_set_color_list(ge, (guint16 *) g_value_get_pointer(value)); + break; + } +} + +static void +gecontrol_get_property(GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GEControl *ge; + + ge = GECONTROL(obj); + + switch (prop_id) { + case TIP_LABEL: + g_value_set_object(value, ge->tip_label); + break; + case GLYPH_IMAGE: + g_value_set_pointer(value, ge->gimage); + break; + case COLOR_LIST: + g_value_set_pointer(value, ge->colors); + break; + } +} + +static void +gecontrol_class_init(gpointer g_class, gpointer class_data) +{ + GObjectClass *goc = G_OBJECT_CLASS(g_class); + GtkWidgetClass *wc = GTK_WIDGET_CLASS(g_class); + GEControlClass *gc = GECONTROL_CLASS(g_class); + + goc->set_property = gecontrol_set_property; + goc->get_property = gecontrol_get_property; + goc->finalize = gecontrol_finalize; + + wc->size_request = gecontrol_preferred_size; + wc->size_allocate = gecontrol_actual_size; + wc->expose_event = gecontrol_expose; + wc->motion_notify_event = gecontrol_motion_notify; + wc->button_press_event = gecontrol_button_press; + wc->button_release_event = gecontrol_button_release; + + g_object_class_install_property(goc, TIP_LABEL, + g_param_spec_object("tipLabel", + _("Tip Label"), + _("A GtkLabel widget where tips are shown when the mouse moves."), + GTK_TYPE_WIDGET, + G_PARAM_READWRITE)); + + g_object_class_install_property(goc, GLYPH_IMAGE, + g_param_spec_pointer("glyphImage", + _("Glyph Image"), + _("The bitmap image of a glyph."), + G_PARAM_READWRITE)); + + g_object_class_install_property(goc, COLOR_LIST, + g_param_spec_pointer("colorList", + _("Color list"), + _("Colors to be used for glyphs having bits-per-pixel > 1."), + G_PARAM_READWRITE)); + + gecontrol_signals[ACTIVATE] = + g_signal_new("activate", + G_TYPE_FROM_CLASS(goc), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(GEControlClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + /* + * Initialize all the pixbufs. + */ + gc->draw = gdk_pixbuf_new_from_xpm_data(draw_xpm); + gc->move = gdk_pixbuf_new_from_xpm_data(move_xpm); + gc->copy = gdk_pixbuf_new_from_xpm_data(copy_xpm); + + gc->fliph = gdk_pixbuf_new_from_xpm_data(fliph_xpm); + gc->flipv = gdk_pixbuf_new_from_xpm_data(flipv_xpm); + gc->shear = gdk_pixbuf_new_from_xpm_data(shear_xpm); + + gc->rleft = gdk_pixbuf_new_from_xpm_data(rleft_xpm); + gc->rright = gdk_pixbuf_new_from_xpm_data(rright_xpm); + gc->rotate = gdk_pixbuf_new_from_xpm_data(rotate_xpm); + + gc->uleft = gdk_pixbuf_new_from_xpm_data(uleft_xpm); + gc->up = gdk_pixbuf_new_from_xpm_data(up_xpm); + gc->uright = gdk_pixbuf_new_from_xpm_data(uright_xpm); + + gc->left = gdk_pixbuf_new_from_xpm_data(left_xpm); + gc->right = gdk_pixbuf_new_from_xpm_data(right_xpm); + + gc->dleft = gdk_pixbuf_new_from_xpm_data(dleft_xpm); + gc->down = gdk_pixbuf_new_from_xpm_data(down_xpm); + gc->dright = gdk_pixbuf_new_from_xpm_data(dright_xpm); + + parent_class = g_type_class_peek_parent(gc); +} + +#define GEC_EVMASK (GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|\ + GDK_BUTTON_RELEASE_MASK) + +static void +gecontrol_init(GTypeInstance *instance, gpointer g_class) +{ + gint i; + GEControl *gw = GECONTROL(instance); + GEControlClass *gc = GECONTROL_CLASS(g_class); + + gw->gimage = 0; + + gw->last_button = gw->timer_button = -1; + gw->timer_count = 0; + + /* + * Enable the button press, release, and motion events. + */ + gtk_widget_add_events(GTK_WIDGET(gw), GEC_EVMASK); + + gw->points_used = gw->points_size = 0; + + gw->buttons[GEC_DRAW_TOGGLE].image = gc->draw; + gw->buttons[GEC_MOVE_TOGGLE].image = gc->move; + gw->buttons[GEC_COPY_TOGGLE].image = gc->copy; + + gw->buttons[GEC_FLIPH_BUTTON].image = gc->fliph; + gw->buttons[GEC_FLIPV_BUTTON].image = gc->flipv; + gw->buttons[GEC_SHEAR_BUTTON].image = gc->shear; + + gw->buttons[GEC_RLEFT_BUTTON].image = gc->rleft; + gw->buttons[GEC_RRIGHT_BUTTON].image = gc->rright; + gw->buttons[GEC_ROTATE_BUTTON].image = gc->rotate; + + gw->buttons[GEC_ULEFT_BUTTON].image = gc->uleft; + gw->buttons[GEC_UP_BUTTON].image = gc->up; + gw->buttons[GEC_URIGHT_BUTTON].image = gc->uright; + + gw->buttons[GEC_LEFT_BUTTON].image = gc->left; + gw->buttons[GEC_RIGHT_BUTTON].image = gc->right; + + gw->buttons[GEC_DLEFT_BUTTON].image = gc->dleft; + gw->buttons[GEC_DOWN_BUTTON].image = gc->down; + gw->buttons[GEC_DRIGHT_BUTTON].image = gc->dright; + + for (i = 0; i < 18; i++) { + gw->buttons[i].help = help_strings[i]; + gw->buttons[i].region = NULL; + gw->buttons[i].x = gw->buttons[i].y = 0; + gw->buttons[i].set = gw->buttons[i].toggle = FALSE; + + /* + * At initialization time, the Draw toggle is always set by + * default. + */ + switch (i) { + case GEC_DRAW_TOGGLE: + gw->buttons[i].set = TRUE; + gw->buttons[i].toggle = TRUE; + gw->buttons[i].other_toggles[0] = GEC_MOVE_TOGGLE; + gw->buttons[i].other_toggles[1] = GEC_COPY_TOGGLE; + break; + case GEC_MOVE_TOGGLE: + gw->buttons[i].toggle = TRUE; + gw->buttons[i].other_toggles[0] = GEC_DRAW_TOGGLE; + gw->buttons[i].other_toggles[1] = GEC_COPY_TOGGLE; + break; + case GEC_COPY_TOGGLE: + gw->buttons[i].toggle = TRUE; + gw->buttons[i].other_toggles[0] = GEC_DRAW_TOGGLE; + gw->buttons[i].other_toggles[1] = GEC_MOVE_TOGGLE; + break; + } + } + + gw->cidx = 0; + gw->spot_region = 0; + gw->spot.x = gw->spot.y = gw->spot.width = gw->spot.height = 0; +} + +/********************************************************************** + * + * API functions. + * + **********************************************************************/ + +GType +gecontrol_get_type(void) +{ + static GType gecontrol_type = 0; + + if (!gecontrol_type) { + static const GTypeInfo gecontrol_info = { + sizeof (GEControlClass), /* class_size */ + 0, /* base_init */ + 0, /* base_finalize */ + gecontrol_class_init, /* class_init */ + 0, /* class_finalize */ + 0, /* class_data */ + sizeof(GEControl), /* instance_size */ + 0, /* n_preallocs */ + gecontrol_init, /* instance_init */ + 0, /* value_table */ + }; + + gecontrol_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, + "GEControl", + &gecontrol_info, 0); + } + + return gecontrol_type; +} + +GtkWidget * +gecontrol_new(const gchar *prop1, ...) +{ + GtkWidget *w; + va_list var_args; + + va_start(var_args, prop1); + w = GTK_WIDGET(g_object_new_valist(gecontrol_get_type(), prop1, var_args)); + va_end(var_args); + + return w; +} + +GtkWidget * +gecontrol_newv(GtkWidget *tip_label, bdf_bitmap_t *image, guint16 *colors) +{ + GEControl *ge = g_object_new(gecontrol_get_type(), + "tipLabel", tip_label, + "glyphImage", image, + "colorList", colors, + NULL); + + return GTK_WIDGET(ge); +} + +void +gecontrol_update_glyph_image(GEControl *ge, bdf_bitmap_t *image) +{ + if (ge->gimage) { + if (ge->gimage->bytes > 0) + g_free(ge->gimage->bitmap); + g_free(ge->gimage); + ge->gimage = 0; + } + if (image != 0) { + ge->gimage = (bdf_bitmap_t *) g_malloc(sizeof(bdf_bitmap_t)); + memcpy(ge->gimage, image, sizeof(bdf_bitmap_t)); + if (ge->gimage->bytes > 0) { + ge->gimage->bitmap = g_malloc(ge->gimage->bytes); + memcpy(ge->gimage->bitmap, image->bitmap, image->bytes); + } + ge->gimage->x = ge->buttons[GEC_GLYPH_IMAGE].x; + ge->gimage->y = ge->buttons[GEC_GLYPH_IMAGE].y; + gecontrol_draw_glyph_image(ge); + } else + gtk_widget_queue_draw(GTK_WIDGET(ge)); +} + +void +gecontrol_set_glyph_image(GEControl *ge, bdf_bitmap_t *image) +{ + g_return_if_fail(ge != NULL); + g_return_if_fail(IS_GECONTROL(ge)); + + if (ge->gimage) { + if (ge->gimage->bytes > 0) + g_free(ge->gimage->bitmap); + g_free(ge->gimage); + ge->gimage = 0; + } + if (image != 0) { + ge->gimage = (bdf_bitmap_t *) g_malloc(sizeof(bdf_bitmap_t)); + memcpy(ge->gimage, image, sizeof(bdf_bitmap_t)); + if (ge->gimage->bytes > 0) { + ge->gimage->bitmap = g_malloc(ge->gimage->bytes); + memcpy(ge->gimage->bitmap, image->bitmap, image->bytes); + } + } + + /* + * Delete any spot region to force a new one to be created. This is + * because the sizes change depending on the bits per pixel. + */ + if (ge->spot_region != 0) { + gdk_region_destroy(ge->spot_region); + ge->spot_region = 0; + } + + /* + * Always make sure the color index is reset in this case. + */ + ge->cidx = 0; + + /* + * Always queue a resize to at least force a redraw of the widget. + */ + gtk_widget_queue_resize(GTK_WIDGET(ge)); +} + +void +gecontrol_set_color_list(GEControl *ge, guint16 *colors) +{ + g_return_if_fail(ge != NULL); + g_return_if_fail(IS_GECONTROL(ge)); + + ge->colors = colors; + gtk_widget_queue_draw(GTK_WIDGET(ge)); +} + +void +gecontrol_change_operation(GEControl *ge, GEControlOperation op) +{ + gint b, i; + + g_return_if_fail(ge != NULL); + g_return_if_fail(IS_GECONTROL(ge)); + + b = -1; + if (op == GECONTROL_DRAW) + b = GEC_DRAW_TOGGLE; + else if (op == GECONTROL_MOVE) + b = GEC_MOVE_TOGGLE; + else if (op == GECONTROL_COPY) + b = GEC_COPY_TOGGLE; + + if (b < 0 || ge->buttons[b].set == TRUE) + return; + + for (i = 0; i < 3; i++) { + if (i != b && ge->buttons[i].set == TRUE) { + ge->buttons[i].set = FALSE; + gecontrol_button_normal(ge, i); + break; + } + } + + gecontrol_button_active(ge, b); + ge->buttons[b].set = TRUE; +} + +void +gecontrol_change_color(GEControl *ge, gint cidx) +{ + g_return_if_fail(ge != NULL); + g_return_if_fail(IS_GECONTROL(ge)); + + /* + * No point in setting a color if this is a one bit per pixel image or + * there is no image. + */ + if (!ge->gimage || ge->gimage->bpp == 1) + return; + + /* + * If the index is out of bounds, then wrap it around the other side. + */ + cidx--; + if (cidx >= (1 << ge->gimage->bpp)) + cidx = 0; + else if (cidx < 0) + cidx = (1 << ge->gimage->bpp) - 1; + + gecontrol_highlight_selected_spot(ge); + ge->cidx = cidx; + gecontrol_highlight_selected_spot(ge); +} diff --git a/gectrl.h b/gectrl.h new file mode 100644 index 0000000..6c2634e --- /dev/null +++ b/gectrl.h @@ -0,0 +1,210 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_gectrl +#define _h_gectrl + +#include +#include "bdfP.h" +#include "gtkcompat.h" + +G_BEGIN_DECLS + +#define GECONTROL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST(obj, gecontrol_get_type(), GEControl)) +#define GECONTROL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST(klass, gecontrol_get_type(), GEControlClass)) + +#define IS_GECONTROL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE(obj, gecontrol_get_type())) +#define IS_GECONTROL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE(klass, gecontrol_get_type())) + +#define GECONTROL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS(obj, gecontrol_get_type(), GEControlClass)) + +typedef struct _GEControl GEControl; +typedef struct _GEControlClass GEControlClass; + +typedef enum { + GECONTROL_DRAW = 0, + GECONTROL_MOVE, + GECONTROL_COPY, + GECONTROL_FLIP_HORIZONTAL, + GECONTROL_FLIP_VERTICAL, + GECONTROL_SHEAR, + GECONTROL_ROTATE_LEFT_90, + GECONTROL_ROTATE_RIGHT_90, + GECONTROL_ROTATE, + GECONTROL_SHIFT_UP_LEFT, + GECONTROL_SHIFT_UP, + GECONTROL_SHIFT_UP_RIGHT, + GECONTROL_SHIFT_LEFT, + GECONTROL_SHIFT_RIGHT, + GECONTROL_SHIFT_DOWN_LEFT, + GECONTROL_SHIFT_DOWN, + GECONTROL_SHIFT_DOWN_RIGHT, + GECONTROL_COLOR_CHANGE +} GEControlOperation; + +/* + * Structure passed to the "activate" signal handler. + */ +typedef struct { + GEControlOperation operation; + gint color; +} GEControlActivateInfo; + +typedef struct { + guchar *help; + gint x; + gint y; + GdkPixbuf *image; + GdkRegion *region; + gint other_toggles[2]; + gboolean set; + gboolean toggle; +} GEControlButton; + +typedef struct { + gint x; + gint y; + GdkRegion *region; + gboolean set; +} GEControlColor; + +struct _GEControl { + GtkDrawingArea da; + + /* + * Public fields. + */ + + /* + * The glyph image. + */ + bdf_bitmap_t *gimage; + + /* + * An application provided label widget where the help + * messages are set. + */ + GtkWidget *tip_label; + + /* + * The list of colors to use. + */ + guint16 *colors; + + /* + * Private fields. + */ + gint last_button; + + /* + * The current color index. + */ + gint cidx; + + /* + * 16 color spots. Used to track mouse position and update the tip label. + */ + GdkRectangle spot; + GdkRegion *spot_region; + + /* + * Timer stuff for holding down the buttons. + */ + gint timer_count; + gint timer_button; + guint timer; + + GdkPoint *points; + gint points_used; + gint points_size; + + /* + * Buffer for building a grayscale glyph image. + */ + guchar *rgb; + guint rgb_used; + guint rgb_size; + + GEControlButton buttons[18]; +}; + +struct _GEControlClass { + GtkDrawingAreaClass parent_class; + + /* + * A GC for drawing the color selection rectangle. + */ + GdkGC *selgc; + + GdkPixbuf *draw; + GdkPixbuf *move; + GdkPixbuf *copy; + + GdkPixbuf *fliph; + GdkPixbuf *flipv; + GdkPixbuf *shear; + + GdkPixbuf *rleft; + GdkPixbuf *rright; + GdkPixbuf *rotate; + + GdkPixbuf *uleft; + GdkPixbuf *up; + GdkPixbuf *uright; + + GdkPixbuf *left; + GdkPixbuf *right; + + GdkPixbuf *dleft; + GdkPixbuf *down; + GdkPixbuf *dright; + + /* + * Signal handlers. + */ + void (*activate)(GtkWidget *, gpointer, gpointer); +}; + +extern GType gecontrol_get_type(void); + +extern GtkWidget *gecontrol_new(const gchar *prop1, ...); + +extern GtkWidget *gecontrol_newv(GtkWidget *tips_label, bdf_bitmap_t *image, + guint16 *colors); + +extern void gecontrol_set_glyph_image(GEControl *ge, bdf_bitmap_t *image); + +extern void gecontrol_update_glyph_image(GEControl *ge, bdf_bitmap_t *image); + +extern void gecontrol_set_color_list(GEControl *ge, guint16 *colors); + +extern void gecontrol_change_operation(GEControl *ge, GEControlOperation op); + +extern void gecontrol_change_color(GEControl *ge, gint cidx); + +G_END_DECLS + +#endif /* _h_gectrl */ diff --git a/gectrlbmaps.h b/gectrlbmaps.h new file mode 100644 index 0000000..75e90eb --- /dev/null +++ b/gectrlbmaps.h @@ -0,0 +1,399 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_gectrlbmaps +#define _h_gectrlbmaps + +static const gchar *copy_xpm[] = { +"16 16 2 1", +" c None", +". c black", +".............. ", +". . ", +". ..............", +". . . .", +". . . .", +". . . .", +". . . .", +". . . .", +". . . .", +". . . .", +". . . .", +". . . .", +". . . .", +".............. .", +" . .", +" .............." +}; + +static const gchar *dleft_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" . ", +" ... ", +" ..... ", +" .. ....... ", +" ........... ", +" ............ ", +" ........... ", +" .......... ", +" ......... ", +" ........ ", +" ......... ", +" ......... ", +" ", +" " +}; + +static const gchar *down_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" ...... ", +" ...... ", +" ...... ", +" ...... ", +" .............. ", +" .............. ", +" ............ ", +" .......... ", +" ........ ", +" ...... ", +" .... ", +" .. ", +" ", +" " +}; + +static const gchar *draw_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ... ", +" .... ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" ... ", +" .. " +}; + +static const gchar *dright_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" . ", +" ... ", +" ..... ", +" ....... .. ", +" ........... ", +" ............ ", +" ........... ", +" .......... ", +" ......... ", +" ........ ", +" ......... ", +" ......... ", +" ", +" " +}; + +static const gchar *fliph_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" .. ", +" .. ", +" .. ", +" ........ .. .. ", +" . .. . ", +" . .. ", +" . .. . ", +" . .. . ", +" . .. ", +" . .. . ", +" ........ .. .. ", +" .. ", +" .. ", +" .. ", +" " +}; + +static const gchar *flipv_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ........ ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .............. ", +" .............. ", +" ", +" . . ", +" . . ", +" ", +" . . ", +" .. .. .. ", +" " +}; + +static const gchar *left_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" .. ", +" ... ", +" .... ", +" ..... ", +" .......... ", +" ........... ", +" ............ ", +" ............ ", +" ........... ", +" .......... ", +" ..... ", +" .... ", +" ... ", +" .. ", +" " +}; + +static const gchar *move_xpm[] = { +"16 16 2 1", +" c None", +". c black", +".. .. .. .. ", +". . ", +" ..............", +" . .", +". . . .", +". . . .", +" . .", +" . .", +". . . .", +". . . .", +" . .", +" . .", +". . . .", +"... .. .. .. .", +" . .", +" .............." +}; + +static const gchar *right_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" .. ", +" ... ", +" .... ", +" ..... ", +" .......... ", +" ........... ", +" ............ ", +" ............ ", +" ........... ", +" .......... ", +" ..... ", +" .... ", +" ... ", +" .. ", +" " +}; + +static const gchar *rleft_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" .. ", +" ... ", +" .... ", +" ............ ", +" .............. ", +" .............. ", +" ............. ", +" .... .... ", +" ... .... ", +" .. .... ", +" .... ", +" .... ", +" ", +" " +}; + +static const gchar *rotate_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" ...... ", +" .......... ", +" .......... ", +" .... .... ", +" ... ... ", +" ... ... ", +" ... . ... ", +" ... ...... ", +" ... ...... ", +" .... ..... ", +" .... ..... ", +" ... ...... ", +" ", +" " +}; + +static const gchar *rright_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" .. ", +" ... ", +" .... ", +" ............ ", +" .............. ", +" .............. ", +" ............. ", +" .... .... ", +" .... ... ", +" .... .. ", +" .... ", +" .... ", +" ", +" " +}; + +static const gchar *shear_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" . ", +" . ", +" .... . ", +" . . . . ", +" . .. . ", +" . ... . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .............. ", +" . ", +" . ", +" " +}; + +static const gchar *uleft_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" ......... ", +" ......... ", +" ........ ", +" ......... ", +" .......... ", +" ........... ", +" ............ ", +" ........... ", +" .. ....... ", +" ..... ", +" ... ", +" . ", +" ", +" " +}; + +static const gchar *up_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" .. ", +" .... ", +" ...... ", +" ........ ", +" .......... ", +" ............ ", +" .............. ", +" .............. ", +" ...... ", +" ...... ", +" ...... ", +" ...... ", +" ", +" " +}; + +static const gchar *uright_xpm[] = { +"16 16 2 1", +" c None", +". c black", +" ", +" ", +" ......... ", +" ......... ", +" ........ ", +" ......... ", +" .......... ", +" ........... ", +" ............ ", +" ........... ", +" ....... .. ", +" ..... ", +" ... ", +" . ", +" ", +" " +}; + +#endif /* _h_gectrlbmaps */ diff --git a/glyphedit.c b/glyphedit.c new file mode 100644 index 0000000..8e453e7 --- /dev/null +++ b/glyphedit.c @@ -0,0 +1,2549 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "glyphedit.h" +#include +#include +#include + +#ifdef HAVE_XLIB +#include +#endif + +#ifdef ENABLE_NLS +#include +#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; +} diff --git a/glyphedit.h b/glyphedit.h new file mode 100644 index 0000000..ecc0d73 --- /dev/null +++ b/glyphedit.h @@ -0,0 +1,337 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_glyphedit +#define _h_glyphedit + +#include +#include +#include +#include "bdfP.h" +#include "gtkcompat.h" + +G_BEGIN_DECLS + +typedef enum { + GLYPHEDIT_NONE = 0, + GLYPHEDIT_SELECT, + GLYPHEDIT_DRAW, + GLYPHEDIT_MOVE, + GLYPHEDIT_COPY, + GLYPHEDIT_FLIP_HORIZONTAL, + GLYPHEDIT_FLIP_VERTICAL, + GLYPHEDIT_SHEAR, + GLYPHEDIT_ROTATE_LEFT, + GLYPHEDIT_ROTATE_RIGHT, + GLYPHEDIT_ROTATE, + GLYPHEDIT_SHIFT_UP_LEFT, + GLYPHEDIT_SHIFT_UP, + GLYPHEDIT_SHIFT_UP_RIGHT, + GLYPHEDIT_SHIFT_LEFT, + GLYPHEDIT_SHIFT_RIGHT, + GLYPHEDIT_SHIFT_DOWN_LEFT, + GLYPHEDIT_SHIFT_DOWN, + GLYPHEDIT_SHIFT_DOWN_RIGHT +} GlypheditOperation; + +/* + * The macros for accessing various parts of the widget class. + */ +#define GLYPHEDIT(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), glyphedit_get_type(), Glyphedit)) + +#define GLYPHEDIT_CLASS(c) \ + (G_TYPE_CHECK_CLASS_CAST((c), glyphedit_get_type(), GlypheditClass)) + +#define IS_GLYPHEDIT(o) G_TYPE_CHECK_INSTANCE_TYPE((o), glyphedit_get_type()) + +#define IS_GLYPHEDIT_CLASS(c) \ + (G_TYPE_CHECK_CLASS_TYPE((c), glyphedit_get_type())) + +#define GLYPHEDIT_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), glyphedit_get_type(), GlypheditClass)) + +typedef struct _Glyphedit Glyphedit; +typedef struct _GlypheditClass GlypheditClass; + +struct _Glyphedit { + GtkWidget widget; + + bdf_glyph_grid_t *grid; + gboolean show_cap_height; + gboolean show_x_height; + + GdkColor baselineColor; + GdkColor selectionColor; + GdkColor cursorColor; + + guint16 *colors; + /* + * Buffer for drawing grayscale pixels and color spots. + */ + guchar *spot; + guint spot_used; + guint spot_size; + + gboolean owns_clipboard; + + GlypheditOperation op; + GlypheditOperation pending_op; + + GdkPoint sel_start; + GdkPoint sel_end; + + gint last_x; + gint last_y; + + gint lcolor; + gint cidx; + + guint16 default_pixel_size; + guint16 pixel_size; + + guint16 vmargin; + guint16 hmargin; + guint16 border; +}; + +struct _GlypheditClass { + GtkWidgetClass parent_class; + + /* + * Cursor. + */ + GdkCursor *cursor; + + /* + * GC's used for drawing. + */ + GdkGC *gridgc; + GdkGC *bbxgc; + GdkGC *pixgc; + GdkGC *cleargc; + GdkGC *selgc; + + /* + * Signal handlers. + */ + void (*glyph_modified)(GtkWidget *, gpointer, gpointer); + void (*pointer_moved)(GtkWidget *, gpointer, gpointer); + void (*operation_change)(GtkWidget *, gpointer, gpointer); + void (*color_change)(GtkWidget *, gpointer, gpointer); +}; + +/************************************************************************** + * + * Structures used for the API. + * + **************************************************************************/ + +/* + * List of callback reasons. + */ +enum { + GLYPHEDIT_GLYPH_MODIFIED = 0, + GLYPHEDIT_POINTER_MOVED, + GLYPHEDIT_OPERATION_CHANGE, + GLYPHEDIT_COLOR_CHANGE +}; + +/* + * The structure passed back in the signals. + */ +typedef struct { + gint reason; + bdf_bitmap_t *image; + bdf_metrics_t *metrics; + GlypheditOperation operation; + gint x; + gint y; + gint color; +} GlypheditSignalInfo; + +/************************************************************************** + * + * General API + * + **************************************************************************/ + +extern GType glyphedit_get_type(void); +extern GtkWidget *glyphedit_new(const gchar *prop1, ...); +extern GtkWidget *glyphedit_newv(bdf_glyph_grid_t *grid, + guint16 default_pixel_size, + gboolean show_x_height, + gboolean show_cap_height, + guint16 *colors); + +/* + * Get the encoding of the current glyph. + */ +extern gint32 glyphedit_get_encoding(Glyphedit *gw); + +/* + * Get the current glyph metrics or the current font metrics. + */ +extern void glyphedit_get_glyph_metrics(Glyphedit *gw, bdf_metrics_t *metrics); +extern void glyphedit_get_font_metrics(Glyphedit *gw, bdf_metrics_t *metrics); + +/* + * Get the PSF Unicode mappings. + */ +extern bdf_psf_unimap_t *glyphedit_get_psf_mappings(Glyphedit *gw); + +/* + * Changes device width, width, and height values from the metrics supplied. + */ +extern void glyphedit_set_metrics(Glyphedit *gw, bdf_metrics_t *metrics); + +/* + * Get the glyph spacing. + */ +extern gint glyphedit_get_spacing(Glyphedit *gw); + +/* + * Changes the font spacing and the mono width if necessary. + */ +extern void glyphedit_set_spacing(Glyphedit *gw, gint spacing, + guint16 monowidth); + +/* + * Get and set the operation. + */ +extern void glyphedit_set_operation(Glyphedit *gw, GlypheditOperation op); +extern GlypheditOperation glyphedit_get_operation(Glyphedit *gw); + +extern void glyphedit_set_pixel_size(Glyphedit *gw, guint pixel_size); +extern guint glyphedit_get_pixel_size(Glyphedit *gw); + +/* + * Sets the glyph grid. + */ +extern void glyphedit_set_grid(Glyphedit *gw, bdf_glyph_grid_t *grid); + +/* + * Check to see if the glyph or associated info has been modified. + */ +extern gboolean glyphedit_get_modified(Glyphedit *gw); +extern void glyphedit_set_modified(Glyphedit *gw, gboolean modified); +extern void glyphedit_signal_modified(Glyphedit *gw); + +/* + * Determine if a selection is in progress. + */ +extern gboolean glyphedit_get_selecting(Glyphedit *gw); + +/* + * Check to see if the glyph editor clipboard is empty or not. + */ +extern gboolean glyphedit_clipboard_empty(Glyphedit *gw); + +/* + * Get the glyph image from the editor. + */ +extern void glyphedit_get_image(Glyphedit *gw, bdf_bitmap_t *image); + +/* + * Retrieve the glyph grid. + */ +extern bdf_glyph_grid_t *glyphedit_get_grid(Glyphedit *gw); + +/* + * Get the glyph itself. + */ +extern bdf_glyph_t *glyphedit_get_glyph(Glyphedit *gw, gboolean *unencoded); + +/* + * Show or hide the cap height. + */ +extern void glyphedit_set_show_cap_height(Glyphedit *gw, gboolean show); + +/* + * Show or hide the x height. + */ +extern void glyphedit_set_show_x_height(Glyphedit *gw, gboolean show); + +/* + * Crop the glyph bitmap to get rid of empty rows and columns around the + * glyph. + */ +extern void glyphedit_crop_glyph(Glyphedit *gw); + +/* + * Shift the bitmap horizontally, vertically or a combination of both. + */ +extern void glyphedit_shift_glyph(Glyphedit *gw, gint16 xcount, gint16 ycount); + +/* + * Rotate the bitmap clockwise (positive count) or counter-clockwise + * (negative count). + */ +extern void glyphedit_rotate_glyph(Glyphedit *w, gint16 degrees); + +/* + * Shear the bitmap clockwise (positive count) or counter-clockwise + * (negative count). Limited to the range of [-20,20] degrees. + */ +extern void glyphedit_shear_glyph(Glyphedit *gw, gint16 degrees); + +/* + * Make the glyph bold. + */ +extern void glyphedit_embolden_glyph(Glyphedit *gw); + +/* + * Flip the bitmap horizontally or vertically. + */ +extern void glyphedit_flip_glyph(Glyphedit *gw, GtkOrientation direction); + +/* + * Change to the draw, select, move, or copy operation. + */ +extern void glyphedit_change_operation(Glyphedit *gw, GlypheditOperation op); + +/* + * Change the current color index. + */ +extern void glyphedit_set_color(Glyphedit *gw, gint idx); + +/* + * Insert a bitmap from some outside source. + */ +extern void glyphedit_insert_bitmap(Glyphedit *gw, bdf_bitmap_t *bitmap); + +/* + * Functions explicitly for importing and exporting XBM bitmaps. + */ +extern int glyphedit_import_xbm(Glyphedit *gw, gchar *filename); +extern int glyphedit_export_xbm(Glyphedit *gw, gchar *filename); + +/* + * Functions dealing with the selection. + */ +extern void glyphedit_copy_selection(Glyphedit *gw); +extern void glyphedit_cut_selection(Glyphedit *gw); +extern void glyphedit_paste_selection(Glyphedit *gw); +extern void glyphedit_select_all(Glyphedit *gw); + +G_END_DECLS + +#endif /* _h_glyphedit */ diff --git a/glyphtest.c b/glyphtest.c new file mode 100644 index 0000000..f3d8392 --- /dev/null +++ b/glyphtest.c @@ -0,0 +1,828 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "glyphtest.h" + +#ifdef ENABLE_NLS +#include +#define _(s) dgettext(GETTEXT_PACKAGE,s) +#else +#define _(s) (s) +#endif + +#define HMARGINS(fw) ((fw)->hmargin << 1) +#define VMARGINS(fw) ((fw)->vmargin << 1) + +/* + * Argument types. + */ +enum { + PROP_0 = 0, + PROP_BASELINE, + PROP_DIRECTION +}; + +enum { + ADD_GLYPH = 0, + NUM_SIGNALS +}; + +static GtkWidgetClass *parent_class = 0; +static guint glyphtest_signals[NUM_SIGNALS]; + +#define GTESTMAX(h,i) ((h) > (i) ? (h) : (i)) + +static gboolean +_glyphtest_set_line_size(Glyphtest *gw) +{ + GtkWidget *w; + gboolean changed = FALSE; + guint32 i; + guint16 wd, wwidth; + GlyphtestLine *lp; + bdf_bbx_t bbx; + + w = GTK_WIDGET(gw); + + lp = &gw->line; + (void) memset((char *) &bbx, 0, sizeof(bdf_bbx_t)); + + wwidth = w->allocation.width - (HMARGINS(gw) + 4); + + for (wd = 0, i = 0; i < lp->glyphs_used; i++) { + bbx.ascent = GTESTMAX(bbx.ascent, lp->glyphs[i].font->bbx.ascent); + bbx.descent = GTESTMAX(bbx.descent, lp->glyphs[i].font->bbx.descent); + bbx.width = GTESTMAX(bbx.width, lp->glyphs[i].font->bbx.width); + wd += (lp->glyphs[i].font->spacing == BDF_PROPORTIONAL) ? + lp->glyphs[i].glyph->dwidth : lp->glyphs[i].font->monowidth; + } + + if (lp->glyphs_used == 0) { + /* + * If no glyphs are present, then set the overall bounding box + * to some simple default. + */ + bbx.ascent = 12; + bbx.descent = 3; + bbx.width = 10; + wd = bbx.width << 3; + } + + /* + * If the actual line width changed, set the indicator. + */ + if (wd != lp->width) { + lp->width = wd; + + /* + * If the line width overflows the window width, set the changed flag. + */ + if (wd > wwidth) + changed = TRUE; + } + + /* + * If the new bounding box is not the same as the current line bounding + * box, then make the new one the current line bounding box. + */ + if (bbx.ascent != lp->bbx.ascent || bbx.descent != lp->bbx.descent || + bbx.width != lp->bbx.width) { + (void) memcpy((char *) &lp->bbx, (char *) &bbx, sizeof(bdf_bbx_t)); + changed = TRUE; + } + + /* + * Now set the line size. + */ + lp->height = lp->bbx.ascent + lp->bbx.descent; + lp->cpoint.y = (VMARGINS(gw) >> 1) + 2 + lp->bbx.ascent; + + return changed; +} + +/************************************************************************** + * + * Class functions. + * + **************************************************************************/ + +static void +glyphtest_set_property(GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + Glyphtest *gw; + + gw = GLYPHTEST(obj); + + switch (prop_id) { + case PROP_BASELINE: + glyphtest_show_baseline(gw, g_value_get_boolean(value)); + break; + case PROP_DIRECTION: + glyphtest_change_direction(gw, g_value_get_int(value)); + break; + } +} + +static void +glyphtest_get_property(GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + Glyphtest *gw; + + gw = GLYPHTEST(obj); + + switch (prop_id) { + case PROP_BASELINE: + g_value_set_boolean(value, gw->show_baseline); + break; + case PROP_DIRECTION: + g_value_set_int(value, gw->dir); + break; + } +} + +static void +glyphtest_destroy(GtkObject *obj) +{ + Glyphtest *gw; + + /* + * Do some checks to make sure the incoming object exists and is the right + * kind. + */ + g_return_if_fail(obj != 0); + g_return_if_fail(IS_GLYPHTEST(obj)); + + gw = GLYPHTEST(obj); + + /* + * Delete the line structure if it was allocated. + */ + if (gw->line.glyphs_size > 0) + g_free(gw->line.glyphs); + gw->line.glyphs_used = gw->line.glyphs_size = 0; + + /* + * Free up any points that have been allocated. + */ + if (gw->image_size > 0) + g_free(gw->image); + gw->image_size = gw->image_used = 0; + + /* + * Follow the class chain back up to free up resources allocated in the + * parent classes. + */ + GTK_OBJECT_CLASS(parent_class)->destroy(obj); +} + +static void +glyphtest_finalize(GObject *obj) +{ + /* + * Do some checks to make sure the incoming object exists and is the right + * kind. + */ + g_return_if_fail(obj != 0); + g_return_if_fail(IS_GLYPHTEST(obj)); + + /* + * Follow the class chain back up to free up resources allocated in the + * parent classes. + */ + G_OBJECT_CLASS(parent_class)->finalize(obj); +} + +static void +glyphtest_preferred_size(GtkWidget *widget, GtkRequisition *preferred) +{ + Glyphtest *gw; + + gw = GLYPHTEST(widget); + + preferred->width = gw->line.width + 4 + HMARGINS(gw); + preferred->height = gw->line.height + 4 + VMARGINS(gw); +} + +static void +glyphtest_actual_size(GtkWidget *widget, GtkAllocation *actual) +{ + widget->allocation = *actual; + + if (GTK_WIDGET_REALIZED(widget)) + gdk_window_move_resize(widget->window, actual->x, actual->y, + actual->width, actual->height); +} + +static void +glyphtest_draw_focus(GtkWidget *widget, GdkRectangle *area) +{ + GdkGC *gc; + gint x, y, wd, ht, fwidth, fpad; + + /* + * Do something with this later to make sure the focus line width + * is set in the GC's. + */ + gtk_widget_style_get(widget, + "focus-line-width", &fwidth, + "focus-padding", &fpad, NULL); + + gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)]; + + x = (widget->style->xthickness + fwidth + fpad) - 1; + y = (widget->style->ythickness + fwidth + fpad) - 1; + wd = (widget->allocation.width - (x * 2)); + ht = (widget->allocation.height - (y * 2)); + + if (GTK_WIDGET_HAS_FOCUS(widget)) + gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget), + area, widget, "glyphtest", x, y, wd, ht); + else { + gdk_gc_set_clip_rectangle(gc, area); + gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1); + gdk_gc_set_clip_rectangle(gc, 0); + } +} + +static void +_glyphtest_get_pixels(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font, + gint16 x, gint y) +{ + gint byte; + guint16 i, j, bpr, si, di, nx; + guchar *masks; + + di = 0; + masks = 0; + gw->image_used = 0; + + switch (font->bpp) { + case 1: masks = bdf_onebpp; di = 7; break; + case 2: masks = bdf_twobpp; di = 3; break; + case 4: masks = bdf_fourbpp; di = 1; break; + case 8: masks = bdf_eightbpp; di = 0; break; + } + + bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3; + for (i = 0; i < glyph->bbx.height; i++) { + for (nx = j = 0; j < glyph->bbx.width; j++, nx += font->bpp) { + si = (nx & 7) / font->bpp; + + byte = glyph->bitmap[(i * bpr) + (nx >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * font->bpp; + + if (byte) { + if (gw->image_used == gw->image_size) { + if (gw->image_size == 0) + gw->image = + (GdkPoint *) g_malloc(sizeof(GdkPoint) * 64); + else + gw->image = (GdkPoint *) + g_realloc(gw->image, + sizeof(GdkPoint) * + (gw->image_size + 64));; + gw->image_size += 64; + } + gw->image[gw->image_used].x = + x + glyph->bbx.x_offset + j; + gw->image[gw->image_used].y = + (y - glyph->bbx.ascent) + i; + gw->image_used++; + } + } + } +} + +static void +_glyphtest_draw_glyph(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font) +{ + GtkWidget *w; + gint16 rx, ry; + + w = GTK_WIDGET(gw); + + if (!GTK_WIDGET_REALIZED(w)) + return; + + ry = gw->line.cpoint.y; + rx = gw->line.cpoint.x; + if (gw->dir != GLYPHTEST_LEFT_TO_RIGHT) + rx -= glyph->bbx.width; + + _glyphtest_get_pixels(gw, glyph, font, rx, ry); + if (gw->image_used > 0) + gdk_draw_points(w->window, w->style->fg_gc[GTK_STATE_NORMAL], + gw->image, gw->image_used); +} + +static void +_glyphtest_redraw_glyphs(Glyphtest *gw) +{ + GtkWidget *w; + guint32 i; + guint16 dwidth; + GlyphtestLine *lp; + GlyphtestGlyph *gp; + + w = GTK_WIDGET(gw); + + if (!GTK_WIDGET_REALIZED(w)) + return; + + lp = &gw->line; + + lp->width = 0; + if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) + lp->cpoint.x = (HMARGINS(gw) >> 1) + 2; + else + lp->cpoint.x = w->allocation.width - ((HMARGINS(gw) >> 1) + 2); + + for (i = 0, gp = lp->glyphs; i < lp->glyphs_used; i++, gp++) { + + /* + * Handle the special cases of the first glyph in case the normal + * drawing position is going to put part of the glyph off the edge of + * the window. + */ + if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) { + if (i == 0 && gp->glyph->bbx.x_offset < 0) + lp->cpoint.x += -gp->glyph->bbx.x_offset; + } else { + if (i == 0 && gp->glyph->bbx.x_offset > 0 && + gp->glyph->bbx.x_offset > gp->glyph->bbx.width) + lp->cpoint.x -= gp->glyph->bbx.width - gp->glyph->bbx.x_offset; + } + _glyphtest_draw_glyph(gw, gp->glyph, gp->font); + + dwidth = (gp->font->spacing == BDF_PROPORTIONAL) ? + gp->glyph->dwidth : gp->font->monowidth; + if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) + lp->cpoint.x += dwidth; + else + lp->cpoint.x -= dwidth; + lp->width += dwidth; + } +} + +static void +glyphtest_draw(GtkWidget *widget, GdkRectangle *area) +{ + Glyphtest *gw; + GdkPoint s, e; + GdkRectangle clear; + + if (!GTK_WIDGET_REALIZED(widget)) + return; + + gw = GLYPHTEST(widget); + + /* + * Erase the window. + */ + clear.x = clear.y = (HMARGINS(gw) >> 1); + clear.width = widget->allocation.width - (clear.x << 1); + clear.height = widget->allocation.height - (clear.y << 1); + gdk_window_clear_area(widget->window, clear.x, clear.y, + clear.width, clear.height); + + /* + * Redraw the glyphs. + */ + _glyphtest_redraw_glyphs(gw); + + /* + * Draw the baseline if indicated. + */ + if (gw->show_baseline == TRUE) { + s.x = (HMARGINS(gw) >> 1) + 2; + e.x = widget->allocation.width - s.x; + s.y = e.y = gw->line.cpoint.y; + + gdk_draw_line(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + s.x, s.y, e.x, e.y); + } +} + +static gboolean +glyphtest_expose(GtkWidget *widget, GdkEventExpose *event) +{ + /* + * Paint the shadow first. + */ + if (GTK_WIDGET_DRAWABLE(widget)) + gtk_paint_shadow(widget->style, widget->window, + GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT, + &event->area, + widget, "glyphtest", + 0, 0, + widget->allocation.width, + widget->allocation.height); + + glyphtest_draw(widget, 0); + + glyphtest_draw_focus(widget, &event->area); + + return FALSE; +} + +static void +glyphtest_realize(GtkWidget *widget) +{ + Glyphtest *gw; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail(widget != NULL); + g_return_if_fail(IS_GLYPHTEST(widget)); + + gw = GLYPHTEST(widget); + GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); + attributes.colormap = gtk_widget_get_colormap(widget); + attributes.event_mask = gtk_widget_get_events(widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_ENTER_NOTIFY_MASK| + GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK); + + attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP; + + widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), + &attributes, attributes_mask); + gdk_window_set_user_data(widget->window, widget); + + widget->style = gtk_style_attach(widget->style, widget->window); + gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL); +} + +static void +glyphtest_unrealize(GtkWidget *widget) +{ + Glyphtest *gw; + + gw = GLYPHTEST(widget); +} + +static gint +glyphtest_focus_in(GtkWidget *widget, GdkEventFocus *event) +{ + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(IS_GLYPHTEST(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); + (void) glyphtest_draw_focus(widget, NULL); + + return FALSE; +} + +static gint +glyphtest_focus_out(GtkWidget *widget, GdkEventFocus *event) +{ + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(IS_GLYPHTEST(widget), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); + (void) glyphtest_draw_focus(widget, NULL); + + return FALSE; +} + +/************************************************************************** + * + * Class and object initialization routines. + * + **************************************************************************/ + +static void +glyphtest_class_init(gpointer g_class, gpointer class_data) +{ + GObjectClass *gocp = G_OBJECT_CLASS(g_class); + GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class); + GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class); + + /* + * Set the class global variables. + */ + parent_class = g_type_class_peek_parent(g_class); + + ocp->destroy = glyphtest_destroy; + gocp->set_property = glyphtest_set_property; + gocp->get_property = glyphtest_get_property; + gocp->finalize = glyphtest_finalize; + + /* + * Add argument (a.k.a. resource) types. + */ + g_object_class_install_property(gocp, PROP_BASELINE, + g_param_spec_boolean("showBaseline", + _("Show baseline"), + _("Draw the baseline."), + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property(gocp, PROP_DIRECTION, + g_param_spec_uint("direction", + _("Direction"), + _("Override for the drawing direction of the glyphs."), + GLYPHTEST_LEFT_TO_RIGHT, + GLYPHTEST_RIGHT_TO_LEFT, + GLYPHTEST_LEFT_TO_RIGHT, + G_PARAM_READWRITE)); + + /* + * Add the signals these objects emit. + */ + glyphtest_signals[ADD_GLYPH] = + g_signal_new("add_glyph", + G_TYPE_FROM_CLASS(gocp), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(GlyphtestClass, glyph_added), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* + * Set all the functions for handling events for objects of this class. + */ + wcp->size_request = glyphtest_preferred_size; + wcp->size_allocate = glyphtest_actual_size; + wcp->realize = glyphtest_realize; + wcp->unrealize = glyphtest_unrealize; + wcp->expose_event = glyphtest_expose; + wcp->focus_in_event = glyphtest_focus_in; + wcp->focus_out_event = glyphtest_focus_out; +} + +static void +glyphtest_init(GTypeInstance *obj, gpointer g_class) +{ + Glyphtest *gw = GLYPHTEST(obj); + gint fwidth; + + GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS); + + (void) memset((char *) &gw->line, 0, sizeof(GlyphtestLine)); + + gw->dir = GLYPHTEST_LEFT_TO_RIGHT; + gw->show_baseline = TRUE; + + gw->image_used = gw->image_size = 0; + + /* + * Determine the space that will be needed for drawing the shadow and the + * focus rectangle. + */ + gtk_widget_style_get(GTK_WIDGET(gw), + "focus-line-width", &fwidth, + NULL); + + /* + * Padding that will appear before and after the focus rectangle. + * Hardcode this for now. + */ + gw->focus_thickness = 3; + gw->hmargin = + gw->widget.style->xthickness + fwidth + (gw->focus_thickness * 2); + gw->vmargin = + gw->widget.style->ythickness + fwidth + (gw->focus_thickness * 2); + + /* + * Call the line size function to set the initial size. + */ + (void) _glyphtest_set_line_size(gw); +} + +/************************************************************************** + * + * API functions. + * + **************************************************************************/ + +static const GTypeInfo glyphtest_info = { + sizeof(GlyphtestClass), + NULL, + NULL, + glyphtest_class_init, + NULL, + NULL, + sizeof(Glyphtest), + 0, + glyphtest_init, + NULL, +}; + +GType +glyphtest_get_type(void) +{ + static GType glyphtest_type = 0; + + if (!glyphtest_type) + glyphtest_type = g_type_register_static(GTK_TYPE_WIDGET, + "Glyphtest", &glyphtest_info, 0); + + return glyphtest_type; +} + +GtkWidget * +glyphtest_new(void) +{ + return GTK_WIDGET(g_object_new(glyphtest_get_type(), NULL)); +} + +void +glyphtest_add_glyph(Glyphtest *gw, bdf_font_t *font, bdf_glyph_t *glyph) +{ + GlyphtestLine *lp; + GlyphtestGlyph *gp; + + g_return_if_fail(gw != 0); + g_return_if_fail(font != 0); + g_return_if_fail(glyph != 0); + + lp = &gw->line; + + if (lp->glyphs_used == lp->glyphs_size) { + if (lp->glyphs_size == 0) + lp->glyphs = (GlyphtestGlyph *) + g_malloc(sizeof(GlyphtestGlyph) << 3); + else + lp->glyphs = (GlyphtestGlyph *) + g_realloc(lp->glyphs, + sizeof(GlyphtestGlyph) * (lp->glyphs_size + 8)); + lp->glyphs_size += 8; + } + gp = lp->glyphs + lp->glyphs_used++; + gp->font = font; + gp->glyph = glyph; + + if (_glyphtest_set_line_size(gw)) + gtk_widget_queue_resize(GTK_WIDGET(gw)); + else { + /* + * Just draw the glyph. + */ + /* + * If the first glyph would be drawn off the edge of the window, make + * sure the initial position is adjusted to display the first glyph at + * the edge. + */ + if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) { + if (gw->line.glyphs_used == 1 && glyph->bbx.x_offset < 0) + lp->cpoint.x += -glyph->bbx.x_offset; + } else { + if (gw->line.glyphs_used == 1 && glyph->bbx.x_offset > 0 && + glyph->bbx.x_offset > glyph->bbx.width) + lp->cpoint.x -= glyph->bbx.width - glyph->bbx.x_offset; + } + + _glyphtest_draw_glyph(gw, glyph, font); + + if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) + lp->cpoint.x += (font->spacing == BDF_PROPORTIONAL) ? + glyph->dwidth : font->monowidth; + else + lp->cpoint.x -= (font->spacing == BDF_PROPORTIONAL) ? + glyph->dwidth : font->monowidth; + } + + /* + * Call the signal that indicates that a glyph has been added. + */ + g_signal_emit(GTK_OBJECT(gw), glyphtest_signals[ADD_GLYPH], 0); +} + +void +glyphtest_remove_font(Glyphtest *gw, bdf_font_t *font) +{ + GtkWidget *w; + guint32 i, j; + gboolean redo; + + g_return_if_fail(gw != 0); + g_return_if_fail(font != 0); + + w = GTK_WIDGET(gw); + + for (redo = FALSE, i = j = 0; i < gw->line.glyphs_used; i++) { + if (gw->line.glyphs[i].font != font) { + gw->line.glyphs[j].font = gw->line.glyphs[i].font; + gw->line.glyphs[j].glyph = gw->line.glyphs[i].glyph; + j++; + } + } + if (gw->line.glyphs_used != j) { + redo = TRUE; + gw->line.glyphs_used = j; + } + + if (redo) { + if (_glyphtest_set_line_size(gw)) + gtk_widget_queue_resize(w); + else + glyphtest_draw(w, 0); + } +} + +void +glyphtest_update_device_width(Glyphtest *gw, bdf_font_t *font) +{ + GtkWidget *w; + guint32 i; + gboolean redraw; + + g_return_if_fail(gw != 0); + + w = GTK_WIDGET(gw); + + /* + * Determine if the device width change will cause a redraw. + */ + redraw = FALSE; + + for (i = 0; redraw == FALSE && i < gw->line.glyphs_used; i++) { + if (gw->line.glyphs[i].font == font) + redraw = TRUE; + } + + if (redraw) { + /* + * Determine if a resize is in order or just a redraw. + */ + if (_glyphtest_set_line_size(gw)) + gtk_widget_queue_resize(w); + else + glyphtest_draw(w, 0); + } +} + +void +glyphtest_change_direction(Glyphtest *gw, gint direction) +{ + g_return_if_fail(gw != 0); + + /* + * Return if the direction is invalid or the same as the current + * direction. + */ + if (direction < GLYPHTEST_LEFT_TO_RIGHT || + direction > GLYPHTEST_RIGHT_TO_LEFT || + direction == gw->dir) + return; + + gw->dir = direction; + glyphtest_draw(GTK_WIDGET(gw), 0); +} + +void +glyphtest_show_baseline(Glyphtest *gw, gboolean baseline) +{ + g_return_if_fail(gw != 0); + + if (gw->show_baseline == baseline) + return; + + gw->show_baseline = baseline; + glyphtest_draw(GTK_WIDGET(gw), 0); +} + +void +glyphtest_erase(Glyphtest *gw) +{ + g_return_if_fail(gw != 0); + + /* + * May change later to shrink the widget. + */ + gw->line.glyphs_used = 0; + glyphtest_draw(GTK_WIDGET(gw), 0); +} diff --git a/glyphtest.h b/glyphtest.h new file mode 100644 index 0000000..2101d0d --- /dev/null +++ b/glyphtest.h @@ -0,0 +1,128 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_glyphtest +#define _h_glyphtest + +#include +#include +#include +#include "bdfP.h" +#include "gtkcompat.h" + +G_BEGIN_DECLS + +/* + * The macros for accessing various parts of the widget class. + */ +#define GLYPHTEST(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), glyphtest_get_type(), Glyphtest)) + +#define GLYPHTEST_CLASS(c) \ + (G_TYPE_CHECK_CLASS_CAST((c), glyphtest_get_type(), GlyphtestClass)) + +#define IS_GLYPHTEST(o) G_TYPE_CHECK_INSTANCE_TYPE((o), glyphtest_get_type()) + +#define IS_GLYPHTEST_CLASS(c) \ + (G_TYPE_CHECK_CLASS_TYPE((c), glyphtest_get_type())) + +#define GLYPHTEST_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), glyphtest_get_type(), GlyphtestClass)) + +typedef struct _Glyphtest Glyphtest; +typedef struct _GlyphtestClass GlyphtestClass; + +typedef struct { + bdf_font_t *font; + bdf_glyph_t *glyph; +} GlyphtestGlyph; + +typedef struct { + GlyphtestGlyph *glyphs; + guint32 glyphs_used; + guint32 glyphs_size; + GdkPoint cpoint; + guint16 width; + guint16 height; + bdf_bbx_t bbx; +} GlyphtestLine; + +struct _Glyphtest { + GtkWidget widget; + + /* + * Public members. + */ + guint dir; + gboolean show_baseline; +#if 0 + /* ENABLE WHEN COLORS ARE WORKING. */ + gulong *colors; +#endif + + /* + * Private members. + */ + GlyphtestLine line; + + /* + * The points used to draw the glyphs. + */ + GdkPoint *image; + guint32 image_used; + guint32 image_size; + + guint16 vmargin; + guint16 hmargin; + guint16 focus_thickness; +}; + +struct _GlyphtestClass { + GtkWidgetClass parent_class; + + void (*glyph_added)(GtkWidget *, gpointer); +}; + +/************************************************************************** + * + * General API + * + **************************************************************************/ + +extern GType glyphtest_get_type(void); +extern GtkWidget *glyphtest_new(void); + +/* + * Direction values for the change_direction call. + */ +#define GLYPHTEST_LEFT_TO_RIGHT 0 +#define GLYPHTEST_RIGHT_TO_LEFT 1 + +extern void glyphtest_add_glyph(Glyphtest *, bdf_font_t *, bdf_glyph_t *); +extern void glyphtest_remove_font(Glyphtest *, bdf_font_t *); +extern void glyphtest_update_device_width(Glyphtest *, bdf_font_t *); +extern void glyphtest_change_direction(Glyphtest *, gint direction); +extern void glyphtest_show_baseline(Glyphtest *, gboolean baseline); +extern void glyphtest_erase(Glyphtest *); + +G_END_DECLS + +#endif /* _h_glyphtest */ diff --git a/grayswatch.c b/grayswatch.c new file mode 100644 index 0000000..1fc5d49 --- /dev/null +++ b/grayswatch.c @@ -0,0 +1,331 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "grayswatch.h" + +#ifdef ENABLE_NLS +#include +#define _(s) dgettext(GETTEXT_PACKAGE,s) +#else +#define _(s) (s) +#endif + +static GtkVBoxClass *parent_class = 0; + +#define SWATCH_MIN_WIDTH 20 +#define SWATCH_MIN_HEIGHT 100 + +/* + * Argument types. + */ +enum { + PROP_0 = 0, + PROP_GRAYLEVEL +}; + +/* + * Signal names. + */ +enum { + VALUE_CHANGED = 0 +}; + +static guint grayswatch_signals[VALUE_CHANGED + 1]; + +/************************************************************************** + * + * Class functions. + * + **************************************************************************/ + +static void +value_changed(GtkSpinButton *b, gpointer data) +{ + gint v; + Grayswatch *gs = GRAYSWATCH(data); + GtkWidget *sw = gs->swatch; + + v = gtk_spin_button_get_value_as_int(b); + + gs->gray = v; + + memset(gs->image, v, gs->image_size); + + if (GTK_WIDGET_DRAWABLE(sw)) + gdk_draw_gray_image(sw->window, + sw->style->fg_gc[GTK_WIDGET_STATE(sw)], + GTK_CONTAINER(gs)->border_width, + GTK_CONTAINER(gs)->border_width, + sw->allocation.width, sw->allocation.height, + GDK_RGB_DITHER_NONE, gs->image, + sw->allocation.width); + + if (gs->signal_blocked == FALSE) + /* + * Now we emit the value_changed signal for this widget. + */ + g_signal_emit(G_OBJECT(data), grayswatch_signals[VALUE_CHANGED], 0, v); +} + +/************************************************************************** + * + * Class functions. + * + **************************************************************************/ + +static gboolean +grayswatch_configure(GtkWidget *widget, GdkEventConfigure *event, + gpointer data) +{ + Grayswatch *gs = GRAYSWATCH(data); + gint nbytes; + + nbytes = gs->swatch->allocation.width * + gs->swatch->allocation.height; + if (nbytes > gs->image_size) { + if (gs->image_size == 0) + gs->image = (guchar *) g_malloc(nbytes); + else + gs->image = (guchar *) g_realloc(gs->image, nbytes); + gs->image_size = nbytes; + } + memset(gs->image, gs->gray, gs->image_size); + + return FALSE; +} + +static gboolean +grayswatch_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + Grayswatch *gs = GRAYSWATCH(data); + + if (gs->image_size > 0) + gdk_draw_gray_image(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + GTK_CONTAINER(gs)->border_width, + GTK_CONTAINER(gs)->border_width, + widget->allocation.width, + widget->allocation.height, + GDK_RGB_DITHER_NONE, gs->image, + widget->allocation.width); + + return FALSE; +} + +static void +grayswatch_get_property(GObject *obj, guint propid, GValue *val, + GParamSpec *pspec) +{ + Grayswatch *gs; + + gs = GRAYSWATCH(obj); + + if (propid == PROP_GRAYLEVEL) + g_value_set_uint(val, gs->gray); +} + +static void +grayswatch_set_property(GObject *obj, guint propid, const GValue *val, + GParamSpec *pspec) +{ + Grayswatch *gs; + guint gray; + + gs = GRAYSWATCH(obj); + + if (propid == PROP_GRAYLEVEL) { + gray = g_value_get_uint(val); + if (gray > 255) + gray = 255; + if (gray != gs->gray) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(gs->value), + (gdouble) gray); + } +} + +static void +grayswatch_add(GtkContainer *container, GtkWidget *child) +{ + /* + * This is here as a placeholder as it seems the container doesn't + * work quite right without it for some reason. + */ +} + +static void +grayswatch_remove(GtkContainer *container, GtkWidget *child) +{ + /* + * This is here as a placeholder as it seems the container doesn't + * work quite right without it for some reason. + */ +} + +static void +grayswatch_foreach(GtkContainer *container, gboolean int_kids, + GtkCallback callback, gpointer callback_data) +{ + Grayswatch *gs = GRAYSWATCH(container); + + if (callback != 0) { + (*callback)(gs->swatch, callback_data); + (*callback)(gs->value, callback_data); + } +} + +/************************************************************************** + * + * Class and object initialization routines. + * + **************************************************************************/ + +static void +grayswatch_class_init(gpointer g_class, gpointer class_data) +{ + GtkContainerClass *cc = GTK_CONTAINER_CLASS(g_class); + GObjectClass *oc = G_OBJECT_CLASS(g_class); + + cc->forall = grayswatch_foreach; + cc->add = grayswatch_add; + cc->remove = grayswatch_remove; + + oc->set_property = grayswatch_set_property; + oc->get_property = grayswatch_get_property; + + g_object_class_install_property(oc, PROP_GRAYLEVEL, + g_param_spec_uint("grayLevel", + _("GrayLevel"), + _("The gray level."), + 0, 255, 0, + G_PARAM_READWRITE)); + + grayswatch_signals[VALUE_CHANGED] = + g_signal_new("value-changed", + G_TYPE_FROM_CLASS(oc), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(GrayswatchClass, value_changed), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + parent_class = g_type_class_peek_parent(g_class); +} + +static void +grayswatch_init(GTypeInstance *obj, gpointer klass) +{ + Grayswatch *gs = GRAYSWATCH(obj); + + GTK_WIDGET_SET_FLAGS(obj, GTK_NO_WINDOW); + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(obj), FALSE); + + gs->swatch = gtk_drawing_area_new(); + g_signal_connect(G_OBJECT(gs->swatch), "configure_event", + G_CALLBACK(grayswatch_configure), (gpointer) gs); + g_signal_connect(G_OBJECT(gs->swatch), "expose_event", + G_CALLBACK(grayswatch_expose), (gpointer) gs); + gtk_box_pack_start(GTK_BOX(obj), gs->swatch, TRUE, TRUE, 0); + gtk_widget_show(gs->swatch); + + gs->value = gtk_spin_button_new_with_range(0.0, 255.0, 1.0); + g_signal_connect(G_OBJECT(gs->value), "value-changed", + G_CALLBACK(value_changed), (gpointer) gs); + + gtk_box_pack_start(GTK_BOX(obj), gs->value, FALSE, FALSE, 0); + gtk_widget_show(gs->value); + + gs->gray = 0; + gs->image_size = 0; + gs->image = NULL; +} + +/************************************************************************** + * + * API functions. + * + **************************************************************************/ + +static const GTypeInfo grayswatch_info = { + sizeof(GrayswatchClass), + NULL, + NULL, + grayswatch_class_init, + NULL, + NULL, + sizeof(Grayswatch), + 0, + grayswatch_init, +}; + +GType +grayswatch_get_type(void) +{ + static GType grayswatch_type = 0; + + if (!grayswatch_type) + grayswatch_type = g_type_register_static(GTK_TYPE_VBOX, + "Grayswatch", + &grayswatch_info, 0); + + return grayswatch_type; +} + +GtkWidget * +grayswatch_new(guint gray) +{ + return gtk_widget_new(grayswatch_get_type(), + "grayLevel", gray, NULL); +} + +void +grayswatch_set_gray(Grayswatch *gs, guint gray) +{ + g_return_if_fail(gs != 0); + g_return_if_fail(IS_GRAYSWATCH(gs)); + + if (gray > 255) + gray = 255; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(gs->value), gray); +} + +guint +grayswatch_get_gray(Grayswatch *gs) +{ + g_return_val_if_fail(gs != 0, 256); + g_return_val_if_fail(IS_GRAYSWATCH(gs), 256); + + return gs->gray; +} + +void +grayswatch_block_signal(Grayswatch *gs, gboolean block) +{ + g_return_if_fail(gs != 0); + g_return_if_fail(IS_GRAYSWATCH(gs)); + + gs->signal_blocked = block; +} diff --git a/grayswatch.h b/grayswatch.h new file mode 100644 index 0000000..a44826a --- /dev/null +++ b/grayswatch.h @@ -0,0 +1,110 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_grayswatch +#define _h_grayswatch + +#include +#include "gtkcompat.h" + +G_BEGIN_DECLS + +/* + * The macros for accessing various parts of the widget class. + */ +#define GRAYSWATCH(obj)(G_TYPE_CHECK_INSTANCE_CAST(obj, grayswatch_get_type(), Grayswatch)) +#define GRAYSWATCH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST(klass, grayswatch_get_type(), GrayswatchClass)) +#define IS_GRAYSWATCH(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, grayswatch_get_type()) +#define IS_GRAYSWATCH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE(klass, grayswatch_get_type())) +#define GRAYSWATCH_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS(obj, grayswatch_get_type(), GrayswatchClass)) + +typedef struct _Grayswatch Grayswatch; +typedef struct _GrayswatchClass GrayswatchClass; + +struct _Grayswatch { + GtkVBox vbox; + + /* + * Public members. + */ + guint gray; + + /* + * Private members. + */ + GtkWidget *swatch; + GtkWidget *value; + + /* + * The grayscale image used to draw the swatch color. + */ + guchar *image; + guint image_size; + + /* + * Flag indicating whether the signal should be blocked or not. + */ + gboolean signal_blocked; +}; + +struct _GrayswatchClass { + GtkVBoxClass parent_class; + + /* + * Signal handlers. + */ + void (*value_changed)(GtkWidget *, gint, gpointer); +}; + +/************************************************************************** + * + * General API + * + **************************************************************************/ + +extern GType grayswatch_get_type(void); + +extern GtkWidget *grayswatch_new(guint gray); + +/* + * The gray value is between 0 and 255. + */ +extern void grayswatch_set_gray(Grayswatch *gs, guint gray); + +/* + * Anything over 255 returned by this routine means an error. + */ +extern guint grayswatch_get_gray(Grayswatch *gs); + +/* + * Block the signal from being emitted while setting the gray level. + * This is for a specific application and may not be useful anywhere else. + * No error checking is done to make sure blocking and unblocking are + * done in pairs. + */ +extern void grayswatch_block_signal(Grayswatch *gs, gboolean block); + +G_END_DECLS + +#endif /* _h_grayswatch */ diff --git a/gtkcompat.h b/gtkcompat.h new file mode 100644 index 0000000..e50ceb4 --- /dev/null +++ b/gtkcompat.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_gtkcompat +#define _h_gtkcompat + +#include + +#if GTK_MAJOR_VERSION >= (2) && GTK_MINOR_VERSION >= (20) +#define GTK_WIDGET_REALIZED gtk_widget_get_realized +#define GTK_WIDGET_STATE gtk_widget_get_state +#define GTK_WIDGET_HAS_FOCUS gtk_widget_has_focus +#define GTK_WIDGET_DRAWABLE gtk_widget_is_drawable +#define GTK_WIDGET_IS_SENSITIVE gtk_widget_is_sensitive +#define GTK_WIDGET_SENSITIVE gtk_widget_get_sensitive +#define GTK_WIDGET_VISIBLE gtk_widget_get_visible +#endif + +#endif /* _h_gtkcompat */ diff --git a/guiedit.c b/guiedit.c new file mode 100644 index 0000000..0f57853 --- /dev/null +++ b/guiedit.c @@ -0,0 +1,1371 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" +#include "labcon.h" + +/* + * The global glyphtest widget. + */ +GtkWidget *glyphtest = 0; +static GtkWidget *gtest_dialog = 0; +static GtkWidget *gtest_erase = 0; + +static void +guiedit_glyphtest_baseline(GtkWidget *w, gpointer data) +{ + GtkToggleButton *tb; + + tb = GTK_TOGGLE_BUTTON(w); + glyphtest_show_baseline(GLYPHTEST(glyphtest), + gtk_toggle_button_get_active(tb)); +} + +static void +guiedit_glyphtest_direction(GtkWidget *w, gpointer data) +{ + GtkToggleButton *tb; + gint dir; + + tb = GTK_TOGGLE_BUTTON(w); + dir = gtk_toggle_button_get_active(tb) ? + GLYPHTEST_RIGHT_TO_LEFT : GLYPHTEST_LEFT_TO_RIGHT; + glyphtest_change_direction(GLYPHTEST(glyphtest), dir); +} + +static void +guiedit_glyphtest_erase(GtkWidget *w, gpointer data) +{ + glyphtest_erase(GLYPHTEST(glyphtest)); + + /* + * Disable the clear button until a glyph is added. + */ + gtk_widget_set_sensitive(gtest_erase, FALSE); +} + +static void +guiedit_glyphtest_enable_erase(GtkWidget *w, gpointer data) +{ + /* + * Enable the clear button if a glyph is added. + */ + gtk_widget_set_sensitive(gtest_erase, TRUE); +} + +void +guiedit_show_glyphtest(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + GtkWidget *hbox, *vbox, *frame, *cb; + + if (gtest_dialog == 0) { + gtest_dialog = gtk_dialog_new(); + (void) g_signal_connect(G_OBJECT(gtest_dialog), + "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + vbox = gtk_vbox_new(FALSE, 0); + + frame = gtk_frame_new("Test Glyphs"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + glyphtest = glyphtest_new(); + (void) g_signal_connect(G_OBJECT(glyphtest), "add_glyph", + G_CALLBACK(guiedit_glyphtest_enable_erase), + 0); + gtk_container_add(GTK_CONTAINER(frame), glyphtest); + + gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + cb = gtk_check_button_new_with_label("Show Baseline"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE); + (void) g_signal_connect(G_OBJECT(cb), "toggled", + G_CALLBACK(guiedit_glyphtest_baseline), + 0); + gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 2); + cb = gtk_check_button_new_with_label("Draw Right To Left"); + (void) g_signal_connect(G_OBJECT(cb), "toggled", + G_CALLBACK(guiedit_glyphtest_direction), + 0); + gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 2); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(gtest_dialog)->vbox), + vbox); + + /* + * Add the buttons. + */ + hbox = GTK_DIALOG(gtest_dialog)->action_area; + + gtest_erase = gtk_button_new_with_label("Erase"); + gtk_widget_set_sensitive(gtest_erase, FALSE); + (void) g_signal_connect(G_OBJECT(gtest_erase), "clicked", + G_CALLBACK(guiedit_glyphtest_erase), 0); + gtk_container_add(GTK_CONTAINER(hbox), gtest_erase); + + cb = gtk_button_new_with_label("Close"); + (void) g_signal_connect_object(G_OBJECT(cb), "clicked", + G_CALLBACK(gtk_widget_hide), + (gpointer) gtest_dialog, + G_CONNECT_SWAPPED); + gtk_container_add(GTK_CONTAINER(hbox), cb); + gtk_widget_show_all(GTK_DIALOG(gtest_dialog)->vbox); + gtk_widget_show_all(GTK_DIALOG(gtest_dialog)->action_area); + } + + guiutil_show_dialog_centered(gtest_dialog, ed->shell); +} + +void +guiedit_set_unicode_glyph_names(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + FILE *in; + + if (options.unicode_name_file == 0) { + guiutil_error_message(ed->shell, + "Unicode Glyph Names: No file provided."); + return; + } + + if ((in = fopen(options.unicode_name_file, "r")) == 0) { + sprintf(buffer1, "Unicode Glyph Names: Unable to open %s.", + options.unicode_name_file); + guiutil_error_message(ed->shell, buffer1); + } + + fontgrid_set_unicode_glyph_names(FONTGRID(ed->fgrid), in); + + fclose(in); +} + +void +guiedit_set_adobe_glyph_names(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + FILE *in; + + if (options.adobe_name_file == 0) { + guiutil_error_message(ed->shell, + "Adobe Glyph Names: No file provided."); + return; + } + + if ((in = fopen(options.adobe_name_file, "r")) == 0) { + sprintf(buffer1, "Adobe Glyph Names: Unable to open %s.", + options.adobe_name_file); + guiutil_error_message(ed->shell, buffer1); + } + + fontgrid_set_adobe_glyph_names(FONTGRID(ed->fgrid), in); + + fclose(in); +} + +void +guiedit_set_uni_glyph_names(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), 'u'); +} + +void +guiedit_set_zerox_glyph_names(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), 'x'); +} + +void +guiedit_set_uplus_glyph_names(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), '+'); +} + +void +guiedit_set_bslashu_glyph_names(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_set_code_glyph_names(FONTGRID(ed->fgrid), '\\'); +} + +static void +guiedit_select_property(GtkTreeSelection *selection, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + gchar *prop_name; + bdf_font_t *font; + bdf_property_t *prop; + GtkToggleButton *tb; + gboolean from_font = TRUE; + GtkTreeModel *model; + GtkTreeIter iter; + GValue val; + + /* + * This is called after the list is cleared as well, so return if there is + * no selection. + */ + if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + (void) memset((char *) &val, 0, sizeof(GValue)); + + gtk_tree_model_get_value(model, &iter, 0, &val); + prop_name = (gchar *) g_value_get_string(&val); + + if ((prop = bdf_get_font_property(font, prop_name)) == 0) { + from_font = FALSE; + prop = bdf_get_property(prop_name); + } + + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_name), prop_name); + + g_value_unset(&val); + + /* + * Always clear the property value field in case of ATOM properties with + * no actual value. + */ + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), ""); + + if (from_font) { + switch (prop->format) { + case BDF_ATOM: + if (prop->value.atom != 0) + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), + prop->value.atom); + break; + case BDF_INTEGER: + sprintf(buffer1, "%d", prop->value.int32); + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), buffer1); + break; + case BDF_CARDINAL: + sprintf(buffer1, "%d", prop->value.card32); + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), buffer1); + break; + } + } + + tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[prop->format]); + gtk_toggle_button_set_active(tb, TRUE); + + /* + * Change the sensitivity of the Delete button depending on whether the + * property was defined in the font or not. If the property came from the + * font and is one of FONT_ASCENT or FONT_DESCENT, do not allow them to be + * deleted. + */ + if (from_font && (strncmp(prop_name, "FONT_ASCENT", 11) == 0 || + strncmp(prop_name, "FONT_DESCENT", 12) == 0)) + from_font = FALSE; + gtk_widget_set_sensitive(ed->finfo_delete_prop, from_font); + + /* + * Always change the sensitivity of the Apply button to False when a + * property is selected. + */ + gtk_widget_set_sensitive(ed->finfo_apply_prop, FALSE); +} + +/* + * Called whenever a new font is loaded into this editor. + */ +void +guiedit_update_font_info(gbdfed_editor_t *ed) +{ + GtkToggleButton *tb; + GtkTextBuffer *text; + bdf_font_t *font; + guint32 i, nprops; + bdf_property_t *props; + GtkListStore *store; + GtkTreeIter iter; + + /* + * Simply return if the editor's info dialog doesn't exist yet. + */ + if (ed->finfo_dialog == 0) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + /* + * Set the dialog title. + */ + if (ed->file == 0) + sprintf(buffer1, "(unnamed%d) Info Edit", ed->id); + else + sprintf(buffer1, "%s Info Edit", ed->file); + gtk_window_set_title(GTK_WINDOW(ed->finfo_dialog), buffer1); + + /* + * Update the font properties. + */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ed->finfo_font_props))); + gtk_list_store_clear(store); + + if ((nprops = bdf_font_property_list(font, &props)) > 0) { + for (i = 0; i < nprops; i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, props[i].name, -1); + } + free((char *) props); + } + + /* + * Update the defined properties. + */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ed->finfo_all_props))); + gtk_list_store_clear(store); + + if ((nprops = bdf_property_list(&props)) > 0) { + for (i = 0; i < nprops; i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, props[i].name, -1); + } + free((char *) props); + } + + /* + * Clear the property name and value fields. + */ + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_name), ""); + gtk_entry_set_text(GTK_ENTRY(ed->finfo_prop_value), ""); + + /* + * Turn off the property apply button until something gets edited. + */ + gtk_widget_set_sensitive(ed->finfo_apply_prop, FALSE); + + /* + * Update the font comments. + */ + text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->finfo_comments)); + if (font && font->comments_len > 0) + gtk_text_buffer_set_text(text, font->comments, font->comments_len); + else + gtk_text_buffer_set_text(text, "", 0); + + gtk_widget_set_sensitive(ed->finfo_apply_comments, FALSE); + + /* + * Update the font spacing. + */ + if (font) + tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[font->spacing >> 4]); + else + tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[options.font_opts.font_spacing >> 4]); + gtk_toggle_button_set_active(tb, TRUE); + + /* + * Update the device width value. + */ + gtk_widget_set_sensitive(ed->finfo_dwidth, TRUE); + if (font && font->spacing != BDF_PROPORTIONAL) + sprintf(buffer1, "%hd", font->monowidth); + else + (void) strcpy(buffer1, "0"); + gtk_entry_set_text(GTK_ENTRY(ed->finfo_dwidth), buffer1); + + if (font) + sprintf(buffer1, "%d", font->font_ascent); + else + sprintf(buffer1, "0"); + gtk_entry_set_text(GTK_ENTRY(ed->finfo_ascent), buffer1); + + if (font) + sprintf(buffer1, "%d", font->font_descent); + else + sprintf(buffer1, "0"); + gtk_entry_set_text(GTK_ENTRY(ed->finfo_descent), buffer1); + + if ((!font && options.font_opts.font_spacing == BDF_PROPORTIONAL) || + font->spacing == BDF_PROPORTIONAL) + gtk_widget_set_sensitive(ed->finfo_dwidth, FALSE); + + /* + * Finally, update the remaining information. + */ + if (font) + sprintf(buffer1, "%d", font->glyphs_used); + else + sprintf(buffer1, "0"); + gtk_label_set_text(GTK_LABEL(ed->finfo_enc_count), buffer1); + + if (font) + sprintf(buffer1, "%d", font->unencoded_used); + else + sprintf(buffer1, "0"); + gtk_label_set_text(GTK_LABEL(ed->finfo_unenc_count), buffer1); + + if (font) { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ed->finfo_hres), + (gfloat) font->resolution_x); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ed->finfo_vres), + (gfloat) font->resolution_y); + } + + if (font) { + switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) { + case 8: sprintf(buffer1, "%o", font->default_glyph); break; + case 10: sprintf(buffer1, "%d", font->default_glyph); break; + case 16: sprintf(buffer1, "%04x", font->default_glyph); break; + } + gtk_entry_set_text(GTK_ENTRY(ed->finfo_default_char), buffer1); + } + + if (font) + sprintf(buffer1, "%hd", font->bpp); + else + sprintf(buffer1, "%d", options.font_opts.bits_per_pixel); + gtk_label_set_text(GTK_LABEL(ed->finfo_bpp), buffer1); + + gtk_widget_set_sensitive(ed->finfo_apply_info, FALSE); +} + +void +guiedit_update_code_base(gbdfed_editor_t *ed) +{ + bdf_font_t *font; + + if (ed->finfo_dialog == 0) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) { + case 8: sprintf(buffer1, "%o", font->default_glyph); break; + case 10: sprintf(buffer1, "%d", font->default_glyph); break; + case 16: sprintf(buffer1, "%04x", font->default_glyph); break; + } + gtk_entry_set_text(GTK_ENTRY(ed->finfo_default_char), buffer1); +} + +void +guiedit_update_font_details(gbdfed_editor_t *ed) +{ + bdf_font_t *font; + + if (ed->finfo_dialog == 0) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + sprintf(buffer1, "%d", font->glyphs_used); + gtk_label_set_text(GTK_LABEL(ed->finfo_enc_count), buffer1); + sprintf(buffer1, "%d", font->unencoded_used); + gtk_label_set_text(GTK_LABEL(ed->finfo_unenc_count), buffer1); +} + +static void +enable_apply(GtkWidget *w, gpointer data) +{ + gtk_widget_set_sensitive(w, TRUE); +} + +static void +guiedit_enable_comment_buttons(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + gtk_widget_set_sensitive(ed->finfo_erase_comments, TRUE); + gtk_widget_set_sensitive(ed->finfo_apply_comments, TRUE); +} + +static void +guiedit_apply_property(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + GtkToggleButton *tb; + gchar *v; + bdf_property_t prop; + + /* + * Get the property info. + */ + prop.name = (char *) gtk_entry_get_text(GTK_ENTRY(ed->finfo_prop_name)); + tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[BDF_ATOM]); + if (gtk_toggle_button_get_active(tb)) + prop.format = BDF_ATOM; + tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[BDF_INTEGER]); + if (gtk_toggle_button_get_active(tb)) + prop.format = BDF_INTEGER; + tb = GTK_TOGGLE_BUTTON(ed->finfo_prop_format[BDF_CARDINAL]); + if (gtk_toggle_button_get_active(tb)) + prop.format = BDF_CARDINAL; + + v = (char *) gtk_entry_get_text(GTK_ENTRY(ed->finfo_prop_value)); + switch (prop.format) { + case BDF_ATOM: prop.value.atom = v; break; + case BDF_INTEGER: prop.value.int32 = _bdf_atol(v, 0, 10); break; + case BDF_CARDINAL: prop.value.card32 = _bdf_atoul(v, 0, 10); break; + } + + fontgrid_set_font_property(FONTGRID(ed->fgrid), &prop); + + if (bdf_is_xlfd_property(prop.name)) + ed->finfo_xlfd_props_modified = TRUE; + + /* + * Turn the apply button back off. + */ + gtk_widget_set_sensitive(ed->finfo_apply_prop, FALSE); +} + +static void +guiedit_delete_property(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + gchar *name; + + name = (char *) gtk_entry_get_text(GTK_ENTRY(ed->finfo_prop_name)); + + fontgrid_delete_font_property(FONTGRID(ed->fgrid), name); + + /* + * Make sure the Apply button is activated in case the delete was + * unintentional so the user can apply it again. + */ + gtk_widget_set_sensitive(ed->finfo_apply_prop, TRUE); +} + +static void +guiedit_erase_comments(GtkWidget *w, gpointer data) +{ + GtkTextBuffer *text; + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + gchar *comments; + + fontgrid_set_font_comments(FONTGRID(ed->fgrid), 0); + + /* + * Clear out the comments text widget. + */ + text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->finfo_comments)); + gtk_text_buffer_set_text(text, "", 0); + + /* + * Disable the Erase button until the next change in the comment text. + */ + gtk_widget_set_sensitive(ed->finfo_erase_comments, FALSE); + + /* + * If the font contains no comments either, disable the Apply button. If + * it has comments, then enable the Apply button so that the existing + * comments can be removed if wanted. + */ + if (fontgrid_get_font_comments(FONTGRID(ed->fgrid), &comments) == 0) + gtk_widget_set_sensitive(ed->finfo_apply_comments, FALSE); + else + gtk_widget_set_sensitive(ed->finfo_apply_comments, TRUE); + + /* + * Force the focus back to the comments widget. + */ + gtk_widget_grab_focus(ed->finfo_comments); +} + +static void +guiedit_apply_comments(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + gchar *comments; + GtkTextBuffer *text; + GtkTextIter start, end; + + text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ed->finfo_comments)); + gtk_text_buffer_get_bounds(text, &start, &end); + comments = gtk_text_buffer_get_text(text, &start, &end, TRUE); + + fontgrid_set_font_comments(FONTGRID(ed->fgrid), comments); + + /* + * Disable the Apply button until the next change in the comment text. + */ + gtk_widget_set_sensitive(ed->finfo_apply_comments, FALSE); + + /* + * Force the focus back to the comments widget. + */ + gtk_widget_grab_focus(ed->finfo_comments); +} + +static void +guiedit_change_spacing(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + /* + * Enable the device width input field depending on which radio button was + * toggled. + */ + if (w == ed->finfo_spacing[BDF_PROPORTIONAL >> 4]) + gtk_widget_set_sensitive(ed->finfo_dwidth, FALSE); + else + gtk_widget_set_sensitive(ed->finfo_dwidth, TRUE); + + /* + * Enable the Apply button. + */ + gtk_widget_set_sensitive(ed->finfo_apply_info, TRUE); +} + +static void +guiedit_apply_details(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + GtkToggleButton *tb; + const gchar *v; + bdf_property_t sp; + FontgridFontInfo finfo; + + v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_dwidth)); + finfo.monowidth = (guint16) _bdf_atos((char *) v, 0, 10); + + /* + * Get the current spacing value. + */ + finfo.spacing = 0; + tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[BDF_PROPORTIONAL >> 4]); + if (gtk_toggle_button_get_active(tb)) + finfo.spacing = BDF_PROPORTIONAL; + tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[BDF_MONOWIDTH >> 4]); + if (gtk_toggle_button_get_active(tb)) + finfo.spacing = BDF_MONOWIDTH; + tb = GTK_TOGGLE_BUTTON(ed->finfo_spacing[BDF_CHARCELL >> 4]); + if (gtk_toggle_button_get_active(tb)) + finfo.spacing = BDF_CHARCELL; + + /* + * Make sure a property gets added to the font as well so it + * gets saved in the event there is no XLFD name. + */ + sp.name = "SPACING"; + sp.format = BDF_ATOM; + switch (finfo.spacing) { + case BDF_PROPORTIONAL: sp.value.atom = "P"; break; + case BDF_MONOWIDTH: sp.value.atom = "M"; break; + case BDF_CHARCELL: sp.value.atom = "C"; break; + } + bdf_add_font_property(fontgrid_get_font(FONTGRID(ed->fgrid)), &sp); + + /* + * Set the font spacing values on all of the visible glyph editors. + */ + guigedit_set_font_spacing(finfo.spacing, finfo.monowidth); + + /* + * Get the default character. + */ + v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_default_char)); + finfo.default_char = + (glong) _bdf_atol((char *) v, 0, + fontgrid_get_code_base(FONTGRID(ed->fgrid))); + + finfo.resolution_x = (glong) + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ed->finfo_hres)); + finfo.resolution_y = (glong) + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ed->finfo_vres)); + + v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_ascent)); + finfo.font_ascent = (glong) _bdf_atol((char *) v, 0, 10); + v = gtk_entry_get_text(GTK_ENTRY(ed->finfo_descent)); + finfo.font_descent = (glong) _bdf_atol((char *) v, 0, 10); + + /* + * Turn off the Apply button until something else changes. + */ + gtk_widget_set_sensitive(ed->finfo_apply_info, FALSE); +} + +static void +notebook_switch_page(GtkNotebook *nb, GtkNotebookPage *nbp, gint pageno, + gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + /* + * Whenever the comments page is shown, force the focus on the text + * widget. + */ + if (pageno == 2) + gtk_widget_grab_focus(ed->finfo_comments); +} + +#define CLINFOMSG "Font Info: Some XLFD properties were modified.\nDo you wish to update the font name with these changes?" + +static void +close_finfo(GtkWidget *w, gint response, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (ed->finfo_xlfd_props_modified == TRUE) { + if (guiutil_yes_or_no(ed->finfo_dialog, CLINFOMSG, TRUE)) + fontgrid_update_font_name_from_properties(FONTGRID(ed->fgrid)); + } + ed->finfo_xlfd_props_modified = FALSE; + + gtk_widget_hide(ed->finfo_dialog); +} + +static void +finfo_sync_res(GtkWidget *w, GdkEventFocus *ev, gpointer data) +{ + gfloat v; + GtkSpinButton *b; + + b = GTK_SPIN_BUTTON(data); + v = (gfloat) gtk_spin_button_get_value(b); + + if (v != (gfloat) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))) { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v); + +#if 0 + /* + * Enable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +#endif + } +} + +void +guiedit_show_font_info(GtkWidget *w, gpointer data) +{ + GtkWidget *hbox, *vbox, *frame, *label, *button, *table; + GtkWidget *nb, *vbox1, *hbox1, *swin, *image; + GtkTextBuffer *text; + GtkRadioButton *rb; + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + bdf_font_t *font; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeSelection *sel; + GtkAdjustment *adj; + gint idx; + + if (ed->finfo_dialog == 0) { + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + ed->finfo_dialog = gtk_dialog_new(); + + (void) g_signal_connect(G_OBJECT(ed->finfo_dialog), + "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + nb = ed->finfo_notebook = gtk_notebook_new(); + + (void) g_signal_connect(G_OBJECT(nb), "switch_page", + G_CALLBACK(notebook_switch_page), + GUINT_TO_POINTER(ed->id)); + + /* + * Create the font info page. + */ + + /* + * Create the Apply button first so it can be used in callbacks + * for the data fields. + */ + ed->finfo_apply_info = gtk_button_new_from_stock(GTK_STOCK_APPLY); + (void) g_signal_connect(G_OBJECT(ed->finfo_apply_info), "clicked", + G_CALLBACK(guiedit_apply_details), + GUINT_TO_POINTER(ed->id)); + + + vbox = gtk_vbox_new(FALSE, 0); + + frame = gtk_frame_new("Glyph Counts"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + + ed->finfo_enc_count = gtk_label_new("0"); + gtk_misc_set_alignment(GTK_MISC(ed->finfo_enc_count), 0.0, 0.5); + label = labcon_new_label_defaults("Encoded:", + ed->finfo_enc_count, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); + + ed->finfo_unenc_count = gtk_label_new("0"); + gtk_misc_set_alignment(GTK_MISC(ed->finfo_unenc_count), 0.0, 0.5); + label = labcon_new_label_defaults("Unencoded:", + ed->finfo_unenc_count, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + frame = gtk_frame_new("Default Character"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + + ed->finfo_default_char = gtk_widget_new(gtk_entry_get_type(), + "max_length", 4, NULL); + gtk_widget_set_size_request(ed->finfo_default_char, 75, -1); + g_signal_connect_object(G_OBJECT(ed->finfo_default_char), "changed", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_info, + G_CONNECT_SWAPPED); + gtk_box_pack_start(GTK_BOX(hbox), ed->finfo_default_char, + FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + frame = gtk_frame_new("Font Spacing"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + + idx = BDF_PROPORTIONAL >> 4; + + button = ed->finfo_spacing[idx] = + gtk_radio_button_new_with_label(0, "Proportional"); + g_signal_connect(G_OBJECT(ed->finfo_spacing[idx]), "toggled", + G_CALLBACK(guiedit_change_spacing), + GUINT_TO_POINTER(ed->id)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + idx++; + rb = GTK_RADIO_BUTTON(button); + button = ed->finfo_spacing[idx] = + gtk_radio_button_new_with_label_from_widget(rb, "Monowidth"); + g_signal_connect(G_OBJECT(ed->finfo_spacing[idx]), "toggled", + G_CALLBACK(guiedit_change_spacing), + GUINT_TO_POINTER(ed->id)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + idx++; + rb = GTK_RADIO_BUTTON(button); + button = ed->finfo_spacing[idx] = + gtk_radio_button_new_with_label_from_widget(rb, "Character Cell"); + g_signal_connect(G_OBJECT(ed->finfo_spacing[idx]), "toggled", + G_CALLBACK(guiedit_change_spacing), + GUINT_TO_POINTER(ed->id)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + frame = gtk_frame_new("Font Resolution"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0, + 1.0, 10.0, 0.0); + ed->finfo_hres = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(ed->finfo_hres), TRUE); + gtk_widget_set_size_request(ed->finfo_hres, 75, -1); + g_signal_connect_object(G_OBJECT(ed->finfo_hres), "value-changed", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_info, + G_CONNECT_SWAPPED); + label = labcon_new_label_defaults("Horizontal:", ed->finfo_hres, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0, + 1.0, 10.0, 0.0); + ed->finfo_vres = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(ed->finfo_vres), TRUE); + gtk_widget_set_size_request(ed->finfo_vres, 75, -1); + g_signal_connect_object(G_OBJECT(ed->finfo_vres), "value-changed", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_info, + G_CONNECT_SWAPPED); + g_signal_connect(G_OBJECT(ed->finfo_vres), "focus-in-event", + G_CALLBACK(finfo_sync_res), + (gpointer) ed->finfo_hres); + + label = labcon_new_label_defaults("Vertical:", ed->finfo_vres, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + ed->finfo_bpp = gtk_label_new("1"); + label = labcon_new_label_defaults("Bits Per Pixel:", ed->finfo_bpp, + label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + frame = gtk_frame_new("Font Metrics"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + + ed->finfo_dwidth = gtk_widget_new(gtk_entry_get_type(), + "max_length", 4, NULL); + gtk_widget_set_size_request(ed->finfo_dwidth, 75, -1); + g_signal_connect_object(G_OBJECT(ed->finfo_dwidth), "changed", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_info, + G_CONNECT_SWAPPED); + label = labcon_new_label_defaults("Device Width:", + ed->finfo_dwidth, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + ed->finfo_ascent = gtk_widget_new(gtk_entry_get_type(), + "max_length", 4, NULL); + gtk_widget_set_size_request(ed->finfo_ascent, 75, -1); + g_signal_connect_object(G_OBJECT(ed->finfo_ascent), "changed", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_info, + G_CONNECT_SWAPPED); + label = labcon_new_label_defaults("Font Ascent:", ed->finfo_ascent, + label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + ed->finfo_descent = gtk_widget_new(gtk_entry_get_type(), + "max_length", 4, NULL); + gtk_widget_set_size_request(ed->finfo_descent, 75, -1); + g_signal_connect_object(G_OBJECT(ed->finfo_descent), "changed", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_info, + G_CONNECT_SWAPPED); + label = labcon_new_label_defaults("Font Descent:", + ed->finfo_descent, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + /* + * Finally, add the Apply button. + */ + gtk_box_pack_end(GTK_BOX(vbox), ed->finfo_apply_info, FALSE, FALSE, 5); + + label = gtk_label_new("Font Info"); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox, label); + + /* + * Create the font properties editor. + */ + vbox = gtk_vbox_new(FALSE, 5); + + table = gtk_table_new(1, 2, TRUE); + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_container_set_border_width(GTK_CONTAINER(swin), 3); + + store = gtk_list_store_new(1, G_TYPE_STRING); + ed->finfo_font_props = + gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + gtk_widget_set_size_request(ed->finfo_font_props, -1, 200); + gtk_container_add(GTK_CONTAINER(swin), ed->finfo_font_props); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Font Properties", + cell_renderer, + "text", 0, + NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(ed->finfo_font_props), + column); + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ed->finfo_font_props)); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(guiedit_select_property), + GUINT_TO_POINTER(ed->id)); + + gtk_table_attach(GTK_TABLE(table), swin, 0, 1, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_container_set_border_width(GTK_CONTAINER(swin), 3); + + store = gtk_list_store_new(1, G_TYPE_STRING); + ed->finfo_all_props = + gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + gtk_widget_set_size_request(ed->finfo_all_props, -1, 200); + gtk_container_add(GTK_CONTAINER(swin), ed->finfo_all_props); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("All Properties", + cell_renderer, + "text", 0, + NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(ed->finfo_all_props), + column); + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ed->finfo_all_props)); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(guiedit_select_property), + GUINT_TO_POINTER(ed->id)); + + gtk_table_attach(GTK_TABLE(table), swin, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 2); + + frame = gtk_frame_new("Property"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + vbox1 = gtk_vbox_new(FALSE, 5); + + table = gtk_table_new(3, 2, FALSE); + + label = gtk_label_new("Name:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 5, 0); + + label = gtk_label_new("Value:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 5, 0); + + label = gtk_label_new("Type:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 5, 0); + + ed->finfo_prop_name = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), ed->finfo_prop_name, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5); + + ed->finfo_prop_value = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), ed->finfo_prop_value, 1, 2, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5); + + hbox = gtk_hbox_new(FALSE, 2); + button = ed->finfo_prop_format[BDF_ATOM] = + gtk_radio_button_new_with_label(0, "String"); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + rb = GTK_RADIO_BUTTON(button); + button = ed->finfo_prop_format[BDF_INTEGER] = + gtk_radio_button_new_with_label_from_widget(rb, "Integer"); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + rb = GTK_RADIO_BUTTON(button); + button = ed->finfo_prop_format[BDF_CARDINAL] = + gtk_radio_button_new_with_label_from_widget(rb, "Cardinal"); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + + gtk_box_pack_start(GTK_BOX(vbox1), table, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + + hbox1 = gtk_hbox_new(FALSE, 0); + label = gtk_label_new_with_mnemonic("_Apply To Font Properties"); + image = gtk_image_new_from_stock(GTK_STOCK_APPLY, + GTK_ICON_SIZE_BUTTON); + gtk_box_pack_start(GTK_BOX(hbox1), image, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0); + + button = ed->finfo_apply_prop = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(button), hbox1); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(guiedit_apply_property), + GUINT_TO_POINTER(ed->id)); + + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + (void) g_signal_connect_object(G_OBJECT(ed->finfo_prop_value), + "changed", + G_CALLBACK(enable_apply), + (gpointer) button, + G_CONNECT_SWAPPED); + + button = ed->finfo_prop_format[BDF_ATOM]; + (void) g_signal_connect_object(G_OBJECT(button), + "toggled", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_prop, + G_CONNECT_SWAPPED); + + button = ed->finfo_prop_format[BDF_INTEGER]; + (void) g_signal_connect_object(G_OBJECT(button), + "toggled", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_prop, + G_CONNECT_SWAPPED); + + button = ed->finfo_prop_format[BDF_CARDINAL]; + (void) g_signal_connect_object(G_OBJECT(button), + "toggled", + G_CALLBACK(enable_apply), + (gpointer) ed->finfo_apply_prop, + G_CONNECT_SWAPPED); + + hbox1 = gtk_hbox_new(FALSE, 0); + label = gtk_label_new_with_mnemonic("_Delete From Font Properties"); + image = gtk_image_new_from_stock(GTK_STOCK_DELETE, + GTK_ICON_SIZE_BUTTON); + gtk_box_pack_start(GTK_BOX(hbox1), image, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0); + + button = ed->finfo_delete_prop = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(button), hbox1); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(guiedit_delete_property), + GUINT_TO_POINTER(ed->id)); + + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 2); + + gtk_container_add(GTK_CONTAINER(frame), vbox1); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2); + + label = gtk_label_new("Font Properties"); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox, label); + + /* + * Create the font comment editor. + */ + frame = gtk_frame_new("Comments"); + gtk_container_set_border_width(GTK_CONTAINER(frame), 3); + + table = gtk_table_new(2, 2, FALSE); + + text = gtk_text_buffer_new(NULL); + ed->finfo_comments = gtk_text_view_new_with_buffer(text); + gtk_widget_set_size_request(ed->finfo_comments, 400, 200); + + swin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_NEVER, + GTK_POLICY_ALWAYS); + gtk_container_add(GTK_CONTAINER(swin), ed->finfo_comments); + gtk_table_attach(GTK_TABLE(table), swin, 0, 1, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 5); + + hbox = gtk_hbox_new(FALSE, 0); + button = ed->finfo_apply_comments = + gtk_button_new_from_stock(GTK_STOCK_APPLY); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(guiedit_apply_comments), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect_data((gpointer) text, + "changed", + G_CALLBACK(guiedit_enable_comment_buttons), + GUINT_TO_POINTER(ed->id), NULL, 0); + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = ed->finfo_erase_comments = + gtk_button_new_from_stock(GTK_STOCK_CLEAR); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(guiedit_erase_comments), + GUINT_TO_POINTER(ed->id)); + + gtk_container_add(GTK_CONTAINER(hbox), button); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 2, 1, 2, + GTK_FILL, GTK_FILL, 0, 5); + gtk_container_add(GTK_CONTAINER(frame), table); + + label = gtk_label_new("Font Comments"); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), frame, label); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(ed->finfo_dialog)->vbox), + nb); + + /* + * Add the buttons. + */ + gtk_dialog_add_button(GTK_DIALOG(ed->finfo_dialog), + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); + + g_signal_connect(G_OBJECT(ed->finfo_dialog), "response", + G_CALLBACK(close_finfo), GUINT_TO_POINTER(ed->id)); + + guiedit_update_font_info(ed); + + gtk_widget_show_all(GTK_DIALOG(ed->finfo_dialog)->vbox); + gtk_widget_show_all(GTK_DIALOG(ed->finfo_dialog)->action_area); + } + + /* + * Center the dialog and show it. + */ + guiutil_show_dialog_centered(ed->finfo_dialog, ed->shell); +} + +void +guiedit_show_font_properties(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + guiedit_show_font_info(w, data); + gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->finfo_notebook), 1); +} + +void +guiedit_show_font_comments(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + guiedit_show_font_info(w, data); + gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->finfo_notebook), 2); +} + +/* + * Called when the font name is used to update the properties so the font info + * editor list of properties is updated. + */ +void +guiedit_update_font_properties(gbdfed_editor_t *ed) +{ + gchar *prop; + guint32 i, nprops; + bdf_font_t *font; + bdf_property_t *props; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreePath *row; + GtkTreeView *tview; + GtkTreeIter iter; + GValue val; + + /* + * Update the font properties list if the font info dialog has been + * created. + */ + if (ed->finfo_dialog == 0) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + /* + * This will be the name of the currently selected property, if one + * is selected. + */ + prop = 0; + + selection = + gtk_tree_view_get_selection(GTK_TREE_VIEW(ed->finfo_font_props)); + if (gtk_tree_selection_get_selected(selection, &model, &iter) != FALSE) { + memset((char *) &val, 0, sizeof(GValue)); + gtk_tree_model_get_value(model, &iter, 0, &val); + prop = (gchar *) g_value_get_string(&val); + } + + /* + * This will track the row that needs to be reselected once the list has + * been updated. + */ + row = 0; + + gtk_list_store_clear(GTK_LIST_STORE(model)); + + if ((nprops = bdf_font_property_list(font, &props)) != 0) { + for (i = 0; i < nprops; i++) { + if (prop && strcmp(prop, props[i].name) == 0) + row = gtk_tree_path_new_from_indices(i, -1); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + 0, props[i].name, -1); + } + } + + if (row != 0) { + gtk_tree_selection_select_path(selection, row); + + /* + * Make sure the selected property is made visible. + */ + tview = gtk_tree_selection_get_tree_view(selection); + gtk_tree_view_scroll_to_cell(tview, row, NULL, TRUE, 0.5, 0.5); + } + + /* + * Make sure memory is deallocated when necessary. + */ + if (prop != 0) + g_value_unset(&val); +} + +void +guiedit_copy_selection(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_copy_selection(FONTGRID(ed->fgrid)); +} + +void +guiedit_cut_selection(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_cut_selection(FONTGRID(ed->fgrid)); +} + +void +guiedit_paste_selection(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_paste_selection(FONTGRID(ed->fgrid), FONTGRID_NORMAL_PASTE); +} + +void +guiedit_overlay_selection(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_paste_selection(FONTGRID(ed->fgrid), FONTGRID_OVERLAY_PASTE); +} + +void +guiedit_insert_selection(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + fontgrid_paste_selection(FONTGRID(ed->fgrid), FONTGRID_INSERT_PASTE); +} diff --git a/guifile.c b/guifile.c new file mode 100644 index 0000000..a9f21e3 --- /dev/null +++ b/guifile.c @@ -0,0 +1,3009 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" +#include "labcon.h" + +#ifdef HAVE_XLIB +#include +#endif + +/* + * These are formats that can appear in the editor for importing/loading and + * exporting fonts. + */ +#define BDF_FORMAT 1 +#define CONSOLE_FORMAT 2 +#define PKGF_FORMAT 3 +#define FNT_FORMAT 4 +#define HBF_FORMAT 5 +#define OTF_FORMAT 6 +#define HEX_FORMAT 7 +#define PSF_FORMAT 8 +#define PSFUNI_FORMAT 9 + +/* + * An array of filters used for the open/import and save dialogs. + */ +static GtkFileFilter *filename_filters[10]; + +/* + * This variable is used to track whether the save dialog has been closed + * so the guifile_save_as_wait() routine knows when to return to the main + * application. + */ +static gboolean save_dialog_done; + +#ifdef HAVE_FREETYPE + +#include FT_GLYPH_H +#include FT_SFNT_NAMES_H +#include FT_TRUETYPE_TABLES_H + +/* + * Globals used for FreeType. + */ +static FT_Library library; +static FT_Face face; + +/* + * Globals used for importing OpenType fonts. + */ +static gboolean ftinit = FALSE; +static gboolean otf_collection; +static gboolean otf_face_open; +static gint otf_select_done = 0; +static gchar *otf_fullpath; + +/* + * These are the widgets that will be needed for importing OpenType fonts. + */ +static GtkWidget *otf_dialog; +static GtkWidget *otf_faces; +static GtkWidget *otf_platforms; +static GtkWidget *otf_encodings; +static GtkWidget *otf_point_size; +static GtkWidget *otf_hres; +static GtkWidget *otf_vres; + +/* + * List of platform IDs seen that is used when platforms are selected + * from OpenType fonts. + */ +static gint16 platforms[32]; +static gint nplatforms; + +/* + * List of encoding IDs seen that is used when encodings are selected + * from OpenType fonts. + */ +static gint16 encodings[34]; +static gint nencodings; + +/* + * Variables to hold the selected platform and encoding ID's. + */ +static gint16 otf_pid_pos; +static gint16 otf_eid_pos; + +#endif /* HAVE_FREETYPE */ + +#ifdef HAVE_XLIB + +/* + * These are for importing fonts from the X server. + */ +#define _XSRV_MAX_FONTS 32767 +#define _XSRV_DEFAULT_FILTER "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" + +static GtkWidget *xsrv_dialog; +static GtkWidget *xsrv_filter_text; +static GtkWidget *xsrv_selection_text; +static GtkWidget *xsrv_font_list; +static GtkWidget *xsrv_import; + +/* + * Because the grab dialog is shared amongst the editors, this tracks which + * editor has control of the font list. + */ +static guint xsrv_active_editor; + +#endif /* HAVE_XLIB */ + +/* + * Widgets for dealing with exporting PSF fonts. + */ +static GtkWidget *psf_export_frame; +static GtkWidget *psf_export_options; + +/* + * Widgets for selecting fonts from a Windows font archive. + */ +static GtkWidget *fnt_dialog; +static GtkWidget *fnt_font_list; +static GtkWidget *fnt_load_button; + +/* + * This is a list of Windows fonts that have been selected. It assumes that + * the font file will never contain more than 32 fonts. + */ +static gint fnt_selected[32]; +static gint fnt_selected_count; + +/* + * A structure used to pass data to the load and cancel callbacks when dealing + * with FON/FNT fonts. + */ +typedef struct { + gchar *file; + gchar *dir; + gchar *dot; + bdffnt_font_t font; +} _bdffnt_callback_data_t; + +/* + * This is used in a couple of cases to point at the active editor. + */ +static gbdfed_editor_t *active_editor; + +static void +make_file_chooser_filters(void) +{ + int i; + + if (filename_filters[0] != NULL) + return; + + filename_filters[BDF_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[BDF_FORMAT], + "*.[Bb][Dd][Ff]"); + + filename_filters[CONSOLE_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[CONSOLE_FORMAT], "*"); + + filename_filters[PKGF_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[PKGF_FORMAT], + "*[PpGg][KkFf]"); + + filename_filters[FNT_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[FNT_FORMAT], + "*.[FfEeDd][OoNnXxLl][NnTtEeLl]"); + + filename_filters[HEX_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[HEX_FORMAT], + "*.[Hh][Ee][Xx]"); + + filename_filters[PSF_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[PSF_FORMAT], + "*.[Ps][Ss][Ff]*"); + + /* + * This one is basically for exporting unimap files that belong to PSF + * fonts. + */ + filename_filters[PSFUNI_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[PSFUNI_FORMAT], + "*.[Uu][Nn][Ii]"); + +#ifdef HAVE_HBF + filename_filters[HBF_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[HBF_FORMAT], + "*.[Hh][Bb][Ff]"); +#endif + +#ifdef HAVE_FREETYPE + filename_filters[OTF_FORMAT] = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filename_filters[OTF_FORMAT], + "*.[OoTt][Tt][FfCcEe]"); +#endif /* HAVE_FREETYPE */ + + filename_filters[0] = (GtkFileFilter *) 1; + + /* + * Add a reference to all the filters so they don't cause a delayed crash + * when popping up the import dialog multiple times. + */ + for (i = 1; i < 10; i++) { + if (filename_filters[i] != NULL) + g_object_ref(filename_filters[i]); + } +} + +static gboolean +export_font(gchar *filename, gbdfed_editor_t *ed, gboolean copy_filename) +{ + FILE *out; + bdf_font_t *font; + gboolean local_font = FALSE; + bdf_property_t vanity; + FontgridSelectionInfo sinfo; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + /* + * First, attempt to make a backup if they are specified. + */ + if (options.backups) { + out = fopen(filename, "rb"); + if (out != 0) { + fclose(out); + + /* + * Attempt to make a backup. + */ + sprintf(buffer2, "%s.bak", filename); + + /* + * %PLATFORM_CHECK% + * + * Don't return here because we want to save the font even if a + * backup can't be made. + */ + if (rename(filename, buffer2)) + guiutil_error_message(ed->shell, + "Backups: Unable to make a backup."); + } + } + + /* + * Try to open the file for writing. Only PSF needs binary. + */ + out = (ed->export_format != PSF_FORMAT) ? + fopen(filename, "w") : fopen(filename, "wb"); + + if (out == 0) { + if (ed->export_format == BDF_FORMAT) + sprintf(buffer2, "Save Font: Unable to write to %s.", filename); + else + sprintf(buffer2, "Export Font: Unable to write to %s.", filename); + guiutil_error_message(ed->shell, buffer2); + return FALSE; + } + + switch (ed->export_format) { + case BDF_FORMAT: + if (!font) { + /* + * We need to create a font with the default options so it + * can be written out as a skeleton. + */ + font = bdf_new_font("unnamed", + options.font_opts.point_size, + options.font_opts.resolution_x, + options.font_opts.resolution_y, + options.font_opts.font_spacing, + options.font_opts.bits_per_pixel); + local_font = TRUE; + } + + /* + * Add a custom property if the font has been + */ + if (font->modified || local_font == TRUE) { + sprintf(buffer2, "Edited with gbdfed %s.", GBDFED_VERSION); + vanity.name = "_GBDFED_INFO"; + vanity.format = BDF_ATOM; + vanity.value.atom = buffer2; + bdf_add_font_property(font, &vanity); + } + bdf_save_font(out, font, &options.font_opts, 0, 0); + if (local_font == TRUE) + bdf_free_font(font); + break; + case HEX_FORMAT: + bdf_export_hex(out, font, &options.font_opts, 0, 0); + break; + case PSF_FORMAT: + sinfo.start = sinfo.end = 0; + (void) fontgrid_has_selection(FONTGRID(ed->fgrid), &sinfo); + if (sinfo.start == sinfo.end) { + sinfo.start = font->glyphs[0].encoding; + sinfo.end = font->glyphs[font->glyphs_used - 1].encoding; + } + switch (bdf_export_psf(out, font, &options.font_opts, + sinfo.start, sinfo.end)) { + case BDF_OK: + buffer1[0] = 0; + break; + case BDF_BAD_RANGE: + sprintf(buffer1, "Export PSF: Invalid range %d-%d.\n", + sinfo.start, sinfo.end); + break; + case BDF_PSF_CORRUPT_UTF8: + strcpy(buffer1, + "Export PSF: Bad UTF-8 encountered in the mappings."); + break; + } + if (buffer1[0] != 0) + /* + * Something went wrong during the PSF export. + */ + guiutil_error_message(ed->shell, buffer1); + } + + fclose(out); + + /* + * The rest of this only applies to BDF fonts and not PSF or HEX fonts. + * PSF and HEX fonts have their own extensions in the save dialog, but + * that does not affect the actual file name in the editor. + */ + if (ed->export_format == BDF_FORMAT) { + + /* + * Copy the path and filename into the editor if specified. + */ + if (copy_filename) { + if (ed->path) + g_free(ed->path); + if (ed->file) + g_free(ed->file); + ed->path = ed->file = 0; + ed->file = g_path_get_basename(filename); + ed->path = g_path_get_dirname(filename); + } + + /* + * Mark the font as being unmodified. + */ + fontgrid_set_font_modified(FONTGRID(ed->fgrid), FALSE); + + /* + * Update the window title accordingly. + */ + if (ed->file) + sprintf(buffer1, "%s - %s", g_get_prgname(), ed->file); + else + sprintf(buffer1, "%s - (unnamed%d)", g_get_prgname(), ed->id); + + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Since the font was saved as BDF, it is no longer marked as being + * imported. + */ + ed->imported = FALSE; + } + + return TRUE; +} + +static void +really_save_font(guint ed_id) +{ + gbdfed_editor_t *ed = editors + ed_id; + gchar *fname; + FILE *have; +#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10) + GtkRecentManager *recent; +#endif + + fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(ed->save_dialog)); + + have = fopen(fname, "rb"); + if (have != 0) { + fclose(have); + + /* + * Check to see if the user wishes to overwrite the existing font. + */ + sprintf(buffer2, "Save Font: %s exists.\nDo you wish to overwrite?", + fname); + if (guiutil_yes_or_no(ed->shell, buffer2, TRUE) == FALSE) { + g_free(fname); + return; + } + } + + /* + * If the write was successful, hide the dialog. + */ + if (export_font(fname, ed, TRUE)) { + save_dialog_done = TRUE; + gtk_widget_hide(ed->save_dialog); +#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10) + recent = gtk_recent_manager_get_default(); + sprintf(buffer1, "file://%s", fname); + if (gtk_recent_manager_has_item(recent, + (const gchar *) buffer1) == FALSE) + gtk_recent_manager_add_item(recent, + (const gchar *) buffer1); +#endif + } + g_free(fname); +} + +/* + * This callback routine handles errors and updating the progress bar if + * one is being used. + */ +static void +handle_import_messages(bdf_callback_struct_t *call_data, void *client_data) +{ + if (call_data->reason == BDF_ERROR) { + sprintf(buffer1, "Import Font:%d: error: See the font messages.", + call_data->errlineno); + guiutil_error_message(GTK_WIDGET(client_data), buffer1); + } +} + +/************************************************************************** + * + * BDF section. + * + **************************************************************************/ + +static void +load_bdf_font(gbdfed_editor_t *ed, const gchar *fullpath, const gchar *dir, + const gchar *file) +{ + FILE *in; + bdf_font_t *font; + + /* + * Check to see if the file can be opened. + */ + if ((in = fopen(fullpath, "rb")) == 0) { + sprintf(buffer1, "Import Font: Unable to open %s.", file); + guiutil_error_message(ed->shell, buffer1); + return; + } + + guiutil_busy_cursor(ed->shell, TRUE); + if (ed->open_dialog != NULL) + guiutil_busy_cursor(ed->open_dialog, TRUE); + + font = bdf_load_font(in, &options.font_opts, + handle_import_messages, (void *) ed->shell); + + guiutil_busy_cursor(ed->shell, FALSE); + if (ed->open_dialog != NULL) + guiutil_busy_cursor(ed->open_dialog, FALSE); + + if (font == 0) { + fclose(in); + sprintf(buffer1, "Import Font: Unable to load %s.", file); + guiutil_error_message(ed->shell, buffer1); + return; + } + + fclose(in); + if (ed->open_dialog != NULL) + gtk_widget_hide(ed->open_dialog); + + /* + * Delete the file and path names so they can be updated. + */ + if (ed->file != 0) + g_free(ed->file); + if (ed->path != 0) + g_free(ed->path); + + ed->file = ed->path = 0; + + ed->file = strdup(file); + ed->path = strdup(dir); + + /* + * Update the window title. + */ + if (font->modified) + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file); + else + sprintf(buffer1, "%s - %s", g_get_prgname(), ed->file); + + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Tell the glyphtest widget to remove references to the current font if + * it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + fontgrid_set_font(FONTGRID(ed->fgrid), font, -1); + + /* + * Finally, update the font name field. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); + + /* + * Make sure the imported flag is cleared in this case. + */ + ed->imported = FALSE; +} + +/************************************************************************** + * + * Console section. + * + **************************************************************************/ + +static void +load_console_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot, + gchar *dir, gchar *file) +{ + FILE *in; + gbdfed_editor_t *ep; + gint i, j, nfonts, len; + gchar *np; + bdf_font_t *fonts[3]; + + /* + * Check to see if the file can be opened. + */ + if ((in = fopen(fullpath, "rb")) == 0) { + sprintf(buffer1, "Import Font: Unable to open %s.", fullpath); + guiutil_error_message(ed->shell, buffer1); + return; + } + + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(ed->open_dialog, TRUE); + + i = bdf_load_console_font(in, &options.font_opts, 0, 0, fonts, &nfonts); + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + fclose(in); + + if (i != BDF_OK) { + /* + * Free up any font structures that happened to be loaded. + */ + for (j = 0; j < nfonts; j++) + bdf_free_font(fonts[j]); + + sprintf(buffer1, "Import Font: %s not a console font.", fullpath); + guiutil_error_message(ed->shell, buffer1); + return; + } + + gtk_widget_hide(ed->open_dialog); + + /* + * Handle creation of the editors. In the case of some console fonts, + * there are three different sizes contained in the font. + */ + for (i = 0; i < nfonts; i++) { + if (i) + ep = editors + gbdfed_make_editor(0, FALSE); + else { + ep = ed; + + /* + * Erase the existing file and directory name in the "root" + * editor. + */ + if (ep->file) + g_free(ep->file); + if (ep->path) + g_free(ep->path); + ep->file = ep->path = 0; + + /* + * Tell the glyphtest widget to remove references to the current + * font, if it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ep->fgrid))); + } + + /* + * Make an XLFD name for the font using the filename. Run through the + * file name and change all occurences of '-' to '_' to avoid problems + * with '-' being the XLFD field separator. + */ + for (j = 0, np = file; np < dot; np++, j++) + buffer2[j] = (*np != '-') ? *np : '_'; + buffer2[j] = 0; + + fonts[i]->name = + bdf_make_xlfd_name(fonts[i], "Unknown", buffer2); + bdf_update_properties_from_name(fonts[i]); + + len = (gint) (dot - file); + + /* + * Create the default name for the font file. + */ + if (nfonts == 3) { + switch (i) { + case 0: + sprintf(buffer1, "%.*s-16.bdf", len, file); + break; + case 1: + sprintf(buffer1, "%.*s-14.bdf", len, file); + break; + case 2: + sprintf(buffer1, "%.*s-08.bdf", len, file); + break; + } + } else + sprintf(buffer1, "%.*s.bdf", len, file); + + /* + * Set the filename for the editor. + */ + ep->file = strdup(buffer1); + ep->path = strdup(dir); + + /* + * Set the new editor title. + */ + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ep->file); + gtk_window_set_title(GTK_WINDOW(ep->shell), buffer1); + + /* + * Change the font in the editor. + */ + fontgrid_set_font(FONTGRID(ep->fgrid), fonts[i], -1); + + /* + * Indicate the font was imported. + */ + ed->imported = TRUE; + + /* + * Update the XLFD name. + */ + gtk_entry_set_text(GTK_ENTRY(ep->fontname), + fontgrid_get_font_name(FONTGRID(ep->fgrid))); + } +} + +/************************************************************************** + * + * PK/GF section. + * + **************************************************************************/ + +static void +load_pkgf_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot, + gchar *dir, gchar *file) +{ + FILE *in; + gint i; + gchar *np; + bdf_font_t *font; + + /* + * Check to see if the file can be opened. + */ + if ((in = fopen(fullpath, "rb")) == 0) { + sprintf(buffer1, "Import Font: Unable to open %s.", file); + guiutil_error_message(ed->shell, buffer1); + return; + } + + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(ed->open_dialog, TRUE); + + i = bdf_load_mf_font(in, &options.font_opts, 0, 0, &font); + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + fclose(in); + + if (i != BDF_OK) { + sprintf(buffer1, "Import Font: %s not a PK or GF font.", fullpath); + guiutil_error_message(ed->shell, buffer1); + return; + } + + gtk_widget_hide(ed->open_dialog); + + /* + * Make an XLFD name for the font using the filename. Run through the + * file name and change all occurences of '-' to '_' to avoid problems + * with '-' being the XLFD field separator. + */ + for (i = 0, np = file; np < dot; np++, i++) + buffer2[i] = (*np != '-') ? *np : '_'; + buffer2[i] = 0; + + font->name = bdf_make_xlfd_name(font, "Unknown", buffer2); + bdf_update_properties_from_name(font); + + /* + * Now set up a file name. + */ + sprintf(buffer1, "%.*s.bdf", (int) (dot - file), file); + + /* + * Delete the file and path names so they can be updated. + */ + if (ed->file != 0) + g_free(ed->file); + if (ed->path != 0) + g_free(ed->path); + + ed->file = strdup(buffer1); + ed->path = strdup(dir); + + /* + * Update the window title. + */ + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file); + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Tell the glyphtest widget to remove references to the current font if + * it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + fontgrid_set_font(FONTGRID(ed->fgrid), font, -1); + + /* + * Indicate the font was imported. + */ + ed->imported = TRUE; + + /* + * Finally, update the font name field. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); +} + +/************************************************************************** + * + * FNT section. + * + **************************************************************************/ + +/* + * Toggles the "Ok" button on or off depending if there was a selection or + * not. + */ +static void +fnt_check_load_button(GtkWidget *w, gpointer data) +{ + GtkTreeSelection *sel = GTK_TREE_SELECTION(data); + + if (gtk_tree_selection_count_selected_rows(sel) == 0) + gtk_widget_set_sensitive(fnt_load_button, FALSE); + else + gtk_widget_set_sensitive(fnt_load_button, TRUE); +} + +static void +fnt_unselect_all(GtkWidget *w, gpointer data) +{ + GtkTreeSelection *sel = GTK_TREE_SELECTION(data); + + gtk_tree_selection_unselect_all(sel); + + /* + * Disable the Ok button since everything is unselected. + */ + gtk_widget_set_sensitive(fnt_load_button, FALSE); +} + +static void +fnt_select_all(GtkWidget *w, gpointer data) +{ + GtkTreeSelection *sel = GTK_TREE_SELECTION(data); + + gtk_tree_selection_select_all(sel); + + /* + * Enable the Ok button since everything is unselected. + */ + gtk_widget_set_sensitive(fnt_load_button, TRUE); +} + +static void +fnt_foreach_selected(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, + gpointer data) +{ + gint *id; + + id = gtk_tree_path_get_indices(path); + fnt_selected[fnt_selected_count++] = *id; +} + +static void +fnt_load_selected_fonts(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ep, *ed = editors + GPOINTER_TO_UINT(data); + GtkTreeSelection *sel = + gtk_tree_view_get_selection(GTK_TREE_VIEW(fnt_font_list)); + gint i, nfonts; + gboolean loaded; + bdf_font_t **fonts; + _bdffnt_callback_data_t *cdata; + + fnt_selected_count = 0; + + if ((cdata = g_object_get_data(G_OBJECT(w), + "bdffnt_callback_data")) == NULL) { + /* + * Big problem. Should never happen. + */ + guiutil_error_message(editors[0].shell, + "BIG PROBLEM PASSING OPEN FON/FNT FONT!!!!"); + return; + } + + /* + * This collects all the selected indices from the list and puts them in + * the global fnt_selected array. + */ + gtk_tree_selection_selected_foreach(sel, fnt_foreach_selected, NULL); + + /* + * CHANGE - maybe. + */ + if (fnt_selected_count == 0) + return; + + /* + * Hide the dialog that allowed selection of the fonts in the file. + */ + gtk_widget_hide(fnt_dialog); + + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(ed->open_dialog, TRUE); + + fonts = (bdf_font_t **) + g_malloc(sizeof(bdf_font_t *) * fnt_selected_count); + for (loaded = TRUE, nfonts = 0; + nfonts < fnt_selected_count && loaded == TRUE; + nfonts++) { + /* + * If the current font can't be loaded, then assume the rest are + * not available either. + */ + if (bdffnt_load_font(cdata->font, fnt_selected[nfonts], + 0, 0, &fonts[nfonts]) != 0) { + /* + * It is easier to get the font name from the font than it is + * from the list store. + */ + (void) bdffnt_get_facename(cdata->font, fnt_selected[nfonts], 0, + (unsigned char *) buffer1); + sprintf(buffer2, "Import Font: Unable to load %s from %s.", + buffer1, cdata->file); + guiutil_error_message(ed->shell, buffer2); + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + loaded = FALSE; + } + } + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + /* + * If no fonts were loaded, then simply return with the open dialog still + * up, giving the user a chance to load another font. + */ + if (nfonts == 0) { + g_free(fonts); + return; + } + + /* + * Hide the open dialog. + */ + gtk_widget_hide(ed->open_dialog); + + /* + * Create the editors for the fonts that did get loaded. + */ + for (i = 0; i < nfonts; i++) { + if (i) + ep = editors + gbdfed_make_editor(0, FALSE); + else { + ep = ed; + + /* + * Erase the existing file and directory name in the "root" + * editor. + */ + if (ep->file) + g_free(ep->file); + if (ep->path) + g_free(ep->path); + ep->file = ep->path = 0; + + /* + * Tell the glyphtest widget to remove references to the current + * font, if it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ep->fgrid))); + } + + /* + * Make the BDF file name for the font. + */ + sprintf(buffer1, "%.*s%d.bdf", (int) (cdata->dot - cdata->file), + cdata->file, fonts[i]->point_size); + + ep->file = strdup(buffer1); + ep->path = strdup(cdata->dir); + + /* + * Set the new editor title. + */ + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ep->file); + gtk_window_set_title(GTK_WINDOW(ep->shell), buffer1); + + /* + * Change the font in the editor. + */ + fontgrid_set_font(FONTGRID(ep->fgrid), fonts[i], -1); + + /* + * Indicate the font was imported. + */ + ep->imported = TRUE; + + /* + * Update the XLFD name. + */ + gtk_entry_set_text(GTK_ENTRY(ep->fontname), + fontgrid_get_font_name(FONTGRID(ep->fgrid))); + } + + g_free(cdata->file); + g_free(cdata->dir); + bdffnt_close_font(cdata->font); + + g_free(fonts); +} + +static void +fnt_cancel(GtkWidget *w, gpointer data) +{ + _bdffnt_callback_data_t *cdata; + + /* + * If the load callback stole the data already, this will be NULL. + */ + if ((cdata = g_object_get_data(G_OBJECT(w), + "bdffnt_callback_data")) == NULL) { + /* + * Big problem. Should never happen. + */ + guiutil_error_message(editors[0].shell, + "BIG PROBLEM PASSING OPEN FON/FNT FONT!!!!"); + return; + } + + g_free(cdata->file); + g_free(cdata->dir); + bdffnt_close_font(cdata->font); + + gtk_widget_hide(fnt_dialog); +} + +static void +fnt_row_activate(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, + gpointer data) +{ + fnt_load_selected_fonts(GTK_WIDGET(view), data); +} + +static void +load_windows_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot, + gchar *dir, gchar *file) +{ + gint i, nfonts; + bdffnt_font_t fnt; + bdf_font_t *font; + _bdffnt_callback_data_t *cdata; + GtkWidget *button, *vbox, *hbox, *swin; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeSelection *sel; + GtkTreePath *tpath; + GtkTreeIter iter; + + if (bdffnt_open_font(fullpath, &fnt) <= 0) { + sprintf(buffer1, "Import Font: Unable to open %s.", file); + guiutil_error_message(ed->shell, buffer1); + g_free(dir); + return; + } + + nfonts = bdffnt_font_count(fnt); + + if (nfonts == 1) { + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(ed->open_dialog, TRUE); + + if (bdffnt_load_font(fnt, 0, 0, 0, &font) != 0) { + sprintf(buffer1, "Import Font: Unable to load %s.", file); + guiutil_error_message(ed->shell, buffer1); + g_free(dir); + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + return; + } + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + gtk_widget_hide(ed->open_dialog); + + /* + * Now set up a file name. + */ + sprintf(buffer1, "%.*s.bdf", (int) (dot - file), file); + + /* + * Delete the file and path names so they can be updated. + */ + if (ed->file != 0) + g_free(ed->file); + if (ed->path != 0) + g_free(ed->path); + + ed->file = strdup(buffer1); + ed->path = strdup(dir); + + /* + * Update the window title. + */ + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file); + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Tell the glyphtest widget to remove references to the current font + * if it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + fontgrid_set_font(FONTGRID(ed->fgrid), font, -1); + + /* + * Indicate the font was imported. + */ + ed->imported = TRUE; + + /* + * Finally, update the font name field. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); + return; + } + + /* + * More than one font was found. Present the dialog to choose the fonts. + */ + if (fnt_dialog == 0) { + /* + * Create a structure that will hold data needed by a couple callback + * routines. + */ + cdata = g_malloc(sizeof(_bdffnt_callback_data_t)); + cdata->file = strdup(file); + cdata->dir = strdup(dir); + cdata->dot = cdata->file + (dot - file); + cdata->font = fnt; + + fnt_dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(fnt_dialog), "Windows Font Selection"); + + g_object_set_data(G_OBJECT(fnt_dialog), "bdffnt_callback_data", + (gpointer) cdata); + + (void) g_signal_connect(G_OBJECT(fnt_dialog), "delete_event", + G_CALLBACK(fnt_cancel), 0); + + vbox = GTK_DIALOG(fnt_dialog)->vbox; + hbox = GTK_DIALOG(fnt_dialog)->action_area; + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_list_store_new(1, G_TYPE_STRING); + fnt_font_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + g_object_set_data(G_OBJECT(fnt_font_list), "bdffnt_callback_data", + (gpointer) cdata); + + gtk_widget_set_size_request(fnt_font_list, -1, 160); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Fonts: 0", + cell_renderer, + "text", 0, + NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(fnt_font_list), column); + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(fnt_font_list)); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(fnt_check_load_button), + (gpointer) sel); + + gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); + + (void) g_signal_connect(G_OBJECT(fnt_font_list), "row_activated", + G_CALLBACK(fnt_row_activate), + GUINT_TO_POINTER(ed->id)); + + gtk_container_add(GTK_CONTAINER(swin), fnt_font_list); + + gtk_box_pack_start(GTK_BOX(vbox), swin, FALSE, FALSE, 0); + + button = gtk_button_new_with_label("Select All"); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(fnt_select_all), + (gpointer) sel); + + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = gtk_button_new_from_stock(GTK_STOCK_CLEAR); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(fnt_unselect_all), + (gpointer) sel); + + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(fnt_cancel), + GUINT_TO_POINTER(ed->id)); + + gtk_container_add(GTK_CONTAINER(hbox), button); + + fnt_load_button = gtk_button_new_from_stock(GTK_STOCK_OK); + + /* + * Here we store a bunch of data to the buttons that are necessary to + * load FON/FNT fonts in the callback. + */ + g_object_set_data(G_OBJECT(fnt_load_button), "bdffnt_callback_data", + (gpointer) cdata); + g_object_set_data(G_OBJECT(button), "bdffnt_callback_data", + (gpointer) cdata); + + (void) g_signal_connect(G_OBJECT(fnt_load_button), "clicked", + G_CALLBACK(fnt_load_selected_fonts), + GUINT_TO_POINTER(ed->id)); + + gtk_container_add(GTK_CONTAINER(hbox), fnt_load_button); + + gtk_widget_show_all(vbox); + gtk_widget_show_all(hbox); + } else { + /* + * Fill the CDATA item in with the latest info. + */ + cdata = g_object_get_data(G_OBJECT(fnt_load_button), + "bdffnt_callback_data"); + cdata->file = strdup(file); + cdata->dir = strdup(dir); + cdata->dot = cdata->file + (dot - file); + cdata->font = fnt; + } + + store = + GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(fnt_font_list))); + column = gtk_tree_view_get_column(GTK_TREE_VIEW(fnt_font_list), 0); + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(fnt_font_list)); + + /* + * Set the number of fonts. + */ + sprintf(buffer1, "Fonts: %d", nfonts); + gtk_tree_view_column_set_title(column, buffer1); + + /* + * Clear the list and add the font names. + */ + gtk_list_store_clear(store); + for (i = 0; i < nfonts; i++) { + (void) bdffnt_get_facename(fnt, i, 0, (unsigned char *) buffer1); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, buffer1, -1); + } + + /* + * Force the first one to be selected by default. + */ + tpath = gtk_tree_path_new_from_indices(0, -1); + gtk_tree_selection_select_path(sel, tpath); + + /* + * Show the dialog and wait until the selection is done. + */ + guiutil_show_dialog_centered(fnt_dialog, ed->shell); + + /* + * Force the user to interact with this dialog before doing anything else. + */ + gtk_window_set_modal(GTK_WINDOW(fnt_dialog), TRUE); +} + +/************************************************************************** + * + * OTF section. + * + **************************************************************************/ + +#ifdef HAVE_FREETYPE + +static void +choose_otf_encoding(GtkTreeSelection *selection, gpointer data) +{ + gint *rows; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *tpath; + + /* + * Get the row of the current selection. + */ + if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE) + return; + tpath = gtk_tree_model_get_path(model, &iter); + rows = gtk_tree_path_get_indices(tpath); + otf_eid_pos = (gint16) rows[0]; +} + +static void +choose_otf_platform(GtkTreeSelection *selection, gpointer data) +{ + gchar *name; + gint i, ncmaps, sel, *rows; + gint16 pid, eid, lasteid; + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + GtkTreePath *tpath; + GtkTreeView *tview; + + /* + * Get the row of the current selection. + */ + if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE) + return; + tpath = gtk_tree_model_get_path(model, &iter); + rows = gtk_tree_path_get_indices(tpath); + otf_pid_pos = (gint16) rows[0]; + + /* + * Clear the encoding list. + */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(otf_encodings))); + gtk_list_store_clear(store); + + /* + * Collect the list of encoding IDs and put their names in the encoding + * list. + */ + nencodings = 0; + ncmaps = face->num_charmaps; + for (lasteid = -1, sel = i = 0; i < ncmaps; i++) { + pid = face->charmaps[i]->platform_id; + eid = face->charmaps[i]->encoding_id; + if (pid == platforms[otf_pid_pos] && eid != lasteid) { + name = bdfotf_encoding_name(pid, eid); + if (strcmp(name, "ISO10646") == 0) + sel = nencodings; + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, name, -1); + encodings[nencodings++] = eid; + lasteid = eid; + } + } + + /* + * Default the selection to the ISO10646 encoding. + */ + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_encodings)); + tpath = gtk_tree_path_new_from_indices(sel, -1); + gtk_tree_selection_select_path(selection, tpath); + + /* + * Make sure the encoding is made visible. + */ + tview = gtk_tree_selection_get_tree_view(selection); + gtk_tree_view_scroll_to_cell(tview, tpath, NULL, TRUE, 0.5, 0.5); +} + +static void +choose_otf(GtkTreeSelection *selection, gpointer data) +{ + gchar *name; + gint i, ncmaps, sel, row, *rows; + gint16 pid, eid, lastpid; + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + GtkTreePath *tpath; + GtkTreeView *tview; + GValue val; + + /* + * This is called after the list is cleared as well, so return if there is + * no selection. + */ + if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE) + return; + + /* + * Get the name of the face currently selected and it's index. This is + * way more complicated than it should be. + */ + (void) memset((char *) &val, 0, sizeof(GValue)); + tpath = gtk_tree_model_get_path(model, &iter); + rows = gtk_tree_path_get_indices(tpath); + row = rows[0]; + + /* + * Clear the platform list before trying to open the new face. + */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(otf_platforms))); + gtk_list_store_clear(store); + + if (otf_collection) { + if (otf_face_open) + FT_Done_Face(face); + if (FT_New_Face(library, otf_fullpath, row, &face)) { + otf_face_open = FALSE; + gtk_tree_selection_get_selected(selection, &model, &iter); + gtk_tree_model_get_value(model, &iter, 0, &val); + name = (gchar *) g_value_get_string(&val); + sprintf(buffer1, + "Import Font: Unable to open OpenType collection %s.", + name); + g_value_unset(&val); + guiutil_error_message(active_editor->shell, buffer1); + return; + } + otf_face_open = TRUE; + } + + /* + * Collect the list of platform IDs and put their names in the platform + * list. + */ + nplatforms = 0; + ncmaps = face->num_charmaps; + for (lastpid = -1, sel = i = 0; i < ncmaps; i++) { + pid = face->charmaps[i]->platform_id; + eid = face->charmaps[i]->encoding_id; + if (pid != lastpid) { + /* + * Add the platform name to the list. If the name happens to be + * Microsoft, select it as the default. + */ + name = bdfotf_platform_name(pid); + if (strcmp(name, "Microsoft") == 0) + sel = nplatforms; + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, name, -1); + platforms[nplatforms++] = pid; + lastpid = pid; + } + } + + /* + * Select the default platform, which is hard-coded to be Microsoft at the + * moment. + */ + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_platforms)); + tpath = gtk_tree_path_new_from_indices(sel, -1); + gtk_tree_selection_select_path(selection, tpath); + + /* + * Make sure the platform is made visible. + */ + tview = gtk_tree_selection_get_tree_view(selection); + gtk_tree_view_scroll_to_cell(tview, tpath, NULL, TRUE, 0.5, 0.5); +} + +static void +otf_dialog_done(GtkWidget *w, gpointer data) +{ + otf_select_done = GPOINTER_TO_INT(data); + gtk_widget_hide(otf_dialog); +} + +static void +otf_reset_metrics(GtkWidget *w, gpointer data) +{ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_point_size), + (gfloat) options.font_opts.point_size); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_hres), + (gfloat) options.font_opts.resolution_x); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_vres), + (gfloat) options.font_opts.resolution_y); +} + +/* + * Synchronize the vertical resolution with the horizontal resolution. + */ +static void +otf_sync_res(GtkWidget *w, GdkEventFocus *ev, gpointer data) +{ + gfloat v; + GtkSpinButton *b; + + b = GTK_SPIN_BUTTON(data); + v = (gfloat) gtk_spin_button_get_value(b); + + if (v != (gfloat) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v); +} + +static void +make_otf_import_dialog(void) +{ + GtkWidget *label, *vbox, *hbox, *button, *table, *swin; + GtkAdjustment *adj; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeSelection *sel; + GList *fchain; + + otf_dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(otf_dialog), "OpenType Selection"); + + (void) g_signal_connect(G_OBJECT(otf_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + vbox = GTK_DIALOG(otf_dialog)->vbox; + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_list_store_new(1, G_TYPE_STRING); + otf_faces = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ("Faces", + cell_renderer, + "text", 0, + NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column (GTK_TREE_VIEW(otf_faces), column); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_faces)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE); + + (void) g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(choose_otf), + NULL); + + /* + * Set the size of the list explicitly to make enough space for + * approximately five entries. + */ + gtk_widget_set_size_request(otf_faces, -1, 100); + + gtk_container_add(GTK_CONTAINER(swin), otf_faces); + + gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0); + + /* + * Create a table to hold the other two lists. + */ + table = gtk_table_new(1, 2, TRUE); + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_list_store_new(1, G_TYPE_STRING); + otf_platforms = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Platforms", + cell_renderer, + "text", 0, + NULL); + + gtk_widget_set_size_request(otf_platforms, 200, 70); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(otf_platforms), column); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_platforms)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(choose_otf_platform), NULL); + + gtk_container_add(GTK_CONTAINER(swin), otf_platforms); + + /* + * Attach the platform list to the table. + */ + gtk_table_attach(GTK_TABLE(table), swin, 0, 1, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_list_store_new(1, G_TYPE_STRING); + otf_encodings = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Encodings", + cell_renderer, + "text", 0, + NULL); + + gtk_widget_set_size_request(otf_encodings, 200, 70); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(otf_encodings), column); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(otf_encodings)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(choose_otf_encoding), NULL); + + gtk_container_add(GTK_CONTAINER(swin), otf_encodings); + + /* + * Attach the encodings list to the table. + */ + gtk_table_attach(GTK_TABLE(table), swin, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); + + gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0); + + /* + * Make a table that will contain the point size and resolution + * spin buttons. + */ + table = gtk_table_new(3, 3, FALSE); + + /* + * Make the spin button labels. + */ + label = gtk_label_new("Point Size:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, + 5, 5); + label = gtk_label_new("Horizontal Resolution:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, + 5, 5); + label = gtk_label_new("Vertical Resolution:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL, + 5, 5); + + /* + * Make the spin buttons. + */ + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 4.0, 256.0, 1.0, 2.0, 0.0); + otf_point_size = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(otf_point_size), TRUE); + gtk_widget_set_size_request(otf_point_size, 100, -1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_point_size), + (gfloat) options.font_opts.point_size); + gtk_table_attach(GTK_TABLE(table), otf_point_size, 1, 2, 0, 1, + GTK_FILL, GTK_FILL, 5, 5); + + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 72.0, 1200.0, + 1.0, 10.0, 0.0); + otf_hres = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(otf_hres), TRUE); + gtk_widget_set_size_request(otf_hres, 100, -1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_hres), + (gfloat) options.font_opts.resolution_x); + gtk_table_attach(GTK_TABLE(table), otf_hres, 1, 2, 1, 2, + GTK_FILL, GTK_FILL, 5, 5); + + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 72.0, 1200.0, + 1.0, 10.0, 0.0); + otf_vres = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(otf_vres), TRUE); + gtk_widget_set_size_request(otf_vres, 100, -1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(otf_vres), + (gfloat) options.font_opts.resolution_y); + (void) g_signal_connect(G_OBJECT(otf_vres), "focus-in-event", + G_CALLBACK(otf_sync_res), + (gpointer) otf_hres); + (void) g_signal_connect(G_OBJECT(otf_hres), "focus-in-event", + G_CALLBACK(otf_sync_res), + (gpointer) otf_vres); + gtk_table_attach(GTK_TABLE(table), otf_vres, 1, 2, 2, 3, + GTK_FILL, GTK_FILL, 5, 5); + + /* + * Make the reset button. + */ + label = gtk_button_new_with_label("Reset"); + (void) g_signal_connect(G_OBJECT(label), "clicked", + G_CALLBACK(otf_reset_metrics), 0); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL, + 10, 0); + + /* + * Do some fiddling to adjust the focus chain so the Reset button is at + * the end instead of in the middle. + */ + fchain = g_list_append(NULL, (gpointer) otf_point_size); + fchain = g_list_append(fchain, (gpointer) otf_hres); + fchain = g_list_append(fchain, (gpointer) otf_vres); + fchain = g_list_append(fchain, (gpointer) label); + gtk_container_set_focus_chain(GTK_CONTAINER(table), fchain); + g_list_free(fchain); + + gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, FALSE, 10); + + /* + * Add the buttons at the bottom of the dialog. + */ + hbox = GTK_DIALOG(otf_dialog)->action_area; + + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(hbox), button); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(otf_dialog_done), + GINT_TO_POINTER(-1)); + button = gtk_button_new_from_stock(GTK_STOCK_OK); + gtk_container_add(GTK_CONTAINER(hbox), button); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(otf_dialog_done), + GINT_TO_POINTER(1)); + + gtk_widget_show_all(vbox); + gtk_widget_show_all(hbox); +} + +static void +load_otf_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot, + gchar *dir, gchar *file) +{ + gint i, res; + gint32 psize, hres, vres; + gchar *np; + bdf_font_t *font; + bdf_property_t prop; + GtkListStore *store; + GtkTreeIter iter; + + active_editor = ed; + otf_fullpath = fullpath; + + /* + * Determine if this is an OT collection or just a normal font. + */ + np = dot + strlen(dot) - 1; + otf_collection = (*np == 'c' || *np == 'C') ? TRUE : FALSE; + + /* + * Initialize the FreeType engine once. + */ + if (!ftinit) { + if (FT_Init_FreeType(&library) != 0) { + strcpy(buffer1, + "Import Font: Unable to initialize the FreeType engine."); + guiutil_error_message(ed->shell, buffer1); + return; + } + ftinit = TRUE; + } + + /* + * Attempt to open the font or collection. + */ + if (FT_New_Face(library, fullpath, 0, &face)) { + if (!otf_collection) + sprintf(buffer1, "Import Font: Unable to open OpenType font '%s'.", + file); + else + sprintf(buffer1, + "Import Font: Unable to open OpenType collection '%s'.", + file); + guiutil_error_message(ed->shell, buffer1); + return; + } + + /* + * Construct the dialog that will display various choices that will be + * needed when loading the font. + */ + if (otf_dialog == 0) + make_otf_import_dialog(); + + /* + * Clear the lists and reset the values. + */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(otf_faces))); + gtk_list_store_clear(store); + + otf_face_open = TRUE; + otf_collection = face->num_faces; + np = buffer1; + + if (otf_collection == 1) { + if (bdfotf_get_english_string(face, BDFOTF_FULLNAME_STRING, + 0, buffer1) == 0) + (void) strcpy(buffer1, "Unknown"); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, buffer1, -1); + } else { + otf_face_open = FALSE; + FT_Done_Face(face); + for (i = 0; i < otf_collection; i++) { + if (!FT_New_Face(library, fullpath, i, &face)) { + if (bdfotf_get_english_string(face, BDFOTF_FULLNAME_STRING, + 0, buffer1) == 0) + sprintf(buffer1, "Unknown%d", i); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, buffer1, -1); + + FT_Done_Face(face); + } + } + } + + guiutil_show_dialog_centered(otf_dialog, ed->shell); + + /* + * Force the user to interact with this dialog before doing anything else. + */ + gtk_window_set_modal(GTK_WINDOW(otf_dialog), TRUE); + + otf_select_done = 0; + while (otf_select_done == 0) + gtk_main_iteration(); + + /* + * Reinitialize various globals when we are done. + */ + active_editor = 0; + otf_fullpath = 0; + + if (otf_select_done < 0) { + if (otf_face_open) + FT_Done_Face(face); + otf_face_open = FALSE; + return; + } + + /* + * Get the requested point size and resolutions. + */ + psize = (gint32) + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(otf_point_size)); + hres = (gint32) + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(otf_hres)); + vres = (gint32) + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(otf_vres)); + + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(ed->open_dialog, TRUE); + + /* + * Actually store the resolution and point size to the options so they + * will be used for other imports. The setup dialog will unfortunately + * assume this are the default values. May fix later so setup knows the + * values changed. + */ + options.font_opts.point_size = psize; + options.font_opts.resolution_x = hres; + options.font_opts.resolution_y = vres; + + /* + * Actually load the font. + */ + res = bdfotf_load_font(face, platforms[otf_pid_pos], + encodings[otf_eid_pos], &options.font_opts, + 0, 0, &font); + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + FT_Done_Face(face); + otf_face_open = FALSE; + + if (!res) { + /* + * Make an error message. + */ + sprintf(buffer1, "Import Font: Unable to load OpenType font %s.", + file); + guiutil_error_message(ed->shell, buffer1); + return; + } + + /* + * Hide the open dialog. + */ + gtk_widget_hide(ed->open_dialog); + + /* + * Add the _OTF_FONTFILE property using the original filename. + */ + prop.name = "_OTF_FONTFILE"; + prop.format = BDF_ATOM; + prop.value.atom = file; + bdf_add_font_property(font, &prop); + + /* + * Now set up a file name. + */ + sprintf(buffer1, "%.*s.bdf", dot - file, file); + + /* + * Delete the file and path names so they can be updated. + */ + if (ed->file != 0) + g_free(ed->file); + if (ed->path != 0) + g_free(ed->path); + + ed->file = strdup(buffer1); + ed->path = strdup(dir); + + /* + * Update the window title. + */ + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file); + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Tell the glyphtest widget to remove references to + * the current font if it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + fontgrid_set_font(FONTGRID(ed->fgrid), font, -1); + + /* + * Indicate the font was imported. + */ + ed->imported = TRUE; + + /* + * Finally, update the font name field. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); +} + +#endif /* HAVE_FREETYPE */ + +#ifdef HAVE_HBF + +/************************************************************************** + * + * HBF section. + * + **************************************************************************/ + +static void +load_hbf_font(gbdfed_editor_t *ed, gchar *fullpath, gchar *dot, + gchar *dir, gchar *file) +{ + bdf_font_t *font; + + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(ed->open_dialog, TRUE); + + font = bdf_load_hbf_font(fullpath, &options.font_opts, 0, 0); + + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(ed->open_dialog, FALSE); + + /* + * Check to see if the file can be opened. + */ + if (font == 0) { + g_free(dir); + sprintf(buffer1, "Import Font: Unable to import %s.", file); + guiutil_error_message(ed->shell, buffer1); + return; + } + + gtk_widget_hide(ed->open_dialog); + + /* + * Now set up a file name. + */ + sprintf(buffer1, "%.*s.bdf", (int) (dot - file), file); + + /* + * Delete the file and path names so they can be updated. + */ + if (ed->file != 0) + g_free(ed->file); + if (ed->path != 0) + g_free(ed->path); + + ed->file = strdup(buffer1); + ed->path = dir; + + /* + * Update the window title. + */ + sprintf(buffer1, "%s - %s [modified]", g_get_prgname(), ed->file); + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Tell the glyphtest widget to remove references to + * the current font if it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + fontgrid_set_font(FONTGRID(ed->fgrid), font, -1); + + /* + * Indicate the font was imported. + */ + ed->imported = TRUE; + + /* + * Finally, update the font name field. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); +} + +#endif + +/* + * This routine actually does the work of opening the font. + */ +static void +really_open_font(guint ed_id) +{ + gbdfed_editor_t *ed = editors + ed_id; + gchar *filename, *path, *file, *dot; + GtkFileChooser *fs; +#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10) + GtkRecentManager *recent; +#endif + + fs = GTK_FILE_CHOOSER(ed->open_dialog); + filename = gtk_file_chooser_get_filename(fs); + + /* + * Split the filename into path and file, locate the extension position in + * the file name, and make a version of the name with no '-' characters + * which are field separators in XLFD font names. + */ + file = g_path_get_basename(filename); + path = g_path_get_dirname(filename); + if ((dot = strrchr(file, '.')) == 0) + dot = file + strlen(file); + + /* + * If the last character of the filename is a slash, no file name was + * provided. + */ + if (filename[strlen(filename) - 1] == G_DIR_SEPARATOR) { + guiutil_error_message(ed->shell, + "Import Font: No file name provided."); + if (path) + g_free(path); + if (file) + g_free(file); + g_free(filename); + return; + } + +#if (GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 10) + recent = gtk_recent_manager_get_default(); + sprintf(buffer1, "file://%s", filename); + if (gtk_recent_manager_has_item(recent, + (const gchar *) buffer1) == FALSE) + gtk_recent_manager_add_item(recent, + (const gchar *) buffer1); +#endif + + switch (ed->import_format) { + case BDF_FORMAT: + load_bdf_font(ed, (const gchar *) filename, (const gchar *) path, + (const gchar *) file); + break; + case CONSOLE_FORMAT: + load_console_font(ed, filename, dot, path, file); + break; + case PKGF_FORMAT: + load_pkgf_font(ed, filename, dot, path, file); + break; + case FNT_FORMAT: + load_windows_font(ed, filename, dot, path, file); + break; +#ifdef HAVE_HBF + case HBF_FORMAT: + load_hbf_font(ed, filename, dot, path, file); + break; +#endif +#ifdef HAVE_FREETYPE + case OTF_FORMAT: + load_otf_font(ed, filename, dot, path, file); + break; +#endif /* HAVE_FREETYPE */ + } + + if (path) + g_free(path); + if (file) + g_free(file); + + g_free(filename); + + /* + * In case the editor list changed, set the pointer to the editor again. + */ + ed = editors + ed_id; + + /* + * Force the editor's info to be updated for the new font. This causes + * it to change if it is already visible. + */ + guiedit_update_font_info(ed); +} + +static gchar * +make_file_dialog_title(guint type, gboolean save) +{ + gchar *title = 0; + + switch (type) { + case BDF_FORMAT: title = "BDF"; break; + case CONSOLE_FORMAT: title = "Console"; break; + case PKGF_FORMAT: title = "PK/GF"; break; + case FNT_FORMAT: title = "Windows"; break; +#ifdef HAVE_HBF + case HBF_FORMAT: title = "HBF"; break; +#endif + case OTF_FORMAT: title = "TrueType"; break; + case PSF_FORMAT: title = "PSF"; break; + case HEX_FORMAT: title = "HEX"; break; + } + + if (save) { + if (type == BDF_FORMAT) + sprintf(buffer1, "Save %s Font", title); + else + sprintf(buffer1, "Export %s Font", title); + } else + sprintf(buffer1, "Open %s Font", title); + + return buffer1; +} + +static void +handle_open_response(GtkDialog *d, gint response, gpointer data) +{ + switch (response) { + case GTK_RESPONSE_ACCEPT: + really_open_font(GPOINTER_TO_UINT(data)); + break; + case GTK_RESPONSE_CANCEL: + gtk_widget_hide(GTK_WIDGET(d)); + break; + } +} + +static void +update_open_dialog(gbdfed_editor_t *ed, guint type) +{ + GtkFileChooser *fs; + + if (ed->open_dialog == 0) { + /* + * Create the file chooser filters if they haven't already been + * created. + */ + make_file_chooser_filters(); + + ed->open_dialog = + gtk_file_chooser_dialog_new(make_file_dialog_title(type, FALSE), + GTK_WINDOW(ed->shell), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + (void) g_signal_connect(G_OBJECT(ed->open_dialog), "response", + G_CALLBACK(handle_open_response), + GUINT_TO_POINTER(ed->id)); + (void) g_signal_connect(G_OBJECT(ed->open_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + } else if (ed->import_format != type) + gtk_window_set_title(GTK_WINDOW(ed->open_dialog), + make_file_dialog_title(type, FALSE)); + fs = GTK_FILE_CHOOSER(ed->open_dialog); + + /* + * Set the file filter. + */ + gtk_file_chooser_set_filter(fs, filename_filters[type]); + + ed->import_format = type; + + /* + * Set the initial path as a file if it exists. This is necessary to + * force the open to occur in the directory where this font was last + * saved, which might be different than the directory that is currently in + * the open file selection dialog. + */ + if (ed->path != 0 && ed->path[0] == G_DIR_SEPARATOR) + gtk_file_chooser_set_current_folder(fs, ed->path); +} + +static void +hide_save_dialog(GtkWidget *w, gpointer data) +{ + gtk_widget_hide(w); + save_dialog_done = TRUE; +} + +static void +set_psf_option(GtkWidget *w, gpointer data) +{ + guint flags = 0; + gint dotpos; + gchar *fname, *dot, *slash, *suff = 0; + GtkFileChooser *fs; + + switch (gtk_combo_box_get_active(GTK_COMBO_BOX(w))) { + case 0: + flags = BDF_PSF_UNIMAP|BDF_PSF_FONT; + break; + case 1: + flags = BDF_PSF_FONT; + break; + case 2: + flags = BDF_PSF_UNIMAP; + break; + } + + if (flags == BDF_PSF_UNIMAP) + /* + * Have to change to the .uni suffix. + */ + suff = ".uni"; + else if (options.font_opts.psf_flags == BDF_PSF_UNIMAP) + /* + * Have to change back to the .psfu suffix. + */ + suff = ".psfu"; + + options.font_opts.psf_flags = flags; + + if (suff) { + /* + * Change the suffix on the filename in the entry area. + */ + fs = GTK_FILE_CHOOSER(g_object_get_data(G_OBJECT(w), + "file-selection-dialog")); + fname = gtk_file_chooser_get_filename(fs); + + slash = fname; + if ((dot = (gchar *) strrchr(fname, '.')) != 0) { + if ((slash = (gchar *) strrchr(fname, G_DIR_SEPARATOR)) == 0) + slash = fname; + dotpos = (gint) (dot - slash) - 1; + + /* + * Copy the new extension in. + */ + (void) strcpy(dot, suff); + } else + dotpos = -1; + + if (*slash == G_DIR_SEPARATOR) + *slash++ = 0; + + gtk_file_chooser_set_current_name(fs, slash); + g_free(fname); + } +} + +static void +handle_save_response(GtkDialog *d, gint response, gpointer data) +{ + switch (response) { + case GTK_RESPONSE_ACCEPT: + really_save_font(GPOINTER_TO_UINT(data)); + break; + case GTK_RESPONSE_CANCEL: + gtk_widget_hide(GTK_WIDGET(d)); + break; + } +} + +static void +update_save_dialog(gbdfed_editor_t *ed, guint type) +{ + GtkWidget *vbox; + gchar *dot, *slash; + gint dotpos; + + if (ed->save_dialog == 0) { + /* + * Create the file chooser filters if they haven't already been + * created. + */ + make_file_chooser_filters(); + + ed->save_dialog = + gtk_file_chooser_dialog_new(make_file_dialog_title(type, TRUE), + GTK_WINDOW(ed->shell), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + vbox = GTK_DIALOG(ed->save_dialog)->vbox; + + psf_export_options = gtk_combo_box_new_text(); + /* + * Since the flags have to be set in the save dialog, attach the + * save dialog to the object so we can set the bits appropriately. + */ + g_object_set_data(G_OBJECT(psf_export_options), + "file-selection-dialog", + (gpointer) ed->save_dialog); + (void) g_signal_connect(G_OBJECT(psf_export_options), "changed", + G_CALLBACK(set_psf_option), 0); + gtk_combo_box_append_text(GTK_COMBO_BOX(psf_export_options), + "Font and Unicode Map"); + gtk_combo_box_append_text(GTK_COMBO_BOX(psf_export_options), + "Font Only"); + gtk_combo_box_append_text(GTK_COMBO_BOX(psf_export_options), + "Unicode Map Only"); + gtk_combo_box_set_active(GTK_COMBO_BOX(psf_export_options), 0); + + psf_export_frame = labcon_new_label_defaults("PSF Export Options:", + psf_export_options, 0); + + (void) g_signal_connect(G_OBJECT(ed->save_dialog), "delete_event", + G_CALLBACK(hide_save_dialog), 0); + + (void) g_signal_connect(G_OBJECT(ed->save_dialog), "response", + G_CALLBACK(handle_save_response), + GUINT_TO_POINTER(ed->id)); + + gtk_widget_show_all(psf_export_frame); + gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(ed->save_dialog), + psf_export_frame); + } else if (ed->export_format != type) + gtk_window_set_title(GTK_WINDOW(ed->save_dialog), + make_file_dialog_title(type, TRUE)); + + /* + * Set the file filter. + */ + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(ed->save_dialog), + filename_filters[type]); + + + ed->export_format = type; + + /* + * Show or hide the PSF exporting options. + */ + if (type == PSF_FORMAT) + gtk_widget_show(psf_export_frame); + else + gtk_widget_hide(psf_export_frame); + + /* + * Use the current path and filename as the default. This is done in case + * the font was loaded from some directory other than the current default + * in the file selection dialog for saving. + */ + if (ed->file != 0) + sprintf(buffer1, "%s", ed->file); + else + sprintf(buffer1, "unnamed%d.bdf", ed->id); + + if ((dot = (gchar *) strrchr(buffer1, '.'))) { + if ((slash = (gchar *) strrchr(buffer1, G_DIR_SEPARATOR)) == 0) + slash = buffer1; + dotpos = (gint) (dot - slash) - 1; + + /* + * If a PSF or HEX font is being exported, change the extension + * here. + */ + if (type == PSF_FORMAT) + (void) strcpy(dot, ".psfu"); + else if (type == HEX_FORMAT) + (void) strcpy(dot, ".hex"); + + } else + dotpos = -1; + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(ed->save_dialog), + buffer1); + if (ed->path != 0 && ed->path[0] == G_DIR_SEPARATOR) + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(ed->save_dialog), + ed->path); +#if 0 + gtk_editable_set_position(GTK_EDITABLE(fs->selection_entry), dotpos); + gtk_editable_select_region(GTK_EDITABLE(fs->selection_entry), 0, dotpos); +#endif +} + +void +guifile_import_bdf_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + update_open_dialog(ed, BDF_FORMAT); + + guiutil_show_dialog_centered(ed->open_dialog, ed->shell); +} + +void +guifile_import_console_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + update_open_dialog(ed, CONSOLE_FORMAT); + + guiutil_show_dialog_centered(ed->open_dialog, ed->shell); +} + +void +guifile_import_pkgf_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + update_open_dialog(ed, PKGF_FORMAT); + + guiutil_show_dialog_centered(ed->open_dialog, ed->shell); +} + +void +guifile_import_windows_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + update_open_dialog(ed, FNT_FORMAT); + + guiutil_show_dialog_centered(ed->open_dialog, ed->shell); +} + +#ifdef HAVE_HBF + +void +guifile_import_hbf_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + update_open_dialog(ed, HBF_FORMAT); + + guiutil_show_dialog_centered(ed->open_dialog, ed->shell); +} + +#endif + +#ifdef HAVE_FREETYPE + +void +guifile_import_otf_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + update_open_dialog(ed, OTF_FORMAT); + + guiutil_show_dialog_centered(ed->open_dialog, ed->shell); +} + +#endif /* HAVE_FREETYPE */ + +/************************************************************************** + * + * X server section. + * + **************************************************************************/ + +#ifdef HAVE_XLIB +/* + * Only compile this in if it is being built for machine running X. + */ + +static void +xsrv_filter(GtkWidget *w, gpointer data) +{ + gchar *pattern, **fonts; + gint i, nfonts; + GtkListStore *store; + GtkTreeViewColumn *col; + GtkTreeIter iter; + + pattern = (gchar *) gtk_entry_get_text(GTK_ENTRY(xsrv_filter_text)); + + fonts = XListFonts(GDK_DISPLAY(), pattern, _XSRV_MAX_FONTS, &nfonts); + + store = + GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(xsrv_font_list))); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(xsrv_font_list), 0); + + /* + * Update the label on the font list with the number of fonts. + */ + sprintf(buffer1, "Font List: %d", nfonts); + gtk_tree_view_column_set_title(col, buffer1); + + gtk_list_store_clear(store); + for (i = 0; i < nfonts; i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, fonts[i], -1); + } + + XFreeFontNames(fonts); +} + +static void +xsrv_xlfd_filter(GtkWidget *w, gpointer data) +{ + gtk_entry_set_text(GTK_ENTRY(xsrv_filter_text), _XSRV_DEFAULT_FILTER); + gtk_widget_activate(xsrv_filter_text); +} + +static void +xsrv_select_font(GtkTreeSelection *sel, gpointer data) +{ + gchar *name; + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected(sel, &model, &iter)) { + gtk_tree_model_get (model, &iter, 0, &name, -1); + gtk_entry_set_text(GTK_ENTRY(xsrv_selection_text), name); + g_free(name); + } +} + +static void +xsrv_clear_selection_text(GtkWidget *w, gpointer data) +{ + gtk_entry_set_text(GTK_ENTRY(xsrv_selection_text), ""); +} + +static void +xsrv_import_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + xsrv_active_editor; + XFontStruct *xfont; + bdf_font_t *font; + gchar *name; + + name = (gchar *) gtk_entry_get_text(GTK_ENTRY(xsrv_selection_text)); + if (strcmp(name, "") == 0) { + guiutil_error_message(ed->shell, + "Import Font: No font name provided."); + return; + } + + guiutil_busy_cursor(ed->shell, TRUE); + guiutil_busy_cursor(xsrv_dialog, TRUE); + if ((xfont = XLoadQueryFont(GDK_DISPLAY(), name)) == 0) { + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(xsrv_dialog, FALSE); + sprintf(buffer1, "Import Font: Unable to load server font %s.", + name); + guiutil_error_message(ed->shell, buffer1); + return; + } + + font = bdf_load_server_font(GDK_DISPLAY(), xfont, name, + &options.font_opts, 0, 0); + guiutil_busy_cursor(ed->shell, FALSE); + guiutil_busy_cursor(xsrv_dialog, FALSE); + XFreeFont(GDK_DISPLAY(), xfont); + + if (font == 0) { + sprintf(buffer1, "Import Font: Unable to import server font %s.", + name); + guiutil_error_message(ed->shell, buffer1); + return; + } + + /* + * Close the dialog. + */ + gtk_widget_hide(xsrv_dialog); + + if (ed->file != 0) + g_free(ed->file); + if (ed->path != 0) + g_free(ed->path); + ed->file = ed->path = 0; + + sprintf(buffer1, "%s - unnamed%d [modified]", g_get_prgname(), + ed->id); + gtk_window_set_title(GTK_WINDOW(ed->shell), buffer1); + + /* + * Tell the glyphtest widget to remove references to + * the current font if it has any, and redraw. + */ + if (glyphtest != 0) + glyphtest_remove_font(GLYPHTEST(glyphtest), + fontgrid_get_font(FONTGRID(ed->fgrid))); + + fontgrid_set_font(FONTGRID(ed->fgrid), font, -1); + + /* + * Indicate the font was imported. + */ + ed->imported = TRUE; + + /* + * Finally, update the font name field. + */ + gtk_entry_set_text(GTK_ENTRY(ed->fontname), + fontgrid_get_font_name(FONTGRID(ed->fgrid))); +} + +static void +xsrv_activate_font(GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *col, gpointer data) +{ + xsrv_import_font(GTK_WIDGET(view), data); +} + +void +guifile_import_xserver_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + GtkWidget *label, *hbox, *vbox, *text, *swin, *button; + gchar *name, **fonts; + gint i, nfonts; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeSelection *sel; + GtkTreeIter iter; + + if (fontgrid_get_font_modified(FONTGRID(ed->fgrid))) { + if (ed->file == 0) + sprintf(buffer1, "Save Font: (unnamed%d) modified. Save?", ed->id); + else + sprintf(buffer1, "Save Font: %s modified. Save?", ed->file); + if (guiutil_yes_or_no(ed->shell, buffer1, TRUE)) { + /* + * If the current file was imported, then make sure to use the + * Save As... dialog instead of just saving under the name that + * was constructed when importing. The user won't know what the + * default BDF file name of imported fonts look like. + */ + if (ed->imported) + guifile_save_as_wait(w, data); + else + guifile_save(w, data); + return; + } + } + + xsrv_active_editor = ed->id; + + if (xsrv_dialog == 0) { + xsrv_dialog = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(xsrv_dialog), + "X Server Font Selection"); + (void) g_signal_connect(G_OBJECT(xsrv_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + vbox = GTK_DIALOG(xsrv_dialog)->vbox; + hbox = GTK_DIALOG(xsrv_dialog)->action_area; + + label = gtk_label_new("Filter"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + + text = xsrv_filter_text = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(text), _XSRV_DEFAULT_FILTER); + (void) g_signal_connect(G_OBJECT(text), "activate", + G_CALLBACK(xsrv_filter), 0); + gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0); + + swin = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + + store = gtk_list_store_new(1, G_TYPE_STRING); + xsrv_font_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + (void) g_signal_connect(G_OBJECT(xsrv_font_list), "row_activated", + G_CALLBACK(xsrv_activate_font), + GUINT_TO_POINTER(ed->id)); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ("Fonts Found: 0", + cell_renderer, + "text", 0, + NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column (GTK_TREE_VIEW(xsrv_font_list), column); + + /* + * Force the list to have a certain width and height. + */ + gtk_widget_set_size_request(xsrv_font_list, 550, 200); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(xsrv_font_list)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(xsrv_select_font), NULL); + + gtk_container_add(GTK_CONTAINER(swin), xsrv_font_list); + + gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0); + + label = gtk_label_new("Selection"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + + text = xsrv_selection_text = gtk_entry_new(); + (void) g_signal_connect(G_OBJECT(text), "activate", + G_CALLBACK(xsrv_import_font), + GUINT_TO_POINTER(ed->id)); + gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0); + + /* + * Now add the buttons. + */ + button = xsrv_import = gtk_button_new_with_label("Import"); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(xsrv_import_font), + GUINT_TO_POINTER(ed->id)); + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = xsrv_import = gtk_button_new_with_label("Clear Selection"); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(xsrv_clear_selection_text), 0); + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = gtk_button_new_with_label("Filter"); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(xsrv_filter), 0); + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = gtk_button_new_with_label("XLFD Filter"); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(xsrv_xlfd_filter), 0); + gtk_container_add(GTK_CONTAINER(hbox), button); + + button = gtk_button_new_with_label("Cancel"); + (void) g_signal_connect_object(G_OBJECT(button), "clicked", + G_CALLBACK(gtk_widget_hide), + (gpointer) xsrv_dialog, + G_CONNECT_SWAPPED); + gtk_container_add(GTK_CONTAINER(hbox), button); + + gtk_widget_show_all(vbox); + gtk_widget_show_all(hbox); + } + + store = + GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(xsrv_font_list))); + column = gtk_tree_view_get_column(GTK_TREE_VIEW(xsrv_font_list), 0); + + /* + * Load the list of fonts using the current filter pattern. This needs to + * be done each time in case the list of font paths has changed between + * calls. + */ + name = (gchar *) gtk_entry_get_text(GTK_ENTRY(xsrv_filter_text)); + fonts = XListFonts(GDK_DISPLAY(), name, _XSRV_MAX_FONTS, &nfonts); + + /* + * Update the label on the font list with the number of fonts. + */ + sprintf(buffer1, "Fonts Found: %d", nfonts); + gtk_tree_view_column_set_title(column, buffer1); + + gtk_list_store_clear(store); + for (i = 0; i < nfonts; i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, fonts[i], -1); + } + + XFreeFontNames(fonts); + + /* + * Show the dialog. + */ + guiutil_show_dialog_centered(xsrv_dialog, ed->shell); + +} + +#endif /* HAVE_XLIB */ + +void +guifile_export_psf_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + bdf_font_t *font; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + /* + * Only character cell and mono width fonts can be exported as PSF2. + */ + if (font->spacing == BDF_PROPORTIONAL) { + sprintf(buffer2, + "Export Font: Font cannot be saved as PSF because %s ", + "the font has proportional width."); + guiutil_error_message(ed->shell, buffer2); + return; + } + + update_save_dialog(ed, PSF_FORMAT); + + guiutil_show_dialog_centered(ed->save_dialog, ed->shell); +} + +void +guifile_export_hex_font(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + /* + * No check is done for a "valid" HEX font because it actually pads the + * output font into bitmaps of two sizes, the wider size twice as wide + * as the narrower size. + */ + + update_save_dialog(ed, HEX_FORMAT); + + guiutil_show_dialog_centered(ed->save_dialog, ed->shell); +} + +void +guifile_save_as(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + update_save_dialog(ed, BDF_FORMAT); + + guiutil_show_dialog_centered(ed->save_dialog, ed->shell); +} + +void +guifile_save_as_wait(GtkWidget *w, gpointer data) +{ + save_dialog_done = FALSE; + + guifile_save_as(w, data); + while (save_dialog_done == FALSE) + gtk_main_iteration(); +} + +void +guifile_save(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + /* + * If this is a new font, then we need to show the file selection dialog. + * Otherwise, simply write the font out. + */ + if (ed->path == 0 && ed->file == 0) { + guifile_save_as(w, data); + return; + } + + ed->export_format = BDF_FORMAT; + sprintf(buffer1, "%s/%s", ed->path, ed->file); + export_font(buffer1, ed, FALSE); +} + +void +guifile_new_editor(GtkWidget *w, gpointer data) +{ + guint n; + + n = gbdfed_make_editor(0, FALSE); + + gtk_widget_show_all(editors[n].shell); +} + +/* + * A routine to load a BDF font directly. + */ +void +guifile_load_bdf_font(gbdfed_editor_t *ed, const gchar *fullpath) +{ + gchar *dir, *file; + + if (fullpath == NULL) + return; + + file = g_path_get_basename(fullpath); + dir = g_path_get_dirname(fullpath); + load_bdf_font(ed, fullpath, dir, file); + if (dir != NULL) + g_free(dir); + if (file != NULL) + g_free(file); +} diff --git a/guigedit.c b/guigedit.c new file mode 100644 index 0000000..235341c --- /dev/null +++ b/guigedit.c @@ -0,0 +1,2267 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" +#include "glyphedit.h" +#include "labcon.h" +#include "gectrl.h" + +#define UPMSG "Glyph Edit: The glyph has been modified.\nDo you want to save?" + +static const gchar *lb_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 3 1", +/* colors */ +". c None", +"# c #000000", +"r c #ff0000", +/* pixels */ +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"..r....r........................", +".rr....rr..##...................", +"rrrrrrrrrr.##...................", +".rr....rr.......................", +"..r....r........................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"##..##..##.##.##..##..##..##..##", +"##..##..##.##.##..##..##..##..##", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##..................." +}; + +static const gchar *rb_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 3 1", +/* colors */ +". c None", +"# c #000000", +"r c #ff0000", +/* pixels */ +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................r............r..", +"...........##..rr............rr.", +"...........##.rrrrrrrrrrrrrrrrrr", +"...............rr............rr.", +"................r............r..", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"##..##..##.##.##..##..##..##..##", +"##..##..##.##.##..##..##..##..##", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##..................." +}; + +static const gchar *as_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 3 1", +/* colors */ +". c None", +"# c #000000", +"r c #ff0000", +/* pixels */ +"...........##.........r.........", +"...........##........rrr........", +"....................rrrrr.......", +"......................r.........", +"...........##.........r.........", +"...........##.........r.........", +"......................r.........", +"......................r.........", +"...........##.........r.........", +"...........##.........r.........", +"......................r.........", +"......................r.........", +"...........##.........r.........", +"...........##.........r.........", +"......................r.........", +"....................rrrrr.......", +"...........##........rrr........", +"...........##.........r.........", +"................................", +"##..##..##.##.##..##..##..##..##", +"##..##..##.##.##..##..##..##..##", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##..................." +}; + +static const char *ds_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 3 1", +/* colors */ +". c None", +"# c #000000", +"r c #ff0000", +/* pixels */ +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"................................", +"...........##...................", +"...........##...................", +"................................", +"##..##..##.##.##..##..##..##..##", +"##..##..##.##.##..##..##..##..##", +"................................", +"...........##.........r.........", +"...........##........rrr........", +"....................rrrrr.......", +"......................r.........", +"...........##.........r.........", +"...........##.........r.........", +"......................r.........", +"....................rrrrr.......", +"...........##........rrr........", +"...........##.........r........." +}; + +/* + * Global pixbufs. + */ +static GdkPixbuf *lb_image = 0; +static GdkPixbuf *rb_image = 0; +static GdkPixbuf *as_image = 0; +static GdkPixbuf *ds_image = 0; + +typedef struct { + GtkWidget *dialog; + GtkWidget *notebook; + GtkWidget *apply; + + /* + * The rotate/shear tab. + */ + GtkWidget *rotate; + GtkWidget *shear; + GtkAdjustment *rotate_adj; + GtkAdjustment *shear_adj; + GtkWidget *degrees; + gboolean degrees_modified; + + /* + * The bounding box resize tab. + */ + GtkWidget *lbearing; + GtkWidget *rbearing; + GtkWidget *ascent; + GtkWidget *descent; + gboolean resize_modified; + + /* + * The PSF mappings tab. + */ + GtkWidget *psf_add; + GtkWidget *psf_delete; + GtkWidget *psf_mappings; + GtkWidget *psf_input; + gboolean psf_modified; +} GlypheditNotebookRec; + +typedef struct { + gulong id; + gulong owner; + gulong handler; + + GtkAccelGroup *ag; + + GtkWidget *shell; + GtkWidget *gedit; + GtkWidget *gectrl; + GtkWidget *name; + GtkWidget *encoding; + GtkWidget *dwidth; + GtkWidget *metrics; + GtkWidget *coords; + GtkWidget *gectltips; + + GtkWidget *file_menu; + GtkWidget *update; + GtkWidget *update_prev; + GtkWidget *update_next; + + GtkWidget *button_update; + GtkWidget *button_prev; + GtkWidget *button_next; + + GtkWidget *edit_menu; + GtkWidget *reload; + GtkWidget *resize; + GtkWidget *paste; + GtkWidget *copy; + GtkWidget *cut; + GtkWidget *select_all; + GtkWidget *next; + GtkWidget *prev; + GtkWidget *unimap; + GtkWidget *unimap_page; + + GtkWidget *ops_menu; + GlypheditNotebookRec ops; +} GlypheditRec; + +static GlypheditRec *glyph_editors; +static gulong num_glyph_editors; + +static GlypheditRec * +_guigedit_get_glyph_editor(gulong owner) +{ + gulong i; + GlypheditRec *ge; + + if (num_glyph_editors == 0) { + glyph_editors = ge = + (GlypheditRec *) g_malloc0(sizeof(GlypheditRec)); + ge->id = num_glyph_editors++; + } else { + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].owner == ~0) { + ge = &glyph_editors[i]; + ge->owner = owner; + return ge; + } + } + glyph_editors = (GlypheditRec *) + g_realloc(glyph_editors, + sizeof(GlypheditRec) * (num_glyph_editors + 1)); + + ge = glyph_editors + num_glyph_editors; + (void) memset((char *) ge, 0, sizeof(GlypheditRec)); + ge->id = num_glyph_editors++; + } + ge->owner = owner; + return ge; +} + +/************************************************************************** + * + * Menu construction. + * + **************************************************************************/ + +static GtkWidget * +make_accel_menu_item(GtkWidget *menu, const gchar *text, const gchar *accel, + GtkAccelGroup *ag) +{ + GtkWidget *mi; + guint key; + GdkModifierType mods; + + mi = gtk_menu_item_new_with_mnemonic(text); + + gtk_accelerator_parse(accel, &key, &mods); + gtk_widget_add_accelerator(mi, "activate", ag, key, mods, + GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); + + return mi; +} + +static void +update_font(GtkWidget *w, gpointer data) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data); + gbdfed_editor_t *ed = editors + ge->owner; + const gchar *s; + gchar *prgname = g_get_prgname(); + gboolean unencoded; + bdf_glyph_t *glyph; + GlypheditOperation op; + + if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) { + if (glyphedit_get_selecting(GLYPHEDIT(ge->gedit)) == TRUE) { + /* + * A selection operation is in progress. Need to switch back to + * the Draw operation to finalize the selection and then switch + * back. + */ + op = glyphedit_get_operation(GLYPHEDIT(ge->gedit)); + glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW); + glyphedit_change_operation(GLYPHEDIT(ge->gedit), op); + } + + glyph = glyphedit_get_glyph(GLYPHEDIT(ge->gedit), &unencoded); + + /* + * Set the new name and device width for the glyph. These may not + * have actually changed, but this is simplest for the moment. + */ + if (glyph->name != 0) + free(glyph->name); + glyph->name = (gchar *) gtk_entry_get_text(GTK_ENTRY(ge->name)); + s = gtk_entry_get_text(GTK_ENTRY(ge->dwidth)); + glyph->dwidth = (guint16) _bdf_atos((char *) s, 0, 10); + + /* + * Now update the font itself. + */ + fontgrid_update_glyph(FONTGRID(ed->fgrid), glyph, unencoded); + + /* + * Free the glyph structure. The name has already been deallocated + * and replaced with a possibly new name. + */ + if (glyph->bytes > 0) + free(glyph->bitmap); + free(glyph); + + glyphedit_set_modified(GLYPHEDIT(ge->gedit), FALSE); + } + + /* + * Just modified the PSF mappings. + */ + fontgrid_update_psf_mappings(FONTGRID(ed->fgrid), + glyphedit_get_encoding(GLYPHEDIT(ge->gedit)), + glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit))); + + /* + * Unset the modified flag and update the title. + */ + glyphedit_set_modified(GLYPHEDIT(ge->gedit), FALSE); + if (ed->file == 0) + sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", + prgname, ed->id); + else + sprintf(buffer1, "%s - Glyph Edit: %s", prgname, ed->file); + + gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1); + + + gtk_widget_set_sensitive(ge->update, FALSE); + gtk_widget_set_sensitive(ge->update_next, FALSE); + gtk_widget_set_sensitive(ge->update_prev, FALSE); + gtk_widget_set_sensitive(ge->button_update, FALSE); + + /* + * Force the focus to be on the glyph grid + */ + gtk_widget_grab_focus(ge->gedit); +} + +/* + * Code common to both next_glyph() and previous_glyph(). + */ +static void +update_glyphedit(gbdfed_editor_t *ed, GlypheditRec *ge, bdf_glyph_grid_t *grid) +{ + Glyphedit *gw; + + gw = GLYPHEDIT(ge->gedit); + gtk_entry_set_text(GTK_ENTRY(ge->name), grid->name); + + if (grid->unencoded) + sprintf(buffer1, "-1"); + else { + switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) { + case 8: sprintf(buffer1, "%o", grid->encoding); break; + case 10: sprintf(buffer1, "%d", grid->encoding); break; + case 16: sprintf(buffer1, "%04X", grid->encoding); break; + } + } + gtk_label_set_text(GTK_LABEL(ge->encoding), buffer1); + + sprintf(buffer1, "%hd", grid->dwidth); + gtk_widget_set_sensitive(ge->dwidth, TRUE); + g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler); + gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1); + g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler); + + if (grid->spacing != BDF_PROPORTIONAL) { + gtk_widget_set_sensitive(ge->dwidth, FALSE); + if (ge->unimap_page != 0) + gtk_widget_set_sensitive(ge->unimap_page, TRUE); + } else if (ge->unimap_page != 0) + gtk_widget_set_sensitive(ge->unimap_page, FALSE); + + sprintf(buffer1, "width %hd height %hd\r\nascent %hd descent %hd", + grid->glyph_bbx.width, grid->glyph_bbx.height, + grid->glyph_bbx.ascent, grid->glyph_bbx.descent); + gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1); + + /* + * Set the new grid in the glyph editor. + */ + glyphedit_set_grid(gw, grid); + + /* + * Set the sensitivity of the update menu items appropriately. + */ + if (grid->modified) { + gtk_widget_set_sensitive(ge->update, TRUE); + gtk_widget_set_sensitive(ge->update_next, TRUE); + gtk_widget_set_sensitive(ge->update_prev, TRUE); + gtk_widget_set_sensitive(ge->button_update, TRUE); + } else { + gtk_widget_set_sensitive(ge->update, FALSE); + gtk_widget_set_sensitive(ge->update_next, FALSE); + gtk_widget_set_sensitive(ge->update_prev, FALSE); + gtk_widget_set_sensitive(ge->button_update, FALSE); + } + + if (glyphedit_get_encoding(gw) == 0) + gtk_widget_set_sensitive(ge->button_prev, FALSE); + else + gtk_widget_set_sensitive(ge->button_prev, TRUE); + + if (glyphedit_get_encoding(gw) == 0xffff) + gtk_widget_set_sensitive(ge->button_next, FALSE); + else + gtk_widget_set_sensitive(ge->button_next, TRUE); + + /* + * Force the focus to be on the glyph grid. + */ + gtk_widget_grab_focus(ge->gedit); +} + +static void +next_glyph(GtkWidget *w, gpointer data) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data); + gbdfed_editor_t *ed = editors + ge->owner; + bdf_font_t *font; + bdf_glyph_grid_t *grid; + bdf_bitmap_t image; + + if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) { + if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE)) + update_font(w, GUINT_TO_POINTER(ge->id)); + } + + grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit)); + + if (fontgrid_select_next_glyph(FONTGRID(ed->fgrid), grid->encoding) == FALSE) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + if (grid->unencoded) + grid = bdf_make_glyph_grid(font, grid->encoding + 1, 1); + else + grid = bdf_make_glyph_grid(font, grid->encoding + 1, 0); + + update_glyphedit(ed, ge, grid); + + glyphedit_get_image(GLYPHEDIT(ge->gedit), &image); + gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image); + if (image.bytes > 0) + free(image.bitmap); +} + +static void +previous_glyph(GtkWidget *w, gpointer data) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data); + gbdfed_editor_t *ed = editors + ge->owner; + bdf_font_t *font; + bdf_glyph_grid_t *grid; + bdf_bitmap_t image; + + if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) { + if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE)) + update_font(w, GUINT_TO_POINTER(ge->id)); + } + + grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit)); + + if (fontgrid_select_previous_glyph(FONTGRID(ed->fgrid), grid->encoding) == FALSE) + return; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + + if (grid->unencoded) + grid = bdf_make_glyph_grid(font, grid->encoding - 1, 1); + else + grid = bdf_make_glyph_grid(font, grid->encoding - 1, 0); + + update_glyphedit(ed, ge, grid); + + glyphedit_get_image(GLYPHEDIT(ge->gedit), &image); + gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image); + if (image.bytes > 0) + free(image.bitmap); +} + +static void +update_and_next_glyph(GtkWidget *w, gpointer data) +{ + update_font(w, data); + next_glyph(w, data); +} + +static void +update_and_previous_glyph(GtkWidget *w, gpointer data) +{ + update_font(w, data); + previous_glyph(w, data); +} + +static gboolean +close_glyph_editor(GtkWidget *w, GdkEvent *ev, gpointer data) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data); + + /* + * Glyph editors with no owners are ignored. This lets us call this + * routine at application shutdown time to update all modified + * glyph editors. + */ + if (ge->owner == ~0) + return TRUE; + + /* + * We don't check to see if the grid has been modified, because + * certain operations cause the modify flag to be set, but they + * don't really represent a modification. + */ + if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) { + if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE)) + update_font(w, GUINT_TO_POINTER(ge->id)); + } + + /* + * Release this editor back into the pool to be reused. + */ + ge->owner = ~0; + + /* + * Hide the shell. + */ + gtk_widget_hide(ge->shell); + + return TRUE; +} + +static void +activate_close_glyph_editor(GtkWidget *w, gpointer data) +{ + (void) close_glyph_editor(w, 0, data); +} + +static GtkWidget * +make_file_menu(GlypheditRec *ge, GtkWidget *menubar) +{ + GtkWidget *file, *menu, *mitem, *sep; + + /* + * Create the File menu. + */ + file = gtk_menu_item_new_with_mnemonic("_File"); + + ge->file_menu = menu = gtk_menu_new(); + + ge->update = make_accel_menu_item(menu, "_Update", + "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", + "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", + "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", "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "F", ge->ag); + g_signal_connect(G_OBJECT(ge->unimap), "activate", + G_CALLBACK(show_unimap_dialog), + GUINT_TO_POINTER(ge->id)); + + return ops; +} + +static void +pointer_moved(GtkWidget *w, gpointer cb, gpointer ged) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged); + GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb; + bdf_glyph_grid_t *g; + + g = glyphedit_get_grid(GLYPHEDIT(ge->gedit)); + if (g->bpp == 1 || si->color == 0) + sprintf(buffer1, "(%d,%d)", si->x, si->y); + else { + switch (g->bpp) { + case 2: + sprintf(buffer1, "(%d,%d,%d)", si->x, si->y, + options.colors[si->color-1]); + break; + case 4: + sprintf(buffer1, "(%d,%d,%d)", si->x, si->y, + options.colors[si->color+4-1]); + break; + case 8: + sprintf(buffer1, "(%d,%d,%d)", si->x, si->y, si->color); + break; + } + } + + gtk_label_set_text(GTK_LABEL(ge->coords), buffer1); +} + +/* + * Under certain circumstances, the glyphedit widget causes the operation to + * change. Basically, when a bitmap is pasted, the widget goes into a MOVE + * operation. All the operations are handled here just in case of future + * changes. + */ +static void +operation_changed(GtkWidget *w, gpointer cb, gpointer ged) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged); + GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb; + + if (si->operation == GLYPHEDIT_DRAW) + gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_DRAW); + else if (si->operation == GLYPHEDIT_MOVE) + gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_MOVE); + else if (si->operation == GLYPHEDIT_COPY) + gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_COPY); +} + +static void +color_changed(GtkWidget *w, gpointer cb, gpointer ged) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged); + GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb; + + gecontrol_change_color(GECONTROL(ge->gectrl), si->color); +} + +static void +glyph_modified(GtkWidget *w, gpointer cb, gpointer ged) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged); + gbdfed_editor_t *ed = editors + ge->owner; + GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb; + gchar *prgname = g_get_prgname(); + + if (si->metrics == 0) + return; + + if (ed->file == 0) + sprintf(buffer1, "%s - Glyph Edit: (unnamed%d) [modified]", + prgname, ed->id); + else + sprintf(buffer1, "%s - Glyph Edit: %s [modified]", prgname, ed->file); + + gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1); + + sprintf(buffer1, "width %hd height %hd\nascent %hd descent %hd", + si->metrics->width, si->metrics->height, + si->metrics->ascent, si->metrics->descent); + gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1); + + if (si->metrics->font_spacing == BDF_PROPORTIONAL) { + sprintf(buffer1, "%hd", si->metrics->dwidth); + g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler); + gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1); + g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler); + } + + /* + * Update the glyph image on the Glyphedit control widget. + */ + gecontrol_update_glyph_image(GECONTROL(ge->gectrl), si->image); + + gtk_widget_set_sensitive(ge->update, TRUE); + gtk_widget_set_sensitive(ge->update_next, TRUE); + gtk_widget_set_sensitive(ge->update_prev, TRUE); + gtk_widget_set_sensitive(ge->button_update, TRUE); + gtk_widget_set_sensitive(ge->button_next, TRUE); + gtk_widget_set_sensitive(ge->button_prev, TRUE); +} + +/* + * This function will be a bit screwy until I can figure out how to make a + * signal pass an enum as the first param after the widget. + */ +static void +gectrl_activate(GtkWidget *w, gpointer info, gpointer data) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data); + GEControlActivateInfo *ai = (GEControlActivateInfo *) info; + + switch (ai->operation) { + case GECONTROL_DRAW: + glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW); + break; + case GECONTROL_MOVE: + glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_MOVE); + break; + case GECONTROL_COPY: + glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_COPY); + break; + case GECONTROL_FLIP_HORIZONTAL: + glyphedit_flip_glyph(GLYPHEDIT(ge->gedit), GTK_ORIENTATION_HORIZONTAL); + break; + case GECONTROL_FLIP_VERTICAL: + glyphedit_flip_glyph(GLYPHEDIT(ge->gedit), GTK_ORIENTATION_VERTICAL); + break; + case GECONTROL_SHEAR: + show_shear_dialog(w, data); + break; + case GECONTROL_ROTATE_LEFT_90: + glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), -90); + break; + case GECONTROL_ROTATE_RIGHT_90: + glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), 90); + break; + case GECONTROL_ROTATE: + show_rotate_dialog(w, data); + break; + case GECONTROL_SHIFT_UP_LEFT: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, -1); + break; + case GECONTROL_SHIFT_UP: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 0, -1); + break; + case GECONTROL_SHIFT_UP_RIGHT: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, -1); + break; + case GECONTROL_SHIFT_LEFT: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, 0); + break; + case GECONTROL_SHIFT_RIGHT: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, 0); + break; + case GECONTROL_SHIFT_DOWN_LEFT: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, 1); + break; + case GECONTROL_SHIFT_DOWN: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 0, 1); + break; + case GECONTROL_SHIFT_DOWN_RIGHT: + glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, 1); + break; + case GECONTROL_COLOR_CHANGE: + glyphedit_set_color(GLYPHEDIT(ge->gedit), ai->color); + break; + } +} + +/* + * This is called when the device width field is changed in any way. + */ +static void +enable_update(GtkWidget *w, gpointer data) +{ + GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data); + gbdfed_editor_t *ed = editors + ge->owner; + + if (ed->file == 0) + sprintf(buffer1, "%s - Glyph Edit: (unnamed%d) [modified]", + g_get_prgname(), ed->id); + else + sprintf(buffer1, "%s - Glyph Edit: %s [modified]", + g_get_prgname(), ed->file); + + gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1); + gtk_widget_set_sensitive(ge->update, TRUE); + gtk_widget_set_sensitive(ge->update_next, TRUE); + gtk_widget_set_sensitive(ge->update_prev, TRUE); + gtk_widget_set_sensitive(ge->button_update, TRUE); +} + +static void +_guigedit_build_editor(GlypheditRec *ge, bdf_glyph_grid_t *grid, guint base, + gbdfed_editor_t *ed) +{ + GtkWidget *mb, *mitem, *frame, *vbox, *vbox1, *hbox, *img; + bdf_bitmap_t image; + + if (ed->file == 0) + sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", g_get_prgname(), + ed->id); + else + sprintf(buffer1, "%s - Glyph Edit: %s", g_get_prgname(), ed->file); + + ge->shell = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1); + + gtk_window_set_resizable(GTK_WINDOW(ge->shell), TRUE); + + (void) g_signal_connect(G_OBJECT(ge->shell), "destroy_event", + G_CALLBACK(close_glyph_editor), + GUINT_TO_POINTER(ge->id)); + (void) g_signal_connect(G_OBJECT(ge->shell), "delete_event", + G_CALLBACK(close_glyph_editor), + GUINT_TO_POINTER(ge->id)); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(ge->shell), vbox); + + ge->ag = gtk_accel_group_new(); + + mb = gtk_menu_bar_new(); + mitem = make_file_menu(ge, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + mitem = make_edit_menu(ge, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + mitem = make_ops_menu(ge, mb); + gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem); + + gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, TRUE, 0); + + /* + * Attach the accelerators to the editor. + */ + gtk_window_add_accel_group(GTK_WINDOW(ge->shell), ge->ag); + + /* + * 1. Add the glyph name, next/previous buttons and encoding widgets. + */ + frame = gtk_frame_new(0); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0); + + vbox1 = gtk_vbox_new(TRUE, 0); + gtk_container_add(GTK_CONTAINER(frame), vbox1); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0); + + ge->name = gtk_widget_new(gtk_entry_get_type(), "max_length", 128, NULL); + mitem = labcon_new_label_defaults("Glyph Name:", ge->name, 0); + gtk_box_pack_start(GTK_BOX(hbox), mitem, TRUE, TRUE, 0); + + /* Update button */ + ge->button_update = gtk_button_new(); + guiutil_util_set_tooltip(ge->button_update, "Update Font"); + img = gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_button_set_image(GTK_BUTTON(ge->button_update), img); + g_signal_connect(G_OBJECT(ge->button_update), "clicked", + G_CALLBACK(update_font), NULL); + gtk_box_pack_start(GTK_BOX(hbox), ge->button_update, FALSE, FALSE, 0); + + /* Previous button */ + ge->button_prev = gtk_button_new(); + guiutil_util_set_tooltip(ge->button_prev, "Previous Glyph"); + img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, + GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_button_set_image(GTK_BUTTON(ge->button_prev), img); + g_signal_connect(G_OBJECT(ge->button_prev), "clicked", + G_CALLBACK(previous_glyph), NULL); + gtk_box_pack_start(GTK_BOX(hbox), ge->button_prev, FALSE, FALSE, 0); + + /* Next button */ + ge->button_next = gtk_button_new(); + guiutil_util_set_tooltip(ge->button_next, "Next Glyph"); + img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, + GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_button_set_image(GTK_BUTTON(ge->button_next), img); + g_signal_connect(G_OBJECT(ge->button_next), "clicked", + G_CALLBACK(next_glyph), NULL); + gtk_box_pack_start(GTK_BOX(hbox), ge->button_next, FALSE, FALSE, 0); + + /* Encoding */ + ge->encoding = gtk_label_new("0000"); + gtk_misc_set_alignment(GTK_MISC(ge->encoding), 0.0, 0.5); + mitem = labcon_new_label_defaults("Encoding:", ge->encoding, mitem); + gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0); + + /* + * 2. Add the device width and metrics widgets. + */ + frame = gtk_frame_new(0); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0); + + vbox1 = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(frame), vbox1); + + ge->dwidth = gtk_widget_new(gtk_entry_get_type(), + "max_length", 6, + NULL); + ge->handler = g_signal_connect(G_OBJECT(ge->dwidth), "changed", + G_CALLBACK(enable_update), + GUINT_TO_POINTER(ge->id)); + mitem = labcon_new_label_defaults("Device Width:", ge->dwidth, mitem); + gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0); + + ge->metrics = gtk_label_new("width 0 height 0\r\nascent 0 descent 0"); + gtk_misc_set_alignment(GTK_MISC(ge->metrics), 0.0, 0.5); + mitem = labcon_new_label_defaults("Metrics:", ge->metrics, mitem); + gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + + vbox1 = gtk_vbox_new(FALSE, 0); + + /* + * Create the coordinates label. + */ + ge->coords = gtk_label_new("(0,0)"); + gtk_misc_set_alignment(GTK_MISC(ge->coords), 0.5, 0.5); + gtk_box_pack_start(GTK_BOX(vbox1), ge->coords, FALSE, TRUE, 0); + + /* + * Create the glyph editor. + */ + ge->gedit = glyphedit_newv(grid, options.pixel_size, options.show_x_height, + options.show_cap_height, options.colors); + g_signal_connect(G_OBJECT(ge->gedit), "glyph-modified", + G_CALLBACK(glyph_modified), GUINT_TO_POINTER(ge->id)); + g_signal_connect(G_OBJECT(ge->gedit), "pointer-moved", + G_CALLBACK(pointer_moved), GUINT_TO_POINTER(ge->id)); + g_signal_connect(G_OBJECT(ge->gedit), "operation-change", + G_CALLBACK(operation_changed), GUINT_TO_POINTER(ge->id)); + g_signal_connect(G_OBJECT(ge->gedit), "color-change", + G_CALLBACK(color_changed), GUINT_TO_POINTER(ge->id)); + gtk_box_pack_start(GTK_BOX(vbox1), ge->gedit, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0); + + vbox1 = gtk_vbox_new(FALSE, 0); + + ge->gectltips = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(ge->gectltips), 0.5, 0.5); + gtk_box_pack_start(GTK_BOX(vbox1), ge->gectltips, FALSE, TRUE, 0); + + /* + * Get the initial glyph image. + */ + glyphedit_get_image(GLYPHEDIT(ge->gedit), &image); + ge->gectrl = gecontrol_newv(ge->gectltips, &image, options.colors); + if (image.bytes > 0) + free(image.bitmap); + g_signal_connect(G_OBJECT(ge->gectrl), "activate", + G_CALLBACK(gectrl_activate), GUINT_TO_POINTER(ge->id)); + + gtk_box_pack_start(GTK_BOX(vbox1), ge->gectrl, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); +} + +void +guigedit_edit_glyph(gbdfed_editor_t *ed, FontgridSelectionInfo *si) +{ + GlypheditRec *ge; + bdf_font_t *font; + guint base; + bdf_glyph_grid_t *grid; + bdf_bitmap_t image; + + font = fontgrid_get_font(FONTGRID(ed->fgrid)); + base = fontgrid_get_code_base(FONTGRID(ed->fgrid)); + + if (si->unencoded) + grid = bdf_make_glyph_grid(font, si->start, 1); + else + grid = bdf_make_glyph_grid(font, si->start, 0); + + ge = _guigedit_get_glyph_editor(ed->id); + + if (ge->name == 0) { + _guigedit_build_editor(ge, grid, base, ed); + } else { + if (ed->file == 0) + sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", g_get_prgname(), + ed->id); + else + sprintf(buffer1, "%s - Glyph Edit: %s", g_get_prgname(), ed->file); + + gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1); + glyphedit_set_grid(GLYPHEDIT(ge->gedit), grid); + + /* + * Update the image in the glypheditor control panel. + */ + if (grid) { + bdf_grid_image(grid, &image); + gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image); + if (image.bytes > 0) + free(image.bitmap); + } else + gecontrol_set_glyph_image(GECONTROL(ge->gectrl), 0); + + /* + * Make sure the control has the most current list of colors. + */ + gecontrol_set_color_list(GECONTROL(ge->gectrl), options.colors); + } + + /* + * Update the text fields and labels with the glyph info. + */ + gtk_entry_set_text(GTK_ENTRY(ge->name), grid->name); + if (grid->unencoded) + sprintf(buffer1, "-1"); + else { + switch (base) { + case 8: sprintf(buffer1, "%o", grid->encoding); break; + case 10: sprintf(buffer1, "%d", grid->encoding); break; + case 16: sprintf(buffer1, "%04X", grid->encoding); break; + } + } + gtk_label_set_text(GTK_LABEL(ge->encoding), buffer1); + + gtk_widget_set_sensitive(ge->dwidth, TRUE); + sprintf(buffer1, "%hd", grid->dwidth); + + g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler); + gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1); + g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler); + + if (grid->spacing != BDF_PROPORTIONAL) + gtk_widget_set_sensitive(ge->dwidth, FALSE); + + sprintf(buffer1, "width %hd height %hd\r\nascent %hd descent %hd", + grid->glyph_bbx.width, grid->glyph_bbx.height, + grid->glyph_bbx.ascent, grid->glyph_bbx.descent); + gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1); + + if (grid->modified) { + gtk_widget_set_sensitive(ge->update, TRUE); + gtk_widget_set_sensitive(ge->button_update, TRUE); + gtk_widget_set_sensitive(ge->update_next, TRUE); + gtk_widget_set_sensitive(ge->update_prev, TRUE); + } else { + gtk_widget_set_sensitive(ge->update, FALSE); + gtk_widget_set_sensitive(ge->button_update, FALSE); + gtk_widget_set_sensitive(ge->update_next, FALSE); + gtk_widget_set_sensitive(ge->update_prev, FALSE); + } + + /* + * Set the sensitivity of the next and previous buttons. + */ + if (glyphedit_get_encoding(GLYPHEDIT(ge->gedit)) == 0) + gtk_widget_set_sensitive(ge->button_prev, FALSE); + else + gtk_widget_set_sensitive(ge->button_prev, TRUE); + + if (glyphedit_get_encoding(GLYPHEDIT(ge->gedit)) == 0xffff) + gtk_widget_set_sensitive(ge->button_next, FALSE); + else + gtk_widget_set_sensitive(ge->button_next, TRUE); + + gtk_widget_show_all(ge->shell); + + /* + * Force the focus to be on the glyph grid. + */ + gtk_widget_grab_focus(ge->gedit); +} + +/* + * Routine to set the show_cap_height value for all the glyph editors. + */ +void +guigedit_show_cap_height(gboolean show) +{ + gulong i; + + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].owner != ~0) + glyphedit_set_show_cap_height(GLYPHEDIT(glyph_editors[i].gedit), + show); + } +} + +/* + * Routine to set the show_cap_height value for all the glyph editors. + */ +void +guigedit_show_x_height(gboolean show) +{ + gulong i; + + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].owner != ~0) + glyphedit_set_show_x_height(GLYPHEDIT(glyph_editors[i].gedit), show); + } +} + +/* + * Routine to set the pixel size on all of the visible editors. + */ +void +guigedit_set_pixel_size(guint pixel_size) +{ + gulong i; + + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].owner != ~0) + glyphedit_set_pixel_size(GLYPHEDIT(glyph_editors[i].gedit), + pixel_size); + } +} + +/* + * Routine to set the spacing and device width on all of the visible editors. + */ +void +guigedit_set_font_spacing(gint spacing, guint16 monowidth) +{ + gulong i; + + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].owner != ~0) + glyphedit_set_spacing(GLYPHEDIT(glyph_editors[i].gedit), spacing, + monowidth); + } +} + +/* + * Routine to set the code base on all the glyph editors that are active. + */ +void +guigedit_set_code_base(gint base) +{ + guint i, enc; + + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].owner != ~0) { + enc = (guint) glyphedit_get_encoding(GLYPHEDIT(glyph_editors[i].gedit)); + switch (base) { + case 8: + sprintf(buffer1, "%o", enc); + break; + case 10: + sprintf(buffer1, "%d", enc); + break; + case 16: + sprintf(buffer1, "%04X", enc); + break; + } + gtk_label_set_text(GTK_LABEL(glyph_editors[i].encoding), buffer1); + } + } +} + +/* + * Routine to clean up everything that was allocated here. + */ +void +guigedit_cleanup(void) +{ + gulong i; + + /* + * Cycle through all the glyph editors and check if they have been + * modified and if they need to be saved. + */ + for (i = 0; i < num_glyph_editors; i++) { + if (glyph_editors[i].id != ~0) + close_glyph_editor(0, 0, GUINT_TO_POINTER(i)); + } + + /* + * Unreference all the pixbufs so they go away. + */ + if (lb_image != 0) + g_object_unref(G_OBJECT(lb_image)); + if (rb_image != 0) + g_object_unref(G_OBJECT(rb_image)); + if (as_image != 0) + g_object_unref(G_OBJECT(as_image)); + if (ds_image != 0) + g_object_unref(G_OBJECT(ds_image)); + + lb_image = rb_image = as_image = ds_image = 0; + + /* + * GTK will take care of the widgets in the glyph editors. + */ + if (num_glyph_editors > 0) + g_free(glyph_editors); +} diff --git a/guihelp.c b/guihelp.c new file mode 100644 index 0000000..b20b632 --- /dev/null +++ b/guihelp.c @@ -0,0 +1,396 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" +#include "htext.h" + +static GtkWidget *help_dialog; +static GtkWidget *help_text; +static GtkWidget *help_topics; + +typedef struct { + int mnemonic; + gchar *help_topic; + gchar *help_text; +} helpmap_t; + +static helpmap_t topics[] = { + {'A', "About", 0}, + {'P', "The Program", 0}, + {'F', "Font Grid", 0}, + {'G', "Glyph Editor", 0}, + {'C', "Configuration File", 0}, + {'r', "Preferences Dialog", 0}, + {'W', "Windows Font Notes", 0}, + {'O', "OpenType Font Notes", 0}, + {'P', "PSF Font Notes", 0}, + {'H', "HEX Font Notes", 0}, + {'o', "Color Notes", 0}, + {'i', "Tips", 0}, +}; + +static guint ntopics = sizeof(topics) / sizeof(topics[0]); + +/************************************************************** + * + * Routines for parsing the markup for the help text buffer. + * + **************************************************************/ + +#define HTEXT_BULLET 0x0001 +#define HTEXT_UL 0x0002 + +typedef struct { + guint flags; + GtkTextBuffer *text; + const gchar *tag_name; + GtkTextIter iter; +} htext_parse_t; + +static htext_parse_t hp; + +static const gchar *bullet = "•"; + +void +help_parse_start(GMarkupParseContext *ctx, const gchar *tag, + const gchar **attr_names, const gchar **attr_vals, + gpointer data, GError **err) +{ + if (strcmp(tag, "bullet") == 0) { + gtk_text_buffer_insert_with_tags_by_name(hp.text, &hp.iter, + bullet, 3, + "margin", + "large_bullet", NULL); + hp.flags |= HTEXT_BULLET; + hp.tag_name = 0; + } else if (strcmp(tag, "help") == 0) + hp.tag_name = 0; + else + hp.tag_name = tag; +} + +void +help_parse_text(GMarkupParseContext *ctx, const gchar *txt, gsize txtlen, + gpointer data, GError **err) +{ + if (hp.tag_name != 0) + gtk_text_buffer_insert_with_tags_by_name(hp.text, &hp.iter, + txt, txtlen, + hp.tag_name, NULL); + else { + if (hp.flags & HTEXT_BULLET) + gtk_text_buffer_insert_with_tags_by_name(hp.text, &hp.iter, + txt, txtlen, + "tabs", NULL); + else + /* + * Plain text insert. + */ + gtk_text_buffer_insert(hp.text, &hp.iter, txt, txtlen); + } +} + +void +help_parse_end(GMarkupParseContext *ctx, const gchar *tag, + gpointer data, GError **err) +{ + if (strcmp(tag, "bullet") == 0) + hp.flags &= ~HTEXT_BULLET; + hp.tag_name = 0; +} + +void +help_parse_error(GMarkupParseContext *ctx, GError *err, gpointer data) +{ +} + +static GMarkupParser markup_funcs = { + help_parse_start, + help_parse_end, + help_parse_text, + 0, + help_parse_error, +}; + +static GMarkupParseContext *markup_context = NULL; + +/* + * Creates all the markup tags. + */ +static void +help_init_markup(void) +{ + PangoTabArray *tabs; + + gtk_text_buffer_create_tag(hp.text, "center", + "justification", GTK_JUSTIFY_CENTER, NULL); + gtk_text_buffer_create_tag(hp.text, "margin", + "left_margin", 20, NULL); + gtk_text_buffer_create_tag(hp.text, "margin1", + "left_margin", 30, NULL); + gtk_text_buffer_create_tag(hp.text, "margin2", + "left_margin", 45, NULL); + gtk_text_buffer_create_tag(hp.text, "margin1.5", + "left_margin", 30, + "weight", PANGO_WEIGHT_BOLD, + NULL); + gtk_text_buffer_create_tag(hp.text, "margin3", + "left_margin", 60, NULL); + gtk_text_buffer_create_tag(hp.text, "large_bullet", + "scale", PANGO_SCALE_X_LARGE, NULL); + gtk_text_buffer_create_tag(hp.text, "param", + "weight", PANGO_WEIGHT_BOLD, + "left_margin", 10, NULL); + gtk_text_buffer_create_tag(hp.text, "ul", + "underline", PANGO_UNDERLINE_SINGLE, NULL); + gtk_text_buffer_create_tag(hp.text, "bul", + "underline", PANGO_UNDERLINE_SINGLE, + "weight", PANGO_WEIGHT_BOLD, NULL); + + tabs = pango_tab_array_new_with_positions(2, TRUE, + PANGO_TAB_LEFT, 50, + PANGO_TAB_LEFT, 70); + gtk_text_buffer_create_tag(hp.text, "tabs", + "tabs", tabs, + "left_margin", 10, + NULL); + + gtk_text_buffer_create_tag(hp.text, "i", + "style", PANGO_STYLE_ITALIC, NULL); + gtk_text_buffer_create_tag(hp.text, "b", + "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(hp.text, "bi", + "style", PANGO_STYLE_ITALIC, + "weight", PANGO_WEIGHT_BOLD, NULL); +} + +/************************************************************** + * + * API functions. + * + **************************************************************/ + +static void +help_free_list_item(gpointer data, gpointer user_data) +{ + gtk_tree_path_free((GtkTreePath *) data); +} + +static void +help_on(GtkTreeSelection *sel, gpointer data) +{ + GList *rowlist; + gint row = 1, *rows; + GError *err = 0; + + /* + * Because this is called before the help text widget is created, + * simply return if the help text widget is NULL. + */ + if (help_text == 0) + return; + + /* + * Sorta complicated, but can't see any other way around it at the moment + * with gtk 2.2.4. + */ + rowlist = gtk_tree_selection_get_selected_rows(sel, NULL); + rows = + gtk_tree_path_get_indices((GtkTreePath *) g_list_nth_data(rowlist, 0)); + row = rows[0]; + + /* + * Clear out the rows that were returned. + */ + g_list_foreach(rowlist, help_free_list_item, 0); + + /* + * Set the dialog title. + */ + sprintf(buffer1, "Help: %s", topics[row].help_topic); + gtk_window_set_title(GTK_WINDOW(help_dialog), buffer1); + + /* + * Clear the text buffer. + */ + gtk_text_buffer_set_text(hp.text, "", 0); + + /* + * Get the iterator at the start of the buffer. + */ + gtk_text_buffer_get_start_iter(hp.text, &hp.iter); + + /* + * Parse the help text. + */ + if (g_markup_parse_context_parse(markup_context, + topics[row].help_text, -1, &err) == FALSE) + fprintf(stderr, "guihelp.c:%s\n", err->message); +} + +void +guihelp_show_help(GtkWidget *w, gpointer data) +{ + guint i; + GtkWidget *hbox, *sw, *button; + GtkTextBuffer *text; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeSelection *sel; + GtkTreePath *tpath; + GtkTreeIter iter; + + if (help_dialog == 0) { + /* + * Store the help text into the array. + */ + topics[GUIHELP_ABOUT].help_text = about_text; + topics[GUIHELP_PROGRAM].help_text = program_text; + topics[GUIHELP_FONTGRID].help_text = fgrid_text; + topics[GUIHELP_GLYPH_EDITOR].help_text = gedit_text; + topics[GUIHELP_CONFIG_FILE].help_text = conf_text; + topics[GUIHELP_PREFERENCES].help_text = preferences_text; + topics[GUIHELP_FNT].help_text = fnt_text; + topics[GUIHELP_OTF].help_text = otf_text; + topics[GUIHELP_PSF].help_text = psf_text; + topics[GUIHELP_HEX].help_text = hex_text; + topics[GUIHELP_COLOR].help_text = color_text; + topics[GUIHELP_TIPS].help_text = tips_text; + + help_dialog = gtk_dialog_new(); + + /* + * Force the help dialog to center over the first editor that + * was created. + */ + (void) g_signal_connect(G_OBJECT(help_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), NULL); + + hbox = gtk_hbox_new(FALSE, 5); + + /* + * Create the list that will be used for the help topics. + */ + store = gtk_list_store_new(1, G_TYPE_STRING); + + /* + * Add the topics to the list. + */ + for (i = 0; i < ntopics; i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, topics[i].help_topic, -1); + } + help_topics = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + + cell_renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ("Help Topics", + cell_renderer, + "text", 0, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column (GTK_TREE_VIEW(help_topics), column); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_topics)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE); + + (void) g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(help_on), NULL); + + gtk_box_pack_start(GTK_BOX(hbox), help_topics, FALSE, FALSE, 0); + + g_object_unref(store); + + gtk_box_pack_start(GTK_BOX(hbox), gtk_vseparator_new(), + FALSE, FALSE, 0); + + /* + * Create the text widget that will display the help text. + */ + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, + GTK_POLICY_ALWAYS); + + text = gtk_text_buffer_new(NULL); + help_text = gtk_text_view_new_with_buffer(text); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(help_text), GTK_WRAP_WORD); + gtk_text_view_set_editable(GTK_TEXT_VIEW(help_text), FALSE); + gtk_widget_set_size_request(help_text, 550, 300); + + gtk_container_add(GTK_CONTAINER(sw), help_text); + + gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0); + + /* + * Add the table to the dialog widget. + */ + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(help_dialog)->vbox), + hbox); + + button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + (void) g_signal_connect_object(G_OBJECT(button), "clicked", + G_CALLBACK(gtk_widget_hide), + (gpointer) help_dialog, + G_CONNECT_SWAPPED); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(help_dialog)->action_area), + button); + gtk_widget_show_all(GTK_DIALOG(help_dialog)->vbox); + gtk_widget_show_all(GTK_DIALOG(help_dialog)->action_area); + + /* + * Create all the markup tags the text buffer will use. + */ + hp.flags = 0; + hp.text = text; + hp.tag_name = ""; + help_init_markup(); + + /* + * Create the context for the parser. + */ + markup_context = g_markup_parse_context_new(&markup_funcs, 0, + NULL, NULL); + } + + /* + * Select the row specified in the callback. + */ + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_topics)); + tpath = gtk_tree_path_new_from_indices(GPOINTER_TO_INT(data), -1); + gtk_tree_selection_select_path(sel, tpath); + + /* + * Show the help dialog and force it to the top. + */ + guiutil_show_dialog_centered(help_dialog, editors[0].shell); + gdk_window_raise(help_dialog->window); +} + +void +guihelp_cleanup(void) +{ + if (markup_context != NULL) + g_markup_parse_context_free(markup_context); + markup_context = NULL; +} diff --git a/guiops.c b/guiops.c new file mode 100644 index 0000000..592170a --- /dev/null +++ b/guiops.c @@ -0,0 +1,409 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" + +static GtkWidget *ops_dialog; +static GtkWidget *ops_notebook; +static GtkWidget *ops_dx; +static GtkWidget *ops_dy; +static GtkWidget *ops_rotate; +static GtkWidget *ops_shear; +static GtkWidget *ops_degrees; +static GtkWidget *ops_all_glyphs; +static GtkWidget *ops_selected_glyphs; +static GtkWidget *ops_apply; +static gboolean translate_changed; +static gboolean degrees_changed; +static gboolean embolden_changed; +static gint page; + +static void +apply_operation(GtkWidget *w, gint response, gpointer data) +{ + gbdfed_editor_t *ed; + gint16 dx, dy; + gboolean all; + + /* + * Turn off the Apply button for this page. + */ + gtk_widget_set_sensitive(ops_apply, FALSE); + + if (response == GTK_RESPONSE_CLOSE) { + gtk_widget_hide(ops_dialog); + gtk_widget_set_sensitive(ops_apply, FALSE); + translate_changed = degrees_changed = embolden_changed = FALSE; + return; + } + + ed = editors + + GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(w), "editor")); + + all = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ops_all_glyphs)); + + if (translate_changed) { + /* + * Do a glyph translate. + */ + dx = _bdf_atos((char *) gtk_entry_get_text(GTK_ENTRY(ops_dx)), 0, 10); + dy = _bdf_atos((char *) gtk_entry_get_text(GTK_ENTRY(ops_dy)), 0, 10); + fontgrid_translate_glyphs(FONTGRID(ed->fgrid), dx, dy, all); + translate_changed = FALSE; + } + + if (degrees_changed) { + /* + * Do either a rotate or a shear. + */ + dx = _bdf_atos((char *) gtk_entry_get_text(GTK_ENTRY(ops_degrees)), + 0, 10); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ops_rotate))) + fontgrid_rotate_glyphs(FONTGRID(ed->fgrid), dx, all); + else + fontgrid_shear_glyphs(FONTGRID(ed->fgrid), dx, all); + degrees_changed = FALSE; + } + + if (embolden_changed) { + /* + * Embolden some glyphs. + */ + fontgrid_embolden_glyphs(FONTGRID(ed->fgrid), all); + embolden_changed = FALSE; + } +} + +static void +notebook_switch_page(GtkNotebook *nb, GtkNotebookPage *nbp, gint pageno, + gpointer data) +{ + GtkWidget *which = 0; + + /* + * Force the focus on the entry fields when these pages become visible. + */ + switch (pageno) { + case 0: + if (ops_apply != 0) + gtk_widget_set_sensitive(ops_apply, translate_changed); + which = ops_dx; + break; + case 1: + if (ops_apply != 0) + gtk_widget_set_sensitive(ops_apply, degrees_changed); + which = ops_degrees; + break; + case 2: + if (ops_apply != 0) + gtk_widget_set_sensitive(ops_apply, embolden_changed); + which = 0; + } + page = pageno; + + if (which) + gtk_widget_grab_focus(which); +} + +static void +enable_apply(GtkWidget *w, gpointer data) +{ + gint which = GPOINTER_TO_INT(data); + gboolean val = TRUE; + + if (which == 0) + translate_changed = TRUE; + else if (which == 1) + degrees_changed = TRUE; + else { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) + embolden_changed = TRUE; + else + embolden_changed = FALSE; + val = embolden_changed; + } + + gtk_widget_set_sensitive(ops_apply, val); +} + +static void +ops_dialog_setup(gbdfed_editor_t *ed) +{ + GtkWidget *vbox, *hbox, *vbox1, *label, *table, *button; + GtkRadioButton *rb; + GtkAdjustment *adj; + + if (ops_dialog == 0) { + translate_changed = degrees_changed = FALSE; + + ops_dialog = gtk_dialog_new(); + (void) g_signal_connect(G_OBJECT(ops_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + vbox = gtk_vbox_new(FALSE, 0); + + ops_notebook = gtk_notebook_new(); + (void) g_signal_connect(G_OBJECT(ops_notebook), "switch_page", + G_CALLBACK(notebook_switch_page), 0); + + /* + * Create the notebook pages. + */ + table = gtk_table_new(2, 2, FALSE); + + label = gtk_label_new("DX:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, + GTK_FILL, 0, 0); + + label = gtk_label_new("DY:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, + GTK_FILL, 0, 0); + + ops_dx = gtk_widget_new(gtk_entry_get_type(), + "max_length", 6, NULL); + (void) g_signal_connect(G_OBJECT(ops_dx), "changed", + G_CALLBACK(enable_apply), + GINT_TO_POINTER(0)); + gtk_table_attach(GTK_TABLE(table), ops_dx, 1, 2, 0, 1, GTK_FILL, + GTK_FILL, 5, 5); + ops_dy = gtk_widget_new(gtk_entry_get_type(), + "max_length", 6, NULL); + (void) g_signal_connect(G_OBJECT(ops_dx), "changed", + G_CALLBACK(enable_apply), + GINT_TO_POINTER(0)); + gtk_table_attach(GTK_TABLE(table), ops_dy, 1, 2, 1, 2, GTK_FILL, + GTK_FILL, 5, 5); + + label = gtk_label_new("Translate Glyphs"); + gtk_notebook_append_page(GTK_NOTEBOOK(ops_notebook), table, label); + + vbox1 = gtk_vbox_new(FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + + ops_rotate = gtk_radio_button_new_with_label(0, "Rotate"); + gtk_box_pack_start(GTK_BOX(hbox), ops_rotate, FALSE, FALSE, 2); + rb = GTK_RADIO_BUTTON(ops_rotate); + ops_shear = gtk_radio_button_new_with_label_from_widget(rb, "Shear"); + gtk_box_pack_start(GTK_BOX(hbox), ops_shear, FALSE, FALSE, 2); + + gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new("Degrees:"); + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, -359.0, 359.0, 1.0, + 10.0, 0.0); + ops_degrees = gtk_widget_new(gtk_spin_button_get_type(), + "max_length", 6, + "adjustment", adj, + "climb_rate", 1.0, + "digits", 0, + "value", 0.0, + "numeric", TRUE, + NULL); + gtk_widget_set_size_request(ops_degrees, 60, -1); + (void) g_signal_connect(G_OBJECT(ops_degrees), "changed", + G_CALLBACK(enable_apply), + GINT_TO_POINTER(1)); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(hbox), ops_degrees, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 3); + + label = gtk_label_new("Rotate/Shear Glyphs"); + gtk_notebook_append_page(GTK_NOTEBOOK(ops_notebook), vbox1, label); + + /* + * Add the embolden check button. + */ + button = gtk_check_button_new_with_label("Embolden"); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(enable_apply), + GINT_TO_POINTER(2)); + label = gtk_label_new("Embolden Glyphs"); + gtk_notebook_append_page(GTK_NOTEBOOK(ops_notebook), button, + label); + + /* + * Add the notebook to the containing vbox. + */ + gtk_box_pack_start(GTK_BOX(vbox), ops_notebook, FALSE, FALSE, 0); + + /* + * Add the radio buttons for choosing between all the glyphs or + * just the selected glyphs. + */ + hbox = gtk_hbox_new(FALSE, 0); + ops_all_glyphs = gtk_radio_button_new_with_label(0, "All Glyphs"); + rb = GTK_RADIO_BUTTON(ops_all_glyphs); + ops_selected_glyphs = + gtk_radio_button_new_with_label_from_widget(rb, "Selected Glyphs"); + /* + * Default to the selected glyphs. + */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_selected_glyphs), + TRUE); + gtk_box_pack_start(GTK_BOX(hbox), ops_all_glyphs, FALSE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(hbox), ops_selected_glyphs, + FALSE, FALSE, 2); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2); + + /* + * Add the vbox to the dialog. + */ + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(ops_dialog)->vbox), vbox); + + ops_apply = gtk_dialog_add_button(GTK_DIALOG(ops_dialog), + GTK_STOCK_APPLY, + GTK_RESPONSE_APPLY); + gtk_widget_set_sensitive(ops_apply, FALSE); + + button = gtk_dialog_add_button(GTK_DIALOG(ops_dialog), + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + + g_signal_connect(G_OBJECT(ops_dialog), "response", + G_CALLBACK(apply_operation), 0); + + gtk_widget_show_all(GTK_DIALOG(ops_dialog)->vbox); + } + + if (fontgrid_has_selection(FONTGRID(ed->fgrid), 0)) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_selected_glyphs), + TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_all_glyphs), TRUE); + + /* + * Set the title of the dialog. + */ + if (ed->file == 0) + sprintf(buffer1, "(unnamed%d): Glyph Operations", ed->id); + else + sprintf(buffer1, "%s: Glyph Operations", ed->file); + + gtk_window_set_title(GTK_WINDOW(ops_dialog), buffer1); +} + +void +guiops_show_translate(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + ops_dialog_setup(ed); + + /* + * Set the object data to be the editor. + */ + g_object_set_data(G_OBJECT(ops_dialog), "editor", data); + + /* + * Show the translate page. + */ + gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 0); + + /* + * Show the dialog. + */ + guiutil_show_dialog_centered(ops_dialog, ed->shell); + + gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE); +} + +void +guiops_show_rotate(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + ops_dialog_setup(ed); + + /* + * Set the object data to be the editor. + */ + g_object_set_data(G_OBJECT(ops_dialog), "editor", data); + + /* + * Show the rotate page. + */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_rotate), TRUE); + + /* + * Show the dialog. + */ + guiutil_show_dialog_centered(ops_dialog, ed->shell); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 1); + + gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE); +} + +void +guiops_show_shear(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + ops_dialog_setup(ed); + + /* + * Set the object data to be the editor. + */ + g_object_set_data(G_OBJECT(ops_dialog), "editor", data); + + /* + * Show the shear page. + */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ops_shear), TRUE); + + /* + * Show the dialog. + */ + guiutil_show_dialog_centered(ops_dialog, ed->shell); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 1); + + gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE); +} + +void +guiops_show_embolden(GtkWidget *w, gpointer data) +{ + gbdfed_editor_t *ed = editors + GPOINTER_TO_UINT(data); + + ops_dialog_setup(ed); + + /* + * Set the object data to be the editor. + */ + g_object_set_data(G_OBJECT(ops_dialog), "editor", data); + + /* + * Show the dialog. + */ + guiutil_show_dialog_centered(ops_dialog, ed->shell); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(ops_notebook), 2); + + gtk_window_set_modal(GTK_WINDOW(ops_dialog), TRUE); +} diff --git a/guipref.c b/guipref.c new file mode 100644 index 0000000..3f5c768 --- /dev/null +++ b/guipref.c @@ -0,0 +1,1161 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" +#include "grayswatch.h" + +static GtkWidget *pref_dialog; +static GtkWidget *pref_unicode; +static GtkWidget *pref_adobe; +static GtkWidget *pref_cursor_font; +static GtkWidget *pref_apply; + +static GtkWidget *pref_fsel_dialog; +static gboolean pref_fsel_unicode; + +static GtkWidget *pref_color; +static GtkWidget *pref_color_dialog; +static GtkWidget *pref_color_win[16]; + +static gbdfed_options_t tmp_opts; + +static void +pref_toggle(GtkWidget *w, gpointer data) +{ + gint which; + gboolean val = FALSE; + + which = GPOINTER_TO_INT(data); + if (which != 10) + val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); + + switch (which) { + case 0: tmp_opts.backups = val; break; + case 1: tmp_opts.font_opts.correct_metrics = val; break; + case 2: tmp_opts.font_opts.pad_cells = val; break; + case 3: tmp_opts.font_opts.keep_unencoded = val; break; + case 4: tmp_opts.font_opts.keep_comments = val; break; + case 5: + if (val == TRUE) + tmp_opts.font_opts.otf_flags &= ~FT_LOAD_NO_HINTING; + else + tmp_opts.font_opts.otf_flags |= FT_LOAD_NO_HINTING; + break; + case 6: tmp_opts.sbit = val; break; + case 7: + tmp_opts.show_cap_height = val; + break; + case 8: + tmp_opts.show_x_height = val; + break; + case 9: + /* + * Toggle the Really Exit dialog. + */ + tmp_opts.really_exit = val; + break; + case 10: + tmp_opts.pixel_size = (unsigned int) + gtk_combo_box_get_active(GTK_COMBO_BOX(w)) + 2; + break; + } + + /* + * Enable the Apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static void +pref_eol(GtkWidget *w, gpointer data) +{ + tmp_opts.font_opts.eol = gtk_combo_box_get_active(GTK_COMBO_BOX(w)) + 1; + + /* + * Enable the Apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static GtkWidget * +pref_make_general_page() +{ + GtkWidget *table, *button, *hbox, *tmp, *omenu, *frame, *vbox; + + vbox = gtk_vbox_new(FALSE, 10); + + /* + * Create the load/save option selection. + */ + frame = gtk_frame_new("Load/Save"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + table = gtk_table_new(2, 3, FALSE); + + button = gtk_check_button_new_with_label("Make Backups"); + if (tmp_opts.backups) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(0)); + gtk_table_attach(GTK_TABLE(table), button, 0, 1, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + button = gtk_check_button_new_with_label("Correct Metrics"); + if (tmp_opts.font_opts.correct_metrics) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(1)); + gtk_table_attach(GTK_TABLE(table), button, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + button = gtk_check_button_new_with_label("Pad Character Cells"); + if (tmp_opts.font_opts.pad_cells) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(2)); + gtk_table_attach(GTK_TABLE(table), button, 2, 3, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + button = gtk_check_button_new_with_label("Keep Unencoded Glyphs"); + if (tmp_opts.font_opts.keep_unencoded) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(3)); + gtk_table_attach(GTK_TABLE(table), button, 0, 1, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + button = gtk_check_button_new_with_label("Keep Comments"); + if (tmp_opts.font_opts.keep_comments) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(4)); + gtk_table_attach(GTK_TABLE(table), button, 1, 2, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + hbox = gtk_hbox_new(FALSE, 0); + tmp = gtk_label_new("EOL:"); + gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0); + + omenu = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), "Unix [LF]"); + gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), "WIN/DOS [CRLF]"); + gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), "MAC [CR]"); + gtk_combo_box_set_active(GTK_COMBO_BOX(omenu), tmp_opts.font_opts.eol - 1); + (void) g_signal_connect(G_OBJECT(omenu), "changed", + G_CALLBACK(pref_eol), 0); + + gtk_box_pack_start(GTK_BOX(hbox), omenu, TRUE, TRUE, 0); + + gtk_table_attach(GTK_TABLE(table), hbox, 2, 3, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5); + + gtk_container_add(GTK_CONTAINER(frame), table); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + frame = gtk_frame_new("OpenType"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + button = gtk_check_button_new_with_label("Hint Glyphs"); + if (!(tmp_opts.font_opts.otf_flags & FT_LOAD_NO_HINTING)) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); +#ifdef HAVE_FREETYPE + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(5)); +#else + /* + * No Freetype support means no point in being able to toggle this + * widget. + */ + gtk_widget_set_sensitive(button, FALSE); +#endif + gtk_container_add(GTK_CONTAINER(frame), button); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + frame = gtk_frame_new("SBIT"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + button = gtk_check_button_new_with_label("Generate SBIT Metrics File"); + if (tmp_opts.sbit) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(6)); + gtk_container_add(GTK_CONTAINER(frame), button); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + return vbox; +} + +static void +pref_change_size(GtkWidget *w, gpointer data) +{ + gint v, which = GPOINTER_TO_INT(data); + + v = (gint) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w)); + + switch (which) { + case 0: tmp_opts.font_opts.point_size = (int) v; break; + case 1: tmp_opts.font_opts.resolution_x = (int) v; break; + case 2: tmp_opts.font_opts.resolution_y = (int) v; break; + } + + /* + * Enable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +/* + * Synchronize the vertical resolution with the horizontal resolution. + */ +static void +pref_sync_res(GtkWidget *w, GdkEventFocus *ev, gpointer data) +{ + gfloat v; + GtkSpinButton *b; + + b = GTK_SPIN_BUTTON(data); + v = (gfloat) gtk_spin_button_get_value(b); + + if (v != (gfloat) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))) { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), v); + + /* + * Enable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); + } +} + +static void +pref_set_spacing(GtkWidget *w, gpointer data) +{ + tmp_opts.font_opts.font_spacing = GPOINTER_TO_INT(data); + + /* + * Enable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static void +pref_set_cursor_font(GtkWidget *w, gpointer data) +{ + tmp_opts.font_opts.cursor_font = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); + + /* + * Enable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static void +pref_color_response(GtkDialog *d, gint response, gpointer data) +{ + gint i; + + if (response == GTK_RESPONSE_REJECT) { + /* + * Replace the colors with those found in the original + * options. + */ + memcpy(&tmp_opts.colors, &options.colors, + sizeof(unsigned short) * 20); + + if (tmp_opts.font_opts.bits_per_pixel == 2) { + for (i = 0; i < 4; i++) + grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]), + tmp_opts.colors[i]); + } else { + for (i = 0; i < 16; i++) + grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]), + tmp_opts.colors[i + 4]); + } + } else if (response == GTK_RESPONSE_CLOSE) + gtk_widget_hide(GTK_WIDGET(data)); +} + +static void +pref_color_update_color(GtkWidget *w, gint color, gpointer data) +{ + gint which = GPOINTER_TO_INT(data); + + if (tmp_opts.font_opts.bits_per_pixel == 2) + tmp_opts.colors[which] = color; + else if (tmp_opts.font_opts.bits_per_pixel == 4) + tmp_opts.colors[which + 4] = color; + + /* + * Make sure the Apply button is enabled. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static void +pref_select_colors(GtkWidget *w, gpointer data) +{ + GtkWidget *hbox; + gint i; + + if (pref_color_dialog == 0) { + + pref_color_dialog = gtk_dialog_new(); + (void) g_signal_connect(G_OBJECT(pref_color_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + (void) g_signal_connect(G_OBJECT(pref_color_dialog), "response", + G_CALLBACK(pref_color_response), + (gpointer) pref_color_dialog); + gtk_window_set_resizable(GTK_WINDOW(pref_color_dialog), TRUE); + + hbox = gtk_hbox_new(FALSE, 0); + + for (i = 0; i < 16; i++) { + pref_color_win[i] = grayswatch_new(tmp_opts.colors[i + 4]); + g_signal_connect(G_OBJECT(pref_color_win[i]), "value-changed", + G_CALLBACK(pref_color_update_color), + GINT_TO_POINTER(i)); + gtk_widget_set_size_request(pref_color_win[i], 50, 75); + gtk_box_pack_start(GTK_BOX(hbox), pref_color_win[i], + FALSE, FALSE, 0); + } + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pref_color_dialog)->vbox), + hbox); + + /* + * Add the buttons. + */ + gtk_dialog_add_buttons(GTK_DIALOG(pref_color_dialog), + GTK_STOCK_REVERT_TO_SAVED, + GTK_RESPONSE_REJECT, + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE, NULL); + + gtk_dialog_set_default_response(GTK_DIALOG(pref_color_dialog), + GTK_RESPONSE_CLOSE); + + gtk_widget_show_all(pref_color_dialog); + } + + /* + * If selecting colors for 2 bits-per-pixel, hide all but the first 4. + * Set the first 4 colors depending on the bits-per-pixel value. + */ + for (i = 0; i < 16; i++) { + /* + * We don't want setting the gray values to trigger the signal. + * That causes the Apply button to be made sensitive. + */ + grayswatch_block_signal(GRAYSWATCH(pref_color_win[i]), TRUE); + if (tmp_opts.font_opts.bits_per_pixel == 2 && i >= 4) + gtk_widget_hide(pref_color_win[i]); + else { + if (i < 4) { + if (tmp_opts.font_opts.bits_per_pixel == 2) + grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]), + tmp_opts.colors[i]); + else + grayswatch_set_gray(GRAYSWATCH(pref_color_win[i]), + tmp_opts.colors[i + 4]); + } else + gtk_widget_show(pref_color_win[i]); + } + grayswatch_block_signal(GRAYSWATCH(pref_color_win[i]), FALSE); + } + + /* + * Center the dialog and show it. + */ + guiutil_show_dialog_centered(pref_color_dialog, pref_dialog); +} + +static void +pref_set_bpp(GtkWidget *w, gpointer data) +{ + gboolean on; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) == FALSE) + return; + + tmp_opts.font_opts.bits_per_pixel = GPOINTER_TO_INT(data); + on = (tmp_opts.font_opts.bits_per_pixel == 1 || + tmp_opts.font_opts.bits_per_pixel == 8) ? FALSE : TRUE; + + if (pref_color_dialog != 0 && GTK_WIDGET_VISIBLE(pref_color_dialog)) { + if (tmp_opts.font_opts.bits_per_pixel == 1 || + tmp_opts.font_opts.bits_per_pixel == 8) + gtk_widget_hide(pref_color_dialog); + else + pref_select_colors(w, data); + } + + gtk_widget_set_sensitive(pref_color, on); + + /* + * Enable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static GtkWidget * +pref_make_newfont_page() +{ + GtkWidget *label, *table, *button, *hbox, *vbox, *frame, *tmp; + GtkAdjustment *adj; + + vbox = gtk_vbox_new(FALSE, 10); + + /* + * Create the font size selection. + */ + frame = gtk_frame_new("Font Size"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + table = gtk_table_new(3, 2, FALSE); + + label = gtk_label_new("Point Size:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 5, 0); + + label = gtk_label_new("Horizontal Resolution:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 5, 5); + + label = gtk_label_new("Vertical Resolution:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 5, 5); + + /* + * Make the spinboxes for the point size and resolutions. + */ + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 4.0, 256.0, 1.0, 2.0, 0.0); + button = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), + (gfloat) tmp_opts.font_opts.point_size); + gtk_widget_set_size_request(button, 100, -1); + (void) g_signal_connect(G_OBJECT(button), "changed", + G_CALLBACK(pref_change_size), + GINT_TO_POINTER(0)); + gtk_table_attach(GTK_TABLE(table), button, 1, 2, 0, 1, + GTK_FILL, GTK_FILL, 5, 5); + + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0, + 1.0, 10.0, 0.0); + tmp = button = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), + (gfloat) tmp_opts.font_opts.resolution_x); + gtk_widget_set_size_request(button, 100, -1); + (void) g_signal_connect(G_OBJECT(button), "changed", + G_CALLBACK(pref_change_size), + GINT_TO_POINTER(1)); + gtk_table_attach(GTK_TABLE(table), button, 1, 2, 1, 2, + GTK_FILL, GTK_FILL, 5, 5); + + adj = (GtkAdjustment *) gtk_adjustment_new(0.0, 20.0, 1200.0, + 1.0, 10.0, 0.0); + button = gtk_spin_button_new(adj, 1.0, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), + (gfloat) tmp_opts.font_opts.resolution_y); + gtk_widget_set_size_request(button, 100, -1); + (void) g_signal_connect(G_OBJECT(button), "changed", + G_CALLBACK(pref_change_size), + GINT_TO_POINTER(2)); + (void) g_signal_connect(G_OBJECT(button), "focus-in-event", + G_CALLBACK(pref_sync_res), (gpointer) tmp); + gtk_table_attach(GTK_TABLE(table), button, 1, 2, 2, 3, + GTK_FILL, GTK_FILL, 5, 5); + + gtk_container_add(GTK_CONTAINER(frame), table); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + /* + * Create the spacing selection. + */ + frame = gtk_frame_new("Spacing"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + hbox = gtk_hbox_new(FALSE, 0); + + button = gtk_radio_button_new_with_label(0, "Proportional"); + if (tmp_opts.font_opts.font_spacing == BDF_PROPORTIONAL) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_spacing), + GINT_TO_POINTER(BDF_PROPORTIONAL)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + button = + gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), + "Monowidth"); + if (tmp_opts.font_opts.font_spacing == BDF_MONOWIDTH) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_spacing), + GINT_TO_POINTER(BDF_MONOWIDTH)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + button = + gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), + "Character Cell"); + if (tmp_opts.font_opts.font_spacing == BDF_CHARCELL) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_spacing), + GINT_TO_POINTER(BDF_CHARCELL)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + /* + * Create the bits-per-pixel selection. + */ + frame = gtk_frame_new("Bits Per Pixel"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + hbox = gtk_hbox_new(FALSE, 0); + + button = gtk_radio_button_new_with_label(0, "1 bpp"); + if (tmp_opts.font_opts.bits_per_pixel == 1) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_bpp), + GINT_TO_POINTER(1)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + button = + gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), + "2 bpp"); + if (tmp_opts.font_opts.bits_per_pixel == 2) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_bpp), + GINT_TO_POINTER(2)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + button = + gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), + "4 bpp"); + if (tmp_opts.font_opts.bits_per_pixel == 4) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_bpp), + GINT_TO_POINTER(4)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + button = + gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), + "8 bpp"); + if (tmp_opts.font_opts.bits_per_pixel == 8) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_set_bpp), + GINT_TO_POINTER(8)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + pref_color = gtk_button_new_with_label("Select Colors"); + (void) g_signal_connect(G_OBJECT(pref_color), "clicked", + G_CALLBACK(pref_select_colors), 0); + if (tmp_opts.font_opts.bits_per_pixel == 1) + gtk_widget_set_sensitive(pref_color, FALSE); + + gtk_box_pack_start(GTK_BOX(hbox), pref_color, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + frame = gtk_frame_new("Cursor Fonts"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + pref_cursor_font = gtk_check_button_new_with_label("Cursor Font"); + if (tmp_opts.font_opts.cursor_font) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref_cursor_font), + TRUE); + (void) g_signal_connect(G_OBJECT(pref_cursor_font), "toggled", + G_CALLBACK(pref_set_cursor_font), 0); + gtk_container_add(GTK_CONTAINER(frame), pref_cursor_font); + gtk_widget_set_sensitive(pref_cursor_font, FALSE); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + return vbox; +} + +static void +pref_fgrid_mode(GtkWidget *w, gpointer data) +{ + tmp_opts.overwrite_mode = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); + + /* + * Enable the Apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); +} + +static void +pref_set_filename(GtkWidget *w, gpointer data) +{ + gchar *fname; + + + fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(w)); + + if (pref_fsel_unicode) + gtk_entry_set_text(GTK_ENTRY(pref_unicode), fname); + else + gtk_entry_set_text(GTK_ENTRY(pref_adobe), fname); + + /* + * Enable the Apply button. + */ + gtk_widget_set_sensitive(pref_apply, TRUE); + + /* + * Hide the dialog. + */ + gtk_widget_hide(w); +} + +static void +handle_filename_response(GtkDialog *d, gint response, gpointer data) +{ + switch (response) { + case GTK_RESPONSE_ACCEPT: + pref_set_filename(GTK_WIDGET(d), data); + break; + case GTK_RESPONSE_CANCEL: + gtk_widget_hide(GTK_WIDGET(d)); + break; + } +} + +static void +pref_show_fsel_dialog(GtkWidget *w, gpointer data) +{ + pref_fsel_unicode = GPOINTER_TO_INT(data); + + if (pref_fsel_dialog == 0) { + pref_fsel_dialog = gtk_file_chooser_dialog_new("Glyph Name", + GTK_WINDOW(pref_dialog), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_APPLY, + GTK_RESPONSE_ACCEPT, + NULL); + (void) g_signal_connect(G_OBJECT(pref_fsel_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + (void) g_signal_connect(G_OBJECT(pref_fsel_dialog), "response", + G_CALLBACK(handle_filename_response), + NULL); + } + + /* + * Set the title of the dialog. + */ + if (pref_fsel_unicode) + strcpy(buffer1, "Unicode Character Database Selection"); + else + strcpy(buffer1, "Adobe Glyph Name File Selection"); + + gtk_window_set_title(GTK_WINDOW(pref_fsel_dialog), buffer1); + + guiutil_show_dialog_centered(pref_fsel_dialog, pref_dialog); + + gtk_window_set_modal(GTK_WINDOW(pref_fsel_dialog), TRUE); +} + +static GtkWidget * +pref_make_edit_page() +{ + gint i; + GtkWidget *vbox, *hbox, *frame, *button, *label, *omenu; + GtkWidget *tmp, *table; + + vbox = gtk_vbox_new(FALSE, 10); + + frame = gtk_frame_new("Font Grid Selection Paste"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + hbox = gtk_hbox_new(FALSE, 5); + + button = gtk_radio_button_new_with_label(0, "Overwrites"); + if (tmp_opts.overwrite_mode) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_fgrid_mode), 0); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + button = + gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), + "Inserts"); + if (tmp_opts.overwrite_mode) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + else + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + frame = gtk_frame_new("Glyph Editors"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + hbox = gtk_hbox_new(FALSE, 5); + + button = gtk_check_button_new_with_label("Show Cap Height"); + if (tmp_opts.show_cap_height) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(7)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + button = gtk_check_button_new_with_label("Show X Height"); + if (tmp_opts.show_cap_height) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GINT_TO_POINTER(8)); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); + + label = gtk_label_new("Pixel Size:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + omenu = gtk_combo_box_new_text(); + for (i = 2; i < 21; i++) { + sprintf(buffer1, "%dx%d", i, i); + gtk_combo_box_append_text(GTK_COMBO_BOX(omenu), buffer1); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(omenu), tmp_opts.pixel_size - 2); + g_signal_connect(G_OBJECT(omenu), "changed", + G_CALLBACK(pref_toggle), GINT_TO_POINTER(10)); + + gtk_box_pack_start(GTK_BOX(hbox), omenu, TRUE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(frame), hbox); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + frame = gtk_frame_new("Glyph Name Lists"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + table = gtk_table_new(2, 3, FALSE); + + label = gtk_label_new("Unicode Character Database:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 5, 5); + + label = gtk_label_new("Adobe Glyph List:"); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 5, 5); + + /* + * Add the entries. + */ + tmp = pref_unicode = gtk_entry_new(); + gtk_widget_set_size_request(tmp, 250, -1); + if (tmp_opts.unicode_name_file) + gtk_entry_set_text(GTK_ENTRY(pref_unicode), tmp_opts.unicode_name_file); + gtk_table_attach(GTK_TABLE(table), tmp, 1, 2, 0, 1, + GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5); + + tmp = pref_adobe = gtk_entry_new(); + gtk_widget_set_size_request(tmp, 250, -1); + if (tmp_opts.adobe_name_file) + gtk_entry_set_text(GTK_ENTRY(pref_adobe), tmp_opts.adobe_name_file); + gtk_table_attach(GTK_TABLE(table), tmp, 1, 2, 1, 2, + GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 5); + + /* + * Add the browse buttons. + */ + button = gtk_button_new_with_label("Browse"); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pref_show_fsel_dialog), + GINT_TO_POINTER(TRUE)); + gtk_table_attach(GTK_TABLE(table), button, 2, 3, 0, 1, + GTK_FILL, 0, 5, 0); + button = gtk_button_new_with_label("Browse"); + (void) g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pref_show_fsel_dialog), + GINT_TO_POINTER(FALSE)); + gtk_table_attach(GTK_TABLE(table), button, 2, 3, 1, 2, + GTK_FILL, 0, 5, 0); + + gtk_container_add(GTK_CONTAINER(frame), table); + + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + return vbox; +} + +static GtkWidget * +pref_make_other_page() +{ + GtkWidget *frame, *button, *vbox; + + frame = gtk_frame_new("Dialogs"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + + vbox = gtk_vbox_new(FALSE, 0); + + button = gtk_check_button_new_with_label("Show \"Really Exit\" Dialog"); + if (tmp_opts.really_exit) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + (void) g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(pref_toggle), + GUINT_TO_POINTER(9)); + gtk_container_add(GTK_CONTAINER(frame), button); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + return vbox; +} + +static void +pref_apply_changes(void) +{ + gchar *fname; + + /* + * Take care of setting the file names for the glyph name lists. + */ + fname = (gchar *) gtk_entry_get_text(GTK_ENTRY(pref_unicode)); + if (fname != 0 && fname[0] != 0) + tmp_opts.unicode_name_file = g_strdup(fname); + + fname = (gchar *) gtk_entry_get_text(GTK_ENTRY(pref_adobe)); + if (fname != 0 && fname[0] != 0) + tmp_opts.adobe_name_file = g_strdup(fname); + + /* + * If the name files are different, delete the old name files first. + */ + if (options.unicode_name_file != 0 && + options.unicode_name_file != tmp_opts.unicode_name_file) + g_free(options.unicode_name_file); + if (options.adobe_name_file != 0 && + options.adobe_name_file != tmp_opts.adobe_name_file) + g_free(options.adobe_name_file); + + /* + * Copy the updated options over. + */ + (void) memcpy((char *) &options, (char *) &tmp_opts, + sizeof(gbdfed_options_t)); + + /* + * Set the glyph edit options. + */ + guigedit_show_cap_height(tmp_opts.show_cap_height); + guigedit_show_x_height(tmp_opts.show_x_height); + guigedit_set_pixel_size(tmp_opts.pixel_size); + + /* + * Disable the apply button. + */ + gtk_widget_set_sensitive(pref_apply, FALSE); +} + +static void +pref_save(void) +{ + FILE *out; + gchar *home; + gint i; + + /* + * If any changes were made, do an update before saving. + */ + if (GTK_WIDGET_SENSITIVE(pref_apply)) + pref_apply_changes(); + + if ((home = getenv("HOME")) == 0) { + guiutil_error_message(editors[0].shell, + "Save Preferences: Unable to locate home directory."); + return; + } + + sprintf(buffer1, "%s/.gbdfedrc", home); + if ((out = fopen(buffer1, "w")) == 0) { + sprintf(buffer2, "Save Preferences: Unable to write to %s.", buffer1); + guiutil_error_message(editors[0].shell, buffer2); + return; + } + + /* + * First, write the gbdfed options. + */ + fprintf(out, "#########################\n"); + fprintf(out, "#\n# gbdfed options.\n#\n"); + fprintf(out, "#########################\n\n"); + + if (options.no_blanks) + fprintf(out, "skip_blank_pages true\n\n"); + else + fprintf(out, "skip_blank_pages false\n\n"); + + if (options.really_exit) + fprintf(out, "really_exit true\n\n"); + else + fprintf(out, "really_exit false\n\n"); + if (options.overwrite_mode) + fprintf(out, "grid_overwrite_mode true\n\n"); + else + fprintf(out, "grid_overwrite_mode false\n\n"); + + if (options.accelerator != 0) + fprintf(out, "close_accelerator %s\n\n", + options.accelerator); + if (options.accelerator_text != 0) + fprintf(out, "close_accelerator_text %s\n\n", + options.accelerator_text); + + if (options.unicode_name_file != 0) + fprintf(out, "name_file %s\n\n", options.unicode_name_file); + + if (options.adobe_name_file != 0) + fprintf(out, "adobe_name_file %s\n\n", + options.adobe_name_file); + + fprintf(out, "pixel_size %d\n\n", options.pixel_size); + + if (options.show_cap_height) + fprintf(out, "show_cap_height true\n\n"); + else + fprintf(out, "show_cap_height false\n\n"); + + if (options.show_x_height) + fprintf(out, "show_x_height true\n\n"); + else + fprintf(out, "show_x_height false\n\n"); + + if (options.sbit) + fprintf(out, "generate_sbit_metrics true\n\n"); + else + fprintf(out, "generate_sbit_metrics false\n\n"); + + /* + * Save the grayscales. + */ + fprintf(out, "#\n# Grayscale values. Must be between 0 and 255.\n#\n"); + fprintf(out, "2bpp_grays "); + for (i = 0; i < 4; i++) { + fprintf(out, "%d", options.colors[i]); + if (i + 1 < 4) + putc(' ', out); + } + fprintf(out, "\n4bpp_grays "); + for (i = 4; i < 20; i++) { + fprintf(out, "%d", options.colors[i]); + if (i + 1 < 20) + putc(' ', out); + } + fprintf(out, "\n\n"); + +#if 0 + /* + * Save the colors. + */ + fprintf(out, "#\n# Color values for 2 bits per pixel.\n#\n"); + for (i = 0; i < 4; i++) { + /* + * Do this to avoid writing negative values. + */ + c = options.colors[i]; + fprintf(out, "color%d %d\n", i, c); + } + + fprintf(out, "\n#\n# Color values for 4 bits per pixel.\n#\n"); + for (i = 4; i < 20; i++) { + /* + * Do this to avoid writing negative values. + */ + c = options.colors[i]; + fprintf(out, "color%d %d\n", i, c); + } + putc('\n', out); +#endif + + /* + * The save the BDF specific options. + */ + fprintf(out, "#########################\n"); + fprintf(out, "#\n# BDF font options.\n#\n"); + fprintf(out, "#########################\n\n"); + bdf_save_options(out, &options.font_opts); + fclose(out); +} + +static void +pref_response(GtkDialog *d, gint response, gpointer data) +{ + if (response == GTK_RESPONSE_APPLY) + pref_apply_changes(); + else if (response == GTK_RESPONSE_OK) + pref_save(); + else { + /* + * Make sure the color chooser dialog is hidden if it + * happens to be up. + */ + if (pref_color_dialog != 0 && GTK_WIDGET_VISIBLE(pref_color_dialog)) + gtk_widget_hide(pref_color_dialog); + + gtk_widget_hide(GTK_WIDGET(d)); + } +} + +void +guiedit_show_preferences(GtkWidget *w, gpointer data) +{ + GtkWidget *dvbox, *nb, *button, *table, *label; + + if (pref_dialog == 0) { + /* + * Initialize the temporary options. + */ + (void) memcpy((char *) &tmp_opts, (char *) &options, + sizeof(gbdfed_options_t)); + + pref_dialog = gtk_dialog_new(); + (void) g_signal_connect(G_OBJECT(pref_dialog), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + sprintf(buffer1, "%s Preferences", g_get_prgname()); + gtk_window_set_title(GTK_WINDOW(pref_dialog), buffer1); + + dvbox = GTK_DIALOG(pref_dialog)->vbox; + + /* + * Create the notebook that will contain the Preference tabs. + */ + nb = gtk_notebook_new(); + + /* + * Create the General Options page. + */ + label = gtk_label_new("General Options"); + table = pref_make_general_page(); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label); + + /* + * Create the New Font Options page. + */ + label = gtk_label_new("New Font Options"); + table = pref_make_newfont_page(); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label); + + /* + * Create the Editing Options page. + */ + label = gtk_label_new("Editing Options"); + table = pref_make_edit_page(); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label); + + /* + * Create the Other Options page. + */ + label = gtk_label_new("Other Options"); + table = pref_make_other_page(); + gtk_notebook_append_page(GTK_NOTEBOOK(nb), table, label); + + /* + * Finally, add the notebook to the dialog's vbox. + */ + gtk_container_add(GTK_CONTAINER(dvbox), nb); + + /* + * Add the buttons at the bottom. + */ + pref_apply = gtk_dialog_add_button(GTK_DIALOG(pref_dialog), + GTK_STOCK_APPLY, + GTK_RESPONSE_APPLY); + gtk_widget_set_sensitive(pref_apply, FALSE); + + button = gtk_dialog_add_button(GTK_DIALOG(pref_dialog), + GTK_STOCK_SAVE, + GTK_RESPONSE_OK); + + button = gtk_dialog_add_button(GTK_DIALOG(pref_dialog), + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + gtk_dialog_set_default_response(GTK_DIALOG(pref_dialog), + GTK_RESPONSE_CLOSE); + + g_signal_connect(G_OBJECT(pref_dialog), "response", + G_CALLBACK(pref_response), 0); + + gtk_widget_show_all(dvbox); + } + + guiutil_show_dialog_centered(pref_dialog, editors[0].shell); +} + +void +guiedit_preference_cleanup() +{ + /* + * Does nothing at the moment. + */ +} diff --git a/guiutil.c b/guiutil.c new file mode 100644 index 0000000..cc7bda4 --- /dev/null +++ b/guiutil.c @@ -0,0 +1,230 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "gbdfed.h" + +/* + * The shared error dialog and the label for the message. + */ +static GtkWidget *errd; +static GtkWidget *errmsg; + +/* + * The shared question dialog, the label for the question, and the + * value representing the answer. + */ +static GtkWidget *questd; +static GtkWidget *question; +static GtkWidget *yes; +static GtkWidget *no; + +void +guiutil_show_dialog_centered(GtkWidget *dialog, GtkWidget *parent) +{ + if (GTK_WINDOW(parent) != gtk_window_get_transient_for(GTK_WINDOW(dialog))) + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent)); + gtk_widget_show(dialog); +} + +void +guiutil_error_message(GtkWidget *parent, gchar *text) +{ + GtkWidget *ok, *hbox, *image; + GtkStockItem item; + + if (errd == 0) { + errd = gtk_dialog_new(); + (void) g_signal_connect(G_OBJECT(errd), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + hbox = gtk_hbox_new(FALSE, 0); + + /* + * Create the error icon. + */ + if (gtk_stock_lookup(GTK_STOCK_DIALOG_ERROR, &item)) { + /* + * Set the dialog title. + */ + gtk_window_set_title(GTK_WINDOW(errd), item.label); + image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR, + GTK_ICON_SIZE_DIALOG); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 2); + } else { + /* + * Probably not necessary, but use some default icon here. + */ + } + + errmsg = gtk_label_new(text); + gtk_box_pack_start(GTK_BOX(hbox), errmsg, TRUE, TRUE, 2); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(errd)->vbox), hbox); + + ok = gtk_dialog_add_button(GTK_DIALOG(errd), GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + (void) g_signal_connect_object(G_OBJECT(ok), "clicked", + G_CALLBACK(gtk_widget_hide), + (gpointer) errd, + G_CONNECT_SWAPPED); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(errd)->action_area), ok); + + gtk_widget_show_all(errd); + + gtk_window_set_modal(GTK_WINDOW(errd), TRUE); + } else + gtk_label_set_text(GTK_LABEL(errmsg), text); + + /* + * Center the dialog and display it. + */ + guiutil_show_dialog_centered(errd, parent); + + /* + * Ring the bell. + */ + gdk_beep(); +} + +gboolean +guiutil_yes_or_no(GtkWidget *parent, gchar *text, gboolean default_answer) +{ + GtkWidget *hbox, *image; + GList *kids; + gint ans; + GtkStockItem item; + + if (questd == 0) { + questd = gtk_dialog_new(); + gtk_window_set_resizable(GTK_WINDOW(questd), TRUE); + + (void) g_signal_connect(G_OBJECT(questd), "delete_event", + G_CALLBACK(gtk_widget_hide), 0); + + hbox = gtk_hbox_new(FALSE, 0); + + /* + * Create the question icon. + */ + if (gtk_stock_lookup(GTK_STOCK_DIALOG_QUESTION, &item)) { + /* + * Set the dialog title. + */ + gtk_window_set_title(GTK_WINDOW(questd), item.label); + image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, + GTK_ICON_SIZE_DIALOG); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 2); + } else { + /* + * Probably not necessary, but use some default icon here. + */ + } + + question = gtk_label_new(text); + + gtk_box_pack_start(GTK_BOX(hbox), question, TRUE, TRUE, 2); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(questd)->vbox), hbox); + + /* + * Make sure the buttons are evenly distributed in the button box. + */ + gtk_button_box_set_layout(GTK_BUTTON_BOX(GTK_DIALOG(questd)->action_area), GTK_BUTTONBOX_SPREAD); + + gtk_dialog_add_buttons(GTK_DIALOG(questd), + GTK_STOCK_YES, GTK_RESPONSE_ACCEPT, + GTK_STOCK_NO, GTK_RESPONSE_CANCEL, NULL); + + /* + * Get the two children buttons out now so focus can be set on either + * when needed. + */ + kids = gtk_container_get_children(GTK_CONTAINER(GTK_DIALOG(questd)->action_area)); + no = GTK_WIDGET(g_list_nth_data(kids, 0)); + yes = GTK_WIDGET(g_list_nth_data(kids, 1)); + g_list_free(kids); + + gtk_widget_show_all(questd); + + gtk_window_set_modal(GTK_WINDOW(questd), TRUE); + } else + gtk_label_set_text(GTK_LABEL(question), text); + + /* + * Force the dialog to reset to its minimum size. + */ + if (questd->window != NULL) + gtk_window_resize(GTK_WINDOW(questd), 1, 1); + + /* + * Center the dialog and display it. + */ + guiutil_show_dialog_centered(questd, parent); + + /* + * Force the default answer button to have the focus. + */ + if (default_answer) + gtk_widget_grab_focus(yes); + else + gtk_widget_grab_focus(no); + + ans = gtk_dialog_run(GTK_DIALOG(questd)); + + gtk_widget_hide(questd); + + return (ans == GTK_RESPONSE_ACCEPT) ? TRUE : FALSE; +} + +void +guiutil_util_set_tooltip(GtkWidget *w, gchar *text) +{ +#if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION >= 12 + gtk_widget_set_tooltip_text(w, text); +#else + GtkTooltips *tt; + + tt = gtk_tooltips_new(); + gtk_tooltips_set_tip(tt, w, text, 0); +#endif +} + +static GdkCursor *watch_cursor; + +void +guiutil_busy_cursor(GtkWidget *w, gboolean on) +{ + if (watch_cursor == 0) + watch_cursor = gdk_cursor_new(GDK_WATCH); + + if (on) + gdk_window_set_cursor(w->window, watch_cursor); + else + gdk_window_set_cursor(w->window, 0); +} + +void +guiutil_cursor_cleanup() +{ + if (watch_cursor != 0) + gdk_cursor_unref(watch_cursor); + watch_cursor = 0; +} diff --git a/hbf.c b/hbf.c new file mode 100644 index 0000000..3c4fba9 --- /dev/null +++ b/hbf.c @@ -0,0 +1,1590 @@ +/* + * Copyright 1993,1994,1995,2005 by Ross Paterson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + * Ross Paterson + * 17 October 1995 + * + * The following people have supplied bug fixes: + * + * Simon Chow + * Fung Fung Lee + * Man-Chi Pong + * Steven Simpson + * Charles Wang + * Werner Lemberg + * + * Ross no longer maintains this code. Please send bug reports to + * Werner Lemberg . + * + */ + +/* + * 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 +#include +#include +#include +#include +#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 + +static void +eprintf(const char *fmt, ...) +{ + if (hbfDebug) { + va_list args; + + (void)fprintf(stderr, "HBF: "); + va_start(args, fmt); + (void)vfprintf(stderr, fmt, args); + va_end(args); + (void)fprintf(stderr, "\n"); + } +} +#else /* ! __STDC__ */ +/* poor man's variable-length argument list */ +static void +eprintf(fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9) + const char *fmt; + int x1, x2, x3, x4, x5, x6, x7, x8, x9; +{ + if (hbfDebug) { + (void)fprintf(stderr, "HBF: "); + (void)fprintf(stderr, fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9); + (void)fprintf(stderr, "\n"); + } +} +#endif /* __STDC__ */ + +static void +clear_bbox(bbox) + HBF_BBOX *bbox; +{ + bbox->hbf_width = bbox->hbf_height = 0; + bbox->hbf_xDisplacement = bbox->hbf_yDisplacement = 0; +} + +static void +clear_record(hbf) + HBF_STRUCT *hbf; +{ + clear_bbox(&(hbf->public.hbf_bitmap_bbox)); + clear_bbox(&(hbf->public.hbf_font_bbox)); + hbf->property = NULL; + hbf->filename = NULL; + hbf->bitmap_buffer = NULL; + hbf->byte_2_range = NULL; + hbf->code_range = NULL; + hbf->bm_file = NULL; +} + +/* + * Byte-2 ranges + */ + +static void +add_b2r(last_b2r, start, finish) +reg B2_RANGE **last_b2r; + int start; + int finish; +{ +reg B2_RANGE *b2r; + + b2r = NEW(B2_RANGE); + while (*last_b2r != NULL && (*last_b2r)->b2r_start < start) + last_b2r = &((*last_b2r)->b2r_next); + b2r->b2r_next = *last_b2r; + b2r->b2r_start = start; + b2r->b2r_finish = finish; + *last_b2r = b2r; +} + +static CHAR_INDEX +b2_pos(hbf, code) + HBF_STRUCT *hbf; + HBF_CHAR code; +{ +reg B2_RANGE *b2r; +reg unsigned c; +reg CHAR_INDEX pos; + + c = SecondByte(code); + pos = 0; + for (b2r = hbf->byte_2_range; b2r != NULL; b2r = b2r->b2r_next) + if (b2r->b2r_start <= c && c <= b2r->b2r_finish) + return pos + c - b2r->b2r_start; + else + pos += b2r->b2r_finish - b2r->b2r_start + 1; + return BAD_CHAR_INDEX; +} + +static int +b2_size(b2r) +reg B2_RANGE *b2r; +{ +reg int size; + + size = 0; + for ( ; b2r != NULL; b2r = b2r->b2r_next) + size += b2r->b2r_finish - b2r->b2r_start + 1; + return size; +} + +/* map a position to a character code */ +static long +code_of(hbf, pos) + HBF_STRUCT *hbf; + long pos; +{ + long code; + int residue; +reg B2_RANGE *b2r; + + code = pos / hbf->b2_size * 256; + residue = pos % hbf->b2_size; + for (b2r = hbf->byte_2_range; b2r != NULL; b2r = b2r->b2r_next) + if (b2r->b2r_start + residue <= b2r->b2r_finish) + return code + b2r->b2r_start + residue; + else + residue -= b2r->b2r_finish - b2r->b2r_start + 1; + /* should never get here */ + return 0L; +} + +/* + * String stuff + */ + +static bool +match(lp, sp) +reg const char *lp; +reg const char *sp; +{ + while (*lp == *sp && *sp != '\0') { + lp++; + sp++; + } + return (*lp == '\0' || isspace(*lp)) && *sp == '\0'; +} + +#ifdef NO_STRDUP +char * +strdup(s) + const char *s; +{ + char *new_s; + + new_s = malloc((unsigned)strlen(s) + 1); + strcpy(new_s, s); + return new_s; +} +#endif + +/* + * Properties + */ + +static void +add_property(hbf, lp) +reg HBF_STRUCT *hbf; +reg const char *lp; +{ +reg PROPERTY *prop; + char tmp[MAXLINE]; +reg char *tp; + + prop = NEW(PROPERTY); + + tp = tmp; + while (*lp != '\0' && ! isspace(*lp)) + *tp++ = *lp++; + *tp = '\0'; + prop->prop_name = strdup(tmp); + + while (*lp != '\0' && isspace(*lp)) + lp++; + + tp = tmp; + if (*lp == QUOTE) { + lp++; + while (*lp != '\0' && ! (*lp == QUOTE && *++lp != QUOTE)) + *tp++ = *lp++; + } + else + for (;;) { + while (*lp != '\0' && ! isspace(*lp)) + *tp++ = *lp++; + while (*lp != '\0' && isspace(*lp)) + lp++; + if (*lp == '\0') + break; + *tp++ = ' '; + } + *tp = '\0'; + prop->prop_value = strdup(tmp); + + prop->prop_next = hbf->property; + hbf->property = prop; +} + +const char * +hbfProperty(hbfFile, propName) + HBF *hbfFile; + const char *propName; +{ +reg HBF_STRUCT *hbf; +reg PROPERTY *prop; + + hbf = (HBF_STRUCT *)hbfFile; + for (prop = hbf->property; prop != NULL; prop = prop->prop_next) + if (strcmp(prop->prop_name, propName) == 0) + return prop->prop_value; + return NULL; +} + +/* + * Compatability routines + */ + +const char * +HBF_GetProperty(handle, propertyName) + HBF *handle; + const char *propertyName; +{ + return hbfProperty(handle, propertyName); +} + +int +HBF_GetFontBoundingBox(handle, width, height, xDisplacement, yDisplacement) + HBF_Handle handle; + unsigned int *width; + unsigned int *height; + int *xDisplacement; + int *yDisplacement; +{ + if (width != NULL) + *width = hbfFontBBox(handle)->hbf_width; + if (height != NULL) + *height = hbfFontBBox(handle)->hbf_height; + if (xDisplacement != NULL) + *xDisplacement = hbfFontBBox(handle)->hbf_xDisplacement; + if (yDisplacement != NULL) + *yDisplacement = hbfFontBBox(handle)->hbf_yDisplacement; + return 0; +} + +int +HBF_GetBitmapBoundingBox(handle, width, height, xDisplacement, yDisplacement) + HBF_Handle handle; + unsigned int *width; + unsigned int *height; + int *xDisplacement; + int *yDisplacement; +{ + if (width != NULL) + *width = hbfBitmapBBox(handle)->hbf_width; + if (height != NULL) + *height = hbfBitmapBBox(handle)->hbf_height; + if (xDisplacement != NULL) + *xDisplacement = hbfBitmapBBox(handle)->hbf_xDisplacement; + if (yDisplacement != NULL) + *yDisplacement = hbfBitmapBBox(handle)->hbf_yDisplacement; + return 0; +} + +/* + * Prepend a directory to a relative filename. + */ +static char * +concat(dir, dirlen, stem) + const char *dir; /* not necessarily null-terminated */ + int dirlen; /* number of significant chars in dir */ + const char *stem; /* relative filename */ +{ + char *fullname; + + if (dirlen == 0) /* null: current directory */ + return strdup(stem); +#ifdef unix + fullname = malloc(dirlen + strlen(stem) + 2); + (void)sprintf(fullname, "%.*s/%s", dirlen, dir, stem); +#else +#ifdef msdos + fullname = malloc(dirlen + strlen(stem) + 2); + (void)sprintf(fullname, "%.*s\\%s", dirlen, dir, stem); +#else +#ifdef vms + if (dir[dirlen-1] == ']' && stem[0] == '[' && stem[1] == '-') { + dirlen--; + stem++; + fullname = malloc(dirlen + strlen(stem) + 2); + (void)sprintf(fullname, "%.*s.%s", dirlen, dir, stem); + } + else { + if (dir[dirlen-1] == ']' && stem[0] == '[' && stem[1] == '.') { + dirlen--; + stem++; + } + fullname = malloc(dirlen + strlen(stem) + 1); + (void)sprintf(fullname, "%.*s%s", dirlen, dir, stem); + } +#else + fullname = strdup(stem); +#endif /* vms */ +#endif /* msdos */ +#endif /* unix */ + return fullname; +} + +/* + * Bitmap files + * + * If the host operating system has a heirarchical file system and + * the bitmap file name is relative, it is relative to the directory + * containing the HBF file. + */ +static char * +expand_filename(name, hbf_name) + const char *name; + const char *hbf_name; +{ +#ifdef unix +reg char *s; +reg int size; + + size = name[0] != '/' && (s = strrchr(hbf_name, '/')) != NULL ? + s - hbf_name + 1 : 0; + s = malloc((unsigned)size + strlen(name) + 1); + (void)sprintf(s, "%.*s%s", size, hbf_name, name); + return s; +#else +#ifdef msdos +reg char *s; +reg int size; + +#ifdef __EMX__ + s = (unsigned char *)hbf_name + strlen((unsigned char *)hbf_name) - 1; + for(;;) { + if (*s == '\\' || *s == '/') + break; + if (s == hbf_name) { + s = NULL; + break; + } + s--; + } + + size = HasDrive(name) ? 0 : + (name[0] == '\\' || name[0] == '/') ? + (HasDrive(hbf_name) ? 2 : 0) : + s != NULL ? s - hbf_name + 1 : 0; +#else + size = HasDrive(name) ? 0 : + name[0] == '\\' ? (HasDrive(hbf_name) ? 2 : 0) : + (s = strrchr(hbf_name, '\\')) != NULL ? + s - hbf_name + 1 : 0; +#endif /* __EMX__ */ + s = malloc((unsigned)size + strlen(name) + 1); + (void)sprintf(s, "%.*s%s", size, hbf_name, name); + return s; +#else +#ifdef vms +reg char *s; +reg const char *copyto; +reg int size; + + if ((s = strchr(hbf_name, ']')) != NULL && RelativeFileName(name)) + return concat(hbf_name, (s - hbf_name) + 1, name); + + copyto = hbf_name; + if ((s = strstr(copyto, "::")) != NULL && strstr(name, "::") == NULL) + copyto = s+2; + if ((s = strchr(copyto, ':')) != NULL && strchr(name, ':') == NULL) + copyto = s+1; + size = copyto - hbf_name; + s = malloc((unsigned)size + strlen(name) + 1); + (void)sprintf(s, "%.*s%s", size, hbf_name, name); + return s; +#else + return strdup(name); +#endif /* vms */ +#endif /* msdos */ +#endif /* unix */ +} + +static BM_FILE * +find_file(hbf, filename) + HBF_STRUCT *hbf; + const char *filename; +{ + BM_FILE **fp; +reg BM_FILE *file; + FILE *f; + char *bmfname; +#ifdef IN_MEMORY +#ifdef unix + bool from_pipe; +#endif +#endif + + for (fp = &(hbf->bm_file); *fp != NULL; fp = &((*fp)->bmf_next)) { + bmfname = strrchr((*fp)->bmf_name, '/'); + bmfname = (bmfname) ? bmfname + 1 : (*fp)->bmf_name; + if (strcmp(bmfname, filename) == 0) + return *fp; + } + + file = NEW(BM_FILE); + if (file == NULL) { + eprintf("out of memory"); + return NULL; + } + file->bmf_name = expand_filename(filename, hbf->filename); + if (file->bmf_name == NULL) { + free((char *)file); + return NULL; + } + f = fopen(file->bmf_name, READ_BINARY); +#ifdef IN_MEMORY +#ifdef unix + from_pipe = FALSE; + if (f == NULL) { + char tmp[400]; + + sprintf(tmp, "%s.gz", file->bmf_name); + if ((f = fopen(tmp, "r")) != NULL) { + fclose(f); + sprintf(tmp, "gzcat %s.gz", file->bmf_name); + if ((f = popen(tmp, "r")) != NULL) + from_pipe = TRUE; + } + } +#endif /* unix */ +#endif /* IN_MEMORY */ + if (f == NULL) { + eprintf("can't open bitmap file '%s'", file->bmf_name); + free(file->bmf_name); + free((char *)file); + return NULL; + } +#ifdef IN_MEMORY + if (! read_bitmap_file(file, f)) { + free(file->bmf_name); + free((char *)file); + return NULL; + } +#ifdef unix + if (from_pipe) + pclose(f); + else + fclose(f); +#else /* ! unix */ + fclose(f); +#endif /* ! unix */ +#else /* ! IN_MEMORY */ + file->bmf_file = f; + fseek(f, 0L, 2); + file->bmf_size = ftell(f); +#endif /* ! IN_MEMORY */ + file->bmf_next = NULL; + *fp = file; + return file; +} + +#ifdef IN_MEMORY +#define GRAIN_SIZE 512 + +static bool +read_bitmap_file(bmf, f) + BM_FILE *bmf; + FILE *f; +{ + byte *contents, *cp; + long size; + int c; + + size = 0; + cp = contents = (byte *)malloc((unsigned)GRAIN_SIZE); + if (contents == NULL) { + eprintf("not enough space for bitmap file"); + return NULL; + } + while ((c = getc(f)) != EOF) { + if (size%GRAIN_SIZE == 0) { + contents = (byte *)realloc((char *)contents, + (unsigned)(size + GRAIN_SIZE)); + if (contents == NULL) { + eprintf("not enough space for bitmap file"); + return NULL; + } + cp = contents + size; + } + *cp++ = c; + size++; + } + bmf->bmf_size = size; + bmf->bmf_contents = (byte *)realloc((char *)contents, (unsigned)size); + return TRUE; +} +#endif /* IN_MEMORY */ + +/* + * Code ranges + */ + +/* check that a code range fits within its bitmap file */ +static bool +too_short(hbf, cp) + HBF_STRUCT *hbf; + CODE_RANGE *cp; +{ + int bm_size; + long offset, end_offset; + BM_FILE *bmf; + long start, finish; + + bm_size = FileBitmapSize(&(hbf->public), cp); + offset = cp->code_offset; + start = cp->code_start; + finish = cp->code_finish; + end_offset = offset + bm_size * + (hbf->b2_size*(long)FirstByte(finish) + + b2_pos(hbf, finish) - cp->code_pos + 1); + bmf = cp->code_bm_file; + if (end_offset <= bmf->bmf_size) + return FALSE; + /* bitmap file is too short: produce a specific error message */ + if (offset > bmf->bmf_size) + eprintf("bitmap file '%s' is shorter than offset 0x%04lx", + bmf->bmf_name, offset); + else if (offset + bm_size > bmf->bmf_size) + eprintf("bitmap file '%s' too short: no room for any bitmaps at offset 0x%04lx", + bmf->bmf_name, offset); + else + eprintf("bitmap file '%s' is too short - code range appears to be 0x%04lx-0x%04lx", + bmf->bmf_name, + start, + code_of(hbf, cp->code_pos + + (bmf->bmf_size - offset)/bm_size) - 1); + return TRUE; +} + +static const char * +skip_word(n, s) + int n; + const char *s; +{ + for ( ; n > 0; n--) { + while (*s != '\0' && ! isspace(*s)) + s++; + while (*s != '\0' && isspace(*s)) + s++; + } + return s; +} + +/* optional keywords at the end of a CODE_RANGE line */ +static void +parse_keywords(cp, s) + CODE_RANGE *cp; + const char *s; +{ + for (s = skip_word(4, s) ; *s != '\0'; s = skip_word(1, s)) { + switch (*s) { + case 's': case 'S': case 't': case 'T': + /* keyword "sideways" or "transposed" */ + cp->code_transposed = TRUE; + break; + case 'i': case 'I': + /* keyword "inverted" */ + cp->code_inverted = TRUE; + } + } +} + +static bool +add_code_range(hbf, line) + HBF_STRUCT *hbf; + const char *line; +{ + CODE_RANGE *cp; + CODE_RANGE **cpp; + long start, finish; + long offset; + char filename[MAXLINE]; + BM_FILE *bmf; + CHAR_INDEX b2pos; + + if (sscanf(line, "HBF_CODE_RANGE %li-%li %s %li", + &start, &finish, filename, &offset) != 4) { + eprintf("syntax error in HBF_CODE_RANGE"); + return FALSE; + } + /* code ranges are checked in real_open() */ + if ((bmf = find_file(hbf, filename)) == NULL) + return FALSE; + if ((cp = NEW(CODE_RANGE)) == NULL) { + eprintf("out of memory"); + return FALSE; + } + + cp->code_start = (CHAR)start; + cp->code_finish = (CHAR)finish; + cp->code_bm_file = bmf; + cp->code_offset = offset; + cp->code_transposed = cp->code_inverted = FALSE; + parse_keywords(cp, line); + /* insert it in order */ + for (cpp = &hbf->code_range; + *cpp != NULL && (*cpp)->code_finish < start; + cpp = &((*cpp)->code_next)) + ; + if (*cpp != NULL && (*cpp)->code_start <= finish) { + eprintf("code ranges overlap"); + return FALSE; + } + cp->code_next = *cpp; + *cpp = cp; + + /* set code_pos, and check range */ + if (start > finish) { + eprintf("illegal code range 0x%04lx-0x%04lx", start, finish); + return FALSE; + } + if ((b2pos = b2_pos(hbf, start)) == BAD_CHAR_INDEX) { + eprintf("illegal start code 0x%04lx", start); + return FALSE; + } + cp->code_pos = hbf->b2_size*(long)FirstByte(start) + b2pos; + if ((b2pos = b2_pos(hbf, finish)) == BAD_CHAR_INDEX) { + eprintf("illegal finish code 0x%04lx", finish); + return FALSE; + } + /* check that the bitmap file has enough bitmaps */ + return ! too_short(hbf, cp); +} + +/* + * Reading and parsing of an HBF file + */ + +/* get line, truncating to len, and trimming trailing spaces */ +static bool +get_line(buf, len, f) + char *buf; + int len; + FILE *f; +{ + int c; + char *bp; + + bp = buf; + for (;;) { + if ((c = getc(f)) == EOF) { + eprintf("unexpected end of file"); + return FALSE; + } + if (c == '\n' || c == '\r') { + /* trim trailing space */ + while (bp > buf && isspace(*(bp-1))) + bp--; + *bp = '\0'; + return TRUE; + } + if (len > 0) { + *bp++ = c; + len--; + } + } +} + +/* get next non-COMMENT line */ +static bool +get_text_line(buf, len, f) + char *buf; + int len; + FILE *f; +{ + while (get_line(buf, len, f)) + if (*buf != '\0' && ! match(buf, "COMMENT")) + return TRUE; + return FALSE; +} + +static bool +get_property(line, keyword, hbf) + const char *line; + const char *keyword; + HBF_STRUCT *hbf; +{ + if (! match(line, keyword)) { + eprintf("%s expected", keyword); + return FALSE; + } + add_property(hbf, line); + return TRUE; +} + +static bool +get_bbox(line, keyword, bbox) + const char *line; + const char *keyword; + HBF_BBOX *bbox; +{ + int w, h, xd, yd; + + if (! match(line, keyword) || + sscanf(line + strlen(keyword), "%i %i %i %i", + &w, &h, &xd, &yd) != 4) { + eprintf("%s expected", keyword); + return FALSE; + } + if (w <= 0 || h <= 0) { + eprintf("illegal %s dimensions %dx%d", keyword, w, h); + return FALSE; + } + bbox->hbf_width = w; + bbox->hbf_height = h; + bbox->hbf_xDisplacement = xd; + bbox->hbf_yDisplacement = yd; + return TRUE; +} + +/* + * HBFHeaderFile ::= + * 'HBF_START_FONT' version EOLN + * 'HBF_CODE_SCHEME' word ... EOLN + * 'FONT' fontName EOLN + * 'SIZE' ptsize xres yres EOLN + * 'HBF_BITMAP_BOUNDING_BOX' w h xd yd EOLN + * 'FONTBOUNDINGBOX' w h xd yd EOLN + * X11R5FontPropertySection + * 'CHARS' n EOLN + * HBFByte2RangeSection + * HBFCodeRangeSection + * 'HBF_END_FONT' EOLN . + * + * This implementation allows extra lines before HBF_END_FONT. + * Anything after HBF_END_FONT is ignored. + */ + +static bool +parse_file(f, hbf) + FILE *f; +reg HBF_STRUCT *hbf; +{ + char line[MAXLINE]; + int start, finish; + + if (! get_text_line(line, MAXLINE, f) || + ! get_property(line, "HBF_START_FONT", hbf)) + return FALSE; + + if (! get_text_line(line, MAXLINE, f) || + ! get_property(line, "HBF_CODE_SCHEME", hbf)) + return FALSE; + + if (! get_text_line(line, MAXLINE, f) || + ! get_property(line, "FONT", hbf)) + return FALSE; + + if (! get_text_line(line, MAXLINE, f) || + ! get_property(line, "SIZE", hbf)) + return FALSE; + + if (! get_text_line(line, MAXLINE, f) || + ! get_bbox(line, "HBF_BITMAP_BOUNDING_BOX", + &(hbf->public.hbf_bitmap_bbox))) + return FALSE; + + if (! get_text_line(line, MAXLINE, f) || + ! get_bbox(line, "FONTBOUNDINGBOX", &(hbf->public.hbf_font_bbox))) + return FALSE; + + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + if (match(line, "STARTPROPERTIES")) { + for (;;) { + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + if (match(line, "ENDPROPERTIES")) + break; + add_property(hbf, line); + } + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + } + + if (match(line, "CHARS")) + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + + if (match(line, "HBF_START_BYTE_2_RANGES")) { + for (;;) { + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + if (match(line, "HBF_END_BYTE_2_RANGES")) + break; + if (sscanf(line, "HBF_BYTE_2_RANGE %i-%i", + &start, &finish) != 2) { + eprintf("HBF_BYTE_2_RANGE expected"); + return FALSE; + } + add_b2r(&(hbf->byte_2_range), start, finish); + } + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + } + else + add_b2r(&(hbf->byte_2_range), 0, 0xff); + hbf->b2_size = b2_size(hbf->byte_2_range); + + if (! match(line, "HBF_START_CODE_RANGES")) { + eprintf("HBF_START_CODE_RANGES expected"); + return FALSE; + } + for (;;) { + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + if (match(line, "HBF_END_CODE_RANGES")) + break; + if (! add_code_range(hbf, line)) + return FALSE; + } + + for (;;) { + if (! get_text_line(line, MAXLINE, f)) + return FALSE; + if (match(line, "HBF_END_FONT")) + break; + /* treat extra lines as properties (for private extensions) */ + add_property(hbf, line); + } + + return TRUE; +} + +static FILE * +path_open(path, filename, fullp) + const char *path; + const char *filename; + char **fullp; +{ + if (LocalFileName(filename) && path != NULL) { +#ifdef PATH_DELIMITER + int len; + char *fullname; + FILE *f; + const char *p_next; + + len = strlen(filename); + for (;;) { + p_next = strchr(path, PATH_DELIMITER); + if (p_next == NULL) + p_next = path + strlen(path); + fullname = concat(path, p_next - path, filename); + if ((f = fopen(fullname, "r")) != NULL) { + *fullp = fullname; + return f; + } + free(fullname); + if (*p_next == '\0') + break; + path = p_next + 1; + } +#endif + return NULL; + } + else { + *fullp = strdup(filename); + return fopen(*fullp, "r"); + } +} + +static bool +real_open(filename, hbf) + const char *filename; +reg HBF_STRUCT *hbf; +{ + FILE *f; + + f = path_open(getenv("HBFPATH"), filename, &(hbf->filename)); + if (f == NULL) { + eprintf("can't read file '%s'", filename); + return FALSE; + } + if (! parse_file(f, hbf)) { + fclose(f); + return FALSE; + } + fclose(f); + return TRUE; +} + +HBF * +hbfOpen(filename) + const char *filename; +{ +reg HBF_STRUCT *hbf; + + if ((hbf = NEW(HBF_STRUCT)) == NULL) { + eprintf("can't allocate HBF structure"); + return NULL; + } + clear_record(hbf); + if (real_open(filename, hbf)) + return &(hbf->public); + hbfClose(&(hbf->public)); + return NULL; +} + +int +HBF_OpenFont(filename, ptrHandleStorage) + const char *filename; + HBF **ptrHandleStorage; +{ + return (*ptrHandleStorage = hbfOpen(filename)) == NULL ? -1 : 0; +} + +/* + * Close files, free everything associated with the HBF. + */ + +int +HBF_CloseFont(hbfFile) + HBF *hbfFile; +{ +reg HBF_STRUCT *hbf; + PROPERTY *prop_ptr, *prop_next; + B2_RANGE *b2r_ptr, *b2r_next; + CODE_RANGE *code_ptr, *code_next; + BM_FILE *bmf_ptr, *bmf_next; + int status; + + status = 0; + hbf = (HBF_STRUCT *)hbfFile; + + if (hbf->filename != NULL) + free(hbf->filename); + if (hbf->bitmap_buffer != NULL) + free(hbf->bitmap_buffer); + + for (prop_ptr = hbf->property; + prop_ptr != NULL; + prop_ptr = prop_next) { + prop_next = prop_ptr->prop_next; + free(prop_ptr->prop_name); + free(prop_ptr->prop_value); + free((char *)prop_ptr); + } + + for (b2r_ptr = hbf->byte_2_range; + b2r_ptr != NULL; + b2r_ptr = b2r_next) { + b2r_next = b2r_ptr->b2r_next; + free((char *)b2r_ptr); + } + + for (code_ptr = hbf->code_range; + code_ptr != NULL; + code_ptr = code_next) { + code_next = code_ptr->code_next; + free((char *)code_ptr); + } + + for (bmf_ptr = hbf->bm_file; + bmf_ptr != NULL; + bmf_ptr = bmf_next) { + bmf_next = bmf_ptr->bmf_next; +#ifdef IN_MEMORY + free((char *)(bmf_ptr->bmf_contents)); +#else + if (bmf_ptr->bmf_file != NULL && + fclose(bmf_ptr->bmf_file) < 0) + status = -1; +#endif + free(bmf_ptr->bmf_name); + free((char *)bmf_ptr); + } + + free((char *)hbf); + + return status; +} + +void +hbfClose(hbfFile) + HBF *hbfFile; +{ + (void)HBF_CloseFont(hbfFile); +} + +/* + * Fetch a bitmap + */ + +const byte * +hbfGetBitmap(hbf, code) + HBF *hbf; + HBF_CHAR code; +{ + return get_bitmap((HBF_STRUCT *)hbf, code, (byte *)NULL); +} + +int +HBF_GetBitmap(hbf, code, buffer) + HBF *hbf; + HBF_CHAR code; + byte *buffer; +{ + return get_bitmap((HBF_STRUCT *)hbf, code, buffer) == NULL ? -1 : 0; +} + +/* + * Internal function to fetch a bitmap. + * If buffer is non-null, it must be used. + */ +static const byte * +get_bitmap(hbf, code, buffer) +reg HBF_STRUCT *hbf; + HBF_CHAR code; + byte *buffer; +{ + CHAR_INDEX pos, b2pos; +reg CODE_RANGE *cp; + BM_FILE *bmf; + int bm_size; + long offset; + + if ((b2pos = b2_pos(hbf, code)) == BAD_CHAR_INDEX) + return NULL; + pos = hbf->b2_size*FirstByte(code) + b2pos; + for (cp = hbf->code_range; cp != NULL; cp = cp->code_next) + if (cp->code_start <= code && code <= cp->code_finish) { + bmf = cp->code_bm_file; + bm_size = FileBitmapSize(&(hbf->public), cp); + offset = cp->code_offset + + (long)(pos - cp->code_pos) * bm_size; +#ifdef IN_MEMORY + if (buffer == NULL && + ! cp->code_transposed && ! cp->code_inverted) + return bmf->bmf_contents + offset; +#endif /* IN_MEMORY */ + if (buffer == NULL && + ((buffer = local_buffer(hbf)) == NULL)) + return NULL; +#ifdef IN_MEMORY + if (cp->code_transposed) + copy_transposed(&(hbf->public), + buffer, + bmf->bmf_contents + offset); + else + memcpy((char *)buffer, + (char *)(bmf->bmf_contents + offset), + bm_size); +#else /* ! IN_MEMORY */ + if (fseek(bmf->bmf_file, offset, 0) != 0) { + eprintf("seek error on code 0x%04x", code); + return NULL; + } + if (cp->code_transposed ? + ! get_transposed(&(hbf->public), bmf->bmf_file, + buffer) : + fread((char *)buffer, + bm_size, 1, bmf->bmf_file) != 1) { + eprintf("read error on code 0x%04x", code); + return NULL; + } +#endif /* IN_MEMORY */ + if (cp->code_inverted) + invert(buffer, HBF_BitmapSize(&(hbf->public))); + return buffer; + } + eprintf("code 0x%04x out of range", code); + return NULL; +} + +static byte * +local_buffer(hbf) + HBF_STRUCT *hbf; +{ + if (hbf->bitmap_buffer == NULL && + (hbf->bitmap_buffer = (byte *)malloc(HBF_BitmapSize(&(hbf->public)))) == NULL) { + eprintf("out of memory"); + return NULL; + } + return hbf->bitmap_buffer; +} + +static void +invert(buffer, length) + byte *buffer; + unsigned int length; +{ + for ( ; length > 0; length--) + *buffer++ ^= 0xff; +} + +#ifdef IN_MEMORY +static bool +copy_transposed(hbf, bitmap, source) + HBF *hbf; +reg byte *bitmap; +reg const byte *source; +{ +reg byte *pos; +reg byte *bm_end; + int x; + int width; +reg int row_size; +reg int c; +reg int imask, omask; + + width = hbfBitmapBBox(hbf)->hbf_width; + row_size = HBF_RowSize(hbf); + bm_end = bitmap + HBF_BitmapSize(hbf); + (void)memset((char *)bitmap, '\0', HBF_BitmapSize(hbf)); + for (x = 0; x < width; x++) { + pos = bitmap + x/8; + omask = Bit(x%8); + /* y = 0 */ + for (;;) { + c = *source++; + for (imask = Bit(0); imask != 0; imask >>= 1) { + /* + * At this point, + * + * imask == Bit(y%8) + * pos == bitmap + y*row_size + x/8 + * + * We examine bit y of row x of the input, + * setting bit x of row y of the output if + * required, by applying omask to *pos. + */ + if ((c & imask) != 0) + *pos |= omask; + /* if (++y > height) goto end_column */ + pos += row_size; + if (pos >= bm_end) + goto end_column; + } + } +end_column: + ; + } + return TRUE; +} +#else /* ! IN_MEMORY */ +static bool +get_transposed(hbf, f, bitmap) + HBF *hbf; + FILE *f; +reg byte *bitmap; +{ +reg byte *pos; +reg byte *bm_end; + int x; + int width; +reg int row_size; +reg int c; +reg int imask, omask; + + width = hbfBitmapBBox(hbf)->hbf_width; + row_size = HBF_RowSize(hbf); + bm_end = bitmap + HBF_BitmapSize(hbf); + (void)memset((char *)bitmap, '\0', HBF_BitmapSize(hbf)); + for (x = 0; x < width; x++) { + pos = bitmap + x/8; + omask = Bit(x%8); + /* y = 0 */ + for (;;) { + if ((c = getc(f)) == EOF) + return FALSE; + for (imask = Bit(0); imask != 0; imask >>= 1) { + /* + * At this point, + * + * imask == Bit(y%8) + * pos == bitmap + y*row_size + x/8 + * + * We examine bit y of row x of the input, + * setting bit x of row y of the output if + * required, by applying omask to *pos. + */ + if ((c & imask) != 0) + *pos |= omask; + /* if (++y > height) goto end_column */ + pos += row_size; + if (pos >= bm_end) + goto end_column; + } + } +end_column: + ; + } + return TRUE; +} +#endif /* ! IN_MEMORY */ + +/* + * Call function on each valid code in ascending order. + */ +void +hbfForEach(hbfFile, func, data) +reg HBF *hbfFile; +reg void (*func)_((HBF *sameHbfFile, HBF_CHAR code, void *data)); +reg void *data; +{ + HBF_STRUCT *hbf; + CODE_RANGE *cp; +reg B2_RANGE *b2r; +reg unsigned byte1, byte2; +reg unsigned finish; + + hbf = (HBF_STRUCT *)hbfFile; + for (cp = hbf->code_range; cp != NULL; cp = cp->code_next) { + byte1 = FirstByte(cp->code_start); + byte2 = SecondByte(cp->code_start); + while (MakeCode(byte1, byte2) <= cp->code_finish) { + for (b2r = hbf->byte_2_range; + b2r != NULL; + b2r = b2r->b2r_next) { + if (byte2 < b2r->b2r_start) + byte2 = b2r->b2r_start; + finish = b2r->b2r_finish; + if (byte1 == FirstByte(cp->code_finish) && + finish > SecondByte(cp->code_finish)) + finish = SecondByte(cp->code_finish); + while (byte2 <= finish) { + (*func)(hbfFile, + MakeCode(byte1, byte2), data); + byte2++; + } + } + byte1++; + byte2 = 0; + } + } +} + +const char * +hbfFileName(hbf) + HBF *hbf; +{ + return ((HBF_STRUCT *)hbf)->filename; +} + +long +hbfChars(hbfFile) + HBF *hbfFile; +{ + HBF_STRUCT *hbf; + CODE_RANGE *cp; + long num_chars; + + hbf = (HBF_STRUCT *)hbfFile; + num_chars = 0; + for (cp = hbf->code_range; cp != NULL; cp = cp->code_next) + num_chars += + hbf->b2_size*FirstByte(cp->code_finish) + + b2_pos(hbf, cp->code_finish) - + (hbf->b2_size*FirstByte(cp->code_start) + + b2_pos(hbf, cp->code_start)) + 1; + return num_chars; +} + +/* + * Functions also implemented as macros + */ + +#ifdef hbfBitmapBBox +#undef hbfBitmapBBox +#endif + +HBF_BBOX * +hbfBitmapBBox(hbf) + HBF *hbf; +{ + return &(hbf->hbf_bitmap_bbox); +} + +#ifdef hbfFontBBox +#undef hbfFontBBox +#endif + +HBF_BBOX * +hbfFontBBox(hbf) + HBF *hbf; +{ + return &(hbf->hbf_font_bbox); +} + +const void * +hbfGetByte2Range(hbfFile, b2r_pointer, startp, finishp) + HBF *hbfFile; + const void *b2r_pointer; + byte *startp; + byte *finishp; +{ + HBF_STRUCT *hbf; + B2_RANGE *b2r; + + hbf = (HBF_STRUCT *)hbfFile; + if (b2r_pointer == NULL) + b2r = hbf->byte_2_range; + else + b2r = ((B2_RANGE *)b2r_pointer)->b2r_next; + if(b2r == NULL) + return NULL; + *startp = b2r->b2r_start; + *finishp = b2r->b2r_finish; + return (void *)b2r; +} + +const void * +hbfGetCodeRange(hbfFile, code_pointer, startp, finishp) + HBF *hbfFile; + const void *code_pointer; + HBF_CHAR *startp; + HBF_CHAR *finishp; +{ + HBF_STRUCT *hbf; + CODE_RANGE *cp; + + hbf = (HBF_STRUCT *)hbfFile; + if (code_pointer == NULL) + cp = hbf->code_range; + else + cp = ((CODE_RANGE *)code_pointer)->code_next; + if(cp == NULL) + return NULL; + *startp = cp->code_start; + *finishp = cp->code_finish; + return (void *)cp; +} diff --git a/hbf.h b/hbf.h new file mode 100644 index 0000000..22fc4c5 --- /dev/null +++ b/hbf.h @@ -0,0 +1,216 @@ +/* + * Copyright 1993,1994,1995,2005 by Ross Paterson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + * Two interfaces to HBF files -- take your pick. + * + * Ross Paterson + * + * Ross no longer maintains this code. Please send bug reports to + * Werner Lemberg . + * + */ +#ifndef _HBF_ +#define _HBF_ + +#ifndef __STDC__ +# ifndef const +# define const +# endif +#endif + +/* + * #1: a lightweight C interface. + */ + +typedef unsigned int HBF_CHAR; + +typedef struct { + unsigned short hbf_width; + unsigned short hbf_height; + short hbf_xDisplacement; + short hbf_yDisplacement; +} HBF_BBOX; + +typedef struct { + /* fields corresponding to the definition */ + HBF_BBOX hbf_bitmap_bbox; /* HBF_BITMAP_BOUNDING_BOX */ + HBF_BBOX hbf_font_bbox; /* FONTBOUNDINGBOX */ +} HBF; + +extern HBF *hbfOpen( +#ifdef __STDC__ + const char *filename +#endif + ); + +extern void hbfClose( +#ifdef __STDC__ + HBF *hbf +#endif + ); + +extern const char *hbfProperty( +#ifdef __STDC__ + HBF *hbf, + const char *propName +#endif + ); + +extern const unsigned char *hbfGetBitmap( +#ifdef __STDC__ + HBF *hbf, + HBF_CHAR code +#endif + ); + +extern void hbfForEach( +#ifdef __STDC__ + HBF *hbf, + void (*func)(HBF *sameHbf, HBF_CHAR code, void *), + void *data +#endif + ); + +extern const char *hbfFileName( +#ifdef __STDC__ + HBF *hbf +#endif + ); + +extern long hbfChars( +#ifdef __STDC__ + HBF *hbf +#endif + ); + +extern HBF_BBOX *hbfBitmapBBox( +#ifdef __STDC__ + HBF *hbf +#endif + ); +/* but defined here as a macro */ +#define hbfBitmapBBox(hbf) (&((hbf)->hbf_bitmap_bbox)) + +extern HBF_BBOX *hbfFontBBox( +#ifdef __STDC__ + HBF *hbf +#endif + ); +/* but defined here as a macro */ +#define hbfFontBBox(hbf) (&((hbf)->hbf_font_bbox)) + +#define HBF_RowSize(hbf)\ + ((hbfBitmapBBox(hbf)->hbf_width + 7)/8) + +#define HBF_BitmapSize(hbf)\ + (HBF_RowSize(hbf) * hbfBitmapBBox(hbf)->hbf_height) + +#define HBF_GetBit(hbf,bitmap,x,y)\ + (((bitmap)[(y)*HBF_RowSize(hbf) + (x)/8]>>(7 - (x)%8))&01) + +extern int hbfDebug; /* set non-zero for error reporting */ + +extern const void *hbfGetCodeRange( +#ifdef __STDC__ + HBF *hbfFile, + const void *code_pointer, + HBF_CHAR *startp, + HBF_CHAR *finishp +#endif + ); + +extern const void *hbfGetByte2Range( +#ifdef __STDC__ + HBF *hbfFile, + const void *b2r_pointer, + unsigned char *startp, + unsigned char *finishp +#endif + ); + +/* + * #2: taken from Appendix 2 of the HBF draft. + */ + +typedef unsigned int HBF_HzCode; +typedef unsigned char HBF_Byte ; +typedef HBF_Byte * HBF_BytePtr ; +typedef HBF * HBF_Handle ; +typedef HBF_Handle * HBF_HandlePtr ; +typedef char * String ; + +extern int HBF_OpenFont( +#ifdef __STDC__ + const char * filename, + HBF_HandlePtr ptrHandleStorage +#endif +); + +extern int HBF_CloseFont( +#ifdef __STDC__ + HBF_Handle handle +#endif +); + +extern const char * HBF_GetProperty( +#ifdef __STDC__ + HBF_Handle handle, + const char * propertyName +#endif +); + +extern int HBF_GetFontBoundingBox( +#ifdef __STDC__ + HBF_Handle handle, + unsigned int *width, + unsigned int *height, + int *xDisplacement, + int *yDisplacement +#endif +); + +extern int HBF_GetBitmapBoundingBox( +#ifdef __STDC__ + HBF_Handle handle, + unsigned int *width, + unsigned int *height, + int *xDisplacement, + int *yDisplacement +#endif +); + +extern int HBF_GetBitmap( +#ifdef __STDC__ + HBF_Handle handle, + HBF_HzCode hanziCode, + HBF_BytePtr ptrBitmapBuffer +#endif +); + +#endif /* ! _HBF_ */ diff --git a/htext.h b/htext.h new file mode 100644 index 0000000..d3339b4 --- /dev/null +++ b/htext.h @@ -0,0 +1,1438 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +static gchar *about_text = "" +"" +"
GBDFEditor 1.6\n" +"mleisher@gmail.com\n" +"15 April 2010
\n" +"\n" +"GBDFEditor is a BDF font editor that supports " +"these main features:\n" +"\n" +" Multiple fonts can be loaded from the command line.\n" +" Multiple fonts can be open at the same time.\n" +" Cutting and pasting glyphs between fonts.\n" +" Multiple glyph bitmap editors can be open at the same time.\n" +" Cutting and pasting between glyph bitmap editors.\n" +" Automatic correction of certain metrics when a font is loaded.\n" +" Generation of XLFD font names for fonts without XLFD names.\n" +" Update an XLFD font name from the font properties.\n" +" Update the font properties from an XLFD font name.\n" +" Font property editor.\n" +" Font comment editor.\n" +" Supports unencoded glyphs (ENCODING of -1).\n" +" Display of glyph encodings in octal, decimal, or hex.\n" +" Builtin on-line help.\n" +" Imports PK/GF fonts.\n" +" Imports HBF (Han Bitmap Font) fonts.\n" +" Imports Linux console fonts (PSF, CP, and FNT).\n" +" Imports Sun console fonts (vfont format).\n" +" Imports fonts from the X server.\n" +" Imports Windows FON/FNT fonts.\n" +" Imports OpenType fonts and collections.\n" +" Exports Linux console PSF2 fonts.\n" +" Exports HEX fonts (http://czyborra.com/unifont).\n" +" Edits gray scale fonts with 2, 4 or 8 bits per pixel.\n" +"\n" +"GBDFEditor was designed to use GTK+ 2.6 or later.\n" +"
"; + +static gchar *program_text = "" +"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" +"-nc\t\tno comments\n" +"-nm\t\tno metrics corrections\n" +"-nu\t\tno unencoded glyphs\n" +"-np\t\tdo not pad character cell bitmaps\n" +"-bp\t\tallow blank pages\n" +"-ed\t\tno Really Exit? dialog\n" +"-ps n\t\tset point size\n" +"-hres n\tset horizontal resolution\n" +"-vres n\tset vertical resolution\n" +"-res n\tset both resolutions\n" +"-sp s\t\tset the font spacing (proportional, monowidth, charactercell)\n" +"-bpp n\tset the font bits per pixel (1, 2, 4, 8)\n" +"-eol e\tset the default end of line char(s) (unix, dos, mac)\n" +"-g code\tset the initial glyph code to be displayed at startup(can be decimal, hex, or octal)\n" +"-cb base\tset the code base for glyph codes (oct, dec, hex)\n" +""; + +static gchar *fgrid_text = "" +"
Font Grid
\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 FONTGRID_CLIPBOARD.\n" +"\n" +"At the top of each editor window there are some " +"fields and buttons. These are:\n" +"\n" +"The Font text field is where the font name " +"is set so it can be edited.\n" +"\n" +"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.\n" +"\n" +"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 Setup dialog.\n" +"\n" +"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 expected to be a decimal " +"number.\n" +"\n" +"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.\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" +"Font Grid Menus\n" +"\n" +"File\n" +"New <Ctrl+N> " +"This creates a new font and asks for the point " +"size, resolution, and font spacing first.\n" +"\n" +"Open <Ctrl+O> " +"This opens a new font in the current Font Grid.\n" +"\n" +"Save <Ctrl+S> " +"Save the current font.\n" +"\n" +"Save As <Ctrl+W> " +"Save the current font with some other name.\n" +"\n" +"Import\n" +"\n" +"PK/GF Font <Ctrl+K> " +"Import a Metafont PK or GF font.\n" +"\n" +"Console Font <Ctrl+L> " +"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.\n" +"\n" +"HBF Font <Ctrl+H> " +"Import an HBF font. Only available if " +"gbdfed was compiled with HBF support.\n" +"\n" +"Windows Font <Ctrl+B> " +"Import a Windows FON/FNT font. This will also " +"import fonts from .EXE and .DLL files.\n" +"\n" +"OpenType Font <Ctrl+Y> " +"Import an OpenType (.otf), TrueType font (.ttf) or " +"TrueType collection (.ttc).\n" +"\n" +"Server Font <Ctrl+G> " +"This will import a font from the X server.\n" +"\n" +"Export\n" +"\n" +"PSF <Ctrl+F> " +"This will export the current BDF font or the current selection " +"of glyphs to a PSF2 font.\n" +"\n" +"During the export, an option menu will let you select whether you " +"want to:\n" +"\n" +"A. Export the font with its Unicode mappings. " +" " +"B. Export just the glyphs.\n" +"\n" +"C. Export just the Unicode mappings in the simple " +"ASCII form used by the psfaddtable(1) program.\n" +"\n" +"Only the first 512 glyphs will be exported from " +"the font.\n" +"\n" +"HEX " +"This will export the current BDF font into the " +"HEX format (See the HEX Font Notes entry).\n" +"\n" +"Exit/Close <Ctrl+F4> " +"Exit the program if this is the primary Font " +"Grid or simply hide the current Font Grid window.\n" +"\n" +"The key binding for this can be changed in the " +"configuration file. See the Configuration File " +"help section.\n" +"\n" +"Edit\n" +"Copy <Ctrl+C> " +"This copies the current selection to the Font " +"Grid clipboard.\n" +"\n" +"Cut <Ctrl+X> " +"This copies the current selection to the Font Grid " +"clipboard and then deletes the selection.\n" +"\n" +"Paste <Ctrl+V> " +"This replaces the glyphs starting at the currently " +"selected position with the Font Grid clipboard.\n" +"\n" +"Overlay <Ctrl+Shift+V> " +"This merges the Font Grid clipboard with the glyphs " +"starting at the currently selected position. " +"The names of the modified glyphs are not changed.\n" +"\n" +"Insert <Ctrl+Meta+V> " +"This inserts the Font Grid cliboard in front of the " +"currently selected position.\n" +"\n" +"Properties <Ctrl+P> " +"This invokes the font property editor.\n" +"\n" +"Comments <Ctrl+M> " +"This invokes the font comments editor.\n" +"\n" +"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.\n" +"\n" +"Font Name\n" +"\n" +"Make XLFD Name " +"If the font does not have an XLFD name, this " +"will save the current font name in the " +"_ORIGINAL_FONT_NAME font property and then " +"generate an XLFD name for the font.\n" +"\n" +"Update Name From Properties " +"This will update the XLFD font name fields from" +"the font property list.\n" +"\n" +"Update Properties From Name " +"This will update the font properties from the " +"XLFD font name.\n" +"\n" +"Update Average Width " +"This will update the average width field of the " +"XLFD font name and will update the " +"AVERAGE_WIDTH font property as a side effect.\n" +"\n" +"Rename Glyphs\n" +"Unicode Names " +"This option will rename all the glyphs using names " +"from a Unicode Character Database file set in the " +"config file or from the Setup->Other Options " +"dialog.\n" +"\n" +"Unicode Values " +"This option will rename all the glyphs with 16-bit " +"hexadecimal values prefixed with 0x, U+, or \\u.\n" +"\n" +"Test Glyphs <Ctrl+Z> " +"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.\n" +"\n" +"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.\n" +"\n" +"Preferences <Ctrl+T> " +"This will invoke the dialog to edit various preferences" +"used by the editor and when loading/creating fonts.\n" +"\n" +"View\n" +"Unencoded <Ctrl+E> " +"If the font has unencoded glyphs (ENCODING " +"field is -1), this will toggle between " +"displaying the unencoded and encoded glyphs.\n" +"\n" +"Code Base\n" +"Octal " +"This option will display glyph encodings in " +"octal (base 8).\n" +"\n" +"Decimal " +"This option will display glyph encodings in " +"decimal (base 10).\n" +"\n" +"Hexadecimal " +"This option will display glyph encodings in " +"hexadecimal (base 16).\n" +"\n" +"Other Page <Ctrl+Shift+S> " +"This will toggle between the current glyph page " +"and the last page that was viewed.\n" +"\n" +"Vertical/Horizontal View <Ctrl+Q> " +"This will toggle the FontGrid between showing the " +"glyphs horizontally (default) or vertically.\n" +"\n" +"Messages <Ctrl+A> " +"This will show messages generated when corrections " +"to the font metrics are done or when errors are " +"encountered.\n" +"\n" +"Operations\n" +"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.\n" +"\n" +"The option of translating the selected glyphs or all " +"of the glyphs is provided.\n" +"\n" +"Rotate <Ctrl+R> " +"This will bring up the dialog for entering the " +"rotation angle. The rotation is limited to between " +"± 1° and 359°.\n" +"\n" +"The option of rotating the selected glyphs or all " +"of the glyphs is provided.\n" +"\n" +"Shear <Ctrl+J> " +"This will bring up the dialog for entering the " +"angle of the shear. The shear is limited to between " +"± 1° and 45°.\n" +"\n" +"The option of rotating the selected glyphs or all " +"of the glyphs is provided.\n" +"\n" +"Embolden <Ctrl+Shift+B> " +"This will bring up the dialog for choosing whether " +"to embolden the selected glyphs or to embolden all " +"glyphs.\n" +"\n" +"To embolden means to make bold. " +"\n\n" +"Windows " +"\n" +"[editor list] " +"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.\n" +"\n" +"Font Grid Translations\n" +"0..9 " +"Typing digits will accumulate a count which is " +"applied to movement done with the arrow and page keys.\n" +"\n" +"Left " +"This will move the single cell selection left.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Right " +"This will move the single cell selection right.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Up " +"This will move the single cell selection up.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Down " +"This will move the single cell selection down.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+Left " +"This will extend the selection to the left.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+Right " +"This will extend the selection to the right.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+Up " +"This will extend the selection up a row or column, " +"depending on the display orientation, horizontal or " +"vertical.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+Down " +"This will extend the selection down a row or column, " +"depending on the display orientation, horizontal or " +"vertical.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"PageUp " +"This will switch to the next page of glyphs.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"PageDown " +"This will switch to the previous page of glyphs.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Home " +"This will switch to the first page of glyphs.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"End " +"This will switch to the last page of glyphs.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+PageUp " +"This will extend the selection to the next page.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+PageDown " +"This will extend the selection to the previous page.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+Home " +"This will extend the selection to the first page that " +"has glyphs.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Shift+End " +"This will extend the selection to the last page that " +"has glyphs.\n" +"If a decimal number is typed before this, this " +"operation will be done that number of times.\n" +"\n" +"Button1Down " +"This will start selecting glyphs. If Button1 is " +"double-clicked, it will edit the current glyph.\n" +"\n" +"Button1Motion " +"This will extend the selected glyphs.\n" +"\n" +"Button1Up " +"This will end glyph selection.\n" +"\n" +"Shift+Button1Down " +"This will adjust the glyphs already selected by " +"adding or removing glyphs from the selection.\n" +"\n" +"Button2Down " +"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.\n" +"\n" +"Shift+Button2Down " +"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.\n" +"\n" +"Ctrl+Button2Down " +"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).\n" +"\n" +"Button3Down " +"This will copy the selected glyphs to the Font " +"Grid clipboard.\n" +"\n" +"Return " +"This will invoke a Glypheditor for the current glyph.\n" +"\n" +"Ctrl+Return " +"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.\n" +"\n" +"Copy " +"This will copy the selected glyphs to the Font " +"Grid clipboard.\n" +"\n" +"Cut " +"This will copy the selected glyphs to the Font " +"Grid clipboard and then delete the glyphs.\n" +"\n" +"Paste " +"This will paste the glyphs on the Font Grid " +"clipboard at the currently selected glyph " +"position.\n" +"\n" +"Delete " +"This will copy the selected glyphs to the Font " +"Grid clipboard and then delete the glyphs.\n" +"\n" +"BackSpace " +"This will copy the selected glyphs to the Font " +"Grid clipboard and then delete the glyphs.\n" +"\n" +"Double clicking with Button1 will invoke the Glyph " +"Editor for the current glyph.\n" +"\n" +"Other Font Grid Features\n" +"The font name can be edited in the Font Grid and " +"page switching can be done with the buttons on the " +"Font Grid.\n" +"
"; + +static gchar *gedit_text = "" +"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 " +"GLYPHEDIT_CLIPBOARD.\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" +"Glyph Editor Menus\n" +"\n" +"File\n" +"Update <Ctrl+S> " +"This will update the Font Grid with the " +"modified glyph.\n" +"To the right of the Glyph Name field is a " +"button that will do this.\n" +"\n" +"Update and Next <Ctrl+U> " +"This will update the FontGrid with the " +"modified glyph and move to the next glyph.\n" +"\n" +"Update and Previous <Ctrl+B> " +"This will update the FontGrid with the " +"modified glyph and move to the previous glyph.\n" +"\n" +"Close <Ctrl+F4> " +"This will close the Glyph Editor.\n" +"\n" +"Edit\n" +"Reload <Ctrl+L> " +"This will reload the glyph and discard any " +"changes in the glyph.\n" +"\n" +"Copy <Ctrl+C> " +"This will copy the currently selected portion " +"of the bitmap to the Glyph Editor clipboard.\n" +"\n" +"Cut <Ctrl+X> " +"This will copy the currently selected portion " +"of the bitmap to the Glyph Editor clipboard " +"and then delete the selection.\n" +"\n" +"Paste <Ctrl+V> " +"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.\n" +"\n" +"Select All <Ctrl+A> " +"This will select the whole glyph bitmap.\n" +"\n" +"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.\n" +"To the right of the Glyph Name field is a " +"button that will do this.\n" +"\n" +"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.\n" +"To the right of the Glyph Name field is a " +"button that will do this.\n" +"\n" +"If you do not close this editor, it will be updated " +"with Unicode mappings if you move to the next or " +"previous glyph.\n" +"\n" +"Operation\n" +"Draw <Ctrl+D> " +"Change the Glyph Editor into Draw mode.\n" +"\n" +"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.\n" +"\n" +"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.\n" +"\n" +"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.\n" +"\n" +"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.\n" +"\n" +"Embolden <Ctrl+H> " +"This will embolden the current glyph in a simple " +"way within the width of the glyph.\n" +"\n" +"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.\n" +"\n" +"Edit PSF Unicode Mappings <Ctrl+F> " +"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.\n" +"\n" +"Glyph Editor Translations\n" +"\n" +"ButtonDown " +"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.\n" +"\n" +"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.\n" +"\n" +"Shift+Button2Down " +"This will paste the contents of the Glyph Editor " +"clipboard into the Glypheditor at the location " +"of the mouse.\n" +"\n" +"Motion " +"This will continue the operation started with " +"ButtonDown as well as report the current mouse " +"coordinates in Cartesian form relative to the " +"bounding box for the glyph.\n" +"\n" +"ButtonUp " +"This will end the operation started with " +"ButtonDown.\n" +"\n" +"Copy " +"This will copy the selected bitmap to the Glyph " +"Editor clipboard.\n" +"\n" +"Cut " +"This will copy the selected bitmap to the Glyph " +"Editor clipboard and then delete it.\n" +"\n" +"Paste " +"This will paste the Glyph Editor clipboard at " +"the mouse position.\n" +"\n" +"Right " +"This will shift the glyph bitmap toward (but not " +"past) the right edge of the bitmap grid.\n" +"\n" +"Left " +"This will shift the glyph bitmap toward (but not " +"past) the left edge of the bitmap grid.\n" +"\n" +"Up " +"This will shift the glyph bitmap toward (but not " +"past) the top edge of the bitmap grid.\n" +"\n" +"Down " +"This will shift the glyph bitmap toward (but not " +"past) the bottom edge of the bitmap grid.\n" +"\n" +"9 " +"This will rotate the glyph bitmap 90° " +"counter-clockwise.\n" +"\n" +"0 " +"This will rotate the glyph bitmap 90° " +"clockwise.\n" +"\n" +"- " +"This will flip the glyph bitmap around the " +"vertical axis (horizontal flip).\n" +"\n" +"= " +"This will flip the glyph bitmap around the " +"horizontal axis (vertical flip).\n" +"\n" +"comma, Z, or z " +"This will select the previous color or " +"cycle back to the last color.\n" +"\n" +"period, X, or x " +"This will select the next color or cycle " +"to the first color.\n" +"\n" +"Other Metrics Features\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 " +"Preferences->Editing Options tab. The size of the " +"pixel used in the Glypheditor can also be set here. These " +"values affect all Glypheditors.\n" +"\n" +"Other Glyph Editor Features\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 DWIDTH 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."; + +static gchar *conf_text = "" +"gbdfed can be configured using an external\n" +"file. This file is always assumed to be in the\n" +"home directory and is called .gbdfedrc.\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" +"<boolean>\n" +"A <boolean> value can be \"0\", \"false\", \"no\", " +"\"1\", \"true\", or \"yes\". Boolean values are " +"case insensitive.\n" +"\n" +"<labelstring>\n" +"A <labelstring> value is a string used as a label " +"for some of the options.\n" +"\n" +"<atom>\n" +"An <atom> is basically a string.\n" +"\n" +"<cardinal>\n" +"A <cardinal> value is an unsigned 32-bit " +"integer value.\n" +"\n" +"<integer>\n" +"An <integer> is a signed 32-bit integer " +"value.\n" +"\n" +"<property-name>\n" +"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.\n" +"\n" +"<property-type>\n" +"A <property-type> can be one of \"atom\", " +"\"cardinal\", or \"integer\" (see above).\n" +"\n" +"<font-spacing>\n" +"A <font-spacing> value can be one of " +"\"proportional\", \"monowidth\", or " +"\"charactercell\".\n" +"\n" +"If an unknown <font-spacing> value is " +"encountered, the default value is " +"\"proportional\".\n" +"\n" +"<codebase>\n" +"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\".\n" +"\n" +"<translation>\n" +"A <translation> is a valid GUI toolkit translation " +"string.\n" +"\n" +"<filename>\n" +"A <filename> is the name of a file including or " +"excluding a partial or full path to the file.\n" +"\n" +"<eolname>\n" +"An <eolname> value can be one of \"unix\" (^J), " +"\"dos\" (^M^J), or \"mac\" (^M). This value is " +"used when saving BDF fonts.\n" +"\n" +"gbdfed Configuration File Options\n" +"\n" +"code_base <codebase> [default: \"hex\"]\n" +"\n" +"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\".\n" +"\n" +"skip_blank_pages <boolean> [default: \"true\"]\n" +"\n" +"By default, the editor will skip font pages " +"that do not have any glyphs when the \"Next " +"Page\" and \"Previous Page\" buttons are used.\n" +"\n" +"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.\n" +"\n" +"This feature is only available in the configuration " +"file and on the command line.\n" +"\n" +"really_exit <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"This feature is only available in the configuration " +"file and on the command line.\n" +"\n" +"grid_overwrite_mode <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"This feature is toggled using the Preferences dialog. \n" +"\n" +"close_accelerator_text <labelstring> [default: \"Ctrl+F4\"]\n" +"\n" +"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.\n" +"\n" +"This feature is only available in the configuration " +"file.\n" +"\n" +"close_accelerator <translation> [default: \"<Control>F4\"]\n" +"\n" +"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.\n" +"\n" +"This feature is only available in the configuration " +"file.\n" +"\n" +"unicode_name_file <filename>\n" +"\n" +"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.\n" +"\n" +"This feature is set using the \"Preferences->Editing Options\" " +"tab.\n" +"\n" +"adobe_name_file <filename>\n" +"\n" +"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.\n" +"\n" +"This feature is set using the Preferences->Editing Options " +"tab.\n" +"\n" +"pixel_size <integer> [default: \"10\"]\n" +"\n" +"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.\n" +"\n" +"The Glypheditors will always attempt to use this default " +"value first before reducing the size, if reducing the size " +"is needed.\n" +"\n" +"This feature is set using the \"Preferences->Editing Options\" " +"tab.\n" +"\n" +"show_cap_height <boolean> [default: \"false\"]\n" +"\n" +"If the font has the CAP_HEIGHT property defined, " +"this flag will toggle the display of this height " +"in the Glypheditors.\n" +"\n" +"The CAP_HEIGHT is shown as a solid horizontal line " +"above the baseline in the same color as the baseline.\n" +"\n" +"This feature is toggled using the Preferences->Editing Options " +"tab.\n" +"\n" +"show_x_height <boolean> [default: \"false\"]\n" +"\n" +"If the font has the X_HEIGHT property defined, " +"this flag will toggle the display of this height " +"in the Glypheditors.\n" +"\n" +"The X_HEIGHT is shown as a solid horizontal line " +"above the baseline in the same color as the baseline.\n" +"\n" +"This feature is toggled using the Setup->Editing Options " +"tab.\n" +"\n" +"font_grid_horizontal <boolean> [default: \"true\"]\n" +"\n" +"This option determines if the glyphs are displayed " +"horizontally or vertically. The default is to display " +"horizontally.\n" +"\n" +"This default orientation option can only be set in " +"the configuration file at the moment.\n" +"\n" +"power2 <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"generate_sbit_metrics <boolean> [default: \"false\"]\n" +"\n" +"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.\n" +"\n" +"This feature is toggled using the \"Preferences->General Options\" " +"tab.\n" +"\n" +"General Font Configuration Options\n" +"\n" +"make_backups <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"This feature is toggled using the Setup dialog.\n" +"\n" +"correct_metrics <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->General Options " +"tab.\n" +"\n" +"keep_unencoded <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->General Options " +"tab.\n" +"\n" +"keep_comments <boolean> [default: \"true\"]\n" +"\n" +"By default, the editor will keep all " +"comments found in the font file. This " +"allows them to be edited.\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->General Options " +"tab.\n" +"\n" +"pad_character_cells <boolean> [default: \"true\"]\n" +"\n" +"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.\n" +"\n" +"This option is \"true\" by default because " +"that seems to be what most people expect, " +"based on numerous \"charactercell\" fonts that " +"were checked.\n" +"\n" +"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.\n" +"\n" +"In either case, the fonts will display " +"correctly, and metrics calculations should " +"not be affected.\n" +"\n" +"This feature is available on the Preferences->General Options " +"tab.\n" +"\n" +"eol <eolname> [default: \"unix\"]\n" +"\n" +"By default, BDF fonts will be saved with a Unix " +"end-of-line character (^J). This option can be " +"\"unix\", \"dos\", or \"mac\".\n" +"\n" +"This feature is available on the Preferences->General Options " +"tab.\n" +"\n" +"hint_opentype_glyphs <boolean> [default: \"true\"]\n" +"\n" +"By default, importing OpenType fonts will have " +"the glyphs hinted. If this option is set to " +"\"false\", the glyphs will not be hinted.\n" +"\n" +"This feature is available on the Preferences->General Options " +"tab.\n" +"\n" +"point_size <cardinal> [default: \"12\"]\n" +"\n" +"By default, the editor will create new fonts " +"with point size 12.\n" +"\n" +"This feature is available on the Preferences->New Font Options " +"tab.\n" +"\n" +"horizontal_resolution <integer> [default: \"display\"]\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->New Font Options " +"tab.\n" +"\n" +"vertical_resolution <integer> [default: \"display\"]\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->New Font Options " +"tab.\n" +"\n" +"font_spacing <font-spacing> [default: \"proportional\"]\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->New Font Options " +"tab.\n" +"\n" +"bits_per_pixel <integer> [default: \"1\"]\n" +"\n" +"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.\n" +"\n" +"This feature is available on the Preferences->New Font Options " +"tab.\n" +"\n" +"2bpp_grays <integer>...\n" +"\n" +"This parameter supplies a default set of gray values " +"between 0 and 255 for 2 bits per pixel fonts. Four values " +"should be supplied.\n" +"\n" +"4bpp_grays <integer>...\n" +"\n" +"This parameter supplies a default set of gray values " +"between 0 and 255 for 2 bits per pixel fonts. Sixteen values " +"should be supplied.\n" +"\n" +"property <property-name> <property-type>\n" +"\n" +"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.\n" +"\n" +"If an unknown user-defined property is " +"encountered in a font, it always defaults to " +"a <property-type> of \"atom\".\n" +"\n" +"There is no limit to the number of " +"\"property\" options set in the configuration " +"file.\n" +""; + +static gchar *otf_text = "" +"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 Preferences dialog.\n" +"\n" +"The renderer used to import OpenType fonts is " +"available from http://www.freetype.org.\n" +""; + +static gchar *fnt_text = "" +"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 Import All button.\n" +""; + +static gchar *psf_text ="" +"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" +""; + +static gchar *hex_text = "" +"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" +""; + +static gchar *preferences_text = "" +"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" +"Insert Mode or Overwrite Mode\n" +"\n" +"This affects the way glyphs are pasted in the " +"Font Grids.\n" +"\n" +"The default mode, Overwrite, will simply replace " +"everything in the range of the glyphs being pasted " +"from the FONTGRID_CLIPBOARD.\n" +"\n" +"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\".\n" +"\n" +"Correct Metrics, Keep Unencoded, Keep Comments, " +"Pad Character Cells, and EOL.\n" +"\n" +"If Correct Metrics 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.\n" +"\n" +"If Keep Unencoded 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.\n" +"\n" +"If Keep Comments 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.\n" +"\n" +"If Pad Character Cells 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.\n" +"\n" +"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.\n" +"\n" +"Point Size, Horizontal and Vertical Resolution\n" +"\n" +"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.\n" +"\n" +"Proportional, Monowidth, and Character Cell\n" +"\n" +"These set the font spacing type for new fonts " +"created using \"Ctrl+N\".\n" +"\n" +"Bits Per Pixel\n" +"\n" +"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.\n" +"\n" +"Along the bottom are some buttons. These buttons are:\n" +"\n" +"Update\n" +"\n" +"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.\n" +"\n" +"Save Setup\n" +"\n" +"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\".\n" +"\n" +"Color\n" +"\n" +"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.\n" +"\n" +"In the color editor, a button at the bottom toggles " +"between the colors for two and four bits per pixel.\n" +"\n" +"Close\n" +"\n" +"This closes the Setup dialog. If any changes were " +"made and not applied with Update (or saved), then " +"the changes are discarded.\n" +"\n" +"Other Options\n" +"\n" +"This button opens another setup dialog to set " +"more options. The close button at the bottom " +"simply closes the window. These options are:\n" +"\n" +"Hint OpenType Glyphs " +"\n" +"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.\n" +"\n" +"Unicode Glyph Name File " +"\n" +"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.\n" +"\n" +"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.\n" +"\n" +"Adobe Glyph Name File " +"\n" +"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.\n" +"\n" +"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.\n" +"\n" +"Pixel Size " +"\n" +"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.\n" +"\n" +"Show Cap Height and Show X Height " +"\n" +"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.\n" +"\n" +"SBIT Metrics " +"\n" +"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.\n" +""; + +static gchar *color_text = "" +"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" +""; + +static gchar *tips_text = "" +"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" +""; diff --git a/labcon.c b/labcon.c new file mode 100644 index 0000000..2306b21 --- /dev/null +++ b/labcon.c @@ -0,0 +1,461 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "labcon.h" +#include +#include + +static GtkContainerClass *parent_class = 0; + +static void +labcon_size_request(GtkWidget *w, GtkRequisition *req) +{ + LabCon *l = LABCON(w); + guint width = 0; + GtkRequisition l_rec, c_rec; + + l_rec.width = c_rec.width = l_rec.height = c_rec.height = 0; + + if (l->label != 0) { + if (GTK_WIDGET_VISIBLE(l->label)) + gtk_widget_size_request(l->label, &l_rec); + } else { + if (GTK_WIDGET_VISIBLE(l->image)) + gtk_widget_size_request(l->image, &l_rec); + } + if (GTK_WIDGET_VISIBLE(l->child)) + gtk_widget_size_request(l->child, &c_rec); + + if (l->leader) + width = LABCON(l->leader)->label_width; + width = MAX(width, l_rec.width); + + req->height = MAX(l_rec.height, c_rec.height) + + (GTK_CONTAINER(l)->border_width * 2); + req->width = width + c_rec.width + l->spacing + + (GTK_CONTAINER(l)->border_width * 2); +} + +static void +labcon_size_allocate(GtkWidget *w, GtkAllocation *all) +{ + guint i; + GtkWidget *label; + LabCon *leader, *l = LABCON(w); + GtkRequisition l_rec, c_rec, ll_rec; + GtkAllocation w_all; + + w->allocation = *all; + + l_rec.width = c_rec.width = l_rec.height = c_rec.height = 0; + + label = (l->label != 0) ? l->label : l->image; + + if (GTK_WIDGET_VISIBLE(label)) + gtk_widget_get_child_requisition(label, &l_rec); + if (GTK_WIDGET_VISIBLE(l->child)) + gtk_widget_get_child_requisition(l->child, &c_rec); + + /* + * Make sure the height is non-zero and leaves the border. + */ + w_all.x = all->x + GTK_CONTAINER(l)->border_width; + w_all.y = all->y + GTK_CONTAINER(l)->border_width; + w_all.height = MAX(1, (gint) all->height - + (gint) (GTK_CONTAINER(l)->border_width * 2)); + + if (l->leader != 0) { + leader = LABCON(l->leader); + + if (leader->label) + gtk_widget_get_child_requisition(leader->label, &ll_rec); + else + gtk_widget_get_child_requisition(leader->image, &ll_rec); + + if (ll_rec.width < l_rec.width) { + if (leader->label) + gtk_widget_set_size_request(leader->label, + l_rec.width, ll_rec.height); + else + gtk_widget_set_size_request(leader->image, + l_rec.width, ll_rec.height); + } + + leader->label_width = MAX(l_rec.width, leader->label_width); + l_rec.width = leader->label_width; + + for (i = 0; i < leader->group_used - 1; i++) { + if (LABCON(leader->group[i])->label) + gtk_widget_get_child_requisition(LABCON(leader->group[i])->label, + &ll_rec); + else + gtk_widget_get_child_requisition(LABCON(leader->group[i])->image, + &ll_rec); + if (ll_rec.width < l_rec.width) { + if (LABCON(leader->group[i])->label) + gtk_widget_set_size_request(LABCON(leader->group[i])->label, + l_rec.width, ll_rec.height); + else + gtk_widget_set_size_request(LABCON(leader->group[i])->image, + l_rec.width, ll_rec.height); + } + } + } else + l->label_width = l_rec.width; + + if (l->pos == GTK_POS_LEFT) { + /* + * Position the label on the left of the child. + */ + + /* + * Calculate the allocation for the label widget. + */ + w_all.width = l_rec.width; + gtk_widget_size_allocate(label, &w_all); + + /* + * Calculate the allocation for the child widget. The child widget + * is expanded to fill the remaining space. + */ + w_all.x += w_all.width + l->spacing; + w_all.width = all->width - (l_rec.width + l->spacing); + gtk_widget_size_allocate(l->child, &w_all); + } else { + /* + * Position the label on the right of the child. + */ + + /* + * Calculate the allocation for the child widget. The child widget + * is expanded to fill the remaining space. + */ + w_all.width = all->width - (l_rec.width + l->spacing); + gtk_widget_size_allocate(l->child, &w_all); + + /* + * Calculate the allocation for the label widget. + */ + w_all.x += w_all.width + l->spacing; + w_all.width = l_rec.width; + gtk_widget_size_allocate(label, &w_all); + } +} + +static void +labcon_forall(GtkContainer *c, gboolean include_internals, + GtkCallback callback, gpointer callback_data) +{ + LabCon *l = LABCON(c); + + if (l->label) + (*callback)(l->label, callback_data); + else + (*callback)(l->image, callback_data); + (*callback)(l->child, callback_data); +} + +static void +labcon_remove(GtkContainer *c, GtkWidget *w) +{ + guint i; + LabCon *l = LABCON(c); + + /* + * Make sure that the group list has been deallocated so we don't + * leak memory. + */ + if (l->child == w) { + if (l->group_size > 0) { + for (i = 0; i < l->group_used; i++) + LABCON(l->group[i])->leader = 0; + l->group_size = l->group_used = 0; + g_free(l->group); + l->group = 0; + } + } +} + +static void +labcon_class_init(gpointer g_class, gpointer class_data) +{ + GtkWidgetClass *wc = GTK_WIDGET_CLASS(g_class); + LabConClass *lc = LABCON_CLASS(g_class); + GtkContainerClass *cc = GTK_CONTAINER_CLASS(g_class); + + wc->size_request = labcon_size_request; + wc->size_allocate = labcon_size_allocate; + + cc->remove = labcon_remove; + cc->forall = labcon_forall; + + parent_class = g_type_class_peek_parent(lc); +} + +static void +labcon_init(GTypeInstance *instance, gpointer g_class) +{ + LabCon *l = LABCON(instance); + + GTK_WIDGET_SET_FLAGS(l, GTK_NO_WINDOW); + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(l), FALSE); + + l->pixbuf = 0; + l->image = l->label = l->child = 0; + l->spacing = 0; + l->align = LABCON_ALIGN_LEFT; + l->pos = GTK_POS_LEFT; + l->label_width = 0; + l->leader = 0; + l->group_size = l->group_used = 0; +} + +static gboolean +draw_pixbuf(GtkWidget *w, GdkEventExpose *event, gpointer data) +{ + GdkPixbuf *p = GDK_PIXBUF(data); + gint x, y, wd, ht; + + wd = gdk_pixbuf_get_width(p); + ht = gdk_pixbuf_get_height(p); + + x = (w->allocation.width >> 1) - (wd >> 1); + y = (w->allocation.height >> 1) - (ht >> 1); + gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)], + p, 0, 0, x, y, wd, ht, GDK_RGB_DITHER_NONE, 0, 0); + + return FALSE; +} + +/********************************************************************** + * + * API functions. + * + **********************************************************************/ + +GType +labcon_get_type(void) +{ + static GType labcon_type = 0; + + if (!labcon_type) { + static const GTypeInfo labcon_info = { + sizeof (LabConClass), /* class_size */ + 0, /* base_init */ + 0, /* base_finalize */ + labcon_class_init, /* class_init */ + 0, /* class_finalize */ + 0, /* class_data */ + sizeof(LabCon), /* instance_size */ + 0, /* n_preallocs */ + labcon_init, /* instance_init */ + 0, /* value_table */ + }; + + labcon_type = g_type_register_static(GTK_TYPE_CONTAINER, "LabCon", + &labcon_info, 0); + } + + return labcon_type; +} + +GtkWidget * +labcon_new_label(const gchar *label, LabConAlignment align, + GtkPositionType pos, guint spacing, + GtkWidget *child, GtkWidget *group) +{ + LabCon *l, *leader = LABCON(group); + + g_return_val_if_fail(GTK_IS_WIDGET(child), NULL); + if (group) { + g_return_val_if_fail(GTK_IS_WIDGET(group), NULL); + g_return_val_if_fail(IS_LABCON(group), NULL); + } + + l = g_object_new(labcon_get_type(), NULL); + l->pos = pos; + l->spacing = spacing; + l->child = child; + + l->label = gtk_label_new(label); + switch (align) { + case LABCON_ALIGN_LEFT: + gtk_misc_set_alignment(GTK_MISC(l->label), 0.0, 0.5); + break; + case LABCON_ALIGN_RIGHT: + gtk_misc_set_alignment(GTK_MISC(l->label), 1.0, 0.5); + break; + case LABCON_ALIGN_CENTER: + gtk_misc_set_alignment(GTK_MISC(l->label), 0.5, 0.5); + break; + } + + /* + * Go back until we get the group leader. + */ + if (group && LABCON(group)->leader) + l->leader = LABCON(group)->leader; + else + l->leader = group; + + if (l->leader) { + /* + * Add this widget to the group which should have all the same + * width labels. + */ + leader = LABCON(l->leader); + if (leader->group_size == leader->group_used) { + if (leader->group_size == 0) + leader->group = (GtkWidget **) g_malloc(sizeof(GtkWidget *) * 4); + else + leader->group = (GtkWidget **) + g_realloc(leader->group, + sizeof(GtkWidget *) * (leader->group_size + 4)); + leader->group_size += 4; + } + leader->group[leader->group_used++] = GTK_WIDGET(l); + } + + gtk_widget_set_parent(l->label, GTK_WIDGET(l)); + gtk_widget_show(l->label); + if (l->child) { + gtk_widget_set_parent(l->child, GTK_WIDGET(l)); + gtk_widget_show(l->child); + } + + return GTK_WIDGET(l); +} + +GtkWidget * +labcon_new_label_defaults(const gchar *label, GtkWidget *child, + GtkWidget *group) +{ + return labcon_new_label(label, LABCON_ALIGN_RIGHT, GTK_POS_LEFT, 5, + child, group); +} + +GtkWidget * +labcon_new_pixbuf(const GdkPixbuf *pixbuf, LabConAlignment align, + GtkPositionType pos, guint spacing, + GtkWidget *child, GtkWidget *group) +{ + LabCon *l, *leader = LABCON(group); + + g_return_val_if_fail(GTK_IS_WIDGET(child), NULL); + if (group) { + g_return_val_if_fail(GTK_IS_WIDGET(group), NULL); + g_return_val_if_fail(IS_LABCON(group), NULL); + } + + l = g_object_new(labcon_get_type(), NULL); + l->pixbuf = pixbuf; + l->pos = pos; + l->spacing = spacing; + l->child = child; + l->align = align; + + /* + * Make the drawing area just big enough to hold the pixbuf at + * first. + */ + l->image = gtk_drawing_area_new(); + gtk_widget_set_size_request(l->image, + gdk_pixbuf_get_width(l->pixbuf), + gdk_pixbuf_get_height(l->pixbuf)); + g_signal_connect(G_OBJECT(l->image), "expose_event", + G_CALLBACK(draw_pixbuf), (gpointer) l->pixbuf); + + /* + * Go back until we get the group leader. + */ + if (group && LABCON(group)->leader) + l->leader = LABCON(group)->leader; + else + l->leader = group; + + if (l->leader) { + /* + * Add this widget to the group which should have all the same + * width labels or pixbufs. + */ + leader = LABCON(l->leader); + if (leader->group_size == leader->group_used) { + if (leader->group_size == 0) + leader->group = (GtkWidget **) g_malloc(sizeof(GtkWidget *) * 4); + else + leader->group = (GtkWidget **) + g_realloc(leader->group, + sizeof(GtkWidget *) * (leader->group_size + 4)); + leader->group_size += 4; + } + leader->group[leader->group_used++] = GTK_WIDGET(l); + } + + gtk_widget_set_parent(l->image, GTK_WIDGET(l)); + gtk_widget_show(l->image); + if (l->child) { + gtk_widget_set_parent(l->child, GTK_WIDGET(l)); + gtk_widget_show(l->child); + } + + return GTK_WIDGET(l); +} + +GtkWidget * +labcon_new_pixbuf_defaults(const GdkPixbuf *pixbuf, GtkWidget *child, + GtkWidget *group) +{ + return labcon_new_pixbuf(pixbuf, LABCON_ALIGN_RIGHT, GTK_POS_LEFT, 5, + child, group); +} + +const GdkPixbuf * +labcon_get_pixbuf(LabCon *l) +{ + g_return_val_if_fail(IS_LABCON(l), 0); + + return l ? l->pixbuf : 0; +} + +GtkWidget * +labcon_get_image(LabCon *l) +{ + g_return_val_if_fail(IS_LABCON(l), 0); + + return l ? l->image : 0; +} + +GtkWidget * +labcon_get_label(LabCon *l) +{ + g_return_val_if_fail(IS_LABCON(l), 0); + + return l ? l->label : 0; +} + +GtkWidget * +labcon_get_child(LabCon *l) +{ + g_return_val_if_fail(IS_LABCON(l), 0); + + return l ? l->child : 0; +} diff --git a/labcon.h b/labcon.h new file mode 100644 index 0000000..728b83f --- /dev/null +++ b/labcon.h @@ -0,0 +1,139 @@ +/* + * Copyright 2008 Department of Mathematical Sciences, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef _h_labcon +#define _h_labcon + +#include +#include "gtkcompat.h" + +G_BEGIN_DECLS + +/* + * The LabCon (Labeled Container) widget. Created to provide a container + * that contains a label and a single child, and can be part of a group + * of other LabCon widgets. All members of the group have the same label + * width. The labels will be on the left or the right side of the child + * widget and can be aligned to the right, left, or center. + */ + +#define LABCON(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST(obj, labcon_get_type(), LabCon)) +#define LABCON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST(klass, labcon_get_type(), LabConClass)) + +#define IS_LABCON(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE(obj, labcon_get_type())) +#define IS_LABCON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE(klass, labcon_get_type())) + +#define LABCON_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS(obj, labcon_get_type(), LabConClass)) + +typedef struct _LabCon LabCon; +typedef struct _LabConClass LabConClass; + +typedef enum { + LABCON_ALIGN_LEFT, + LABCON_ALIGN_RIGHT, + LABCON_ALIGN_CENTER +} LabConAlignment; + +struct _LabCon { + GtkContainer container; + + GtkWidget *image; + GtkWidget *label; + GtkWidget *child; + + /* + * The pixbuf that will be shown as the image instead of a label. + */ + const GdkPixbuf *pixbuf; + + /* + * Spacing between the label and the child widget. + */ + guint spacing; + + /* + * Alignment. + */ + LabConAlignment align; + + /* + * Label positioning (GTK_POS_LEFT or GTK_POS_RIGHT). + */ + GtkPositionType pos; + + /* + * The current width all labels should be. + */ + guint label_width; + + /* + * All the widgets that should have the same width labels. + */ + GtkWidget *leader; + GtkWidget **group; + guint group_size; + guint group_used; +}; + +struct _LabConClass { + GtkContainerClass parent_class; +}; + +/********************************************************************** + * + * API functions. + * + **********************************************************************/ + +extern GType labcon_get_type(void); + +extern GtkWidget *labcon_new_label(const gchar *label, LabConAlignment align, + GtkPositionType pos, guint spacing, + GtkWidget *child, GtkWidget *group); + +extern GtkWidget *labcon_new_label_defaults(const gchar *label, + GtkWidget *child, + GtkWidget *group); + +extern GtkWidget *labcon_new_pixbuf(const GdkPixbuf *pixbuf, + LabConAlignment align, + GtkPositionType pos, + guint spacing, + GtkWidget *child, + GtkWidget *group); + +extern GtkWidget *labcon_new_pixbuf_defaults(const GdkPixbuf *pixbuf, + GtkWidget *child, + GtkWidget *group); + +extern const GdkPixbuf *labcon_get_pixbuf(LabCon *l); +extern GtkWidget *labcon_get_image(LabCon *l); +extern GtkWidget *labcon_get_label(LabCon *l); +extern GtkWidget *labcon_get_child(LabCon *l); + +G_END_DECLS + +#endif /* _h_labcon */ diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 0000000..f945dbf --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,38 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# 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