From 12b3170e0a2c3d4a1c5ed89b80b3caea2f6a5f9c Mon Sep 17 00:00:00 2001 From: Nils Faerber Date: Sun, 5 May 2013 03:44:32 +0200 Subject: [PATCH] Initial import of upstream V1.6 from http://sofia.nmsu.edu/~mleisher/Software/gbdfed/ by Mark Leisher --- GNUMakefile | 78 + Makefile.in | 152 ++ NEWS | 117 + README | 262 ++ aclocal.m4 | 56 + bdf.c | 7049 ++++++++++++++++++++++++++++++++++++++++++++++++ bdf.h | 727 +++++ bdfP.h | 161 ++ bdfcons.c | 594 +++++ bdffnt.c | 1042 ++++++++ bdfgname.c | 357 +++ bdfgrab.c | 486 ++++ bdfgrid.c | 3402 ++++++++++++++++++++++++ bdfotf.c | 746 ++++++ bdfpkgf.c | 1495 +++++++++++ bdfpsf.c | 971 +++++++ configure | 7086 +++++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 67 + fontgrid.c | 4846 +++++++++++++++++++++++++++++++++ fontgrid.h | 359 +++ gbdfed.c | 2503 +++++++++++++++++ gbdfed.h | 425 +++ gbdfed.man | 711 +++++ gectrl.c | 1655 ++++++++++++ gectrl.h | 210 ++ gectrlbmaps.h | 399 +++ glyphedit.c | 2549 ++++++++++++++++++ glyphedit.h | 337 +++ glyphtest.c | 828 ++++++ glyphtest.h | 128 + grayswatch.c | 331 +++ grayswatch.h | 110 + gtkcompat.h | 37 + guiedit.c | 1371 ++++++++++ guifile.c | 3009 +++++++++++++++++++++ guigedit.c | 2267 ++++++++++++++++ guihelp.c | 396 +++ guiops.c | 409 +++ guipref.c | 1161 ++++++++ guiutil.c | 230 ++ hbf.c | 1590 +++++++++++ hbf.h | 216 ++ htext.h | 1438 ++++++++++ labcon.c | 461 ++++ labcon.h | 139 + mkinstalldirs | 38 + 46 files changed, 53001 insertions(+) create mode 100644 GNUMakefile create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 aclocal.m4 create mode 100644 bdf.c create mode 100644 bdf.h create mode 100644 bdfP.h create mode 100644 bdfcons.c create mode 100644 bdffnt.c create mode 100644 bdfgname.c create mode 100644 bdfgrab.c create mode 100644 bdfgrid.c create mode 100644 bdfotf.c create mode 100644 bdfpkgf.c create mode 100644 bdfpsf.c create mode 100755 configure create mode 100644 configure.in create mode 100644 fontgrid.c create mode 100644 fontgrid.h create mode 100644 gbdfed.c create mode 100644 gbdfed.h create mode 100644 gbdfed.man create mode 100644 gectrl.c create mode 100644 gectrl.h create mode 100644 gectrlbmaps.h create mode 100644 glyphedit.c create mode 100644 glyphedit.h create mode 100644 glyphtest.c create mode 100644 glyphtest.h create mode 100644 grayswatch.c create mode 100644 grayswatch.h create mode 100644 gtkcompat.h create mode 100644 guiedit.c create mode 100644 guifile.c create mode 100644 guigedit.c create mode 100644 guihelp.c create mode 100644 guiops.c create mode 100644 guipref.c create mode 100644 guiutil.c create mode 100644 hbf.c create mode 100644 hbf.h create mode 100644 htext.h create mode 100644 labcon.c create mode 100644 labcon.h create mode 100755 mkinstalldirs 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 -- 2.39.2